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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/lib/double.dart ('k') | runtime/lib/integers.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/lib/fraction.dart
diff --git a/runtime/lib/fraction.dart b/runtime/lib/fraction.dart
new file mode 100644
index 0000000000000000000000000000000000000000..8d52b2957febde56e93ac1e7ca56bb0747d63d63
--- /dev/null
+++ b/runtime/lib/fraction.dart
@@ -0,0 +1,427 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(regis): Move abstract class fraction to sdk/lib/core/fraction.dart:
+abstract class fraction extends num {
+ // TODO(regis): In order to support 0r here, we need a C++ constructor.
+ // static const fraction NAN = 0r / 0r;
+ static const fraction NAN = const _Fraction(0, 0);
+ static const fraction INFINITY = const _Fraction(1, 0);
+ static const fraction NEGATIVE_INFINITY = -INFINITY;
+ // TODO(regis): Expand as needed with API documentation.
+}
+
+class _Fraction implements fraction {
+ final int _numerator;
+ final int _denominator;
+ // Ideally, we want to keep the denominator at 100 and the numerator a Smi,
+ // which works well when expressing pixels as percent.
+ static const fraction _ONE = const _Fraction._percent(100);
+ static const fraction _MINUS_ONE = const _Fraction._percent(-100);
+
+ const _Fraction(int this._numerator, int this._denominator);
+ const _Fraction._percent(int this._numerator) : _denominator = 100;
+
+ factory _Fraction._reduce(int numerator, int denominator) {
+ // No check for zero _numerator and zero _denominator, representing NaN.
+ // Do not reduce if denominator is 100, 1, or 0.
+ if (denominator != 100 && denominator != 1 && denominator != 0) {
+ if (denominator < 0) {
+ // Comparison operations assume a positive denominator.
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+ while (denominator != 100 && numerator.isEven && denominator.isEven) {
+ numerator >>= 1;
+ denominator >>= 1;
+ }
+ if (denominator != 100 && denominator != 1 && numerator.abs() != 1) {
+ var gcd = numerator.gcd(denominator);
+ if (gcd != 1) {
+ numerator ~/= gcd;
+ denominator ~/= gcd;
+ }
+ }
+ }
+ return new _Fraction(numerator, denominator);
+ }
+
+ factory _Fraction.fromInteger(int value) => new _Fraction._percent(100*value);
+ factory _Fraction.fromDouble(double value) => value.toFraction();
+
+ factory _Fraction.fromRationalLiteral(String value) {
+ final decimalPoint = ".".codeUnits.first;
+ final digit0 = "0".codeUnits.first;
+ final digit9 = "9".codeUnits.first;
+ int numerator = 0;
+ int denominator = 1;
+ bool seenDecimalPoint = false;
+ for (var i = 0; i < value.length; i++) {
+ var c = value.codeUnitAt(i);
+ if (c == decimalPoint) {
+ seenDecimalPoint = true;
+ } else if (digit0 <= c && c <= digit9) {
+ numerator = numerator*10 + (c - digit0);
+ if (seenDecimalPoint) denominator *= 10;
+ } else {
+ break;
+ }
+ }
+ // Try to work with percent.
+ while (denominator < 100) {
+ numerator *= 10;
+ denominator *= 10;
+ }
+ return new _Fraction._reduce(numerator, denominator);
+ }
+
+ Type get runtimeType => fraction;
+
+ int get _identityHashCode {
+ return _numerator ^ _denominator;
+ }
+
+ num operator +(num other) {
+ return other._toDoubleOrFraction()._addFromFraction(this);
+ }
+ fraction _addFromFraction(fraction other) {
+ return other._add(this);
+ }
+ fraction _add(fraction other) {
+ if ((_denominator == 100) && (other._denominator == 100)) {
+ return new _Fraction._percent(_numerator + other._numerator);
+ }
+ return new _Fraction._reduce(
+ (_numerator * other._denominator) + (other._numerator * _denominator),
+ _denominator * other._denominator);
+ }
+
+ num operator -(num other) {
+ return other._toDoubleOrFraction()._subFromFraction(this);
+ }
+ fraction _subFromFraction(fraction other) {
+ return other._sub(this);
+ }
+ fraction _sub(fraction other) {
+ if ((_denominator == 100) && (other._denominator == 100)) {
+ return new _Fraction._percent(_numerator - other._numerator);
+ }
+ return new _Fraction._reduce(
+ (_numerator * other._denominator) - (other._numerator * _denominator),
+ _denominator * other._denominator);
+ }
+
+ num operator *(num other) {
+ return other._toDoubleOrFraction()._mulFromFraction(this);
+ }
+ fraction _mulFromFraction(fraction other) {
+ return other._mul(this);
+ }
+ fraction _mul(fraction other) {
+ return new _Fraction._reduce(_numerator * other._numerator,
+ _denominator * other._denominator);
+ }
+
+ num operator /(num other) {
+ return other._toDoubleOrFraction()._divFromFraction(this);
+ }
+ fraction _divFromFraction(fraction other) {
+ return other._div(this);
+ }
+ fraction _div(fraction other) {
+ return new _Fraction._reduce(_numerator * other._denominator,
+ _denominator * other._numerator);
+ }
+
+ num operator %(num other) {
+ return other._toDoubleOrFraction()._moduloFromFraction(this);
+ }
+ fraction _moduloFromFraction(fraction other) {
+ return other._modulo(this);
+ }
+ fraction _modulo(fraction other) {
+ var q = _div(other);
+ if (other._numerator < 0) {
+ q = q.ceil();
+ } else {
+ q = q.floor();
+ }
+ return _sub(q.toFraction()._mul(other));
+ }
+
+ int operator ~/(num other) {
+ return other._toDoubleOrFraction()._truncDivFromFraction(this);
+ }
+ int _truncDivFromFraction(fraction other) {
+ return other._truncDiv(this);
+ }
+ int _truncDiv(fraction other) {
+ var d = _denominator * other._numerator;
+ if (d == 0) throw const IntegerDivisionByZeroException();
+ return (_numerator * other._denominator) ~/ d;
+ }
+
+ num remainder(num other) {
+ return other._toDoubleOrFraction()._remainderFromFraction(this);
+ }
+ fraction _remainderFromFraction(fraction other) {
+ return other._remainder(this);
+ }
+ fraction _remainder(fraction other) {
+ return new _Fraction._reduce(
+ (_numerator * other._denominator) -
+ (_truncDiv(other) * other._numerator * _denominator),
+ _denominator * other._denominator);
+ }
+
+ fraction operator -() {
+ return new _Fraction(-_numerator, _denominator);
+ }
+
+ bool operator ==(other) {
+ if (!(other is num)) return false;
+ return other._equalToFraction(this);
+ }
+ bool _equalToFraction(fraction other) {
+ return other._equal(this);
+ }
+ bool _equal(fraction other) {
+ return
+ (_numerator * other._denominator) == (_denominator * other._numerator);
+ }
+
+ bool _equalToInteger(int other) {
+ return _numerator == (_denominator * other);
+ }
+
+ bool operator <(num other) {
+ return other > this;
+ }
+ bool operator >(num other) {
+ return other._greaterThanFromFraction(this);
+ }
+ bool _greaterThanFromFraction(fraction other) {
+ return
+ (other._numerator * _denominator) > (other._denominator * _numerator);
+ }
+ bool operator >=(num other) {
+ return (this == other) || (this > other);
+ }
+ bool operator <=(num other) {
+ return (this == other) || (this < other);
+ }
+
+ fraction _addFromInteger(int other) {
+ return new _Fraction((other * _denominator) + _numerator, _denominator);
+ }
+
+ fraction _subFromInteger(int other) {
+ return new _Fraction((other * _denominator) - _numerator, _denominator);
+ }
+
+ fraction _mulFromInteger(int other) {
+ return new _Fraction(other * _numerator, _denominator);
+ }
+
+ fraction _divFromInteger(int other) {
+ // We return infinity for a zero _numerator instead of throwing.
+ return new _Fraction._reduce(other * _denominator, _numerator);
+ }
+
+ fraction _moduloFromInteger(int other) {
+ var q = _divFromInteger(other);
+ if (_numerator < 0) {
+ q = q.ceil();
+ } else {
+ q = q.floor(); // Will throw for a zero _numerator (zero q._denominator).
+ }
+ return _mul(q.toFraction())._subFromInteger(other);
+ }
+
+ int _truncDivFromInteger(int other) {
+ if (_numerator == 0) throw const IntegerDivisionByZeroException();
+ return (other * _denominator) ~/ _numerator;
+ }
+
+ fraction _remainderFromInteger(int other) {
+ if (_numerator == 0) throw const IntegerDivisionByZeroException();
+ var od = other * _denominator;
+ return new _Fraction(od - ((od ~/ _numerator) * _numerator), _denominator);
+ }
+
+ bool _greaterThanFromInteger(int other) {
+ return (other * _denominator) > _numerator;
+ }
+
+ bool get isNegative {
+ return _numerator < 0; // _denominator is always >= 0.
+ }
+
+ bool get isInfinite => (_numerator != 0) && (_denominator == 0);
+ bool get isNaN => (_numerator == 0) && (_denominator == 0);
+ bool get isFinite => _denominator != 0;
+
+ fraction abs() {
+ return isNegative ? -this : this;
+ }
+
+ fraction get sign {
+ if (_numerator > 0) return _ONE;
+ if (_numerator < 0) return _MINUS_ONE;
+ return this;
+ }
+
+ int round() {
+ if (_denominator == 0) throw new UnsupportedError("Must be finite");
+ if (_denominator == 1) return _numerator;
+ if (_numerator > 0) {
+ // Add 1/2.
+ return ((_numerator << 1) + _denominator) ~/ (_denominator << 1);
+ }
+ if (_numerator < 0) {
+ // Sub 1/2.
+ return ((_numerator << 1) - _denominator) ~/ (_denominator << 1);
+ }
+ return 0;
+ }
+
+ int floor() {
+ if (_denominator == 0) throw new UnsupportedError("Must be finite");
+ if (_denominator == 1) return _numerator;
+ if (_numerator > 0) return _numerator ~/ _denominator;
+ if (_numerator < 0) return (_numerator - _denominator + 1) ~/ _denominator;
+ return 0;
+ }
+
+ int ceil() {
+ if (_denominator == 0) throw new UnsupportedError("Must be finite");
+ if (_denominator == 1) return _numerator;
+ if (_numerator > 0) return (_numerator + _denominator - 1) ~/ _denominator;
+ if (_numerator < 0) return _numerator ~/ _denominator;
+ return 0;
+ }
+
+ int truncate() {
+ if (_denominator == 0) throw new UnsupportedError("Must be finite");
+ return _numerator ~/ _denominator;
+ }
+
+ double roundToDouble() {
+ if (_denominator == 0) {
+ if (_numerator > 0) {
+ return double.INFINITY;
+ } else {
+ return double.NEGATIVE_INFINITY;
+ }
+ }
+ return round().toDouble();
+ }
+
+ double floorToDouble() {
+ if (_denominator == 0) {
+ if (_numerator > 0) {
+ return double.INFINITY;
+ } else {
+ return double.NEGATIVE_INFINITY;
+ }
+ }
+ return floor().toDouble();
+ }
+
+ double ceilToDouble() {
+ if (_denominator == 0) {
+ if (_numerator > 0) {
+ return double.INFINITY;
+ } else {
+ return double.NEGATIVE_INFINITY;
+ }
+ }
+ return ceil().toDouble();
+ }
+
+ double truncateToDouble() {
+ if (_denominator == 0) {
+ if (_numerator > 0) {
+ return double.INFINITY;
+ } else {
+ return double.NEGATIVE_INFINITY;
+ }
+ }
+ return truncate().toDouble();
+ }
+
+ num clamp(num lowerLimit, num upperLimit) {
+ if (lowerLimit is! num) {
+ throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number");
+ }
+ if (upperLimit is! num) {
+ throw new ArgumentError.value(upperLimit, "upperLimit", "not a number");
+ }
+
+ if (lowerLimit.compareTo(upperLimit) > 0) {
+ throw new ArgumentError(lowerLimit);
+ }
+ if (lowerLimit.isNaN) return lowerLimit;
+ if (this.compareTo(lowerLimit) < 0) return lowerLimit;
+ if (this.compareTo(upperLimit) > 0) return upperLimit;
+ return this;
+ }
+
+ int toInt() {
+ if (_denominator == 0) throw new UnsupportedError("Must be finite");
+ return _numerator ~/ _denominator;
+ }
+
+ double toDouble() { return _numerator / _denominator; }
+ fraction toFraction() { return this; }
+ fraction toPercent() { return new _Fraction._percent((this*100).toInt()); }
+ fraction _toDoubleOrFraction() { return this; }
+ num _toBigintIfInteger() { return this; }
+
+ String toString() => "$_numerator/${_denominator}r (${toDouble()})"; // DEBUG
+
+ String toStringAsFixed(int fractionDigits) {
+ return this.toDouble().toStringAsFixed(fractionDigits);
+ }
+ String toStringAsExponential([int fractionDigits]) {
+ return this.toDouble().toStringAsExponential(fractionDigits);
+ }
+ String toStringAsPrecision(int precision) {
+ return this.toDouble().toStringAsPrecision(precision);
+ }
+
+ int compareTo(num other) {
+ const int EQUAL = 0, LESS = -1, GREATER = 1;
+ if (isNaN) {
+ return GREATER; // TODO(regis): Correct?
+ }
+ if (other is double) {
+ double d = other;
+ if (d.isInfinite) {
+ if (isInfinite) {
+ if (((d == double.NEGATIVE_INFINITY) && (_numerator < 0)) ||
+ ((d == double.INFINITY) && (_numerator > 0))) {
+ return EQUAL;
+ }
+ }
+ return _numerator > 0 ? GREATER : LESS;
+ }
+ if (d.isNaN) {
+ return LESS; // TODO(regis): unless this.isNaN? See above.
+ }
+ if ((d == 0.0) && (_numerator == 0)) {
+ return d.isNegative ? GREATER : EQUAL;
+ }
+ other = other.toFraction();
+ }
+ if (this < other) {
+ return LESS;
+ } else if (this > other) {
+ return GREATER;
+ } else {
+ return EQUAL;
+ }
+ }
+}
+
« 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