OLD | NEW |
(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 |
OLD | NEW |