Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(336)

Side by Side Diff: runtime/lib/fraction.dart

Issue 2005723004: Fraction class prototype and test (not to be committed). (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: work in progress Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/lib/double.dart ('k') | runtime/lib/integers.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 // TODO(regis): Move abstract class fraction to sdk/lib/core/fraction.dart:
6 abstract class fraction extends num {
7 // TODO(regis): In order to support 0r here, we need a C++ constructor.
8 // static const fraction NAN = 0r / 0r;
9 static const fraction NAN = const _Fraction(0, 0);
10 static const fraction INFINITY = const _Fraction(1, 0);
11 static const fraction NEGATIVE_INFINITY = -INFINITY;
12 // TODO(regis): Expand as needed with API documentation.
13 }
14
15 class _Fraction implements fraction {
16 final int _numerator;
17 final int _denominator;
18 // Ideally, we want to keep the denominator at 100 and the numerator a Smi,
19 // which works well when expressing pixels as percent.
20 static const fraction _ONE = const _Fraction._percent(100);
21 static const fraction _MINUS_ONE = const _Fraction._percent(-100);
22
23 const _Fraction(int this._numerator, int this._denominator);
24 const _Fraction._percent(int this._numerator) : _denominator = 100;
25
26 factory _Fraction._reduce(int numerator, int denominator) {
27 // No check for zero _numerator and zero _denominator, representing NaN.
28 // Do not reduce if denominator is 100, 1, or 0.
29 if (denominator != 100 && denominator != 1 && denominator != 0) {
30 if (denominator < 0) {
31 // Comparison operations assume a positive denominator.
32 numerator = -numerator;
33 denominator = -denominator;
34 }
35 while (denominator != 100 && numerator.isEven && denominator.isEven) {
36 numerator >>= 1;
37 denominator >>= 1;
38 }
39 if (denominator != 100 && denominator != 1 && numerator.abs() != 1) {
40 var gcd = numerator.gcd(denominator);
41 if (gcd != 1) {
42 numerator ~/= gcd;
43 denominator ~/= gcd;
44 }
45 }
46 }
47 return new _Fraction(numerator, denominator);
48 }
49
50 factory _Fraction.fromInteger(int value) => new _Fraction._percent(100*value);
51 factory _Fraction.fromDouble(double value) => value.toFraction();
52
53 factory _Fraction.fromRationalLiteral(String value) {
54 final decimalPoint = ".".codeUnits.first;
55 final digit0 = "0".codeUnits.first;
56 final digit9 = "9".codeUnits.first;
57 int numerator = 0;
58 int denominator = 1;
59 bool seenDecimalPoint = false;
60 for (var i = 0; i < value.length; i++) {
61 var c = value.codeUnitAt(i);
62 if (c == decimalPoint) {
63 seenDecimalPoint = true;
64 } else if (digit0 <= c && c <= digit9) {
65 numerator = numerator*10 + (c - digit0);
66 if (seenDecimalPoint) denominator *= 10;
67 } else {
68 break;
69 }
70 }
71 // Try to work with percent.
72 while (denominator < 100) {
73 numerator *= 10;
74 denominator *= 10;
75 }
76 return new _Fraction._reduce(numerator, denominator);
77 }
78
79 Type get runtimeType => fraction;
80
81 int get _identityHashCode {
82 return _numerator ^ _denominator;
83 }
84
85 num operator +(num other) {
86 return other._toDoubleOrFraction()._addFromFraction(this);
87 }
88 fraction _addFromFraction(fraction other) {
89 return other._add(this);
90 }
91 fraction _add(fraction other) {
92 if ((_denominator == 100) && (other._denominator == 100)) {
93 return new _Fraction._percent(_numerator + other._numerator);
94 }
95 return new _Fraction._reduce(
96 (_numerator * other._denominator) + (other._numerator * _denominator),
97 _denominator * other._denominator);
98 }
99
100 num operator -(num other) {
101 return other._toDoubleOrFraction()._subFromFraction(this);
102 }
103 fraction _subFromFraction(fraction other) {
104 return other._sub(this);
105 }
106 fraction _sub(fraction other) {
107 if ((_denominator == 100) && (other._denominator == 100)) {
108 return new _Fraction._percent(_numerator - other._numerator);
109 }
110 return new _Fraction._reduce(
111 (_numerator * other._denominator) - (other._numerator * _denominator),
112 _denominator * other._denominator);
113 }
114
115 num operator *(num other) {
116 return other._toDoubleOrFraction()._mulFromFraction(this);
117 }
118 fraction _mulFromFraction(fraction other) {
119 return other._mul(this);
120 }
121 fraction _mul(fraction other) {
122 return new _Fraction._reduce(_numerator * other._numerator,
123 _denominator * other._denominator);
124 }
125
126 num operator /(num other) {
127 return other._toDoubleOrFraction()._divFromFraction(this);
128 }
129 fraction _divFromFraction(fraction other) {
130 return other._div(this);
131 }
132 fraction _div(fraction other) {
133 return new _Fraction._reduce(_numerator * other._denominator,
134 _denominator * other._numerator);
135 }
136
137 num operator %(num other) {
138 return other._toDoubleOrFraction()._moduloFromFraction(this);
139 }
140 fraction _moduloFromFraction(fraction other) {
141 return other._modulo(this);
142 }
143 fraction _modulo(fraction other) {
144 var q = _div(other);
145 if (other._numerator < 0) {
146 q = q.ceil();
147 } else {
148 q = q.floor();
149 }
150 return _sub(q.toFraction()._mul(other));
151 }
152
153 int operator ~/(num other) {
154 return other._toDoubleOrFraction()._truncDivFromFraction(this);
155 }
156 int _truncDivFromFraction(fraction other) {
157 return other._truncDiv(this);
158 }
159 int _truncDiv(fraction other) {
160 var d = _denominator * other._numerator;
161 if (d == 0) throw const IntegerDivisionByZeroException();
162 return (_numerator * other._denominator) ~/ d;
163 }
164
165 num remainder(num other) {
166 return other._toDoubleOrFraction()._remainderFromFraction(this);
167 }
168 fraction _remainderFromFraction(fraction other) {
169 return other._remainder(this);
170 }
171 fraction _remainder(fraction other) {
172 return new _Fraction._reduce(
173 (_numerator * other._denominator) -
174 (_truncDiv(other) * other._numerator * _denominator),
175 _denominator * other._denominator);
176 }
177
178 fraction operator -() {
179 return new _Fraction(-_numerator, _denominator);
180 }
181
182 bool operator ==(other) {
183 if (!(other is num)) return false;
184 return other._equalToFraction(this);
185 }
186 bool _equalToFraction(fraction other) {
187 return other._equal(this);
188 }
189 bool _equal(fraction other) {
190 return
191 (_numerator * other._denominator) == (_denominator * other._numerator);
192 }
193
194 bool _equalToInteger(int other) {
195 return _numerator == (_denominator * other);
196 }
197
198 bool operator <(num other) {
199 return other > this;
200 }
201 bool operator >(num other) {
202 return other._greaterThanFromFraction(this);
203 }
204 bool _greaterThanFromFraction(fraction other) {
205 return
206 (other._numerator * _denominator) > (other._denominator * _numerator);
207 }
208 bool operator >=(num other) {
209 return (this == other) || (this > other);
210 }
211 bool operator <=(num other) {
212 return (this == other) || (this < other);
213 }
214
215 fraction _addFromInteger(int other) {
216 return new _Fraction((other * _denominator) + _numerator, _denominator);
217 }
218
219 fraction _subFromInteger(int other) {
220 return new _Fraction((other * _denominator) - _numerator, _denominator);
221 }
222
223 fraction _mulFromInteger(int other) {
224 return new _Fraction(other * _numerator, _denominator);
225 }
226
227 fraction _divFromInteger(int other) {
228 // We return infinity for a zero _numerator instead of throwing.
229 return new _Fraction._reduce(other * _denominator, _numerator);
230 }
231
232 fraction _moduloFromInteger(int other) {
233 var q = _divFromInteger(other);
234 if (_numerator < 0) {
235 q = q.ceil();
236 } else {
237 q = q.floor(); // Will throw for a zero _numerator (zero q._denominator).
238 }
239 return _mul(q.toFraction())._subFromInteger(other);
240 }
241
242 int _truncDivFromInteger(int other) {
243 if (_numerator == 0) throw const IntegerDivisionByZeroException();
244 return (other * _denominator) ~/ _numerator;
245 }
246
247 fraction _remainderFromInteger(int other) {
248 if (_numerator == 0) throw const IntegerDivisionByZeroException();
249 var od = other * _denominator;
250 return new _Fraction(od - ((od ~/ _numerator) * _numerator), _denominator);
251 }
252
253 bool _greaterThanFromInteger(int other) {
254 return (other * _denominator) > _numerator;
255 }
256
257 bool get isNegative {
258 return _numerator < 0; // _denominator is always >= 0.
259 }
260
261 bool get isInfinite => (_numerator != 0) && (_denominator == 0);
262 bool get isNaN => (_numerator == 0) && (_denominator == 0);
263 bool get isFinite => _denominator != 0;
264
265 fraction abs() {
266 return isNegative ? -this : this;
267 }
268
269 fraction get sign {
270 if (_numerator > 0) return _ONE;
271 if (_numerator < 0) return _MINUS_ONE;
272 return this;
273 }
274
275 int round() {
276 if (_denominator == 0) throw new UnsupportedError("Must be finite");
277 if (_denominator == 1) return _numerator;
278 if (_numerator > 0) {
279 // Add 1/2.
280 return ((_numerator << 1) + _denominator) ~/ (_denominator << 1);
281 }
282 if (_numerator < 0) {
283 // Sub 1/2.
284 return ((_numerator << 1) - _denominator) ~/ (_denominator << 1);
285 }
286 return 0;
287 }
288
289 int floor() {
290 if (_denominator == 0) throw new UnsupportedError("Must be finite");
291 if (_denominator == 1) return _numerator;
292 if (_numerator > 0) return _numerator ~/ _denominator;
293 if (_numerator < 0) return (_numerator - _denominator + 1) ~/ _denominator;
294 return 0;
295 }
296
297 int ceil() {
298 if (_denominator == 0) throw new UnsupportedError("Must be finite");
299 if (_denominator == 1) return _numerator;
300 if (_numerator > 0) return (_numerator + _denominator - 1) ~/ _denominator;
301 if (_numerator < 0) return _numerator ~/ _denominator;
302 return 0;
303 }
304
305 int truncate() {
306 if (_denominator == 0) throw new UnsupportedError("Must be finite");
307 return _numerator ~/ _denominator;
308 }
309
310 double roundToDouble() {
311 if (_denominator == 0) {
312 if (_numerator > 0) {
313 return double.INFINITY;
314 } else {
315 return double.NEGATIVE_INFINITY;
316 }
317 }
318 return round().toDouble();
319 }
320
321 double floorToDouble() {
322 if (_denominator == 0) {
323 if (_numerator > 0) {
324 return double.INFINITY;
325 } else {
326 return double.NEGATIVE_INFINITY;
327 }
328 }
329 return floor().toDouble();
330 }
331
332 double ceilToDouble() {
333 if (_denominator == 0) {
334 if (_numerator > 0) {
335 return double.INFINITY;
336 } else {
337 return double.NEGATIVE_INFINITY;
338 }
339 }
340 return ceil().toDouble();
341 }
342
343 double truncateToDouble() {
344 if (_denominator == 0) {
345 if (_numerator > 0) {
346 return double.INFINITY;
347 } else {
348 return double.NEGATIVE_INFINITY;
349 }
350 }
351 return truncate().toDouble();
352 }
353
354 num clamp(num lowerLimit, num upperLimit) {
355 if (lowerLimit is! num) {
356 throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number");
357 }
358 if (upperLimit is! num) {
359 throw new ArgumentError.value(upperLimit, "upperLimit", "not a number");
360 }
361
362 if (lowerLimit.compareTo(upperLimit) > 0) {
363 throw new ArgumentError(lowerLimit);
364 }
365 if (lowerLimit.isNaN) return lowerLimit;
366 if (this.compareTo(lowerLimit) < 0) return lowerLimit;
367 if (this.compareTo(upperLimit) > 0) return upperLimit;
368 return this;
369 }
370
371 int toInt() {
372 if (_denominator == 0) throw new UnsupportedError("Must be finite");
373 return _numerator ~/ _denominator;
374 }
375
376 double toDouble() { return _numerator / _denominator; }
377 fraction toFraction() { return this; }
378 fraction toPercent() { return new _Fraction._percent((this*100).toInt()); }
379 fraction _toDoubleOrFraction() { return this; }
380 num _toBigintIfInteger() { return this; }
381
382 String toString() => "$_numerator/${_denominator}r (${toDouble()})"; // DEBUG
383
384 String toStringAsFixed(int fractionDigits) {
385 return this.toDouble().toStringAsFixed(fractionDigits);
386 }
387 String toStringAsExponential([int fractionDigits]) {
388 return this.toDouble().toStringAsExponential(fractionDigits);
389 }
390 String toStringAsPrecision(int precision) {
391 return this.toDouble().toStringAsPrecision(precision);
392 }
393
394 int compareTo(num other) {
395 const int EQUAL = 0, LESS = -1, GREATER = 1;
396 if (isNaN) {
397 return GREATER; // TODO(regis): Correct?
398 }
399 if (other is double) {
400 double d = other;
401 if (d.isInfinite) {
402 if (isInfinite) {
403 if (((d == double.NEGATIVE_INFINITY) && (_numerator < 0)) ||
404 ((d == double.INFINITY) && (_numerator > 0))) {
405 return EQUAL;
406 }
407 }
408 return _numerator > 0 ? GREATER : LESS;
409 }
410 if (d.isNaN) {
411 return LESS; // TODO(regis): unless this.isNaN? See above.
412 }
413 if ((d == 0.0) && (_numerator == 0)) {
414 return d.isNegative ? GREATER : EQUAL;
415 }
416 other = other.toFraction();
417 }
418 if (this < other) {
419 return LESS;
420 } else if (this > other) {
421 return GREATER;
422 } else {
423 return EQUAL;
424 }
425 }
426 }
427
OLDNEW
« no previous file with comments | « runtime/lib/double.dart ('k') | runtime/lib/integers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698