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; |
+ } |
+ } |
+} |
+ |