Skip to content

Commit

Permalink
Dev prng factory (#20)
Browse files Browse the repository at this point in the history
* newPrng method added

In order to support independent PRNG creation new method added. It
supports all PRNG types, as well as seed. Some PRNG bugs fixed.

* Default PRNG type changed to taus113

* Tests refactored

Co-authored-by: Alexey Kiselev <[email protected]>
  • Loading branch information
AlexeySKiselev and kiselev-alexey committed Jul 4, 2021
1 parent dc44683 commit ebe41d6
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 11 deletions.
20 changes: 20 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,26 @@ If you want to unset *seed* and generate different values each time use:
unirand.seed(); // unset seed value for all generators
```

#### PRNG Factory
Unirand allows you to generate independent PRNGs of all supported types.

```javascript
let prng = newPrng(<prng name>[, <seed value>]); // unseeded by default

// example
prng = newPrng('r250');
prng.random(); // 0.6259469939395785
prng.random(); // 0.3127290401607752
prng.next(); // 0.10631363722495735

// you can generate seeded PRNG
prng = newPrng('tuchei', 'unirand');
prng.random(); // 0.026891989167779684
prng.random(); // same 0.026891989167779684
prng.next(); // 0.23777238093316555
prng.nextInt(); // 2513331331
```

### Random number
Generates random number with given distribution. For example, if you want to generate random number with *normal distribution*:
```javascript
Expand Down
1 change: 1 addition & 0 deletions core/prng/Dx1597PRNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class Dx1597PRNG extends BasicPRNG implements IPRNG {
this._no_seed = false;
} else if (typeof seed_value === 'string') {
this._seed = seed_value;
this._pointer = 0;
for (let i = 0; i < this._seed.length; i += 1) {
this._pointer = (this._pointer + this._seed.charCodeAt(i)) % WORD_4;
}
Expand Down
1 change: 1 addition & 0 deletions core/prng/Gfsr4PRNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class Gfsr4PRNG extends BasicPRNG implements IPRNG {
this._no_seed = false;
} else if (typeof seed_value === 'string') {
this._seed = seed_value;
this._pointer = 0;
for (let i = 0; i < this._seed.length; i += 1) {
this._pointer = (this._pointer + this._seed.charCodeAt(i)) % WORD_D;
}
Expand Down
1 change: 1 addition & 0 deletions core/prng/MarsenneTwisterPRNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class MarsenneTwisterPRNG extends BasicPRNG implements IPRNG {
this._no_seed = false;
} else if (typeof seed_value === 'string') {
this._seed = seed_value;
this._pointer = 0;
for (let i = 0; i < this._seed.length; i += 1) {
this._pointer = (this._pointer + this._seed.charCodeAt(i)) % WORD_LEFT;
}
Expand Down
1 change: 1 addition & 0 deletions core/prng/Mrg5PRNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class Mrg5PRNG extends BasicPRNG implements IPRNG {
this._no_seed = false;
} else if (typeof seed_value === 'string') {
this._seed = seed_value;
this._pointer = 0;
for (let i = 0; i < this._seed.length; i += 1) {
this._pointer = (this._pointer + this._seed.charCodeAt(i)) % WORD_LEFT;
}
Expand Down
1 change: 1 addition & 0 deletions core/prng/R250PRNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class R250PRNG extends BasicPRNG implements IPRNG {
this._no_seed = false;
} else if (typeof seed_value === 'string') {
this._seed = seed_value;
this._pointer = 0;
for (let i = 0; i < this._seed.length; i += 1) {
this._pointer = (this._pointer + this._seed.charCodeAt(i)) % WORD_LEFT;
}
Expand Down
1 change: 1 addition & 0 deletions core/prng/Swb2712PRNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class Swb2712PRNG extends BasicPRNG implements IPRNG {
this._no_seed = false;
} else if (typeof seed_value === 'string') {
this._seed = seed_value;
this._pointer = 0;
for (let i = 0; i < this._seed.length; i += 1) {
this._pointer = (this._pointer + this._seed.charCodeAt(i)) % LAG_R;
}
Expand Down
2 changes: 1 addition & 1 deletion core/prng/prngProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import PhiloxPRNG from './PhiloxPRNG';
import Taus113PRNG from './Taus113PRNG';
import Swb2712PRNG from './Swb2712PRNG';

const DEFAULT_GENERATOR = 'tuchei';
const DEFAULT_GENERATOR: string = 'taus113';

class PRNGProxy implements IPRNGProxy {

Expand Down
17 changes: 16 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Winsorize from './core/array_manipulation/winsorize';
import KFold from './core/array_manipulation/kfold';
import hashProxy from './core/utils/hash';
import smoothProxy from './core/array_manipulation/smooth';
import prngProxy from './core/prng/prngProxy';
import prngProxy, { PRNGProxy } from './core/prng/prngProxy';
import encoderProxy from './core/utils/encoders/encoderProxy';
import UidFactory from './core/uidFactory';
import {DEFAULT_GENERATOR} from './core/prng/prngProxy';
Expand Down Expand Up @@ -60,6 +60,7 @@ class RandomJS {
smooth: ISmooth;
smoothSync: ISmooth;
newRouletteWheel: IRouletteWheel;
newPrng: IPRNGProxy;
_randomColorFabric: IRandomColor;
randomColor: any;
nextColor: any;
Expand Down Expand Up @@ -289,6 +290,19 @@ class RandomJS {
}
}: Object));

/**
* Factory produces new PRNGs
*/
Object.defineProperty(this, 'newPrng', ({
__proto__: null,
value: (name: string, seed: any = undefined): IPRNGProxy => {
const _prngProxy: IPRNGProxy = new PRNGProxy();
_prngProxy.seed(seed);
_prngProxy.set_prng(name);
return _prngProxy;
}
}: Object));

/**
* PRNG seed
*/
Expand Down Expand Up @@ -421,6 +435,7 @@ const methods = {
randomInRange: randomjs.randomInRange,
nextInRange: randomjs.nextInRange,
newRouletteWheel: randomjs.newRouletteWheel,
newPrng: randomjs.newPrng,
randomColor: randomjs.randomColor,
nextColor: randomjs.nextColor
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "unirand",
"version": "2.12.0",
"version": "2.12.2",
"description": "Random numbers and Distributions generation",
"main": "./lib/index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ chai.should();

const methods = ['analyze', 'utils', 'stringutils', 'sample', 'kfold', 'shuffle', 'derange', 'chance', 'winsorize',
'hash', 'smooth', 'smoothSync', 'seed', 'random', 'next', 'randomInt', 'nextInt',
'randomInRange', 'nextInRange', 'newRouletteWheel', 'randomColor', 'nextColor', 'encoder', 'uid']; // check prng separately
'randomInRange', 'nextInRange', 'newRouletteWheel', 'newPrng', 'randomColor', 'nextColor', 'encoder', 'uid']; // check prng separately

describe('Index', () => {
it('unirand should have all supported methods', () => {
Expand Down
56 changes: 55 additions & 1 deletion test/prng.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
let chai = require('chai'),
expect = chai.expect,
{describe, it} = require('mocha'),
prng = require('../lib/prng/prngProxy').default;
prng = require('../lib/prng/prngProxy').default,
newPrng = require('../lib').newPrng;

chai.should();

Expand Down Expand Up @@ -216,6 +217,59 @@ describe('PRNGProxy', () => {
});
});

describe('newPrng', () => {
it('should support .random(), .randomInt(), .next(), .nextInt(), .seed() methods', () => {
const prng = newPrng('tt800', 12345);
expect(prng).to.have.property('random');
expect(prng).to.respondsTo('random');

expect(prng).to.have.property('randomInt');
expect(prng).to.respondsTo('randomInt');

expect(prng).to.have.property('next');
expect(prng).to.respondsTo('next');

expect(prng).to.have.property('nextInt');
expect(prng).to.respondsTo('nextInt');

expect(prng).to.have.property('seed');
expect(prng).to.respondsTo('seed');
});
it('should support all allowed generators', () => {
const prng = newPrng('default');
const generators = prng.generators;

for (let generator of generators) {
let goodPrng = () => {
return newPrng(generator);
};
goodPrng.should.not.throw(Error);
}
});
it('should throw an error for unsupported generator', () => {
let badPrng = () => {
return newPrng('abc');
};
badPrng.should.throw(Error);
});
it('should support seed', () => {
const prng = newPrng('tt800', 'unirand');
expect(prng.random()).to.be.closeTo(prng.random(), 0.00001);

const prngNoSeed = newPrng('tt800');
expect(Math.abs(prngNoSeed.random() - prngNoSeed.random()) > 0.00001).to.be.equal(true);
});
it('should be independent', () => {
const prng1 = newPrng('tt800', 'unirand');
const prng2 = newPrng('tt800', 'random seed');
const prng3 = newPrng('r250', 'unirand');

expect(Math.abs(prng1.random() - prng2.random()) > 0.00001).to.be.equal(true);
expect(Math.abs(prng2.random() - prng3.random()) > 0.00001).to.be.equal(true);
expect(Math.abs(prng1.random() - prng3.random()) > 0.00001).to.be.equal(true);
});
});

describe('tuchei PRNG', () => {
prngTest('tuchei');
});
Expand Down
12 changes: 6 additions & 6 deletions test/stringutils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe('String Utils', () => {
let randomString;
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 10000; i += 1) {
randomString = StringUtils.random(alphabet, 100);
randomString = StringUtils.random(alphabet, 200);
randomStrings[randomString] = 1;
}
expect(Object.keys(randomStrings).length).to.be.equal(10000);
Expand Down Expand Up @@ -157,7 +157,7 @@ describe('String Utils', () => {
const randomStrings = {};
let randomString;
for (let i = 0; i < 10000; i += 1) {
randomString = StringUtils.randomAlphabetic(100);
randomString = StringUtils.randomAlphabetic(200);
randomStrings[randomString] = 1;
}
expect(Object.keys(randomStrings).length).to.be.equal(10000);
Expand Down Expand Up @@ -220,7 +220,7 @@ describe('String Utils', () => {
const randomStrings = {};
let randomString;
for (let i = 0; i < 10000; i += 1) {
randomString = StringUtils.randomAscii(100);
randomString = StringUtils.randomAscii(200);
randomStrings[randomString] = 1;
}
expect(Object.keys(randomStrings).length).to.be.equal(10000);
Expand Down Expand Up @@ -283,7 +283,7 @@ describe('String Utils', () => {
const randomStrings = {};
let randomString;
for (let i = 0; i < 10000; i += 1) {
randomString = StringUtils.randomAlphanumeric(100);
randomString = StringUtils.randomAlphanumeric(200);
randomStrings[randomString] = 1;
}
expect(Object.keys(randomStrings).length).to.be.equal(10000);
Expand Down Expand Up @@ -346,7 +346,7 @@ describe('String Utils', () => {
const randomStrings = {};
let randomString;
for (let i = 0; i < 10000; i += 1) {
randomString = StringUtils.randomNumeric(100);
randomString = StringUtils.randomNumeric(200);
randomStrings[randomString] = 1;
}
expect(Object.keys(randomStrings).length).to.be.equal(10000);
Expand Down Expand Up @@ -409,7 +409,7 @@ describe('String Utils', () => {
const randomStrings = {};
let randomString;
for (let i = 0; i < 10000; i += 1) {
randomString = StringUtils.randomHex(100);
randomString = StringUtils.randomHex(200);
randomStrings[randomString] = 1;
}
expect(Object.keys(randomStrings).length).to.be.equal(10000);
Expand Down

0 comments on commit ebe41d6

Please sign in to comment.