pseudorandom.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview PseudoRandom provides a mechanism for generating deterministic
  16. * pseudo random numbers based on a seed. Based on the Park-Miller algorithm.
  17. * See http://dx.doi.org/10.1145%2F63039.63042 for details.
  18. *
  19. */
  20. goog.setTestOnly('goog.testing.PseudoRandom');
  21. goog.provide('goog.testing.PseudoRandom');
  22. goog.require('goog.Disposable');
  23. /**
  24. * Class for unit testing code that uses Math.random. Generates deterministic
  25. * random numbers.
  26. *
  27. * @param {number=} opt_seed The seed to use.
  28. * @param {boolean=} opt_install Whether to install the PseudoRandom at
  29. * construction time.
  30. * @extends {goog.Disposable}
  31. * @constructor
  32. * @final
  33. */
  34. goog.testing.PseudoRandom = function(opt_seed, opt_install) {
  35. goog.Disposable.call(this);
  36. if (!goog.isDef(opt_seed)) {
  37. opt_seed = goog.testing.PseudoRandom.seedUniquifier_++ + goog.now();
  38. }
  39. this.seed(opt_seed);
  40. if (opt_install) {
  41. this.install();
  42. }
  43. };
  44. goog.inherits(goog.testing.PseudoRandom, goog.Disposable);
  45. /**
  46. * Helps create a unique seed.
  47. * @type {number}
  48. * @private
  49. */
  50. goog.testing.PseudoRandom.seedUniquifier_ = 0;
  51. /**
  52. * Constant used as part of the algorithm.
  53. * @type {number}
  54. */
  55. goog.testing.PseudoRandom.A = 48271;
  56. /**
  57. * Constant used as part of the algorithm. 2^31 - 1.
  58. * @type {number}
  59. */
  60. goog.testing.PseudoRandom.M = 2147483647;
  61. /**
  62. * Constant used as part of the algorithm. It is equal to M / A.
  63. * @type {number}
  64. */
  65. goog.testing.PseudoRandom.Q = 44488;
  66. /**
  67. * Constant used as part of the algorithm. It is equal to M % A.
  68. * @type {number}
  69. */
  70. goog.testing.PseudoRandom.R = 3399;
  71. /**
  72. * Constant used as part of the algorithm to get values from range [0, 1).
  73. * @type {number}
  74. */
  75. goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE =
  76. 1.0 / (goog.testing.PseudoRandom.M - 1);
  77. /**
  78. * The seed of the random sequence and also the next returned value (before
  79. * normalization). Must be between 1 and M - 1 (inclusive).
  80. * @type {number}
  81. * @private
  82. */
  83. goog.testing.PseudoRandom.prototype.seed_ = 1;
  84. /**
  85. * Whether this PseudoRandom has been installed.
  86. * @type {boolean}
  87. * @private
  88. */
  89. goog.testing.PseudoRandom.prototype.installed_;
  90. /**
  91. * The original Math.random function.
  92. * @type {function(): number}
  93. * @private
  94. */
  95. goog.testing.PseudoRandom.prototype.mathRandom_;
  96. /**
  97. * Installs this PseudoRandom as the system number generator.
  98. */
  99. goog.testing.PseudoRandom.prototype.install = function() {
  100. if (!this.installed_) {
  101. this.mathRandom_ = Math.random;
  102. Math.random = goog.bind(this.random, this);
  103. this.installed_ = true;
  104. }
  105. };
  106. /** @override */
  107. goog.testing.PseudoRandom.prototype.disposeInternal = function() {
  108. goog.testing.PseudoRandom.superClass_.disposeInternal.call(this);
  109. this.uninstall();
  110. };
  111. /**
  112. * Uninstalls the PseudoRandom.
  113. */
  114. goog.testing.PseudoRandom.prototype.uninstall = function() {
  115. if (this.installed_) {
  116. Math.random = this.mathRandom_;
  117. this.installed_ = false;
  118. }
  119. };
  120. /**
  121. * Seed the generator.
  122. *
  123. * @param {number=} opt_seed The seed to use.
  124. */
  125. goog.testing.PseudoRandom.prototype.seed = function(opt_seed) {
  126. this.seed_ = opt_seed % (goog.testing.PseudoRandom.M - 1);
  127. if (this.seed_ <= 0) {
  128. this.seed_ += goog.testing.PseudoRandom.M - 1;
  129. }
  130. };
  131. /**
  132. * @return {number} The next number in the sequence.
  133. */
  134. goog.testing.PseudoRandom.prototype.random = function() {
  135. var hi = Math.floor(this.seed_ / goog.testing.PseudoRandom.Q);
  136. var lo = this.seed_ % goog.testing.PseudoRandom.Q;
  137. var test =
  138. goog.testing.PseudoRandom.A * lo - goog.testing.PseudoRandom.R * hi;
  139. if (test > 0) {
  140. this.seed_ = test;
  141. } else {
  142. this.seed_ = test + goog.testing.PseudoRandom.M;
  143. }
  144. return (this.seed_ - 1) * goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE;
  145. };