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