Files
sdk/pkg/dev_compiler/tool/input_sdk/patch/math_patch.dart
T
Leaf Petersen 1b8f4ac1e7 Optimize DDC private runtime library files.
Eliminate numerous type checks not needed in strong mode.  Use
private nullability annotations instead of inline JS to mark known
non-null variables, and to make the compiler generate null checks
for null checked variables. Use nullability annotations and code refactoring to
remove redundant null checks and to move checks out of loops.

BUG=
R=jmesserly@google.com

Review-Url: https://codereview.chromium.org/2994203002 .
2017-08-23 10:28:45 -07:00

349 lines
9.6 KiB
Dart

// Copyright (c) 2012, 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.
// Patch file for dart:math library.
import 'dart:_foreign_helper' show JS;
import 'dart:_js_helper' show patch, nullCheck, notNull;
import 'dart:typed_data' show ByteData;
@patch
@notNull
T min<T extends num>(@nullCheck T a, @nullCheck T b) =>
JS('-dynamic', r'Math.min(#, #)', a, b);
@patch
@notNull
T max<T extends num>(@nullCheck T a, @nullCheck T b) =>
JS('-dynamic', r'Math.max(#, #)', a, b);
@patch
@notNull
double sqrt(@nullCheck num x) => JS('num', r'Math.sqrt(#)', x);
@patch
@notNull
double sin(@nullCheck num radians) => JS('num', r'Math.sin(#)', radians);
@patch
@notNull
double cos(@nullCheck num radians) => JS('num', r'Math.cos(#)', radians);
@patch
@notNull
double tan(@nullCheck num radians) => JS('num', r'Math.tan(#)', radians);
@patch
@notNull
double acos(@nullCheck num x) => JS('num', r'Math.acos(#)', x);
@patch
@notNull
double asin(@nullCheck num x) => JS('num', r'Math.asin(#)', x);
@patch
@notNull
double atan(@nullCheck num x) => JS('num', r'Math.atan(#)', x);
@patch
@notNull
double atan2(@nullCheck num a, @nullCheck num b) =>
JS('num', r'Math.atan2(#, #)', a, b);
@patch
@notNull
double exp(@nullCheck num x) => JS('num', r'Math.exp(#)', x);
@patch
@notNull
double log(@nullCheck num x) => JS('num', r'Math.log(#)', x);
@patch
@notNull
num pow(@nullCheck num x, @nullCheck num exponent) =>
JS('num', r'Math.pow(#, #)', x, exponent);
const int _POW2_32 = 0x100000000;
@patch
class Random {
static final _secureRandom = new _JSSecureRandom();
@patch
factory Random([int seed]) =>
(seed == null) ? const _JSRandom() : new _Random(seed);
@patch
factory Random.secure() => _secureRandom;
}
class _JSRandom implements Random {
// The Dart2JS implementation of Random doesn't use a seed.
const _JSRandom();
@notNull
int nextInt(int max) {
if (max <= 0 || max > _POW2_32) {
throw new RangeError("max must be in range 0 < max ≤ 2^32, was $max");
}
return JS("int", "(Math.random() * #) >>> 0", max);
}
/**
* Generates a positive random floating point value uniformly distributed on
* the range from 0.0, inclusive, to 1.0, exclusive.
*/
@notNull
double nextDouble() => JS("double", "Math.random()");
/**
* Generates a random boolean value.
*/
@notNull
bool nextBool() => JS("bool", "Math.random() < 0.5");
}
class _Random implements Random {
// Constants used by the algorithm or masking.
static const double _POW2_53_D = 1.0 * (0x20000000000000);
static const double _POW2_27_D = 1.0 * (1 << 27);
static const int _MASK32 = 0xFFFFFFFF;
// State comprised of two unsigned 32 bit integers.
@notNull
int _lo = 0;
@notNull
int _hi = 0;
// Implements:
// uint64_t hash = 0;
// do {
// hash = hash * 1037 ^ mix64((uint64_t)seed);
// seed >>= 64;
// } while (seed != 0 && seed != -1); // Limits for pos/neg seed.
// if (hash == 0) {
// hash = 0x5A17;
// }
// _lo = hash & _MASK_32;
// _hi = hash >> 32;
// and then does four _nextState calls to shuffle bits around.
_Random(int seed) {
int empty_seed = 0;
if (seed < 0) {
empty_seed = -1;
}
do {
int low = seed & _MASK32;
seed = (seed - low) ~/ _POW2_32;
int high = seed & _MASK32;
seed = (seed - high) ~/ _POW2_32;
// Thomas Wang's 64-bit mix function.
// http://www.concentric.net/~Ttwang/tech/inthash.htm
// via. http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
// key = ~key + (key << 21);
int tmplow = low << 21;
int tmphigh = (high << 21) | (low >> 11);
tmplow = (~low & _MASK32) + tmplow;
low = tmplow & _MASK32;
high = (~high + tmphigh + ((tmplow - low) ~/ 0x100000000)) & _MASK32;
// key = key ^ (key >> 24).
tmphigh = high >> 24;
tmplow = (low >> 24) | (high << 8);
low ^= tmplow;
high ^= tmphigh;
// key = key * 265
tmplow = low * 265;
low = tmplow & _MASK32;
high = (high * 265 + (tmplow - low) ~/ 0x100000000) & _MASK32;
// key = key ^ (key >> 14);
tmphigh = high >> 14;
tmplow = (low >> 14) | (high << 18);
low ^= tmplow;
high ^= tmphigh;
// key = key * 21
tmplow = low * 21;
low = tmplow & _MASK32;
high = (high * 21 + (tmplow - low) ~/ 0x100000000) & _MASK32;
// key = key ^ (key >> 28).
tmphigh = high >> 28;
tmplow = (low >> 28) | (high << 4);
low ^= tmplow;
high ^= tmphigh;
// key = key + (key << 31);
tmplow = low << 31;
tmphigh = (high << 31) | (low >> 1);
tmplow += low;
low = tmplow & _MASK32;
high = (high + tmphigh + (tmplow - low) ~/ 0x100000000) & _MASK32;
// Mix end.
// seed = seed * 1037 ^ key;
tmplow = _lo * 1037;
_lo = tmplow & _MASK32;
_hi = (_hi * 1037 + (tmplow - _lo) ~/ 0x100000000) & _MASK32;
_lo ^= low;
_hi ^= high;
} while (seed != empty_seed);
if (_hi == 0 && _lo == 0) {
_lo = 0x5A17;
}
_nextState();
_nextState();
_nextState();
_nextState();
}
// The algorithm used here is Multiply with Carry (MWC) with a Base b = 2^32.
// http://en.wikipedia.org/wiki/Multiply-with-carry
// The constant A (0xFFFFDA61) is selected from "Numerical Recipes 3rd
// Edition" p.348 B1.
// Implements:
// var state = (A * _lo + _hi) & _MASK_64;
// _lo = state & _MASK_32;
// _hi = state >> 32;
void _nextState() {
// Simulate (0xFFFFDA61 * lo + hi) without overflowing 53 bits.
int tmpHi = 0xFFFF0000 * _lo; // At most 48 bits of significant result.
int tmpHiLo = tmpHi & _MASK32; // Get the lower 32 bits.
int tmpHiHi = tmpHi - tmpHiLo; // And just the upper 32 bits.
int tmpLo = 0xDA61 * _lo;
int tmpLoLo = tmpLo & _MASK32;
int tmpLoHi = tmpLo - tmpLoLo;
int newLo = tmpLoLo + tmpHiLo + _hi;
_lo = newLo & _MASK32;
int newLoHi = newLo - _lo;
_hi = ((tmpLoHi + tmpHiHi + newLoHi) ~/ _POW2_32) & _MASK32;
assert(_lo < _POW2_32);
assert(_hi < _POW2_32);
}
@notNull
int nextInt(@nullCheck int max) {
if (max <= 0 || max > _POW2_32) {
throw new RangeError("max must be in range 0 < max ≤ 2^32, was $max");
}
if ((max & (max - 1)) == 0) {
// Fast case for powers of two.
_nextState();
return _lo & (max - 1);
}
int rnd32;
int result;
do {
_nextState();
rnd32 = _lo;
result = rnd32.remainder(max); // % max;
} while ((rnd32 - result + max) >= _POW2_32);
return result;
}
@notNull
double nextDouble() {
_nextState();
int bits26 = _lo & ((1 << 26) - 1);
_nextState();
int bits27 = _lo & ((1 << 27) - 1);
return (bits26 * _POW2_27_D + bits27) / _POW2_53_D;
}
@notNull
bool nextBool() {
_nextState();
return (_lo & 1) == 0;
}
}
class _JSSecureRandom implements Random {
// Reused buffer with room enough for a double.
final _buffer = new ByteData(8);
_JSSecureRandom() {
var crypto = JS("", "self.crypto");
if (crypto != null) {
var getRandomValues = JS("", "#.getRandomValues", crypto);
if (getRandomValues != null) {
return;
}
}
throw new UnsupportedError(
"No source of cryptographically secure random numbers available.");
}
/// Fill _buffer from [start] to `start + length` with random bytes.
void _getRandomBytes(int start, int length) {
JS("void", "crypto.getRandomValues(#)",
_buffer.buffer.asUint8List(start, length));
}
@notNull
bool nextBool() {
_getRandomBytes(0, 1);
return _buffer.getUint8(0).isOdd;
}
@notNull
double nextDouble() {
_getRandomBytes(1, 7);
// Set top bits 12 of double to 0x3FF which is the exponent for numbers
// between 1.0 and 2.0.
_buffer.setUint8(0, 0x3F);
int highByte = _buffer.getUint8(1);
_buffer.setUint8(1, highByte | 0xF0);
// Buffer now contains double in the range [1.0-2.0)
// with 52 bits of entropy (not 53).
// To get 53 bits, we extract the 53rd bit from higthByte before
// overwriting it, and add that as a least significant bit.
// The getFloat64 method is big-endian as default.
double result = _buffer.getFloat64(0) - 1.0;
if (highByte & 0x10 != 0) {
result += 1.1102230246251565e-16; // pow(2,-53).
}
return result;
}
@notNull
int nextInt(@nullCheck int max) {
if (max <= 0 || max > _POW2_32) {
throw new RangeError("max must be in range 0 < max ≤ 2^32, was $max");
}
int byteCount = 1;
if (max > 0xFF) {
byteCount++;
if (max > 0xFFFF) {
byteCount++;
if (max > 0xFFFFFF) {
byteCount++;
}
}
}
_buffer.setUint32(0, 0);
int start = 4 - byteCount;
int randomLimit = pow(256, byteCount);
while (true) {
_getRandomBytes(start, byteCount);
// The getUint32 method is big-endian as default.
int random = _buffer.getUint32(0);
if (max & (max - 1) == 0) {
// Max is power of 2.
return random & (max - 1);
}
int result = random.remainder(max);
// Ensure results have equal probability by rejecting values in the
// last range of k*max .. 256**byteCount.
// TODO: Consider picking a higher byte count if the last range is a
// significant portion of the entire range - a 50% chance of having
// to use two more bytes is no worse than always using one more.
if (random - result + max < randomLimit) {
return result;
}
}
}
}