| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart._interceptors; | 5 part of dart._interceptors; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * The implementation of Dart's int & double methods. | 8 * The implementation of Dart's int & double methods. |
| 9 * These are made available as extension methods on `Number` in JS. | 9 * These are made available as extension methods on `Number` in JS. |
| 10 */ | 10 */ |
| 11 @JsPeerInterface(name: 'Number') | 11 @JsPeerInterface(name: 'Number') |
| 12 class JSNumber extends Interceptor implements int, double { | 12 class JSNumber extends Interceptor implements int, double { |
| 13 const JSNumber(); | 13 const JSNumber(); |
| 14 | 14 |
| 15 int compareTo(num b) { | 15 @notNull |
| 16 int compareTo(@nullCheck num b) { |
| 16 if (this < b) { | 17 if (this < b) { |
| 17 return -1; | 18 return -1; |
| 18 } else if (this > b) { | 19 } else if (this > b) { |
| 19 return 1; | 20 return 1; |
| 20 } else if (this == b) { | 21 } else if (this == b) { |
| 21 if (this == 0) { | 22 if (this == 0) { |
| 22 bool bIsNegative = b.isNegative; | 23 bool bIsNegative = b.isNegative; |
| 23 if (isNegative == bIsNegative) return 0; | 24 if (isNegative == bIsNegative) return 0; |
| 24 if (isNegative) return -1; | 25 if (isNegative) return -1; |
| 25 return 1; | 26 return 1; |
| 26 } | 27 } |
| 27 return 0; | 28 return 0; |
| 28 } else if (isNaN) { | 29 } else if (isNaN) { |
| 29 if (b.isNaN) { | 30 if (b.isNaN) { |
| 30 return 0; | 31 return 0; |
| 31 } | 32 } |
| 32 return 1; | 33 return 1; |
| 33 } else { | 34 } else { |
| 34 return -1; | 35 return -1; |
| 35 } | 36 } |
| 36 } | 37 } |
| 37 | 38 |
| 39 @notNull |
| 38 bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; | 40 bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; |
| 39 | 41 |
| 42 @notNull |
| 40 bool get isNaN => JS('bool', r'isNaN(#)', this); | 43 bool get isNaN => JS('bool', r'isNaN(#)', this); |
| 41 | 44 |
| 45 @notNull |
| 42 bool get isInfinite { | 46 bool get isInfinite { |
| 43 return JS('bool', r'# == (1/0)', this) || JS('bool', r'# == (-1/0)', this); | 47 return JS('bool', r'# == (1/0)', this) || JS('bool', r'# == (-1/0)', this); |
| 44 } | 48 } |
| 45 | 49 |
| 50 @notNull |
| 46 bool get isFinite => JS('bool', r'isFinite(#)', this); | 51 bool get isFinite => JS('bool', r'isFinite(#)', this); |
| 47 | 52 |
| 48 JSNumber remainder(num b) { | 53 @notNull |
| 49 if (b is! num) throw argumentErrorValue(b); | 54 JSNumber remainder(@nullCheck num b) { |
| 50 return JS('num', r'# % #', this, b); | 55 return JS('num', r'# % #', this, b); |
| 51 } | 56 } |
| 52 | 57 |
| 58 @notNull |
| 53 JSNumber abs() => JS('num', r'Math.abs(#)', this); | 59 JSNumber abs() => JS('num', r'Math.abs(#)', this); |
| 54 | 60 |
| 61 @notNull |
| 55 JSNumber get sign => this > 0 ? 1 : this < 0 ? -1 : this; | 62 JSNumber get sign => this > 0 ? 1 : this < 0 ? -1 : this; |
| 56 | 63 |
| 64 @notNull |
| 57 static const int _MIN_INT32 = -0x80000000; | 65 static const int _MIN_INT32 = -0x80000000; |
| 66 @notNull |
| 58 static const int _MAX_INT32 = 0x7FFFFFFF; | 67 static const int _MAX_INT32 = 0x7FFFFFFF; |
| 59 | 68 |
| 69 @notNull |
| 60 int toInt() { | 70 int toInt() { |
| 61 if (this >= _MIN_INT32 && this <= _MAX_INT32) { | 71 if (this >= _MIN_INT32 && this <= _MAX_INT32) { |
| 62 return JS('int', '# | 0', this); | 72 return JS('int', '# | 0', this); |
| 63 } | 73 } |
| 64 if (JS('bool', r'isFinite(#)', this)) { | 74 if (JS('bool', r'isFinite(#)', this)) { |
| 65 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. | 75 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. |
| 66 } | 76 } |
| 67 // This is either NaN, Infinity or -Infinity. | 77 // This is either NaN, Infinity or -Infinity. |
| 68 throw new UnsupportedError(JS("String", '"" + #', this)); | 78 throw new UnsupportedError(JS("String", '"" + #', this)); |
| 69 } | 79 } |
| 70 | 80 |
| 81 @notNull |
| 71 int truncate() => toInt(); | 82 int truncate() => toInt(); |
| 72 | 83 |
| 84 @notNull |
| 73 int ceil() => ceilToDouble().toInt(); | 85 int ceil() => ceilToDouble().toInt(); |
| 74 | 86 |
| 87 @notNull |
| 75 int floor() => floorToDouble().toInt(); | 88 int floor() => floorToDouble().toInt(); |
| 76 | 89 |
| 90 @notNull |
| 77 int round() { | 91 int round() { |
| 78 if (this > 0) { | 92 if (this > 0) { |
| 79 // This path excludes the special cases -0.0, NaN and -Infinity, leaving | 93 // This path excludes the special cases -0.0, NaN and -Infinity, leaving |
| 80 // only +Infinity, for which a direct test is faster than [isFinite]. | 94 // only +Infinity, for which a direct test is faster than [isFinite]. |
| 81 if (JS('bool', r'# !== (1/0)', this)) { | 95 if (JS('bool', r'# !== (1/0)', this)) { |
| 82 return JS('int', r'Math.round(#)', this); | 96 return JS('int', r'Math.round(#)', this); |
| 83 } | 97 } |
| 84 } else if (JS('bool', '# > (-1/0)', this)) { | 98 } else if (JS('bool', '# > (-1/0)', this)) { |
| 85 // This test excludes NaN and -Infinity, leaving only -0.0. | 99 // This test excludes NaN and -Infinity, leaving only -0.0. |
| 86 // | 100 // |
| 87 // Subtraction from zero rather than negation forces -0.0 to 0.0 so code | 101 // Subtraction from zero rather than negation forces -0.0 to 0.0 so code |
| 88 // inside Math.round and code to handle result never sees -0.0, which on | 102 // inside Math.round and code to handle result never sees -0.0, which on |
| 89 // some JavaScript VMs can be a slow path. | 103 // some JavaScript VMs can be a slow path. |
| 90 return JS('int', r'0 - Math.round(0 - #)', this); | 104 return JS('int', r'0 - Math.round(0 - #)', this); |
| 91 } | 105 } |
| 92 // This is either NaN, Infinity or -Infinity. | 106 // This is either NaN, Infinity or -Infinity. |
| 93 throw new UnsupportedError(JS("String", '"" + #', this)); | 107 throw new UnsupportedError(JS("String", '"" + #', this)); |
| 94 } | 108 } |
| 95 | 109 |
| 110 @notNull |
| 96 double ceilToDouble() => JS('num', r'Math.ceil(#)', this); | 111 double ceilToDouble() => JS('num', r'Math.ceil(#)', this); |
| 97 | 112 |
| 113 @notNull |
| 98 double floorToDouble() => JS('num', r'Math.floor(#)', this); | 114 double floorToDouble() => JS('num', r'Math.floor(#)', this); |
| 99 | 115 |
| 116 @notNull |
| 100 double roundToDouble() { | 117 double roundToDouble() { |
| 101 if (this < 0) { | 118 if (this < 0) { |
| 102 return JS('num', r'-Math.round(-#)', this); | 119 return JS('num', r'-Math.round(-#)', this); |
| 103 } else { | 120 } else { |
| 104 return JS('num', r'Math.round(#)', this); | 121 return JS('num', r'Math.round(#)', this); |
| 105 } | 122 } |
| 106 } | 123 } |
| 107 | 124 |
| 125 @notNull |
| 108 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); | 126 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); |
| 109 | 127 |
| 110 num clamp(num lowerLimit, num upperLimit) { | 128 @notNull |
| 129 num clamp(@nullCheck num lowerLimit, @nullCheck num upperLimit) { |
| 111 if (lowerLimit.compareTo(upperLimit) > 0) { | 130 if (lowerLimit.compareTo(upperLimit) > 0) { |
| 112 throw argumentErrorValue(lowerLimit); | 131 throw argumentErrorValue(lowerLimit); |
| 113 } | 132 } |
| 114 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | 133 if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
| 115 if (this.compareTo(upperLimit) > 0) return upperLimit; | 134 if (this.compareTo(upperLimit) > 0) return upperLimit; |
| 116 return this; | 135 return this; |
| 117 } | 136 } |
| 118 | 137 |
| 138 @notNull |
| 119 double toDouble() => this; | 139 double toDouble() => this; |
| 120 | 140 |
| 121 String toStringAsFixed(int fractionDigits) { | 141 @notNull |
| 122 checkInt(fractionDigits); | 142 String toStringAsFixed(@notNull int fractionDigits) { |
| 123 if (fractionDigits < 0 || fractionDigits > 20) { | 143 if (fractionDigits < 0 || fractionDigits > 20) { |
| 124 throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits"); | 144 throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits"); |
| 125 } | 145 } |
| 126 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); | 146 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); |
| 127 if (this == 0 && isNegative) return "-$result"; | 147 if (this == 0 && isNegative) return "-$result"; |
| 128 return result; | 148 return result; |
| 129 } | 149 } |
| 130 | 150 |
| 151 @notNull |
| 131 String toStringAsExponential([int fractionDigits]) { | 152 String toStringAsExponential([int fractionDigits]) { |
| 132 String result; | 153 String result; |
| 133 if (fractionDigits != null) { | 154 if (fractionDigits != null) { |
| 134 checkInt(fractionDigits); | 155 @notNull |
| 135 if (fractionDigits < 0 || fractionDigits > 20) { | 156 var _fractionDigits = fractionDigits; |
| 136 throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits"); | 157 if (_fractionDigits < 0 || _fractionDigits > 20) { |
| 158 throw new RangeError.range(_fractionDigits, 0, 20, "fractionDigits"); |
| 137 } | 159 } |
| 138 result = JS('String', r'#.toExponential(#)', this, fractionDigits); | 160 result = JS('String', r'#.toExponential(#)', this, _fractionDigits); |
| 139 } else { | 161 } else { |
| 140 result = JS('String', r'#.toExponential()', this); | 162 result = JS('String', r'#.toExponential()', this); |
| 141 } | 163 } |
| 142 if (this == 0 && isNegative) return "-$result"; | 164 if (this == 0 && isNegative) return "-$result"; |
| 143 return result; | 165 return result; |
| 144 } | 166 } |
| 145 | 167 |
| 146 String toStringAsPrecision(int precision) { | 168 @notNull |
| 147 checkInt(precision); | 169 String toStringAsPrecision(@nullCheck int precision) { |
| 148 if (precision < 1 || precision > 21) { | 170 if (precision < 1 || precision > 21) { |
| 149 throw new RangeError.range(precision, 1, 21, "precision"); | 171 throw new RangeError.range(precision, 1, 21, "precision"); |
| 150 } | 172 } |
| 151 String result = JS('String', r'#.toPrecision(#)', this, precision); | 173 String result = JS('String', r'#.toPrecision(#)', this, precision); |
| 152 if (this == 0 && isNegative) return "-$result"; | 174 if (this == 0 && isNegative) return "-$result"; |
| 153 return result; | 175 return result; |
| 154 } | 176 } |
| 155 | 177 |
| 156 String toRadixString(int radix) { | 178 @notNull |
| 157 checkInt(radix); | 179 String toRadixString(@nullCheck int radix) { |
| 158 if (radix < 2 || radix > 36) { | 180 if (radix < 2 || radix > 36) { |
| 159 throw new RangeError.range(radix, 2, 36, "radix"); | 181 throw new RangeError.range(radix, 2, 36, "radix"); |
| 160 } | 182 } |
| 161 String result = JS('String', r'#.toString(#)', this, radix); | 183 String result = JS('String', r'#.toString(#)', this, radix); |
| 162 const int rightParenCode = 0x29; | 184 const int rightParenCode = 0x29; |
| 163 if (result.codeUnitAt(result.length - 1) != rightParenCode) { | 185 if (result.codeUnitAt(result.length - 1) != rightParenCode) { |
| 164 return result; | 186 return result; |
| 165 } | 187 } |
| 166 return _handleIEtoString(result); | 188 return _handleIEtoString(result); |
| 167 } | 189 } |
| 168 | 190 |
| 191 @notNull |
| 169 static String _handleIEtoString(String result) { | 192 static String _handleIEtoString(String result) { |
| 170 // Result is probably IE's untraditional format for large numbers, | 193 // Result is probably IE's untraditional format for large numbers, |
| 171 // e.g., "8.0000000000008(e+15)" for 0x8000000000000800.toString(16). | 194 // e.g., "8.0000000000008(e+15)" for 0x8000000000000800.toString(16). |
| 172 var match = JS('List|Null', | 195 var match = JS('List|Null', |
| 173 r'/^([\da-z]+)(?:\.([\da-z]+))?\(e\+(\d+)\)$/.exec(#)', result); | 196 r'/^([\da-z]+)(?:\.([\da-z]+))?\(e\+(\d+)\)$/.exec(#)', result); |
| 174 if (match == null) { | 197 if (match == null) { |
| 175 // Then we don't know how to handle it at all. | 198 // Then we don't know how to handle it at all. |
| 176 throw new UnsupportedError("Unexpected toString result: $result"); | 199 throw new UnsupportedError("Unexpected toString result: $result"); |
| 177 } | 200 } |
| 178 result = JS('String', '#', match[1]); | 201 result = JS('String', '#', match[1]); |
| 179 int exponent = JS("int", "+#", match[3]); | 202 int exponent = JS("int", "+#", match[3]); |
| 180 if (match[2] != null) { | 203 if (match[2] != null) { |
| 181 result = JS('String', '# + #', result, match[2]); | 204 result = JS('String', '# + #', result, match[2]); |
| 182 exponent -= JS('int', '#.length', match[2]); | 205 exponent -= JS('int', '#.length', match[2]); |
| 183 } | 206 } |
| 184 return result + "0" * exponent; | 207 return result + "0" * exponent; |
| 185 } | 208 } |
| 186 | 209 |
| 187 // Note: if you change this, also change the function [S]. | 210 // Note: if you change this, also change the function [S]. |
| 211 @notNull |
| 188 String toString() { | 212 String toString() { |
| 189 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { | 213 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { |
| 190 return '-0.0'; | 214 return '-0.0'; |
| 191 } else { | 215 } else { |
| 192 return JS('String', r'"" + (#)', this); | 216 return JS('String', r'"" + (#)', this); |
| 193 } | 217 } |
| 194 } | 218 } |
| 195 | 219 |
| 220 @notNull |
| 196 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | 221 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); |
| 197 | 222 |
| 223 @notNull |
| 198 JSNumber operator -() => JS('num', r'-#', this); | 224 JSNumber operator -() => JS('num', r'-#', this); |
| 199 | 225 |
| 200 JSNumber operator +(num other) { | 226 @notNull |
| 201 if (other is! num) throw argumentErrorValue(other); | 227 JSNumber operator +(@nullCheck num other) { |
| 202 return JS('num', '# + #', this, other); | 228 return JS('num', '# + #', this, other); |
| 203 } | 229 } |
| 204 | 230 |
| 205 JSNumber operator -(num other) { | 231 @notNull |
| 206 if (other is! num) throw argumentErrorValue(other); | 232 JSNumber operator -(@nullCheck num other) { |
| 207 return JS('num', '# - #', this, other); | 233 return JS('num', '# - #', this, other); |
| 208 } | 234 } |
| 209 | 235 |
| 210 double operator /(num other) { | 236 @notNull |
| 211 if (other is! num) throw argumentErrorValue(other); | 237 double operator /(@nullCheck num other) { |
| 212 return JS('num', '# / #', this, other); | 238 return JS('num', '# / #', this, other); |
| 213 } | 239 } |
| 214 | 240 |
| 215 JSNumber operator *(num other) { | 241 @notNull |
| 216 if (other is! num) throw argumentErrorValue(other); | 242 JSNumber operator *(@nullCheck num other) { |
| 217 return JS('num', '# * #', this, other); | 243 return JS('num', '# * #', this, other); |
| 218 } | 244 } |
| 219 | 245 |
| 220 JSNumber operator %(num other) { | 246 @notNull |
| 221 if (other is! num) throw argumentErrorValue(other); | 247 JSNumber operator %(@nullCheck num other) { |
| 222 // Euclidean Modulo. | 248 // Euclidean Modulo. |
| 223 num result = JS('num', r'# % #', this, other); | 249 num result = JS('num', r'# % #', this, other); |
| 224 if (result == 0) return (0 as JSNumber); // Make sure we don't return -0.0. | 250 if (result == 0) return (0 as JSNumber); // Make sure we don't return -0.0. |
| 225 if (result > 0) return result; | 251 if (result > 0) return result; |
| 226 if (JS('num', '#', other) < 0) { | 252 if (JS('num', '#', other) < 0) { |
| 227 return result - JS('num', '#', other); | 253 return result - JS('num', '#', other); |
| 228 } else { | 254 } else { |
| 229 return result + JS('num', '#', other); | 255 return result + JS('num', '#', other); |
| 230 } | 256 } |
| 231 } | 257 } |
| 232 | 258 |
| 233 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | 259 @notNull |
| 260 bool _isInt32(@notNull num value) => |
| 261 JS('bool', '(# | 0) === #', value, value); |
| 234 | 262 |
| 235 int operator ~/(num other) { | 263 @notNull |
| 264 int operator ~/(@nullCheck num other) { |
| 236 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { | 265 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { |
| 237 return JS('int', r'(# / #) | 0', this, other); | 266 return JS('int', r'(# / #) | 0', this, other); |
| 238 } else { | 267 } else { |
| 239 return _tdivSlow(other); | 268 return _tdivSlow(other); |
| 240 } | 269 } |
| 241 } | 270 } |
| 242 | 271 |
| 272 @notNull |
| 243 int _tdivSlow(num other) { | 273 int _tdivSlow(num other) { |
| 244 if (other is! num) throw argumentErrorValue(other); | |
| 245 return (JS('num', r'# / #', this, other)).toInt(); | 274 return (JS('num', r'# / #', this, other)).toInt(); |
| 246 } | 275 } |
| 247 | 276 |
| 248 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | 277 // TODO(ngeoffray): Move the bit operations below to [JSInt] and |
| 249 // make them take an int. Because this will make operations slower, | 278 // make them take an int. Because this will make operations slower, |
| 250 // we define these methods on number for now but we need to decide | 279 // we define these methods on number for now but we need to decide |
| 251 // the grain at which we do the type checks. | 280 // the grain at which we do the type checks. |
| 252 | 281 |
| 253 int operator <<(num other) { | 282 @notNull |
| 254 if (other is! num) throw argumentErrorValue(other); | 283 int operator <<(@nullCheck num other) { |
| 255 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); | 284 if (other < 0) throwArgumentErrorValue(other); |
| 256 return _shlPositive(other); | 285 return _shlPositive(other); |
| 257 } | 286 } |
| 258 | 287 |
| 259 int _shlPositive(num other) { | 288 @notNull |
| 289 int _shlPositive(@notNull num other) { |
| 260 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | 290 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting |
| 261 // by 33 is hence equivalent to a shift by 1. | 291 // by 33 is hence equivalent to a shift by 1. |
| 262 return JS('bool', r'# > 31', other) | 292 return JS('bool', r'# > 31', other) |
| 263 ? 0 | 293 ? 0 |
| 264 : JS('int', r'(# << #) >>> 0', this, other); | 294 : JS('int', r'(# << #) >>> 0', this, other); |
| 265 } | 295 } |
| 266 | 296 |
| 267 int operator >>(num other) { | 297 @notNull |
| 268 if (other is! num) throw argumentErrorValue(other); | 298 int operator >>(@nullCheck num other) { |
| 269 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); | 299 if (JS('num', '#', other) < 0) throwArgumentErrorValue(other); |
| 270 return _shrOtherPositive(other); | 300 return _shrOtherPositive(other); |
| 271 } | 301 } |
| 272 | 302 |
| 273 int _shrOtherPositive(num other) { | 303 @notNull |
| 304 int _shrOtherPositive(@notNull num other) { |
| 274 return JS('num', '#', this) > 0 | 305 return JS('num', '#', this) > 0 |
| 275 ? _shrBothPositive(other) | 306 ? _shrBothPositive(other) |
| 276 // For negative numbers we just clamp the shift-by amount. | 307 // For negative numbers we just clamp the shift-by amount. |
| 277 // `this` could be negative but not have its 31st bit set. | 308 // `this` could be negative but not have its 31st bit set. |
| 278 // The ">>" would then shift in 0s instead of 1s. Therefore | 309 // The ">>" would then shift in 0s instead of 1s. Therefore |
| 279 // we cannot simply return 0xFFFFFFFF. | 310 // we cannot simply return 0xFFFFFFFF. |
| 280 : JS('int', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | 311 : JS('int', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); |
| 281 } | 312 } |
| 282 | 313 |
| 283 int _shrBothPositive(num other) { | 314 @notNull |
| 315 int _shrBothPositive(@notNull num other) { |
| 284 return JS('bool', r'# > 31', other) | 316 return JS('bool', r'# > 31', other) |
| 285 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | 317 // JavaScript only looks at the last 5 bits of the shift-amount. In JS |
| 286 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the | 318 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the |
| 287 // computation when that happens. | 319 // computation when that happens. |
| 288 ? 0 | 320 ? 0 |
| 289 // Given that `this` is positive we must not use '>>'. Otherwise a | 321 // Given that `this` is positive we must not use '>>'. Otherwise a |
| 290 // number that has the 31st bit set would be treated as negative and | 322 // number that has the 31st bit set would be treated as negative and |
| 291 // shift in ones. | 323 // shift in ones. |
| 292 : JS('int', r'# >>> #', this, other); | 324 : JS('int', r'# >>> #', this, other); |
| 293 } | 325 } |
| 294 | 326 |
| 295 int operator &(num other) { | 327 @notNull |
| 296 if (other is! num) throw argumentErrorValue(other); | 328 int operator &(@nullCheck num other) { |
| 297 return JS('int', r'(# & #) >>> 0', this, other); | 329 return JS('int', r'(# & #) >>> 0', this, other); |
| 298 } | 330 } |
| 299 | 331 |
| 300 int operator |(num other) { | 332 @notNull |
| 301 if (other is! num) throw argumentErrorValue(other); | 333 int operator |(@nullCheck num other) { |
| 302 return JS('int', r'(# | #) >>> 0', this, other); | 334 return JS('int', r'(# | #) >>> 0', this, other); |
| 303 } | 335 } |
| 304 | 336 |
| 305 int operator ^(num other) { | 337 @notNull |
| 306 if (other is! num) throw argumentErrorValue(other); | 338 int operator ^(@nullCheck num other) { |
| 307 return JS('int', r'(# ^ #) >>> 0', this, other); | 339 return JS('int', r'(# ^ #) >>> 0', this, other); |
| 308 } | 340 } |
| 309 | 341 |
| 310 bool operator <(num other) { | 342 @notNull |
| 311 if (other is! num) throw argumentErrorValue(other); | 343 bool operator <(@nullCheck num other) { |
| 312 return JS('bool', '# < #', this, other); | 344 return JS('bool', '# < #', this, other); |
| 313 } | 345 } |
| 314 | 346 |
| 315 bool operator >(num other) { | 347 @notNull |
| 316 if (other is! num) throw argumentErrorValue(other); | 348 bool operator >(@nullCheck num other) { |
| 317 return JS('bool', '# > #', this, other); | 349 return JS('bool', '# > #', this, other); |
| 318 } | 350 } |
| 319 | 351 |
| 320 bool operator <=(num other) { | 352 @notNull |
| 321 if (other is! num) throw argumentErrorValue(other); | 353 bool operator <=(@nullCheck num other) { |
| 322 return JS('bool', '# <= #', this, other); | 354 return JS('bool', '# <= #', this, other); |
| 323 } | 355 } |
| 324 | 356 |
| 325 bool operator >=(num other) { | 357 @notNull |
| 326 if (other is! num) throw argumentErrorValue(other); | 358 bool operator >=(@nullCheck num other) { |
| 327 return JS('bool', '# >= #', this, other); | 359 return JS('bool', '# >= #', this, other); |
| 328 } | 360 } |
| 329 | 361 |
| 330 // int members. | 362 // int members. |
| 331 // TODO(jmesserly): all numbers will have these in dynamic dispatch. | 363 // TODO(jmesserly): all numbers will have these in dynamic dispatch. |
| 332 // We can fix by checking it at dispatch time but we'd need to structure them | 364 // We can fix by checking it at dispatch time but we'd need to structure them |
| 333 // differently. | 365 // differently. |
| 334 | 366 |
| 367 @notNull |
| 335 bool get isEven => (this & 1) == 0; | 368 bool get isEven => (this & 1) == 0; |
| 336 | 369 |
| 370 @notNull |
| 337 bool get isOdd => (this & 1) == 1; | 371 bool get isOdd => (this & 1) == 1; |
| 338 | 372 |
| 339 int toUnsigned(int width) { | 373 @notNull |
| 374 int toUnsigned(@nullCheck int width) { |
| 340 return this & ((1 << width) - 1); | 375 return this & ((1 << width) - 1); |
| 341 } | 376 } |
| 342 | 377 |
| 343 int toSigned(int width) { | 378 @notNull |
| 379 int toSigned(@nullCheck int width) { |
| 344 int signMask = 1 << (width - 1); | 380 int signMask = 1 << (width - 1); |
| 345 return (this & (signMask - 1)) - (this & signMask); | 381 return (this & (signMask - 1)) - (this & signMask); |
| 346 } | 382 } |
| 347 | 383 |
| 384 @notNull |
| 348 int get bitLength { | 385 int get bitLength { |
| 349 int nonneg = this < 0 ? -this - 1 : this; | 386 int nonneg = this < 0 ? -this - 1 : this; |
| 350 if (nonneg >= 0x100000000) { | 387 if (nonneg >= 0x100000000) { |
| 351 nonneg = nonneg ~/ 0x100000000; | 388 nonneg = nonneg ~/ 0x100000000; |
| 352 return _bitCount(_spread(nonneg)) + 32; | 389 return _bitCount(_spread(nonneg)) + 32; |
| 353 } | 390 } |
| 354 return _bitCount(_spread(nonneg)); | 391 return _bitCount(_spread(nonneg)); |
| 355 } | 392 } |
| 356 | 393 |
| 357 // Returns pow(this, e) % m. | 394 // Returns pow(this, e) % m. |
| 358 int modPow(int e, int m) { | 395 @notNull |
| 359 if (e is! int) { | 396 int modPow(@nullCheck int e, @nullCheck int m) { |
| 360 throw new ArgumentError.value(e, "exponent", "not an integer"); | |
| 361 } | |
| 362 if (m is! int) { | |
| 363 throw new ArgumentError.value(m, "modulus", "not an integer"); | |
| 364 } | |
| 365 if (e < 0) throw new RangeError.range(e, 0, null, "exponent"); | 397 if (e < 0) throw new RangeError.range(e, 0, null, "exponent"); |
| 366 if (m <= 0) throw new RangeError.range(m, 1, null, "modulus"); | 398 if (m <= 0) throw new RangeError.range(m, 1, null, "modulus"); |
| 367 if (e == 0) return 1; | 399 if (e == 0) return 1; |
| 368 int b = this; | 400 int b = this; |
| 369 if (b < 0 || b > m) { | 401 if (b < 0 || b > m) { |
| 370 b %= m; | 402 b %= m; |
| 371 } | 403 } |
| 372 int r = 1; | 404 int r = 1; |
| 373 while (e > 0) { | 405 while (e > 0) { |
| 374 if (e.isOdd) { | 406 if (e.isOdd) { |
| 375 r = (r * b) % m; | 407 r = (r * b) % m; |
| 376 } | 408 } |
| 377 e ~/= 2; | 409 e ~/= 2; |
| 378 b = (b * b) % m; | 410 b = (b * b) % m; |
| 379 } | 411 } |
| 380 return r; | 412 return r; |
| 381 } | 413 } |
| 382 | 414 |
| 383 // If inv is false, returns gcd(x, y). | 415 // If inv is false, returns gcd(x, y). |
| 384 // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. | 416 // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. |
| 385 // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime"). | 417 // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime"). |
| 386 static int _binaryGcd(int x, int y, bool inv) { | 418 @notNull |
| 419 static int _binaryGcd(@notNull int x, @notNull int y, @notNull bool inv) { |
| 387 int s = 1; | 420 int s = 1; |
| 388 if (!inv) { | 421 if (!inv) { |
| 389 while (x.isEven && y.isEven) { | 422 while (x.isEven && y.isEven) { |
| 390 x ~/= 2; | 423 x ~/= 2; |
| 391 y ~/= 2; | 424 y ~/= 2; |
| 392 s *= 2; | 425 s *= 2; |
| 393 } | 426 } |
| 394 if (y.isOdd) { | 427 if (y.isOdd) { |
| 395 var t = x; | 428 var t = x; |
| 396 x = y; | 429 x = y; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 d += x; | 477 d += x; |
| 445 if (d < 0) d += x; | 478 if (d < 0) d += x; |
| 446 } else if (d > x) { | 479 } else if (d > x) { |
| 447 d -= x; | 480 d -= x; |
| 448 if (d > x) d -= x; | 481 if (d > x) d -= x; |
| 449 } | 482 } |
| 450 return d; | 483 return d; |
| 451 } | 484 } |
| 452 | 485 |
| 453 // Returns 1/this % m, with m > 0. | 486 // Returns 1/this % m, with m > 0. |
| 454 int modInverse(int m) { | 487 @notNull |
| 455 if (m is! int) { | 488 int modInverse(@nullCheck int m) { |
| 456 throw new ArgumentError.value(m, "modulus", "not an integer"); | |
| 457 } | |
| 458 if (m <= 0) throw new RangeError.range(m, 1, null, "modulus"); | 489 if (m <= 0) throw new RangeError.range(m, 1, null, "modulus"); |
| 459 if (m == 1) return 0; | 490 if (m == 1) return 0; |
| 460 int t = this; | 491 int t = this; |
| 461 if ((t < 0) || (t >= m)) t %= m; | 492 if ((t < 0) || (t >= m)) t %= m; |
| 462 if (t == 1) return 1; | 493 if (t == 1) return 1; |
| 463 if ((t == 0) || (t.isEven && m.isEven)) { | 494 if ((t == 0) || (t.isEven && m.isEven)) { |
| 464 throw new Exception("Not coprime"); | 495 throw new Exception("Not coprime"); |
| 465 } | 496 } |
| 466 return _binaryGcd(m, t, true); | 497 return _binaryGcd(m, t, true); |
| 467 } | 498 } |
| 468 | 499 |
| 469 // Returns gcd of abs(this) and abs(other). | 500 // Returns gcd of abs(this) and abs(other). |
| 470 int gcd(int other) { | 501 @notNull |
| 471 if (other is! int) { | 502 int gcd(@nullCheck int other) { |
| 472 throw new ArgumentError.value(other, "other", "not an integer"); | |
| 473 } | |
| 474 int x = this.abs(); | 503 int x = this.abs(); |
| 475 int y = other.abs(); | 504 int y = other.abs(); |
| 476 if (x == 0) return y; | 505 if (x == 0) return y; |
| 477 if (y == 0) return x; | 506 if (y == 0) return x; |
| 478 if ((x == 1) || (y == 1)) return 1; | 507 if ((x == 1) || (y == 1)) return 1; |
| 479 return _binaryGcd(x, y, false); | 508 return _binaryGcd(x, y, false); |
| 480 } | 509 } |
| 481 | 510 |
| 482 // Assumes i is <= 32-bit and unsigned. | 511 // Assumes i is <= 32-bit and unsigned. |
| 483 static int _bitCount(int i) { | 512 @notNull |
| 513 static int _bitCount(@notNull int i) { |
| 484 // See "Hacker's Delight", section 5-1, "Counting 1-Bits". | 514 // See "Hacker's Delight", section 5-1, "Counting 1-Bits". |
| 485 | 515 |
| 486 // The basic strategy is to use "divide and conquer" to | 516 // The basic strategy is to use "divide and conquer" to |
| 487 // add pairs (then quads, etc.) of bits together to obtain | 517 // add pairs (then quads, etc.) of bits together to obtain |
| 488 // sub-counts. | 518 // sub-counts. |
| 489 // | 519 // |
| 490 // A straightforward approach would look like: | 520 // A straightforward approach would look like: |
| 491 // | 521 // |
| 492 // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); | 522 // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); |
| 493 // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); | 523 // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); |
| 494 // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); | 524 // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); |
| 495 // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); | 525 // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); |
| 496 // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); | 526 // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); |
| 497 // | 527 // |
| 498 // The code below removes unnecessary &'s and uses a | 528 // The code below removes unnecessary &'s and uses a |
| 499 // trick to remove one instruction in the first line. | 529 // trick to remove one instruction in the first line. |
| 500 | 530 |
| 501 i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); | 531 i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); |
| 502 i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); | 532 i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); |
| 503 i = 0x0F0F0F0F & (i + _shru(i, 4)); | 533 i = 0x0F0F0F0F & (i + _shru(i, 4)); |
| 504 i += _shru(i, 8); | 534 i += _shru(i, 8); |
| 505 i += _shru(i, 16); | 535 i += _shru(i, 16); |
| 506 return (i & 0x0000003F); | 536 return (i & 0x0000003F); |
| 507 } | 537 } |
| 508 | 538 |
| 539 @notNull |
| 509 static int _shru(int value, int shift) => JS('int', '# >>> #', value, shift); | 540 static int _shru(int value, int shift) => JS('int', '# >>> #', value, shift); |
| 541 @notNull |
| 510 static int _shrs(int value, int shift) => JS('int', '# >> #', value, shift); | 542 static int _shrs(int value, int shift) => JS('int', '# >> #', value, shift); |
| 543 @notNull |
| 511 static int _ors(int a, int b) => JS('int', '# | #', a, b); | 544 static int _ors(int a, int b) => JS('int', '# | #', a, b); |
| 512 | 545 |
| 513 // Assumes i is <= 32-bit | 546 // Assumes i is <= 32-bit |
| 514 static int _spread(int i) { | 547 @notNull |
| 548 static int _spread(@notNull int i) { |
| 515 i = _ors(i, _shrs(i, 1)); | 549 i = _ors(i, _shrs(i, 1)); |
| 516 i = _ors(i, _shrs(i, 2)); | 550 i = _ors(i, _shrs(i, 2)); |
| 517 i = _ors(i, _shrs(i, 4)); | 551 i = _ors(i, _shrs(i, 4)); |
| 518 i = _ors(i, _shrs(i, 8)); | 552 i = _ors(i, _shrs(i, 8)); |
| 519 i = _shru(_ors(i, _shrs(i, 16)), 0); | 553 i = _shru(_ors(i, _shrs(i, 16)), 0); |
| 520 return i; | 554 return i; |
| 521 } | 555 } |
| 522 | 556 |
| 523 int operator ~() => JS('int', r'(~#) >>> 0', this); | 557 int operator ~() => JS('int', r'(~#) >>> 0', this); |
| 524 } | 558 } |
| OLD | NEW |