├── .eslintignore
├── .eslintrc.json
├── .flowconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Readme.md
├── core
├── analyzer
│ ├── Readme.md
│ ├── common.js
│ ├── index.js
│ └── percentiles.js
├── analyzerFactory.js
├── array_manipulation
│ ├── base.js
│ ├── kfold.js
│ ├── rouletteWheel.js
│ ├── sample.js
│ ├── shuffle.js
│ ├── smooth
│ │ ├── index.js
│ │ ├── movingAverage.js
│ │ └── smoothProxy.js
│ └── winsorize.js
├── decorators.js
├── distributionFactory.js
├── help.js
├── interfaces.js
├── methods
│ ├── Readme.md
│ ├── bates.js
│ ├── bernoulli.js
│ ├── beta.js
│ ├── betaprime.js
│ ├── binomial.js
│ ├── cauchy.js
│ ├── chi.js
│ ├── chisquare.js
│ ├── compertz.js
│ ├── delaporte.js
│ ├── erlang.js
│ ├── exponential.js
│ ├── extremevalue.js
│ ├── fatigue.js
│ ├── gamma.js
│ ├── geometric.js
│ ├── index.js
│ ├── irwinhall.js
│ ├── laplace.js
│ ├── logistic.js
│ ├── lognormal.js
│ ├── negativebinomial.js
│ ├── normal.js
│ ├── pareto.js
│ ├── poisson.js
│ ├── rayleigh.js
│ ├── student.js
│ ├── triangular.js
│ ├── uniform.js
│ ├── weibull.js
│ └── zipf.js
├── prng
│ ├── BasicPRNG.js
│ ├── CoveyouPRNG.js
│ ├── DefaultPRNG.js
│ ├── Dx1597PRNG.js
│ ├── Gfsr4PRNG.js
│ ├── KissPRNG.js
│ ├── Knuthran2PRNG.js
│ ├── MarsenneTwisterPRNG.js
│ ├── Mrg5PRNG.js
│ ├── ParkMillerPRNG.js
│ ├── PhiloxPRNG.js
│ ├── R250PRNG.js
│ ├── Swb2712PRNG.js
│ ├── Taus113PRNG.js
│ ├── Tt800PRNG.js
│ ├── TucheiPRNG.js
│ ├── XorshiftPRNG.js
│ ├── XorwowPRNG.js
│ └── prngProxy.js
├── types.js
├── uidFactory.js
└── utils
│ ├── Readme.md
│ ├── encoders
│ ├── base32.js
│ ├── base58.js
│ ├── base62.js
│ ├── base64.js
│ ├── commonEncoder.js
│ └── encoderProxy.js
│ ├── hash
│ ├── hashProxy.js
│ ├── index.js
│ ├── jenkins.js
│ └── murmur3.js
│ ├── randomColor.js
│ ├── string_utils
│ ├── index.js
│ ├── randomBitStringHelper.js
│ └── uid
│ │ ├── baseUid.js
│ │ ├── betterguid.js
│ │ ├── index.js
│ │ ├── ksuid.js
│ │ ├── shortuuid.js
│ │ ├── sno.js
│ │ ├── snowflake.js
│ │ ├── sonyflake.js
│ │ ├── ulid.js
│ │ ├── uuid.js
│ │ └── xid.js
│ └── utils.js
├── gulpfile.js
├── index.js
├── package.json
├── smooth.png
└── test
├── analyzer.test.js
├── array_manipulation.seed.test.js
├── array_manipulation.test.js
├── distributions.seed.test.js
├── distributions.test.js
├── encoder.test.js
├── hash.test.js
├── index.test.js
├── prng.test.js
├── randomColor.test.js
├── sample_beta.array
├── sample_normal.array
├── stringutils.test.js
├── testBuild.js
├── utils.test.js
├── winsorize_after.data
├── winsorize_before.data
└── winsorize_limits.data
/.eslintignore:
--------------------------------------------------------------------------------
1 | # lib folder, which contains bundled files
2 | lib/
3 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "node": true,
7 | "mocha": true
8 | },
9 | "parser": "babel-eslint",
10 | "plugins": [
11 | "flowtype",
12 | "mocha"
13 | ],
14 | "extends": [
15 | "eslint:recommended",
16 | "plugin:flowtype/recommended"
17 | ],
18 | "parserOptions": {
19 | "ecmaFeatures": {
20 | "experimentalObjectRestSpread": true,
21 | "jsx": true
22 | },
23 | "sourceType": "module"
24 | },
25 | "rules": {
26 | "mocha/no-exclusive-tests": "error",
27 | "indent": [
28 | "error",
29 | 4
30 | ],
31 | "quotes": [
32 | "error",
33 | "single"
34 | ],
35 | "semi": [
36 | "error",
37 | "always"
38 | ],
39 | "comma-dangle": [
40 | "error",
41 | "never"
42 | ],
43 | "no-console": [
44 | "error",
45 | {"allow": [
46 | "log",
47 | "error"
48 | ]}
49 | ]
50 | }
51 | }
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/lib/*
3 | .*/test/*
4 | .*/node_modules/*
5 | .*/.nyc_output/*
6 | [include]
7 | .*/
8 | [libs]
9 |
10 | [lints]
11 |
12 | [options]
13 | module.ignore_non_literal_requires=true
14 | [strict]
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 |
3 | # Node Modules
4 | node_modules/
5 |
6 | # IDE environment
7 | .idea/
8 |
9 | # lib Folder - This folder contains bundled files
10 | lib/
11 |
12 | # Log files
13 | /*.log
14 |
15 | # nyc reports
16 | .nyc_output/
17 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | node_modules/
3 |
4 | .nyc_output/
5 | .idea/
6 |
7 | test/
8 |
9 | core/
10 |
11 | gulpfile.js
12 |
13 | .eslintignore
14 |
15 | .eslintrc.json
16 |
17 | .flowconfig
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os: linux
2 | dist: bionic
3 | language: node_js
4 | branches:
5 | only:
6 | - master
7 | before_install:
8 | - echo "TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST"
9 | - echo "TRAVIS_BRANCH=$TRAVIS_BRANCH"
10 | - if [[ $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_BRANCH == "master" ]]; then echo "NOT a PullRequest, skipping tests" && travis_terminate 0; fi
11 | before_script:
12 | - npm install -g gulp
13 | - npm run lint
14 | - npm run build:node
15 | jobs:
16 | include:
17 | - stage: Tests (Latest NodeJS LTS)
18 | name: Analyzer tests
19 | script: npm run test:analyzer
20 | node_js: "lts/*"
21 | - name: Array manipulation tests
22 | script: npm run test:array-manipulation
23 | node_js: "lts/*"
24 | - name: Distribution tests
25 | script: npm run test:distribution
26 | node_js: "lts/*"
27 | - name: Encoder tests
28 | script: npm run test:encoder
29 | node_js: "lts/*"
30 | - name: Hash tests
31 | script: npm run test:hash
32 | node_js: "lts/*"
33 | - name: Index tests
34 | script: npm run test:index
35 | node_js: "lts/*"
36 | - name: PRNG tests
37 | script: npm run test:prng
38 | node_js: "lts/*"
39 | - name: Random color tests
40 | script: npm run test:random-color
41 | node_js: "lts/*"
42 | - name: String utils tests
43 | script: npm run test:stringutils
44 | node_js: "lts/*"
45 | - name: Utils tests
46 | script: npm run test:utils
47 | node_js: "lts/*"
48 | - stage: Tests (NodeJS 12.x)
49 | name: Analyzer tests
50 | script: npm run test:analyzer
51 | node_js: "12"
52 | - name: Array manipulation tests
53 | script: npm run test:array-manipulation
54 | node_js: "12"
55 | - name: Distribution tests
56 | script: npm run test:distribution
57 | node_js: "12"
58 | - name: Encoder tests
59 | script: npm run test:encoder
60 | node_js: "12"
61 | - name: Hash tests
62 | script: npm run test:hash
63 | node_js: "12"
64 | - name: Index tests
65 | script: npm run test:index
66 | node_js: "12"
67 | - name: PRNG tests
68 | script: npm run test:prng
69 | node_js: "12"
70 | - name: Random color tests
71 | script: npm run test:random-color
72 | node_js: "12"
73 | - name: String utils tests
74 | script: npm run test:stringutils
75 | node_js: "12"
76 | - name: Utils tests
77 | script: npm run test:utils
78 | node_js: "12"
79 | - stage: Tests (NodeJS 10.x)
80 | name: Analyzer tests
81 | script: npm run test:analyzer
82 | node_js: "10"
83 | - name: Array manipulation tests
84 | script: npm run test:array-manipulation
85 | node_js: "10"
86 | - name: Distribution tests
87 | script: npm run test:distribution
88 | node_js: "10"
89 | - name: Encoder tests
90 | script: npm run test:encoder
91 | node_js: "10"
92 | - name: Hash tests
93 | script: npm run test:hash
94 | node_js: "10"
95 | - name: Index tests
96 | script: npm run test:index
97 | node_js: "10"
98 | - name: PRNG tests
99 | script: npm run test:prng
100 | node_js: "10"
101 | - name: Random color tests
102 | script: npm run test:random-color
103 | node_js: "10"
104 | - name: String utils tests
105 | script: npm run test:stringutils
106 | node_js: "10"
107 | - name: Utils tests
108 | script: npm run test:utils
109 | node_js: "10"
110 | cache:
111 | directories:
112 | - "node_modules"
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
3 |
4 |
5 | # [2.3.0](https://github.com/AlexeySKiselev/randomjs/compare/master@%7B1day%7D...master) (2018-12-15)
6 |
7 | ### Features
8 |
9 | * **winsorize:** Winsorization replaces extreme data values with less extreme values ([c9098eb](https://github.com/AlexeySKiselev/randomjs/commit/c9098eb980776cbf7f8608e74434eda0555f5a4c))
10 |
11 |
12 | # [2.2.0](https://github.com/AlexeySKiselev/randomjs/compare/969a2a5...e2143fc) (2018-08-24)
13 |
14 | ### Features
15 |
16 | * **sample:** Get "k" random elements from array/string with "n" elements (0 < k <= n) ([8f86od9](https://github.com/AlexeySKiselev/randomjs/8f86od9))
17 | * **shuffle:** Returns a random permutation of array/string ([6c1b91f](https://github.com/AlexeySKiselev/randomjs/6c1b91f))
18 |
--------------------------------------------------------------------------------
/core/analyzer/Readme.md:
--------------------------------------------------------------------------------
1 | # Analyzer
2 |
3 | Analyzer object contains next fields:
4 |
5 | {
6 | min: [min value: number],
7 | max: [max value: number],
8 | mean: [mean value: number],
9 | median: [median value: number],
10 | mode: [array of modes: Array[number]],
11 | variance: [variance: number],
12 | standard_deviation: [standard deviation: number]
13 | entropy: [entropy: number],
14 | skewness: [skewness: number],
15 | kurtosis: [kurtosis: number],
16 | pdf: {
17 | values: [distribution values: Array[number]],
18 | probabilities: [distribution values probabilities: Array[number]]
19 | },
20 | cdf: {
21 | values: [distribution values: Array[number]],
22 | probabilities: [distribution values probabilities: Array[number]]
23 | },
24 | quartiles: {
25 | q1: [25% quartile: number],
26 | q2: [50% quartile: number],
27 | q3: [75% quartile: number]
28 | },
29 | interquartile_range: [number]
30 | }
31 |
32 |
33 | Also you can calculate percentiles using .percentile() function. Input value must be between 0 and 1:
34 |
35 | // when you point single value
36 | analyze([1, 2, 3, 4, 5, 6]).percentile(0.6).then((p) => {
37 | console.log(p); // output 60% percentile
38 | });
39 | // or you can point a list of values
40 | analyze([1, 2, 3, 4, 5, 6]).percentile([0.2, 0.6, 0.3]).then((p) => {
41 | console.log(p); // output [20%, 60%, 30%] percentiles
42 | });
43 |
44 |
--------------------------------------------------------------------------------
/core/analyzer/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Returns a hash map of all analyzer methods
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import { IAnalyzerSingleton } from '../interfaces';
8 |
9 | const Common: IAnalyzerSingleton = require('./common');
10 | const Percentiles: IAnalyzerSingleton = require('./percentiles');
11 | const AnalyzerMethodsMapper: {[string]: IAnalyzerSingleton} = {
12 | Common,
13 | Percentiles
14 | };
15 |
16 | module.exports = AnalyzerMethodsMapper;
17 |
--------------------------------------------------------------------------------
/core/analyzer/percentiles.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Common analyzer's methods
4 | * This class contains max, min, mean, median, mode, entropy, variance ... methods
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type { PercentileInput, RandomArray } from '../types';
9 | import { AnalyzerPublicMethod, AnalyzerPublicFunction, AnalyzerSingleton } from '../decorators';
10 | import type { IAnalyzerSingleton } from '../interfaces';
11 |
12 | @AnalyzerSingleton
13 | class Percentiles implements IAnalyzerSingleton {
14 | /**
15 | * Main input Array
16 | */
17 | randomArray: RandomArray;
18 |
19 | constructor(randomArray: RandomArray): void {
20 | this.randomArray = randomArray.slice().sort((a, b) => {
21 | return a - b;
22 | });
23 | }
24 |
25 | /**
26 | * Calculate particular percentile
27 | * @returns {number} or {undefined} in case of impossible percentile
28 | * @private
29 | */
30 | _calculate_percentile(value: number): ?number {
31 | if(value < 0 || value > 1 || typeof value !== 'number') {
32 | return undefined;
33 | }
34 | if(value === 0 || value === 1) {
35 | return this.randomArray[(this.randomArray.length - 1) * value];
36 | }
37 | let percentile_index: number = Math.floor(this.randomArray.length * value) - 1;
38 | if(percentile_index < 0) {
39 | return undefined;
40 | }
41 | if(this.randomArray.length % 2 === 0){
42 | return this.randomArray[percentile_index] + (1 - value) * (this.randomArray[percentile_index + 1] - this.randomArray[percentile_index]);
43 | }
44 | return this.randomArray[percentile_index + 1];
45 | }
46 |
47 | /**
48 | * Quartiles - 25%, 50% (median), 75%
49 | * @returns {{q1: number, q2: number, q3: number}}
50 | */
51 | @AnalyzerPublicMethod
52 | get quartiles(): {[string]: ?number} {
53 | return {
54 | 'q1': this._calculate_percentile(0.25),
55 | 'q2': this._calculate_percentile(0.5),
56 | 'q3': this._calculate_percentile(0.75)
57 | };
58 | }
59 |
60 | /**
61 | * Median value - 50% Quartile
62 | * @returns {number}
63 | */
64 | @AnalyzerPublicMethod
65 | get median(): ?number {
66 | return this._calculate_percentile(0.5);
67 | }
68 |
69 | /**
70 | * Percentiles
71 | * Keep correct order for percentiles
72 | * @returns number or list of value depends on input
73 | */
74 | @AnalyzerPublicFunction
75 | percentile(value: PercentileInput): ?PercentileInput {
76 | if(typeof value === 'number') {
77 | return this._calculate_percentile(value);
78 | } else if(Array.isArray(value)) {
79 | let percentile_array: Array = [];
80 | for(let i = 0; i < value.length; i += 1) {
81 | percentile_array[i] = this._calculate_percentile(value[i]);
82 | }
83 | return percentile_array;
84 | }
85 | throw new Error('Analyzer: input must be a number or an Array of numbers');
86 | }
87 |
88 | /**
89 | * Public method for analyzer
90 | * @returns {number} - Interquartile range
91 | */
92 | @AnalyzerPublicMethod
93 | get interquartile_range(): ?number {
94 | let q1: ?number = this._calculate_percentile(0.25),
95 | q3: ?number = this._calculate_percentile(0.75);
96 | if(typeof q1 !== 'undefined' && typeof q3 !== 'undefined') {
97 | // $FlowFixMe
98 | return q3 - q1;
99 | }
100 | return undefined;
101 | }
102 | }
103 |
104 | module.exports = Percentiles;
105 |
--------------------------------------------------------------------------------
/core/array_manipulation/base.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Array manipulation base class
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | class ArrayManipulation {
8 | /**
9 | * Validate input
10 | * @param input: any
11 | * @param allowObjects: boolean - allow objects checking
12 | * @private
13 | */
14 | _validateInput(input: any, allowObjects: boolean = true): void {
15 | if(Array.isArray(input) || typeof input === 'string') {
16 | if(input.length === 0) {
17 | throw new TypeError('Input length must be greater then zero');
18 | }
19 | } else if(allowObjects && typeof input === 'object') {
20 | if(Object.keys(input).length === 0) {
21 | throw new TypeError('Input object must have at least one key');
22 | }
23 | } else if(allowObjects) {
24 | throw new TypeError('Input must be array, string or object');
25 | } else
26 | throw new TypeError('Input must be array or string');
27 | }
28 | }
29 |
30 | export default ArrayManipulation;
31 |
--------------------------------------------------------------------------------
/core/array_manipulation/rouletteWheel.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Roulette wheel algorithm using stochastic acceptance
4 | * "Roulette-wheel selection via stochastic acceptance, A. Lipowski, D. Lipowska" (https://arxiv.org/pdf/1109.3627.pdf)
5 | * Returns index of element due to its weight
6 | * Does not mutate weights array
7 | * Created by Alexey S. Kiselev
8 | */
9 |
10 | import type {IRouletteWheel, IPRNGProxy} from '../interfaces';
11 | import type {NumberString} from '../types';
12 |
13 | import {PRNGProxy, DEFAULT_GENERATOR} from '../prng/prngProxy';
14 |
15 | class RouletteWheel implements IRouletteWheel {
16 |
17 | _weights: Array;
18 | _w_length: number;
19 | _w_max: number;
20 | _prng: IPRNGProxy;
21 |
22 | constructor(weights: Array, options: {[string]: any} = {}) {
23 | this._validate(weights);
24 | this._w_max = this._getMaxWeight(weights);
25 | this._weights = weights; // does not mutate original weights array, it is safe
26 | this._w_length = weights.length;
27 | this._prng = new PRNGProxy();
28 | if (this._isDefined(options.seed)) {
29 | this.seed(options.seed);
30 | }
31 | this.setPrng(this._isDefined(options.prng) ? options.prng : DEFAULT_GENERATOR, true);
32 | }
33 |
34 | /**
35 | * Check whether option is defined
36 | * @param {any} option
37 | * @private
38 | */
39 | _isDefined(option: any): boolean {
40 | return option !== null && typeof option !== 'undefined';
41 | }
42 |
43 | /**
44 | * Validate weight array
45 | * @param {any} weights
46 | * @private
47 | */
48 | _validate(weights: any): void {
49 | if (!Array.isArray(weights)) {
50 | throw new Error('RouletteWheel: weights must be array');
51 | }
52 | if (weights.length < 1) {
53 | throw new Error('RouletteWheel: weights array must contain at least one element');
54 | }
55 | for (let i = 0; i < weights.length; i += 1) {
56 | if (typeof weights[i] !== 'number' || weights[i] <= 0) {
57 | throw new Error('RouletteWheel: weights must be a positive numbers');
58 | }
59 | }
60 | }
61 |
62 | /**
63 | * Calculates max weight
64 | * @param {Array} weights
65 | * @private
66 | */
67 | _getMaxWeight(weights: Array): number {
68 | let _max: number = -Infinity;
69 | for (let i = 0; i < weights.length; i += 1) {
70 | if (weights[i] > _max) {
71 | _max = weights[i];
72 | }
73 | }
74 | return _max;
75 | }
76 |
77 | /**
78 | * Return random index of weights array
79 | * @private
80 | */
81 | _getRandomWeightIndex(): number {
82 | return Math.floor(this._prng.next() * this._w_length);
83 | }
84 |
85 | /**
86 | * In case of using seeded PRNG reset prng to initial state
87 | * @public
88 | */
89 | reset(): void {
90 | this._prng.random();
91 | }
92 |
93 | /**
94 | * Set seed value for local PRNG
95 | * @param {NumberString} seed_value
96 | * @public
97 | */
98 | seed(seed_value: ?NumberString): void {
99 | this._prng.seed(seed_value);
100 | }
101 |
102 | /**
103 | * Set local PRNG algorithm
104 | * @param {string} prng_name
105 | * @param {boolean} reset - whether reset PRNG state for new one or not
106 | * @public
107 | */
108 | setPrng(prng_name: string, reset?: boolean = false): void {
109 | this._prng.set_prng(prng_name);
110 | if (reset) {
111 | this.reset();
112 | }
113 | }
114 |
115 | /**
116 | * Select random element due to weights
117 | * @public
118 | */
119 | // $FlowFixMe - will be always accepted as weights are validated, number will ba always returned
120 | select(): number {
121 | /* eslint-disable no-constant-condition */
122 | while (true) {
123 | const _index = this._getRandomWeightIndex();
124 | if (this._prng.next() < (this._weights[_index] / this._w_max) ) {
125 | return _index;
126 | }
127 | }
128 | }
129 | }
130 |
131 | export default RouletteWheel;
132 |
--------------------------------------------------------------------------------
/core/array_manipulation/smooth/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Data smoothing using different algorithms
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type {ISmoothProxy} from '../../interfaces';
8 | import SmoothProxy from './smoothProxy';
9 |
10 | const smoothProxy: ISmoothProxy = new SmoothProxy();
11 | const defaultSmoothAlgorithmName: string = smoothProxy.getDefaultAlgorithmName();
12 |
13 | const smooth: Function = (name: string): ISmoothProxy => {
14 | smoothProxy.setSmoothAlgorithm(name || defaultSmoothAlgorithmName);
15 | return smoothProxy;
16 | };
17 |
18 | export default smooth(defaultSmoothAlgorithmName);
19 |
--------------------------------------------------------------------------------
/core/array_manipulation/smooth/smoothProxy.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Data smoothing proxy
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type { IAnalyzerSingleton, ISmooth, ISmoothProxy } from '../../interfaces';
8 | import type { RandomArray, SmoothData } from '../../types';
9 |
10 | import MovingAverage from './movingAverage';
11 |
12 | const AnalyzerMethods: {[string]: IAnalyzerSingleton} = require('../../analyzer');
13 | const DEFAULT_SMOOTH_ALGORITHM = 'moving_average';
14 |
15 | class SmoothProxy implements ISmoothProxy {
16 |
17 | _allowedSmoothAlgorithms: {[string]: any};
18 | _smoothAlgorithms: {[string]: ISmooth};
19 | _currentSmoothAlgorithm: ISmooth;
20 | _currentSmoothAlgorithmName: string;
21 |
22 | constructor() {
23 | this._allowedSmoothAlgorithms = {
24 | 'moving_average': MovingAverage
25 | };
26 | this._smoothAlgorithms = {};
27 | this.setSmoothAlgorithm(DEFAULT_SMOOTH_ALGORITHM);
28 | }
29 |
30 | /**
31 | * Apply smoothing using different algorithms and parameters
32 | * @param {RandomArray} data
33 | * @param {{[string]: any}} options
34 | * @returns {RandomArray}
35 | */
36 | smoothSync(data: RandomArray, options: ?{[string]: any} = {}): SmoothData {
37 | if (options && options.algorithm) {
38 | this.setSmoothAlgorithm(options.algorithm);
39 | }
40 |
41 | if (options && options.diff) {
42 | const smoothData: RandomArray = this._currentSmoothAlgorithm.smooth(data, options);
43 | return {
44 | smoothData,
45 | diff: this._constructDiff(data, smoothData)
46 | };
47 | }
48 |
49 | return this._currentSmoothAlgorithm.smooth(data, options);
50 | }
51 |
52 | smooth(data: RandomArray, options: ?{[string]: any} = {}): Promise {
53 | return new Promise((resolve, reject) => {
54 | setTimeout(() => {
55 | try {
56 | resolve(this.smoothSync(data, options));
57 | } catch (e) {
58 | reject(e.message);
59 | }
60 | }, 0);
61 | });
62 | }
63 |
64 | /**
65 | * Sets algorithm
66 | * @param {string} name
67 | */
68 | setSmoothAlgorithm(name: string): void {
69 | if (!this._allowedSmoothAlgorithms[name]) {
70 | throw new Error(`Smooth: algorithm ${name} is not allowed`);
71 | }
72 |
73 | if (!this._smoothAlgorithms[name]) {
74 | this._smoothAlgorithms[name] = new this._allowedSmoothAlgorithms[name]();
75 | }
76 |
77 | this._currentSmoothAlgorithmName = name;
78 | this._currentSmoothAlgorithm = this._smoothAlgorithms[name];
79 | }
80 |
81 | /**
82 | * Returns current algorithm name
83 | * @returns {string}
84 | */
85 | getAlgorithmName(): string {
86 | return this._currentSmoothAlgorithm.getName();
87 | }
88 |
89 | /**
90 | * Returns a list of allowed algorithms
91 | * @returns {Array}
92 | */
93 | listSmoothAlgorithms(): Array {
94 | return Object.keys(this._allowedSmoothAlgorithms);
95 | }
96 |
97 | /**
98 | * Returns default smooth algorithm name
99 | * @returns {string}
100 | */
101 | getDefaultAlgorithmName(): string {
102 | return DEFAULT_SMOOTH_ALGORITHM;
103 | }
104 |
105 | /**
106 | * Construct diff data and analyze it
107 | * @param {RandomArray} initialData
108 | * @param {RandomArray} smoothData
109 | * @returns {{[string]: any}}
110 | * @private
111 | */
112 | _constructDiff(initialData: RandomArray, smoothData: RandomArray): {[string]: any} {
113 | const diffData = [];
114 | for (let i = 0; i < initialData.length; i += 1) {
115 | diffData[i] = initialData[i] - smoothData[i];
116 | }
117 |
118 | // analyze diff
119 | const result: {[string]: any} = {
120 | diffData
121 | };
122 |
123 | Object.keys(AnalyzerMethods).forEach((mKey: string) => {
124 | const methodsClass = AnalyzerMethods[mKey].getInstance(diffData, {
125 | pdf: 20 // TODO: add it to smooth params
126 | });
127 | for (let prop: any in methodsClass.constructor.publicMethods) {
128 | result[prop] = methodsClass[prop];
129 | }
130 | });
131 |
132 | return result;
133 | }
134 | }
135 |
136 | export default SmoothProxy;
137 |
--------------------------------------------------------------------------------
/core/array_manipulation/winsorize.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Winsorize method
4 | * https://en.wikipedia.org/wiki/Winsorizing
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import ArrayManipulation from './base';
9 | import Percentiles from '../analyzer/percentiles';
10 |
11 | import type { IWinsorize } from '../interfaces';
12 | import type { PercentileInput, RandomArray } from '../types';
13 |
14 | class Winsorize extends ArrayManipulation implements IWinsorize {
15 |
16 | _percentile: Percentiles;
17 |
18 | constructor(): void {
19 | super();
20 | }
21 |
22 | winsorize(input: RandomArray, limits: PercentileInput = 0.05, mutate: boolean = true): RandomArray {
23 | this._validateInput(input, false);
24 | this._percentile = new Percentiles(input.slice());
25 |
26 | let _percentileLimits: Array = this._parseLimits(limits),
27 | _percentiles: any = this._percentile.percentile(_percentileLimits),
28 | min_percentile_value: number = this._binarySearch(_percentiles[0]),
29 | max_percentile_value: number = this._binarySearch(_percentiles[1]);
30 |
31 | if(mutate === false) {
32 | input = input.slice();
33 | }
34 |
35 | // Change input array values
36 | for(let i = 0; i < input.length; i += 1) {
37 | if(input[i] < _percentiles[0]) {
38 | input[i] = min_percentile_value;
39 | }
40 | if (input[i] > _percentiles[1]) {
41 | input[i] = max_percentile_value;
42 | }
43 | }
44 | return input;
45 | }
46 |
47 | _parseLimits(limits: any): Array {
48 | if(typeof limits === 'number') {
49 | if(limits >= 0.5 || limits <=0) {
50 | throw new Error('Winsorize: limits should be less then 0.5 and greater then 0');
51 | }
52 | return [limits, 1 - limits];
53 | } else if(Array.isArray(limits)) {
54 | let limit1: number = limits[0],
55 | limit2: number = limits[1];
56 | if(typeof limit1 !== 'number' || typeof limit2 !== 'number') {
57 | throw new Error('Winsorize: should point limits as numbers');
58 | }
59 | if(limit1 <= 0 || limit1 >= 1 || limit2 <= limit1 || limit2 >= 1) {
60 | throw new Error('Winsorize: You should point correct limits');
61 | }
62 | return [limit1, limit2];
63 | }
64 | return [0.05, 0.95];
65 | }
66 |
67 | /**
68 | * Binary search for elements less then lower percentile
69 | * and elements greater then upper percentile
70 | * @private
71 | */
72 | _binarySearch(value: number): number {
73 | let start: number = 0,
74 | stop: number = this._percentile.randomArray.length - 1,
75 | middle: number;
76 |
77 | while (start <= stop) {
78 | middle = Math.floor((stop + start) / 2);
79 | if (value < this._percentile.randomArray[middle]) {
80 | stop = middle - 1;
81 | } else if (value > this._percentile.randomArray[middle]) {
82 | start = middle + 1;
83 | } else {
84 | return this._percentile.randomArray[middle];
85 | }
86 | }
87 | return ((this._percentile.randomArray[start] - value) < (value - this._percentile.randomArray[stop]))
88 | ? this._percentile.randomArray[start]
89 | : this._percentile.randomArray[stop];
90 | }
91 | }
92 |
93 | export default Winsorize;
94 |
--------------------------------------------------------------------------------
/core/decorators.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Decorators for different purposes
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import { IAnalyzerMethods } from './interfaces';
8 | import type { AnalyzerPublicProperties } from './types';
9 |
10 | /**
11 | * Decorator for making method public for AnalyzerFactory
12 | * I this decorator I am going to check publicMethods property
13 | * If this object does not exist - create empty object
14 | * Then add name of public method to this object via propertyKey value
15 | * In class add @PublicMethod string before every public method
16 | */
17 | export const AnalyzerPublicMethod = (Target: { [property: string ]: AnalyzerPublicProperties }, propertyKey: string): void => {
18 | if(!Target.constructor.publicMethods){
19 | Target.constructor.publicMethods = {};
20 | }
21 | Target.constructor.publicMethods[propertyKey] = 1;
22 | };
23 |
24 | export const AnalyzerPublicFunction = (Target: { [property: string ]: AnalyzerPublicProperties }, propertyKey: string): void => {
25 | if(!Target.constructor.publicFunctions){
26 | Target.constructor.publicFunctions = {};
27 | }
28 | Target.constructor.publicFunctions[propertyKey] = 1;
29 | };
30 |
31 | export const AnalyzerSingleton = (Target: any): void => {
32 | /**
33 | * Create instance object initially assigned to null
34 | */
35 | Target._instance = null;
36 |
37 | /**
38 | * Add static method getInstance to target
39 | * Instead of using "new" keyword I use getInstance method
40 | */
41 | Object.assign(Target, {
42 | getInstance: (...args: any): IAnalyzerMethods => {
43 | /**
44 | * If instance haven't created - create it with arguments
45 | * If instance created - update params via calling constructor without creating new object
46 | * In total returns instance
47 | */
48 | if(!Target._instance) {
49 | Target._instance = new Target(...args);
50 | } else {
51 | Target._instance.constructor.apply(Target._instance, args);
52 | }
53 |
54 | return Target._instance;
55 | }
56 | });
57 | };
58 |
59 | export function Singleton(Target: any): void {
60 | /**
61 | * Create instance object initially assigned to null
62 | */
63 | Target._instance = null;
64 |
65 | /**
66 | * Add static method getInstance to target
67 | * Instead of using "new" keyword I use getInstance method
68 | */
69 | Object.defineProperty(Target, 'getInstance', {
70 | value: (...args: any): T => {
71 | /**
72 | * If instance haven't created - create it with arguments
73 | * If instance created - update params via calling constructor without creating new object
74 | * In total returns instance
75 | */
76 | if(!Target._instance) {
77 | Target._instance = new Target(...args);
78 | } else {
79 | Target._instance.constructor.apply(Target._instance, args);
80 | }
81 | return Target._instance;
82 | }
83 | });
84 | }
85 |
--------------------------------------------------------------------------------
/core/help.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Help function for Distributions
3 | * Created by Alexey S. Kiselev
4 | */
5 |
6 | module.exports = {
7 | 'uniform': 'Uniform Distribution',
8 | 'normal': 'Gaussian Distribution',
9 | 'bernoulli': 'Bernoulli Distribution',
10 | 'binomial': 'Binomial Distribution'
11 | };
12 |
--------------------------------------------------------------------------------
/core/methods/Readme.md:
--------------------------------------------------------------------------------
1 | | Name | Parameters | Usage |
2 | | --- | --- | --- |
3 | | Uniform distribution | `min` - any value, `max` - any value, `min` < `max` | `unirand.uniform(min, max).random()` |
4 | | Normal (Gaussian) distribution | `mu` - any value, `sigma` > 0 | `unirand.normal(mu, sigma).random()` |
5 | | Bates distribution | `n` - integer, `n` >= 1, `a` - any value, `b` - any value, `b` > `a` | `unirand.bates(n, a, b).random()` |
6 | | Bernoulli distribution | `p` - float number, 0 <= `p` <= 1 | `unirand.bernoulli(p).random()` |
7 | | Beta distribution | `alpha` - integer, `alpha` > 0, `beta` > integer, `beta` > 0 | `unirand.beta(alpha, beta).random()` |
8 | | BetaPrime distribution | `alpha` - integer, `alpha` > 0, `beta` > integer, `beta` > 0 | `unirand.betaprime(alpha, beta).random()` |
9 | | Binomial distribution | `n` - integer, `n` > 0, `p` - float number, 0 <= `p` <= 1 | `unirand.binomial(n, p).random()` |
10 | | Cauchy (Lorenz) distribution | `x` - any value, `gamma` > 0 | `unirand.cauchy(x, gamma).random()` |
11 | | Chi distribution | `k` - integer, `k` > 0 | `unirand.chi(k).random()` |
12 | | Chi Square distribution | `k` - integer, `k` > 0 | `unirand.chisquare(k).random()` |
13 | | Compertz distribution | `nu` > 0 - float value, `b` > 0 - float value | `unirand.compertz(nu, b).random()` |
14 | | Delaporte distribution | `alpha` > 0 - float value, `beta` > 0 - float value, `lambda` > 0 - float value | `unirand.delaporte(alpha, beta, lambda).random()` |
15 | | Erlang distribution | `k` - integer, `k` > 0, `mu` - float value, `mu` > 0 | `unirand.erlang(k, mu).random()` |
16 | | Exponential distribution | `lambda` - float value, `lambda` > 0 | `unirand.exponential(lambda).random()` |
17 | | Extreme (Gumbel-type) Value distribution | `mu` - any value, `sigma` - float number, `sigma` > 0 | `unirand.extremevalue(mu, sigma).random()` |
18 | | Fatigue life distribution | `alpha` > 0, `beta` > 0 | `unirand.fatigue(alpha, beta).random()` |
19 | | Gamma distribution | `alpha` - float value, `alpha` > 0, `beta` - integer, `beta` > 0 | `unirand.gamma(alpha, beta).random()` |
20 | | Geometric distribution | `p` - float value, 0 <= `p` <= 1 | `unirand.geometric(p).random()` |
21 | | Irwin-Hall distribution | `n` - integer, `n` > 0 | `unirand.irwinhall(n).random()` |
22 | | Laplace distribution | `mu` - any value, `b` - float value, `b` > 0 | `unirand.laplace(mu, b).random()` |
23 | | Logistic distribution | `mu` - any value, `s` - float value, `s` > 0 | `unirand.logistic(mu, s).random()` |
24 | | Lognormal distribution | `mu` - any value, `sigma` - float value, `sigma` > 0 | `unirand.lognormal(mu, sigma).random()` |
25 | | Negative Binomial distribution | `r` - integer, `r` > 0, `p` - float value, 0 <= `p` <= 1 | `unirand.negativebinomial(r, p).random()` |
26 | | Pareto distribution | `xm` - float value, `xm` > 0, `alpha` - float value, `alpha` > 0 | `unirand.pareto(xm, alpha).random()` |
27 | | Poisson distribution | `lambda` - integer, `lambda` > 0 | `unirand.poisson(lambda).random()` |
28 | | Rayleigh distribution | `sigma` - float value, `sigma` > 0 | `unirand.rayleigh(sigma).random()` |
29 | | Student's t-distribution | `v` - integer, `v` > 0 | `unirand.student(v).random()` |
30 | | Triangular distribution | `a`, `b`, `c` - any number, `b` > `a`, `a` <= `c` <= `b` | `unirand.triangular(a, b, c).random()` |
31 | | Weibull distribution | `k` - float value, `k` > 0, `lambda` - float value, `lambda` > 0 | `unirand.weibull(k, lambda).random()` |
32 | | Zipf distribution | `alpha` - float value, `alpha` >= 0, `shape` - integer, `shape` > 1 | `unirand.zipf(alpha, shape).random()` |
33 |
--------------------------------------------------------------------------------
/core/methods/bates.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Bates distribution
4 | * https://en.wikipedia.org/wiki/Bates_distribution
5 | * @param a: number (any value)
6 | * @param b: number (any value), b > a
7 | * @param n: integer, n >= 1
8 | * @returns random number with Bates distribution
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | let Uniform = require('./uniform');
15 |
16 | class Bates implements IDistribution {
17 | a: number;
18 | b: number;
19 | n: number;
20 | _uniform: Uniform;
21 |
22 | constructor(n: number, a: number = 0, b: number = 1) {
23 | this.n = Math.floor(n);
24 | this.a = Math.min(a, b);
25 | this.b = Math.max(a, b);
26 | this._uniform = new Uniform(this.a, this.b);
27 | }
28 |
29 | /**
30 | * Generates a random number
31 | * @returns a Bates distributed number
32 | */
33 | random(): number {
34 | let m: number = 0,
35 | random_number: number = 0,
36 | random: RandomArray = this._uniform.distribution(this.n);
37 | for(let k = 0; k < this.n; k += 1) {
38 | m += 1;
39 | random_number += (random[k] - random_number) / m;
40 | }
41 | return random_number;
42 | }
43 |
44 | /**
45 | * Generates next seeded random number
46 | * @returns {number}
47 | */
48 | next(): number {
49 | let m: number = 0,
50 | random_number: number = 0,
51 | random: number;
52 | for(let k = 0; k < this.n; k += 1) {
53 | m += 1;
54 | random = this._uniform.next();
55 | random_number += (random - random_number) / m;
56 | }
57 | return random_number;
58 | }
59 |
60 | /**
61 | * Generates random distribution
62 | * @returns an array with Bates distributed numbers
63 | */
64 | distribution(n: number): RandomArray {
65 | let batesArray: RandomArray = [],
66 | random: RandomArray = this._uniform.distribution(n * this.n),
67 | m: number,
68 | random_number: number;
69 | for(let i: number = 0; i < n; i += 1){
70 | m = 0;
71 | random_number = 0;
72 | for(let k = 0; k < this.n; k += 1) {
73 | m += 1;
74 | random_number += (random[i * this.n + k] - random_number) / m;
75 | }
76 | batesArray[i] = random_number;
77 | }
78 | return batesArray;
79 | }
80 |
81 | isError(): MethodError {
82 | if(!this.a && this.a !== 0) {
83 | return {error: 'Bates distribution: you should point "a" numerical value'};
84 | }
85 |
86 | if(!this.b && this.b !== 0) {
87 | return {error: 'Bates distribution: you should point "b" numerical value'};
88 | }
89 |
90 | if(!this.n || this.n < 1) {
91 | return {error: 'Bates distribution: you should point "n" positive integer value'};
92 | }
93 |
94 | if(this.a === this.b) {
95 | return {error: 'Bates distribution: Parameter "b" must be greater then "a"'};
96 | }
97 | return { error: false };
98 | }
99 |
100 | /**
101 | * Refresh method
102 | * @param newA: number - new parameter "a"
103 | * @param newB: number - new parameter "b"
104 | * @param newN: number - new parameter "n"
105 | * This method does not return values
106 | */
107 | refresh(newN: number, newA: number = 0, newB: number = 1): void {
108 | this.n = Math.floor(newN);
109 | this.a = Math.min(newA, newB);
110 | this.b = Math.max(newA, newB);
111 | }
112 |
113 | /**
114 | * Class .toString method
115 | * @returns {string}
116 | */
117 | toString(): string {
118 | let info = [
119 | 'Bates Distribution',
120 | `Usage: unirand.bates(${this.n}, ${this.a}, ${this.b}).random()`
121 | ];
122 | return info.join('\n');
123 | }
124 |
125 | /**
126 | * Mean value
127 | * Information only
128 | * For calculating real mean value use analyzer
129 | */
130 | get mean(): number {
131 | return (this.a + this.b) / 2;
132 | }
133 |
134 | /**
135 | * Variance value
136 | * Information only
137 | * For calculating real variance value use analyzer
138 | */
139 | get variance(): number {
140 | return Math.pow(this.b - this.a, 2) / (12 * this.n);
141 | }
142 |
143 | /**
144 | * Skewness value
145 | * Information only
146 | * For calculating real skewness value use analyzer
147 | */
148 | get skewness(): number {
149 | return 0;
150 | }
151 |
152 | /**
153 | * Kurtosis value
154 | * Information only
155 | */
156 | get kurtosis(): number {
157 | return -1.2 / this.n;
158 | }
159 |
160 | /**
161 | * All parameters of distribution in one object
162 | * Information only
163 | */
164 | get parameters(): {} {
165 | return {
166 | mean: this.mean,
167 | variance: this.variance,
168 | skewness: this.skewness,
169 | kurtosis: this.kurtosis
170 | };
171 | }
172 | }
173 |
174 | module.exports = Bates;
175 |
--------------------------------------------------------------------------------
/core/methods/bernoulli.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Bernoulli Distribution
5 | * https://en.wikipedia.org/wiki/Bernoulli_distribution
6 | * This is discreet distribution
7 | * @param p: number (0 <= p <= 1) - Probability of success
8 | * @returns 0 or 1
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type {MethodError, RandomArray} from '../types';
13 | import prng from '../prng/prngProxy';
14 | import type { IDistribution } from '../interfaces';
15 |
16 | class Bernoulli implements IDistribution {
17 | p: number;
18 |
19 | constructor(p: number): void {
20 | this.p = Number(p);
21 | }
22 |
23 | /**
24 | * Generates a random number
25 | * @returns a Bernoulli distributed number
26 | * This method returns only "1" or "0"
27 | */
28 | random(): number {
29 | return this._random((prng.random(): any));
30 | }
31 |
32 | /**
33 | * Generates next seeded random number
34 | * @returns {number}
35 | */
36 | next(): number {
37 | return this._random(prng.next());
38 | }
39 |
40 | _random(u: number): 0 | 1 {
41 | return (u <= this.p) ? 1 : 0;
42 | }
43 |
44 | /**
45 | * Generates Bernoulli distributed numbers
46 | * @param n: number - Number of elements in resulting array, n > 0
47 | * @returns Array - Bernoulli distributed numbers
48 | */
49 | distribution(n: number): Array {
50 | let bernoulliArray: Array = [],
51 | random: RandomArray = (prng.random(n): any);
52 | for(let i: number = 0; i < n; i += 1){
53 | bernoulliArray[i] = this._random(random[i]);
54 | }
55 | return bernoulliArray;
56 | }
57 |
58 | /**
59 | * Error handling
60 | * For this distribution parameter "p" must be 0 <= p <= 1
61 | * @returns {boolean}
62 | */
63 | isError(): MethodError {
64 | if(!this.p && this.p !== 0) {
65 | return {error: 'Bernoulli distribution: you should point "p" numerical value'};
66 | }
67 |
68 | if(this.p < 0 || this.p > 1) {
69 | return {error: 'Bernoulli distribution: Parameter "p" must be from 0 to 1 inclusive'};
70 | }
71 | return { error: false };
72 | }
73 |
74 | /**
75 | * Refresh method
76 | * @param newP: number - new parameter "p"
77 | * This method does not return values
78 | */
79 | refresh(newP: number): void {
80 | this.p = Number(newP);
81 | }
82 |
83 | /**
84 | * Class .toString method
85 | * @returns {string}
86 | */
87 | toString(): string {
88 | let info = [
89 | 'Bernoulli Distribution',
90 | `Usage: unirand.bernoulli(${this.p}).random()`
91 | ];
92 | return info.join('\n');
93 | }
94 |
95 | /**
96 | * Mean value
97 | * Information only
98 | * For calculating real mean value use analyzer
99 | */
100 | get mean(): number {
101 | return this.p;
102 | }
103 |
104 | /**
105 | * Median value
106 | * Information only
107 | * For calculating real median value use analyzer
108 | */
109 | get median(): number {
110 | if(this.p < (1 - this.p)){
111 | return 0;
112 | } else if(this.p === 0.5){
113 | return 0.5;
114 | } else {
115 | return 1;
116 | }
117 | }
118 |
119 | /**
120 | * Variance value
121 | * Information only
122 | * For calculating real variance value use analyzer
123 | */
124 | get variance(): number {
125 | return this.p * (1 - this.p);
126 | }
127 |
128 | /**
129 | * Skewness value
130 | * Information only
131 | * For calculating real skewness value use analyzer
132 | */
133 | get skewness(): number {
134 | return (1 - 2 * this.p) / Math.sqrt(this.p * (1 - this.p));
135 | }
136 |
137 | /**
138 | * Entropy value
139 | * Information only
140 | * For calculating real entropy value use analyzer
141 | */
142 | get entropy(): number {
143 | return -1 * (Math.log(1- this.p) * (1 - this.p) + Math.log(this.p) * this.p);
144 | }
145 |
146 | /**
147 | * Fisher information
148 | * Information only
149 | */
150 | get fisher(): number {
151 | return 1 / (this.p * (1 - this.p));
152 | }
153 |
154 | /**
155 | * All parameters of distribution in one object
156 | * Information only
157 | */
158 | get parameters(): {} {
159 | return {
160 | mean: this.mean,
161 | median: this.median,
162 | variance: this.variance,
163 | skewness: this.skewness,
164 | entropy: this.entropy,
165 | fisher: this.fisher
166 | };
167 | }
168 | }
169 |
170 | module.exports = Bernoulli;
171 |
--------------------------------------------------------------------------------
/core/methods/betaprime.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Beta Distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Beta_prime_distribution
7 | * @param alpha: number - alpha > 0, alpha must be integer
8 | * @param beta: number - beta > 0, beta must be integer
9 | * @returns Beta Prime Distributed value
10 | * Created by Alexey S. Kiselev
11 | */
12 |
13 | import type { MethodError, RandomArray } from '../types';
14 | import type { IDistribution } from '../interfaces';
15 |
16 | const Beta = require('./beta');
17 |
18 | class BetaPrime implements IDistribution {
19 | alpha: number;
20 | beta: number;
21 | betaRandom: Beta;
22 |
23 | constructor(alpha: number, beta: number): void {
24 | this.alpha = Number(alpha);
25 | this.beta = Number(beta);
26 | this.betaRandom = new Beta(this.alpha, this.beta);
27 | }
28 |
29 | /**
30 | * Generates a random number
31 | * Use Beta distribution for calculation
32 | * Refresh Beta before calculating
33 | * @returns a Beta Prime distributed number
34 | */
35 | random(): number {
36 | let betaVariance: number;
37 | this.betaRandom.refresh(this.alpha, this.beta);
38 | betaVariance = this.betaRandom.random();
39 |
40 | return betaVariance / (1 - betaVariance);
41 | }
42 |
43 | /**
44 | * Generates next seeded random number
45 | * @returns {number}
46 | */
47 | next(): number {
48 | let betaVariance: number;
49 | this.betaRandom.refresh(this.alpha, this.beta);
50 | betaVariance = this.betaRandom.next();
51 |
52 | return betaVariance / (1 - betaVariance);
53 | }
54 |
55 | /**
56 | * Generates Beta Prime distributed numbers
57 | * @param n: number - Number of elements in resulting array, n > 0
58 | * @returns Array - Beta Prime distributed numbers
59 | */
60 | distribution(n: number): RandomArray {
61 | this.betaRandom.refresh(this.alpha, this.beta);
62 | let betaPrimeArray: RandomArray = [],
63 | betaVariance: RandomArray = this.betaRandom.distribution(n);
64 | for(let i: number = 0; i < n; i += 1){
65 | betaPrimeArray[i] = betaVariance[i] / (1 - betaVariance[i]);
66 | }
67 | return betaPrimeArray;
68 | }
69 |
70 | /**
71 | * Error handling
72 | * Parameter "alpha" must be positive
73 | * Parameter "beta" must be positive
74 | * @returns {boolean}
75 | */
76 | isError(): MethodError {
77 | if(!this.alpha || !this.beta) {
78 | return {error: 'Beta Prime distribution: you should point "alpha" and "beta" positive numerical values'};
79 | }
80 | if(this.alpha <= 0){
81 | return {error: 'Beta Prime distribution: Parameter "alpha" must be positive'};
82 | }
83 | if(this.beta <= 0){
84 | return {error: 'Beta Prime distribution: Parameter "beta" must be positive'};
85 | }
86 | return { error: false };
87 | }
88 |
89 | /**
90 | * Refresh method
91 | * @param newAlpha: number - new parameter "alpha"
92 | * @param newBeta: number - new parameter "beta"
93 | * This method does not return values
94 | */
95 | refresh(newAlpha: number, newBeta: number): void {
96 | this.alpha = Number(newAlpha);
97 | this.beta = Number(newBeta);
98 | }
99 |
100 | /**
101 | * Class .toString method
102 | * @returns {string}
103 | */
104 | toString(): string {
105 | let info = [
106 | 'Beta Prime Distribution',
107 | `Usage: unirand.betaprime(${this.alpha}, ${this.beta}).random()`
108 | ];
109 | return info.join('\n');
110 | }
111 |
112 | /**
113 | * Mean value
114 | * Information only
115 | * For calculating real mean value use analyzer
116 | */
117 | get mean(): number {
118 | if(this.beta > 1) {
119 | return this.alpha / (this.beta - 1);
120 | }
121 | return Infinity;
122 | }
123 |
124 | /**
125 | * Mode value
126 | * Information only
127 | * For calculating real mean value use analyzer
128 | */
129 | get mode(): number {
130 | if(this.alpha >= 1) {
131 | return (this.alpha - 1) / (this.beta + 1);
132 | }
133 | return 0;
134 | }
135 |
136 | /**
137 | * Variance value
138 | * Information only
139 | * For calculating real variance value use analyzer
140 | */
141 | get variance(): ?number {
142 | if(this.beta > 2){
143 | return this.alpha * (this.alpha + this.beta - 1) / ((this.beta - 1) * (this.beta - 1) * (this.beta - 2));
144 | }
145 | return undefined;
146 | }
147 |
148 | /**
149 | * All parameters of distribution in one object
150 | * Information only
151 | */
152 | get parameters(): {} {
153 | return {
154 | mean: this.mean,
155 | mode: this.mode,
156 | variance: this.variance
157 | };
158 | }
159 | }
160 |
161 | module.exports = BetaPrime;
162 |
--------------------------------------------------------------------------------
/core/methods/cauchy.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Cauchy Distribution (also called Lorenz distribution)
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Cauchy_distribution
7 | * @param x: number - location any number
8 | * @param gamma: number - scale positive number
9 | * @returns a Cauchy Distributed number
10 | * Created by Alexey S. Kiselev
11 | */
12 |
13 | import type { MethodError, RandomArray } from '../types';
14 | import type { IDistribution } from '../interfaces';
15 | import prng from '../prng/prngProxy';
16 | const TAN_LIMIT = 12;
17 |
18 | class Cauchy implements IDistribution {
19 | location: number;
20 | scale: number;
21 |
22 | constructor(x: number, gamma: number): void {
23 | this.location = Number(x);
24 | this.scale = Number(gamma);
25 | }
26 |
27 | /**
28 | * Generates a random number
29 | * So, Math.tan can be huge, and it make analysis very hard (because value can be Infinity)
30 | * I am going to limit values by 12gamma
31 | * I implement recursive approach
32 | * @returns a normal distributed number
33 | */
34 | random(): number {
35 | let randomTan: number = Math.tan(Math.PI * ((prng.random(): any) - 0.5));
36 | while (Math.abs(randomTan) > TAN_LIMIT) {
37 | randomTan = Math.tan(Math.PI * (prng.next() - 0.5));
38 | }
39 | return this.location + this.scale * randomTan;
40 | }
41 |
42 | /**
43 | * Generates next seeded random number
44 | * @returns {number}
45 | */
46 | next(): number {
47 | let randomTan: number = Math.tan(Math.PI * ((prng.next(): any) - 0.5));
48 | while (Math.abs(randomTan) > TAN_LIMIT) {
49 | randomTan = Math.tan(Math.PI * (prng.next() - 0.5));
50 | }
51 | return this.location + this.scale * randomTan;
52 | }
53 |
54 | /**
55 | * Generates Cauchy distributed numbers
56 | * @param n: number - Number of elements in resulting array, n > 0
57 | * @returns Array - Cauchy distributed numbers
58 | */
59 | distribution(n: number): RandomArray {
60 | let cauchyArray: RandomArray = [],
61 | random: RandomArray = (prng.random(n): any),
62 | randomTan: number;
63 | for(let i: number = 0; i < n; i += 1){
64 | randomTan = Math.tan(Math.PI * (random[i] - 0.5));
65 | while (Math.abs(randomTan) > TAN_LIMIT) {
66 | randomTan = Math.tan(Math.PI * (prng.next() - 0.5));
67 | }
68 | cauchyArray[i] = this.location + this.scale * randomTan;
69 | }
70 | return cauchyArray;
71 | }
72 |
73 | /**
74 | * Error handling
75 | * Parameter "gamma" must be positive
76 | * @returns {boolean}
77 | */
78 | isError(): MethodError {
79 | if(!this.location || (!this.scale && this.scale !== 0)){
80 | return {error: 'Cauchy distribution: you should point "x" and "gamma" numerical values'};
81 | }
82 | if(this.scale <= 0) {
83 | return {error: 'Cauchy distribution: parameter "gamma" must be positive'};
84 | }
85 | return { error: false };
86 | }
87 |
88 | /**
89 | * Refresh method
90 | * @param newX: number - new parameter "x"
91 | * @param newGamma: number - new parameter "gamma"
92 | * This method does not return values
93 | */
94 | refresh(newX: number, newGamma: number): void {
95 | this.location = Number(newX);
96 | this.scale = Number(newGamma);
97 | }
98 |
99 | /**
100 | * Class .toString method
101 | * @returns {string}
102 | */
103 | toString(): string {
104 | let info = [
105 | 'Cauchy Distribution',
106 | `Usage: unirand.cauchy(${this.location}, ${this.scale}).random()`
107 | ];
108 | return info.join('\n');
109 | }
110 |
111 | /**
112 | * Mean value of Cauchy distribution is undefined
113 | */
114 |
115 | /**
116 | * Median value
117 | * Information only
118 | * For calculating real median value use analyzer
119 | */
120 | get median(): number {
121 | return this.location;
122 | }
123 |
124 | /**
125 | * Mode value - value, which appears most often
126 | * Information only
127 | * For calculating real mode value use analyzer
128 | */
129 | get mode(): number {
130 | return this.location;
131 | }
132 |
133 | /**
134 | * Variance and Skewness values of Cauchy distribution are undefined
135 | */
136 |
137 | /**
138 | * Entropy value
139 | * Information only
140 | * For calculating real entropy value use analyzer
141 | */
142 | get entropy(): number {
143 | return Math.log(4 * Math.PI * this.scale);
144 | }
145 |
146 | /**
147 | * All parameters of distribution in one object
148 | * Information only
149 | */
150 | get parameters(): {} {
151 | return {
152 | median: this.median,
153 | mode: this.mode,
154 | entropy: this.entropy
155 | };
156 | }
157 | }
158 |
159 | module.exports = Cauchy;
160 |
--------------------------------------------------------------------------------
/core/methods/chi.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Chi Distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Chi_distribution
7 | * @param k: number - degrees of freedom k > 0
8 | * @returns Chi Distributed value
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | const ChiSquare = require('./chisquare');
15 | const Utils = require('../utils/utils');
16 |
17 | class Chi implements IDistribution {
18 | degrees: number;
19 | chiSquare: ChiSquare;
20 |
21 | constructor(k: number): void {
22 | this.degrees = Number(k);
23 | this.chiSquare = new ChiSquare(this.degrees);
24 | }
25 |
26 | /**
27 | * Generates a random number
28 | * @returns a Chi distributed number
29 | */
30 | random(): number {
31 | this.chiSquare.refresh(this.degrees);
32 | return Math.sqrt(this.chiSquare.random());
33 | }
34 |
35 | /**
36 | * Generates next seeded random number
37 | * @returns {number}
38 | */
39 | next(): number {
40 | this.chiSquare.refresh(this.degrees);
41 | return Math.sqrt(this.chiSquare.next());
42 | }
43 |
44 | /**
45 | * Generates Chi distributed numbers
46 | * @param n: number - Number of elements in resulting array, n > 0
47 | * @returns Array - Chi distributed numbers
48 | */
49 | distribution(n: number): RandomArray {
50 | this.chiSquare.refresh(this.degrees);
51 | let chiArray: RandomArray = [],
52 | random: RandomArray = this.chiSquare.distribution(n);
53 | for(let i: number = 0; i < n; i += 1){
54 | chiArray[i] = Math.sqrt(random[i]);
55 | }
56 | return chiArray;
57 | }
58 |
59 | /**
60 | * Error handling
61 | * Parameter "k" must be positive
62 | * @returns {boolean}
63 | */
64 | isError(): MethodError {
65 | if(!this.degrees){
66 | return {error: 'Chi distribution: you should point parameter "k" positive numerical value'};
67 | }
68 | if(this.degrees <= 0){
69 | return {error: 'Chi distribution: parameter "k" must be positive integer'};
70 | }
71 | return { error: false };
72 | }
73 |
74 | /**
75 | * Refresh method
76 | * @param newK: number - new parameter "k"
77 | * This method does not return values
78 | */
79 | refresh(newK: number): void {
80 | this.degrees = Number(newK);
81 | }
82 |
83 | /**
84 | * Class .toString method
85 | * @returns {string}
86 | */
87 | toString(): string {
88 | let info = [
89 | 'Chi Distribution',
90 | `Usage: unirand.chi(${this.degrees}).random()`
91 | ];
92 | return info.join('\n');
93 | }
94 |
95 | /**
96 | * Mean value
97 | * Information only
98 | * For calculating real mean value use analyzer
99 | */
100 | get mean(): number {
101 | return Math.sqrt(2) * Utils.gamma((this.degrees + 1) / 2) / Utils.gamma(this.degrees / 2);
102 | }
103 |
104 | /**
105 | * Median value (approximate value)
106 | * Information only
107 | * For calculating real median value use analyzer
108 | */
109 | get median(): number {
110 | return Math.sqrt(this.degrees * Math.pow(1 - 2 / (9 * this.degrees), 3));
111 | }
112 |
113 | /**
114 | * Mode value
115 | * Information only
116 | * For calculating real mode value use analyzer
117 | */
118 | get mode(): any {
119 | if(this.degrees >= 1){
120 | return Math.sqrt(this.degrees - 1);
121 | }
122 | return undefined;
123 | }
124 |
125 | /**
126 | * Variance value
127 | * Information only
128 | * For calculating real variance value use analyzer
129 | */
130 | get variance(): number {
131 | return this.degrees - Math.pow(this.mean, 2);
132 | }
133 |
134 | /**
135 | * Skewness value
136 | * Information only
137 | * For calculating real skewness value use analyzer
138 | */
139 | get skewness(): number {
140 | return this.mean * (1 - 2 * this.variance) / (this.variance * Math.sqrt(this.variance));
141 | }
142 |
143 | /**
144 | * Kurtosis value
145 | * Information only
146 | * For calculating real kurtosis value use analyzer
147 | */
148 | get kurtosis(): number {
149 | return 2 * (1 - this.mean * Math.sqrt(this.variance) * this.skewness - this.variance) / this.variance;
150 | }
151 |
152 | /**
153 | * Entropy value
154 | * Information only
155 | * For calculating real entropy value use analyzer
156 | */
157 | get entropy(): number {
158 | return Math.log(Utils.gamma(this.degrees / 2)) + 0.5 * (this.degrees - Math.log(2) - (this.degrees - 1) * Utils.digamma(this.degrees / 2));
159 | }
160 |
161 | /**
162 | * All parameters of distribution in one object
163 | * Information only
164 | */
165 | get parameters(): {} {
166 | return {
167 | mean: this.mean,
168 | mode: this.mode,
169 | median: this.median,
170 | variance: this.variance,
171 | skewness: this.skewness,
172 | entropy: this.entropy,
173 | kurtosis: this.kurtosis
174 | };
175 | }
176 | }
177 |
178 | module.exports = Chi;
179 |
--------------------------------------------------------------------------------
/core/methods/chisquare.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Chi Square Distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Chi-squared_distribution
7 | * @param k: number - degrees of freedom k > 0
8 | * @returns Chi Square Distributed value
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | const Normal = require('./normal');
15 | const Utils = require('../utils/utils');
16 |
17 | class ChiSquare implements IDistribution {
18 | degrees: number;
19 | normal: Normal;
20 |
21 | constructor(k: number): void {
22 | this.degrees = Number(k);
23 | this.normal = new Normal(0, 1);
24 | }
25 |
26 | /**
27 | * Generates a random number
28 | * @returns a Chi Square distributed number
29 | */
30 | random(): number {
31 | let res: number = 0,
32 | random: RandomArray = this.normal.distribution(this.degrees);
33 | for(let i: number = 0; i < this.degrees; i += 1){
34 | res += Math.pow(random[i], 2);
35 | }
36 | return res;
37 | }
38 |
39 | /**
40 | * Generates next seeded random number
41 | * @returns {number}
42 | */
43 | next(): number {
44 | let res: number = 0,
45 | random: number;
46 | for(let i: number = 0; i < this.degrees; i += 1){
47 | random = this.normal.next();
48 | res += Math.pow(random, 2);
49 | }
50 | return res;
51 | }
52 |
53 | /**
54 | * Generates Chi Square distributed numbers
55 | * @param n: number - Number of elements in resulting array, n > 0
56 | * @returns Array - Chi Square distributed numbers
57 | */
58 | distribution(n: number): RandomArray {
59 | let chiSquareArray: RandomArray = [],
60 | res: number,
61 | random: RandomArray = this.normal.distribution(n * this.degrees);
62 | for(let i: number = 0; i < n; i += 1){
63 | res = 0;
64 | for(let j: number = 0; j < this.degrees; j += 1){
65 | res += Math.pow(random[i * this.degrees + j], 2);
66 | }
67 | chiSquareArray[i] = res;
68 | }
69 | return chiSquareArray;
70 | }
71 |
72 | /**
73 | * Error handling
74 | * Parameter "k" must be positive
75 | * @returns {boolean}
76 | */
77 | isError(): MethodError {
78 | if(!this.degrees){
79 | return {error: 'Chi Square distribution: you should point parameter "k" positive numerical value'};
80 | }
81 | if(this.degrees <= 0){
82 | return {error: 'Chi Square distribution: parameter "k" must be positive integer'};
83 | }
84 | return { error: false };
85 | }
86 |
87 | /**
88 | * Refresh method
89 | * @param newK: number - new parameter "k"
90 | * This method does not return values
91 | */
92 | refresh(newK: number): void {
93 | this.degrees = Number(newK);
94 | }
95 |
96 | /**
97 | * Class .toString method
98 | * @returns {string}
99 | */
100 | toString(): string {
101 | let info = [
102 | 'Chi Square Distribution',
103 | `Usage: unirand.chisquare(${this.degrees}).random()`
104 | ];
105 | return info.join('\n');
106 | }
107 |
108 | /**
109 | * Mean value
110 | * Information only
111 | * For calculating real mean value use analyzer
112 | */
113 | get mean(): number {
114 | return this.degrees;
115 | }
116 |
117 | /**
118 | * Median value (approximate value)
119 | * Information only
120 | * For calculating real median value use analyzer
121 | */
122 | get median(): number {
123 | return this.degrees * Math.pow(1 - 2 / (9 * this.degrees), 3);
124 | }
125 |
126 | /**
127 | * Mode value
128 | * Information only
129 | * For calculating real mode value use analyzer
130 | */
131 | get mode(): number {
132 | return Math.max(this.degrees - 2, 0);
133 | }
134 |
135 | /**
136 | * Variance value
137 | * Information only
138 | * For calculating real variance value use analyzer
139 | */
140 | get variance(): number {
141 | return 2 * this.degrees;
142 | }
143 |
144 | /**
145 | * Skewness value
146 | * Information only
147 | * For calculating real skewness value use analyzer
148 | */
149 | get skewness(): number {
150 | return Math.sqrt(8 / this.degrees);
151 | }
152 |
153 | /**
154 | * Kurtosis value
155 | * Information only
156 | * For calculating real kurtosis value use analyzer
157 | */
158 | get kurtosis(): number {
159 | return 12 / this.degrees;
160 | }
161 |
162 | /**
163 | * Entropy value
164 | * Information only
165 | * For calculating real entropy value use analyzer
166 | */
167 | get entropy(): number {
168 | return 0.5 * this.degrees + Math.log(2 * Utils.gamma(this.degrees / 2)) + (1 - 0.5 * this.degrees) * Utils.digamma(this.degrees / 2);
169 | }
170 |
171 | /**
172 | * All parameters of distribution in one object
173 | * Information only
174 | */
175 | get parameters(): {} {
176 | return {
177 | mean: this.mean,
178 | median: this.median,
179 | mode: this.mode,
180 | variance: this.variance,
181 | skewness: this.skewness,
182 | entropy: this.entropy,
183 | kurtosis: this.kurtosis
184 | };
185 | }
186 | }
187 |
188 | module.exports = ChiSquare;
189 |
--------------------------------------------------------------------------------
/core/methods/compertz.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Compertz Distribution
4 | * This is continuous distribution
5 | * https://en.wikipedia.org/wiki/Gompertz_distribution
6 | * @param nu: number - shape > 0
7 | * @param b: number - scale > 0
8 | * @returns Compertz Distributed value
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import prng from '../prng/prngProxy';
13 | import type { MethodError, RandomArray } from '../types';
14 | import type { IDistribution } from '../interfaces';
15 |
16 | class Compertz implements IDistribution {
17 | nu: number;
18 | b: number;
19 |
20 | constructor(nu: number, b: number): void {
21 | this.nu = Number(nu);
22 | this.b = Number(b);
23 | }
24 |
25 | /**
26 | * Generates a random number
27 | * @returns a Compertz distributed number
28 | */
29 | _random(u: number): number {
30 | return Math.log(1 - Math.log(1 - u) / this.nu) / this.b;
31 | }
32 |
33 | random(): number {
34 | return this._random(prng.random());
35 | }
36 |
37 | next(): number {
38 | return this._random(prng.next());
39 | }
40 |
41 | /**
42 | * Generates Compertz distributed numbers
43 | * @param n: number - Number of elements in resulting array, n > 0
44 | * @returns Array - Compertz distributed numbers
45 | */
46 | distribution(n: number): RandomArray {
47 | let compertzArray: RandomArray = [],
48 | random: RandomArray = (prng.random(n): any);
49 | for(let i: number = 0; i < n; i += 1){
50 | compertzArray[i] = this._random(random[i]);
51 | }
52 | return compertzArray;
53 | }
54 |
55 | /**
56 | * Error handling
57 | * @returns {boolean}
58 | */
59 | isError(): MethodError {
60 | if(!this.nu || this.nu <= 0) {
61 | return {error: 'Compertz distribution: you should point parameter "nu" (shape) with positive numerical value'};
62 | }
63 | if(!this.b || this.b <= 0) {
64 | return {error: 'Compertz distribution: you should point parameter "b" (scale) with positive numerical value'};
65 | }
66 | return { error: false };
67 | }
68 |
69 | /**
70 | * Refresh method
71 | * @param newNu: number - new parameter "bu"
72 | * @param newB: number - new parameter "b"
73 | * This method does not return values
74 | */
75 | refresh(newNu: number, newB: number): void {
76 | this.nu = Number(newNu);
77 | this.b = Number(newB);
78 | }
79 |
80 | /**
81 | * Class .toString method
82 | * @returns {string}
83 | */
84 | toString(): string {
85 | let info = [
86 | 'Compertz Distribution',
87 | `Usage: unirand.compertz(${this.nu}, ${this.b}).random()`
88 | ];
89 | return info.join('\n');
90 | }
91 |
92 | /**
93 | * Median value
94 | * Information only
95 | * For calculating real median value use analyzer
96 | */
97 | get median(): number {
98 | return this._random(0.5);
99 | }
100 |
101 | /**
102 | * All parameters of distribution in one object
103 | * Information only
104 | */
105 | get parameters(): {} {
106 | return {
107 | median: this.median
108 | };
109 | }
110 | }
111 |
112 | module.exports = Compertz;
113 |
--------------------------------------------------------------------------------
/core/methods/exponential.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Exponential distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Exponential_distribution
7 | * @param lambda - rate parameter, lambda > 0
8 | * @returns Exponentially distribution numbers
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | import prng from '../prng/prngProxy';
15 |
16 | class Exponential implements IDistribution {
17 | lambda: number;
18 |
19 | constructor(lambda: number): void {
20 | this.lambda = Number(lambda);
21 | }
22 |
23 | /**
24 | * Generates a random number
25 | * @returns a exponential distributed number
26 | */
27 | random(): number {
28 | return this._random((prng.random(): any));
29 | }
30 |
31 | /**
32 | * Generates next seeded random number
33 | * @returns {number}
34 | */
35 | next(): number {
36 | return this._random(prng.next());
37 | }
38 |
39 | _random(u: number): number {
40 | return -Math.log(u) / this.lambda;
41 | }
42 |
43 | /**
44 | * Generates exponential distributed numbers
45 | * @param n: number - Number of elements in resulting array, n > 0
46 | * @returns Array - exponential distributed numbers
47 | */
48 | distribution(n: number): RandomArray {
49 | let exponentialArray: RandomArray = [],
50 | random = (prng.random(n): any);
51 | for(let i: number = 0; i < n; i += 1){
52 | exponentialArray[i] = this._random(random[i]);
53 | }
54 | return exponentialArray;
55 | }
56 |
57 | /**
58 | * Error handling
59 | * @returns {boolean}
60 | */
61 | isError(): MethodError {
62 | if(!this.lambda) {
63 | return {error: 'Exponential distribution: you should point parameter "lambda" with numerical value'};
64 | }
65 | if(this.lambda <= 0){
66 | return {error: 'Exponential distribution: parameter "lambda" must be a positive number'};
67 | }
68 | return { error: false };
69 | }
70 |
71 | /**
72 | * Refresh method
73 | * @param newLambda: number - new parameter "lambda"
74 | * This method does not return values
75 | */
76 | refresh(newLambda: number): void {
77 | this.lambda = Number(newLambda);
78 | }
79 |
80 | /**
81 | * Class .toString method
82 | * @returns {string}
83 | */
84 | toString(): string {
85 | let info = [
86 | 'Exponential Distribution',
87 | `Usage: unirand.exponential(${this.lambda}).random()`
88 | ];
89 | return info.join('\n');
90 | }
91 |
92 | /**
93 | * Mean value
94 | * Information only
95 | * For calculating real mean value use analyzer
96 | */
97 | get mean(): number {
98 | return 1 / this.lambda;
99 | }
100 |
101 | /**
102 | * Median value
103 | * Information only
104 | * For calculating real median value use analyzer
105 | */
106 | get median(): number {
107 | return Math.log(2) / this.lambda;
108 | }
109 |
110 | /**
111 | * Mode value - value, which appears most often
112 | * Information only
113 | * For calculating real mode value use analyzer
114 | */
115 | get mode(): number {
116 | return 0;
117 | }
118 |
119 | /**
120 | * Variance value
121 | * Information only
122 | * For calculating real variance value use analyzer
123 | */
124 | get variance(): number {
125 | return 1 / Math.pow(this.lambda, 2);
126 | }
127 |
128 | /**
129 | * Skewness value
130 | * Information only
131 | * For calculating real skewness value use analyzer
132 | */
133 | get skewness(): number {
134 | return 2;
135 | }
136 |
137 | /**
138 | * Kurtosis value
139 | * Information only
140 | * For calculating real kurtosis value use analyzer
141 | */
142 | get kurtosis(): number {
143 | return 6;
144 | }
145 |
146 | /**
147 | * Entropy value
148 | * Information only
149 | * For calculating real entropy value use analyzer
150 | */
151 | get entropy(): number {
152 | return 1 - Math.log(this.lambda);
153 | }
154 |
155 | /**
156 | * Fisher information matrix
157 | * Information only
158 | */
159 | get fisher(): number {
160 | return this.variance;
161 | }
162 |
163 | /**
164 | * All parameters of distribution in one object
165 | * Information only
166 | */
167 | get parameters(): {} {
168 | return {
169 | mean: this.mean,
170 | median: this.median,
171 | mode: this.mode,
172 | variance: this.variance,
173 | skewness: this.skewness,
174 | entropy: this.entropy,
175 | fisher: this.fisher
176 | };
177 | }
178 | }
179 |
180 | module.exports = Exponential;
181 |
--------------------------------------------------------------------------------
/core/methods/extremevalue.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Extreme (Gumbel-type) Value distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Generalized_extreme_value_distribution
7 | * @param mu - location, any value
8 | * @param sigma - scale, sigma > 0
9 | * @returns Extreme Value distributed numbers
10 | * Created by Alexey S. Kiselev
11 | */
12 |
13 | import type { MethodError, RandomArray } from '../types';
14 | import type { IDistribution } from '../interfaces';
15 | import prng from '../prng/prngProxy';
16 |
17 | class ExtremeValue implements IDistribution {
18 | mu: number;
19 | sigma: number;
20 |
21 | constructor(mu: number,sigma: number): void {
22 | this.mu = Number(mu);
23 | this.sigma = Number(sigma);
24 | }
25 |
26 | /**
27 | * Generates a random number
28 | * @returns a extreme value distributed number
29 | */
30 | random(): number {
31 | return this._random((prng.random(): any));
32 | }
33 |
34 | /**
35 | * Generates next seeded random number
36 | * @returns {number}
37 | */
38 | next(): number {
39 | return this._random(prng.next());
40 | }
41 |
42 | _random(u: number): number {
43 | return this.mu - this.sigma * Math.log(-Math.log(u));
44 | }
45 |
46 | /**
47 | * Generates extreme value distributed numbers
48 | * @param n: number - Number of elements in resulting array, n > 0
49 | * @returns Array - extreme value distributed numbers
50 | */
51 | distribution(n: number): RandomArray {
52 | const random: RandomArray = (prng.random(n): any);
53 | const extremeValueArray: RandomArray = [];
54 | for(let i: number = 0; i < n; i += 1){
55 | extremeValueArray[i] = this._random(random[i]);
56 | }
57 | return extremeValueArray;
58 | }
59 |
60 | /**
61 | * Error handling
62 | * @returns {boolean}
63 | */
64 | isError(): MethodError {
65 | if((!this.mu && this.mu !== 0) || !this.sigma) {
66 | return {error: 'Extreme Value (Gumbel type) distribution: you should point parameters "mu" and "sigma" with numerical values'};
67 | }
68 | if(this.sigma <= 0){
69 | return {error: 'Extreme Value (Gumbel type) distribution: parameter "sigma" must be a positive number'};
70 | }
71 | return { error: false };
72 | }
73 |
74 | /**
75 | * Refresh method
76 | * @param newMu: number - new parameter "mu"
77 | * @param newSigma: number - new parameter "sigma"
78 | * This method does not return values
79 | */
80 | refresh(newMu: number, newSigma: number): void {
81 | this.mu = Number(newMu);
82 | this.sigma = Number(newSigma);
83 | }
84 |
85 | /**
86 | * Class .toString method
87 | * @returns {string}
88 | */
89 | toString(): string {
90 | let info = [
91 | 'Extreme Value (Gumbel type) Distribution',
92 | `Usage: unirand.extremevalue(${this.mu}, ${this.sigma}).random()`
93 | ];
94 | return info.join('\n');
95 | }
96 |
97 | /**
98 | * Mean value
99 | * Information only
100 | * Calculate this value using Euler constant
101 | * For calculating real mean value use analyzer
102 | */
103 | get mean(): number {
104 | return this.mu + this.sigma * 0.57721566490153286;
105 | }
106 |
107 | /**
108 | * Median value
109 | * Information only
110 | * For calculating real median value use analyzer
111 | */
112 | get median(): number {
113 | return this.mu - this.sigma * Math.log(Math.log(2));
114 | }
115 |
116 | /**
117 | * Mode value - value, which appears most often
118 | * Information only
119 | * For calculating real mode value use analyzer
120 | */
121 | get mode(): number {
122 | return this.mu;
123 | }
124 |
125 | /**
126 | * Variance value
127 | * Information only
128 | * For calculating real variance value use analyzer
129 | */
130 | get variance(): number {
131 | return Math.pow(this.sigma * Math.PI, 2) / 6;
132 | }
133 |
134 | /**
135 | * Entropy value
136 | * Information only
137 | * For calculating real entropy value use analyzer
138 | */
139 | get entropy(): number {
140 | return Math.log(this.sigma) + 1.57721566490153286;
141 | }
142 |
143 | /**
144 | * Skewness value
145 | * Information only
146 | * For calculating real value of skewness use analyzer
147 | */
148 | get skewness(): number {
149 | let riemann: number = 1.202056903159594;
150 | return 12 * Math.sqrt(6) * riemann / Math.pow(Math.PI, 3);
151 | }
152 |
153 | /**
154 | * Kurtosis value
155 | * Information only
156 | * For calculating real value of kurtosis use analyzer
157 | */
158 | get kurtosis(): number {
159 | return 12 / 5;
160 | }
161 |
162 | /**
163 | * All parameters of distribution in one object
164 | * Information only
165 | */
166 | get parameters(): {} {
167 | return {
168 | mean: this.mean,
169 | median: this.median,
170 | mode: this.mode,
171 | variance: this.variance,
172 | entropy: this.entropy,
173 | skewness: this.skewness,
174 | kurtosis: this.kurtosis
175 | };
176 | }
177 | }
178 |
179 | module.exports = ExtremeValue;
180 |
--------------------------------------------------------------------------------
/core/methods/fatigue.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Fatigue life Distribution, also known as Birnbaum–Saunders distribution
4 | * Continuous distribution
5 | * https://en.wikipedia.org/wiki/Birnbaum-Saunders_distribution
6 | * @param alpha: number - shape parameter, alpha > 0
7 | * @param beta: number - scale parameter, beta > 0
8 | * @returns Fatigue Distributed value
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 |
15 | const Normal = require('./normal');
16 |
17 | class Fatigue implements IDistribution {
18 | alpha: number;
19 | beta: number;
20 | normal: Normal;
21 |
22 | constructor(alpha: number, beta: number): void {
23 | this.alpha = Number(alpha);
24 | this.beta = Number(beta);
25 | this.normal = new Normal(0, 1);
26 | }
27 |
28 | _random(norm: number): number {
29 | return this.beta * Math.pow(this.alpha * norm + Math.sqrt(Math.pow(this.alpha * norm, 2) + 4), 2) / 4;
30 | }
31 |
32 | /**
33 | * Generates a random number
34 | * @returns {number} a Fatigue distributed number
35 | */
36 | random(): number {
37 | return this._random(this.normal.random());
38 | }
39 |
40 | /**
41 | * Generates next seeded random number
42 | * @returns {number} a Fatigue distributed number
43 | */
44 | next(): number {
45 | return this._random(this.normal.next());
46 | }
47 |
48 | /**
49 | * Generates Fatigue distributed numbers
50 | * @param n: number - Number of elements in resulting array, n > 0
51 | * @returns Array - Fatigue distributed numbers
52 | */
53 | distribution(n: number): RandomArray {
54 | let fatigueArray: RandomArray = [],
55 | random: RandomArray = this.normal.distribution(n);
56 | for (let i: number = 0; i < n; i += 1){
57 | fatigueArray[i] = this._random(random[i]);
58 | }
59 | return fatigueArray;
60 | }
61 |
62 | /**
63 | * Error handling
64 | * @returns {boolean}
65 | */
66 | isError(): MethodError {
67 | if (!this.alpha || !this.beta) {
68 | return {error: 'Fatigue distribution: you should point parameters "alpha" and "beta" as numerical values'};
69 | }
70 | if (this.alpha <= 0 || this.beta <= 0) {
71 | return {error: 'Fatigue distribution: parameters "alpha" and "beta" must be a positive numbers'};
72 | }
73 |
74 | return { error: false };
75 | }
76 |
77 | /**
78 | * Refresh method
79 | * @param newAlpha: number - new parameter "alpha"
80 | * @param newBeta: number - new parameter "beta"
81 | * This method does not return values
82 | */
83 | refresh(newAlpha: number, newBeta: number): void {
84 | this.alpha = Number(newAlpha);
85 | this.beta = Number(newBeta);
86 | }
87 |
88 | /**
89 | * Class .toString method
90 | * @returns {string}
91 | */
92 | toString(): string {
93 | let info = [
94 | 'Fatigue Distribution',
95 | `Usage: unirand.fatigue(${this.alpha}, ${this.beta}).random()`
96 | ];
97 | return info.join('\n');
98 | }
99 |
100 | /**
101 | * Mean value
102 | * Information only
103 | * For calculating real mean value use analyzer
104 | */
105 | get mean(): number {
106 | return this.beta * (Math.pow(this.alpha, 2) + 2) /2;
107 | }
108 |
109 | /**
110 | * Median value
111 | * Information only
112 | * For calculating real mean value use analyzer
113 | */
114 | get median(): number {
115 | return this.beta;
116 | }
117 |
118 | /**
119 | * Variance value
120 | * Information only
121 | * For calculating real variance value use analyzer
122 | */
123 | get variance(): number {
124 | return Math.pow(this.alpha * this.beta, 2) * (5 * Math.pow(this.alpha, 2) + 4) / 4;
125 | }
126 |
127 | /**
128 | * Skewness value
129 | * Information only
130 | * For calculating real skewness value use analyzer
131 | */
132 | get skewness(): number {
133 | return 4 * this.alpha * (11 * Math.pow(this.alpha, 2) + 6) / Math.pow(5 * this.alpha * this.alpha + 4, 1.5);
134 | }
135 |
136 | /**
137 | * Kurtosis value
138 | * Information only
139 | * For calculating real kurtosis value use analyzer
140 | */
141 | get kurtosis(): number {
142 | return 3 + Math.pow(this.alpha, 2) * (558 * this.alpha * this.alpha + 240) / Math.pow(5 * this.alpha * this.alpha + 4, 2);
143 | }
144 |
145 | /**
146 | * All parameters of distribution in one object
147 | * Information only
148 | */
149 | get parameters(): {} {
150 | return {
151 | mean: this.mean,
152 | median: this.median,
153 | variance: this.variance,
154 | skewness: this.skewness,
155 | kurtosis: this.kurtosis
156 | };
157 | }
158 | }
159 |
160 | module.exports = Fatigue;
161 |
--------------------------------------------------------------------------------
/core/methods/geometric.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Geometric Distribution
5 | * This is discreet distribution
6 | * https://en.wikipedia.org/wiki/Geometric_distribution
7 | * @param p: number (0 <= p <= 1) - Probability of success
8 | * @returns Geometric Distributed Value
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | import prng from '../prng/prngProxy';
15 |
16 | class Geometric implements IDistribution {
17 | successProb: number;
18 |
19 | constructor(p: number): void {
20 | this.successProb = Number(p);
21 | }
22 |
23 | /**
24 | * Generates a random number
25 | * @returns a Geometric distributed number
26 | */
27 | random(): number {
28 | let res: number = 1,
29 | random: number = (prng.random(): any);
30 | while(random >= this.successProb){
31 | res += 1;
32 | random = prng.next();
33 | }
34 | return res;
35 | }
36 |
37 | /**
38 | * Generates next seeded random number
39 | * @returns {number}
40 | */
41 | next(): number {
42 | let res: number = 1,
43 | random: number = prng.next();
44 | while(random >= this.successProb){
45 | res += 1;
46 | random = prng.next();
47 | }
48 | return res;
49 | }
50 |
51 | /**
52 | * Generates Geometric distributed numbers
53 | * @param n: number - Number of elements in resulting array, n > 0
54 | * @returns Array - Geometric distributed numbers
55 | */
56 | distribution(n: number): RandomArray {
57 | let geometricArray: RandomArray = [],
58 | random: number = (prng.random(): any),
59 | res: number;
60 | for(let i = 0; i < n; i += 1){
61 | res = 1;
62 | random = prng.next();
63 | while(random >= this.successProb){
64 | res += 1;
65 | random = prng.next();
66 | }
67 | geometricArray[i] = res;
68 | }
69 | return geometricArray;
70 | }
71 |
72 | /**
73 | * Error handling
74 | * Parameter "p" must be 0 <= p <= 1
75 | * @returns {boolean}
76 | */
77 | isError(): MethodError {
78 | if(!this.successProb && this.successProb !== 0){
79 | return {error: 'Geometric distribution: you should specify parameter "p" with numerical value'};
80 | }
81 | if(this.successProb < 0 || this.successProb > 1) {
82 | return {error: 'Geometric distribution: parameter "p" (probability of success) must be 0 <= p <= 1'};
83 | }
84 | return { error: false };
85 | }
86 |
87 | /**
88 | * Refresh method
89 | * @param newP: number - new parameter "p"
90 | * This method does not return values
91 | */
92 | refresh(newP: number): void {
93 | this.successProb = Number(newP);
94 | }
95 |
96 | /**
97 | * Class .toString method
98 | * @returns {string}
99 | */
100 | toString(): string {
101 | let info = [
102 | 'Geometric Distribution',
103 | `Usage: unirand.geometric(${this.successProb}).random()`
104 | ];
105 | return info.join('\n');
106 | }
107 |
108 | /**
109 | * Mean value
110 | * Information only
111 | * For calculating real mean value use analyzer
112 | */
113 | get mean(): number {
114 | return 1 / this.successProb;
115 | }
116 |
117 | /**
118 | * Geometric distribution doesn't have unique Median value
119 | */
120 |
121 | /**
122 | * Mode value - value, which appears most often
123 | * Information only
124 | * For calculating real mode value use analyzer
125 | */
126 | get mode(): number {
127 | return 1;
128 | }
129 |
130 | /**
131 | * Variance value
132 | * Information only
133 | * For calculating real variance value use analyzer
134 | */
135 | get variance(): number {
136 | return (1 - this.successProb) / Math.pow(this.successProb, 2);
137 | }
138 |
139 | /**
140 | * Skewness value
141 | * Information only
142 | * For calculating real skewness value use analyzer
143 | */
144 | get skewness(): number {
145 | return (2 - this.successProb) / Math.sqrt(1 - this.successProb);
146 | }
147 |
148 | /**
149 | * Kurtosis value
150 | * Information only
151 | * For calculating real kurtosis value use analyzer
152 | */
153 | get kurtosis(): number {
154 | return 6 + Math.pow(this.successProb, 2) / (1 - this.successProb);
155 | }
156 |
157 | /**
158 | * Entropy value
159 | * Information only
160 | * For calculating real entropy value use analyzer
161 | */
162 | get entropy(): number {
163 | return (- (1 - this.successProb) * Math.log(1 - this.successProb) - this.successProb * Math.log(this.successProb)) / this.successProb;
164 | }
165 |
166 | /**
167 | * All parameters of distribution in one object
168 | * Information only
169 | */
170 | get parameters(): {} {
171 | return {
172 | mean: this.mean,
173 | mode: this.mode,
174 | variance: this.variance,
175 | skewness: this.skewness,
176 | entropy: this.entropy,
177 | kurtosis: this.kurtosis
178 | };
179 | }
180 | }
181 |
182 | module.exports = Geometric;
183 |
--------------------------------------------------------------------------------
/core/methods/index.js:
--------------------------------------------------------------------------------
1 | const bates = require('./bates');
2 | const bernoulli = require('./bernoulli');
3 | const beta = require('./beta');
4 | const betaprime = require('./betaprime');
5 | const binomial = require('./binomial');
6 | const cauchy = require('./cauchy');
7 | const chi = require('./chi');
8 | const chisquare = require('./chisquare');
9 | const compertz = require('./compertz');
10 | const delaporte = require('./delaporte');
11 | const erlang = require('./erlang');
12 | const exponential = require('./exponential');
13 | const extremevalue = require('./extremevalue');
14 | const fatigue = require('./fatigue');
15 | const gamma = require('./gamma');
16 | const geometric = require('./geometric');
17 | const irwinhall = require('./irwinhall');
18 | const laplace = require('./laplace');
19 | const logistic = require('./logistic');
20 | const lognormal = require('./lognormal');
21 | const negativebinomial = require('./negativebinomial');
22 | const normal = require('./normal');
23 | const pareto = require('./pareto');
24 | const poisson = require('./poisson');
25 | const rayleigh = require('./rayleigh');
26 | const student = require('./student');
27 | const triangular = require('./triangular');
28 | const uniform = require('./uniform');
29 | const weibull = require('./weibull');
30 | const zipf = require('./zipf');
31 |
32 | module.exports = {
33 | bates,
34 | bernoulli,
35 | beta,
36 | betaprime,
37 | binomial,
38 | cauchy,
39 | chi,
40 | chisquare,
41 | compertz,
42 | delaporte,
43 | erlang,
44 | exponential,
45 | extremevalue,
46 | fatigue,
47 | gamma,
48 | geometric,
49 | irwinhall,
50 | laplace,
51 | logistic,
52 | lognormal,
53 | negativebinomial,
54 | normal,
55 | pareto,
56 | poisson,
57 | rayleigh,
58 | student,
59 | triangular,
60 | uniform,
61 | weibull,
62 | zipf
63 | };
64 |
--------------------------------------------------------------------------------
/core/methods/irwinhall.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Irwin-Hall distribution
5 | * https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution
6 | * @param n: number, n >= 1
7 | * Created by Alexey S. Kiselev
8 | */
9 |
10 | import type { MethodError, RandomArray } from '../types';
11 | import type { IDistribution } from '../interfaces';
12 | const Uniform = require('./uniform');
13 |
14 | class IrwinHall implements IDistribution {
15 | n: number;
16 | _uniform: Uniform;
17 |
18 | constructor(n: number) {
19 | this.n = Math.floor(n);
20 | this._uniform = new Uniform(0, 1);
21 | }
22 |
23 | /**
24 | * Generates a random number
25 | * @returns a Irwin-Hall distributed number
26 | */
27 | random(): number {
28 | let random_number: number = 0,
29 | random: RandomArray = this._uniform.distribution(this.n);
30 | for(let k = 0; k < this.n; k += 1) {
31 | random_number += random[k];
32 | }
33 | return random_number;
34 | }
35 |
36 | /**
37 | * Generates next seeded random number
38 | * @returns {number}
39 | */
40 | next(): number {
41 | let random_number: number = 0;
42 | for(let k = 0; k < this.n; k += 1) {
43 | random_number += this._uniform.next();
44 | }
45 | return random_number;
46 | }
47 |
48 | /**
49 | * Generates random distribution
50 | * @returns an array with Irwin-Hall distributed numbers
51 | */
52 | distribution(n: number): RandomArray {
53 | let irwinHallArray: RandomArray = [],
54 | random_number: number,
55 | random: RandomArray = this._uniform.distribution(n * this.n);
56 | for(let i: number = 0; i < n; i += 1){
57 | random_number = 0;
58 | for(let k = 0; k < this.n; k += 1) {
59 | random_number += random[i * this.n + k];
60 | }
61 | irwinHallArray[i] = random_number;
62 | }
63 | return irwinHallArray;
64 | }
65 |
66 | isError(): MethodError {
67 | if(!this.n || this.n < 1) {
68 | return {error: 'Irwin-Hall distribution: you should point "n" positive integer value'};
69 | }
70 | return { error: false };
71 | }
72 |
73 | /**
74 | * Refresh method
75 | * @param newN: number - new parameter "n"
76 | * This method does not return values
77 | */
78 | refresh(newN: number): void {
79 | this.n = Math.floor(newN);
80 | }
81 |
82 | /**
83 | * Class .toString method
84 | * @returns {string}
85 | */
86 | toString(): string {
87 | let info = [
88 | 'Irwin-Hall Distribution',
89 | `Usage: unirand.irwinhall(${this.n}).random()`
90 | ];
91 | return info.join('\n');
92 | }
93 |
94 | /**
95 | * Mean value
96 | * Information only
97 | * For calculating real mean value use analyzer
98 | */
99 | get mean(): number {
100 | return this.n / 2;
101 | }
102 |
103 | /**
104 | * Median value
105 | * Information only
106 | * For calculating real mean value use analyzer
107 | */
108 | get median(): number {
109 | return this.n / 2;
110 | }
111 |
112 | /**
113 | * Variance value
114 | * Information only
115 | * For calculating real variance value use analyzer
116 | */
117 | get variance(): number {
118 | return this.n / 12;
119 | }
120 |
121 | /**
122 | * Skewness value
123 | * Information only
124 | * For calculating real skewness value use analyzer
125 | */
126 | get skewness(): number {
127 | return 0;
128 | }
129 |
130 | /**
131 | * Kurtosis value
132 | * Information only
133 | */
134 | get kurtosis(): number {
135 | return -1.2 / this.n;
136 | }
137 |
138 | /**
139 | * All parameters of distribution in one object
140 | * Information only
141 | */
142 | get parameters(): {} {
143 | return {
144 | mean: this.mean,
145 | median: this.median,
146 | variance: this.variance,
147 | skewness: this.skewness,
148 | kurtosis: this.kurtosis
149 | };
150 | }
151 | }
152 |
153 | module.exports = IrwinHall;
154 |
--------------------------------------------------------------------------------
/core/methods/laplace.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Laplace distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Laplace_distribution
7 | * @param mu - location, any value
8 | * @param b - scale, b > 0
9 | * @returns Laplace distributed number
10 | * Created by Alexey S. Kiselev
11 | */
12 |
13 | import type { MethodError, RandomArray } from '../types';
14 | import type { IDistribution } from '../interfaces';
15 | import prng from '../prng/prngProxy';
16 |
17 | class Laplace implements IDistribution {
18 | location: number;
19 | scale: number;
20 |
21 | constructor(mu: number, b: number): void {
22 | this.location = Number(mu);
23 | this.scale = Number(b);
24 | }
25 |
26 | /**
27 | * Generates a random number
28 | * @returns a Laplace distributed number
29 | */
30 | random(): number {
31 | let u: number = (prng.random(): any);
32 | return this._random(u);
33 | }
34 |
35 | /**
36 | * Generates next seeded random number
37 | * @returns {number}
38 | */
39 | next(): number {
40 | return this._random(prng.next());
41 | }
42 |
43 | _random(u: number): number {
44 | if(u <= 0.5){
45 | return this.location + this.scale * Math.log(2 * u);
46 | }
47 | return this.location - this.scale * Math.log(2 * (1 - u));
48 | }
49 |
50 | /**
51 | * Generates Laplace distributed numbers
52 | * @param n: number - Number of elements in resulting array, n > 0
53 | * @returns Array - Laplace distributed numbers
54 | */
55 | distribution(n: number): RandomArray {
56 | let laplaceArray: RandomArray = [],
57 | random: RandomArray = (prng.random(n): any);
58 | for(let i: number = 0; i < n; i += 1){
59 | laplaceArray[i] = this._random(random[i]);
60 | }
61 | return laplaceArray;
62 | }
63 |
64 | /**
65 | * Error handling
66 | * @returns {boolean}
67 | */
68 | isError(): MethodError {
69 | if((!this.location && this.location !== 0) || !this.scale) {
70 | return {error: 'Laplace distribution: you should point parameters "mu" and "b" (scale) with numerical values'};
71 | }
72 | if(this.scale <= 0){
73 | return {error: 'Laplace distribution: parameter "b" (scale) must be a positive number'};
74 | }
75 | return { error: false };
76 | }
77 |
78 | /**
79 | * Refresh method
80 | * @param newMu: number - new parameter "mu"
81 | * @param newB: number - new parameter "b"
82 | * This method does not return values
83 | */
84 | refresh(newMu: number, newB: number): void {
85 | this.location = Number(newMu);
86 | this.scale = Number(newB);
87 | }
88 |
89 | /**
90 | * Class .toString method
91 | * @returns {string}
92 | */
93 | toString(): string {
94 | let info = [
95 | 'Laplace Distribution',
96 | `Usage: unirand.laplace(${this.location}, ${this.scale}).random()`
97 | ];
98 | return info.join('\n');
99 | }
100 |
101 | /**
102 | * Mean value
103 | * Information only
104 | * For calculating real mean value use analyzer
105 | */
106 | get mean(): number {
107 | return this.location;
108 | }
109 |
110 | /**
111 | * Median value
112 | * Information only
113 | * For calculating real median value use analyzer
114 | */
115 | get median(): number {
116 | return this.location;
117 | }
118 |
119 | /**
120 | * Mode value - value, which appears most often
121 | * Information only
122 | * For calculating real mode value use analyzer
123 | */
124 | get mode(): number {
125 | return this.location;
126 | }
127 |
128 | /**
129 | * Variance value
130 | * Information only
131 | * For calculating real variance value use analyzer
132 | */
133 | get variance(): number {
134 | return 2 * Math.pow(this.scale, 2);
135 | }
136 |
137 | /**
138 | * Skewness value
139 | * Information only
140 | * For calculating real skewness value use analyzer
141 | */
142 | get skewness(): number {
143 | return 0;
144 | }
145 |
146 | /**
147 | * Kurtosis value
148 | * Information only
149 | * For calculating real kurtosis value use analyzer
150 | */
151 | get kurtosis(): number {
152 | return 3;
153 | }
154 |
155 | /**
156 | * Entropy value
157 | * Information only
158 | * This formula uses Euler's number (base of natural logarithm)
159 | * For calculating real entropy value use analyzer
160 | */
161 | get entropy(): number {
162 | return Math.log(2 * this.scale * 2.71828182845904523536);
163 | }
164 |
165 | /**
166 | * All parameters of distribution in one object
167 | * Information only
168 | */
169 | get parameters(): {} {
170 | return {
171 | mean: this.mean,
172 | median: this.median,
173 | mode: this.mode,
174 | variance: this.variance,
175 | skewness: this.skewness,
176 | entropy: this.entropy,
177 | kurtosis: this.kurtosis
178 | };
179 | }
180 | }
181 |
182 | module.exports = Laplace;
183 |
--------------------------------------------------------------------------------
/core/methods/logistic.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Logistic distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Logistic_distribution
7 | * @param mu - location, any number
8 | * @param s - scale, s > 0
9 | * @returns Logistic distributed number
10 | * Created by Alexey S. Kiselev
11 | */
12 |
13 | import type { RandomArray, MethodError } from '../types';
14 | import type { IDistribution } from '../interfaces';
15 | import prng from '../prng/prngProxy';
16 |
17 | class Logistic implements IDistribution {
18 | location: number;
19 | scale: number;
20 |
21 | constructor(mu: number, s: number): void {
22 | this.location = Number(mu);
23 | this.scale = Number(s);
24 | }
25 |
26 | /**
27 | * Generates a random number
28 | * @returns a Logistic distributed number
29 | */
30 | random(): number {
31 | let u: number = (prng.random(): any);
32 | return this._random(u);
33 | }
34 |
35 | /**
36 | * Generates next seeded random number
37 | * @returns {number}
38 | */
39 | next(): number {
40 | return this._random(prng.next());
41 | }
42 |
43 | _random(u: number): number {
44 | return this.location + this.scale * Math.log(u / (1 - u));
45 | }
46 |
47 | /**
48 | * Generates Logistic distributed numbers
49 | * @param n: number - Number of elements in resulting array, n > 0
50 | * @returns Array - Logistic distributed numbers
51 | */
52 | distribution(n: number): RandomArray {
53 | let logisticArray: RandomArray = [],
54 | random: RandomArray = (prng.random(n): any);
55 | for(let i: number = 0; i < n; i += 1){
56 | logisticArray[i] = this._random(random[i]);
57 | }
58 | return logisticArray;
59 | }
60 |
61 | /**
62 | * Error handling
63 | * @returns {boolean}
64 | */
65 | isError(): MethodError {
66 | if((!this.location && this.location !== 0) || !this.scale) {
67 | return {error: 'Logistic distribution: you should point parameters "mu" and "s" (scale) with numerical values'};
68 | }
69 | if(this.scale <= 0){
70 | return {error: 'Logistic distribution: parameter "s" (scale) must be a positive number'};
71 | }
72 | return { error: false };
73 | }
74 |
75 | /**
76 | * Refresh method
77 | * @param newMu: number - new parameter "mu"
78 | * @param newS: number - new parameter "sigma"
79 | * This method does not return values
80 | */
81 | refresh(newMu: number, newS: number): void {
82 | this.location = Number(newMu);
83 | this.scale = Number(newS);
84 | }
85 |
86 | /**
87 | * Class .toString method
88 | * @returns {string}
89 | */
90 | toString(): string {
91 | let info = [
92 | 'Logistic Distribution',
93 | `Usage: unirand.logistic(${this.location}, ${this.scale}).random()`
94 | ];
95 | return info.join('\n');
96 | }
97 |
98 | /**
99 | * Mean value
100 | * Information only
101 | * For calculating real mean value use analyzer
102 | */
103 | get mean(): number {
104 | return this.location;
105 | }
106 |
107 | /**
108 | * Median value
109 | * Information only
110 | * For calculating real median value use analyzer
111 | */
112 | get median(): number {
113 | return this.location;
114 | }
115 |
116 | /**
117 | * Mode value - value, which appears most often
118 | * Information only
119 | * For calculating real mode value use analyzer
120 | */
121 | get mode(): number {
122 | return this.location;
123 | }
124 |
125 | /**
126 | * Variance value
127 | * Information only
128 | * For calculating real variance value use analyzer
129 | */
130 | get variance(): number {
131 | return Math.pow(this.scale * Math.PI, 2) / 3;
132 | }
133 |
134 | /**
135 | * Kurtosis value
136 | * Information only
137 | * For calculating real kurtosis value use analyzer
138 | */
139 | get kurtosis(): number {
140 | return 1.2;
141 | }
142 |
143 | /**
144 | * Skewness value
145 | * Information only
146 | * For calculating real skewness value use analyzer
147 | */
148 | get skewness(): number {
149 | return 0;
150 | }
151 |
152 | /**
153 | * Entropy value
154 | * Information only
155 | * This formula esus Euler's number (base of natural logarithm)
156 | * For calculating real entropy value use analyzer
157 | */
158 | get entropy(): number {
159 | return Math.log(this.scale) + 2;
160 | }
161 |
162 | /**
163 | * All parameters of distribution in one object
164 | * Information only
165 | */
166 | get parameters(): {} {
167 | return {
168 | mean: this.mean,
169 | median: this.median,
170 | mode: this.mode,
171 | variance: this.variance,
172 | skewness: this.skewness,
173 | entropy: this.entropy
174 | };
175 | }
176 | }
177 |
178 | module.exports = Logistic;
179 |
--------------------------------------------------------------------------------
/core/methods/poisson.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Poisson Distribution (Knuth algorithm)
5 | * This is discreet distribution
6 | * https://en.wikipedia.org/wiki/Poisson_distribution
7 | * @param lambda: number (lambda > 0)
8 | * @returns Poisson Distributed integer number
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | import prng from '../prng/prngProxy';
15 |
16 | class Poisson implements IDistribution {
17 | lambda: number;
18 |
19 | constructor(lambda: number): void {
20 | this.lambda = Number(lambda);
21 | }
22 |
23 | /**
24 | * Generates a random number
25 | * @returns a Poisson distributed number
26 | */
27 | random(): number {
28 | prng.random();
29 | return this._random();
30 | }
31 |
32 | /**
33 | * Generates next seeded random number
34 | * @returns {number}
35 | */
36 | next(): number {
37 | return this._random();
38 | }
39 |
40 | _random(): number {
41 | let res: number = 0,
42 | p: number = 1,
43 | L: number = Math.exp(-this.lambda);
44 | do {
45 | p *= prng.next();
46 | res += 1;
47 | } while(p >= L);
48 | return res - 1;
49 | }
50 |
51 | /**
52 | * Generates Poisson distributed numbers
53 | * @param n: number - Number of elements in resulting array, n > 0
54 | * @returns Array - Poisson distributed numbers
55 | */
56 | distribution(n: number): RandomArray {
57 | let poissonArray: RandomArray = [];
58 | prng.random();
59 | for(let i: number = 0; i < n; i += 1){
60 | poissonArray[i] = this._random();
61 | }
62 | return poissonArray;
63 | }
64 |
65 | /**
66 | * Error handling
67 | * Parameter "lambda" must be positive integer
68 | * @returns {boolean}
69 | */
70 | isError(): MethodError {
71 | if (!this.lambda) {
72 | return {error: 'Poisson distribution: you should point parameter "lambda" with numerical value'};
73 | }
74 | if (this.lambda <= 0) {
75 | return {error: 'Poisson distribution: parameter "lambda" must be positive integer'};
76 | }
77 | return { error: false };
78 | }
79 |
80 | /**
81 | * Refresh method
82 | * @param newLambda: number - new parameter "lambda"
83 | * This method does not return values
84 | */
85 | refresh(newLambda: number): void {
86 | this.lambda = Number(newLambda);
87 | }
88 |
89 | /**
90 | * Class .toString method
91 | * @returns {string}
92 | */
93 | toString(): string {
94 | let info = [
95 | 'Poisson Distribution',
96 | `Usage: unirand.poisson(${this.lambda}).random()`
97 | ];
98 | return info.join('\n');
99 | }
100 |
101 | /**
102 | * Mean value
103 | * Information only
104 | * For calculating real mean value use analyzer
105 | */
106 | get mean(): number {
107 | return this.lambda;
108 | }
109 |
110 | /**
111 | * Median (approximate value)
112 | * Information only
113 | * For calculating real median value use analyzer
114 | */
115 | get median(): number {
116 | return Math.floor(this.lambda + 0.33333333 - 0.02 * this.lambda);
117 | }
118 |
119 | /**
120 | * There are no exact Mode value
121 | */
122 |
123 | /**
124 | * Variance value
125 | * Information only
126 | * For calculating real variance value use analyzer
127 | */
128 | get variance(): number {
129 | return this.lambda;
130 | }
131 |
132 | /**
133 | * Skewness value
134 | * Information only
135 | * For calculating real skewness value use analyzer
136 | */
137 | get skewness(): number {
138 | return 1 / Math.sqrt(this.lambda);
139 | }
140 |
141 | /**
142 | * Kurtosis value
143 | * Information only
144 | * For calculating real kurtosis value use analyzer
145 | */
146 | get kurtosis(): number {
147 | return 1 / this.lambda;
148 | }
149 |
150 | /**
151 | * Fisher information
152 | * Information only
153 | * For calculating real fisher information value use analyzer
154 | */
155 | get fisher(): number {
156 | return 1 / this.lambda;
157 | }
158 |
159 | /**
160 | * Entropy value (result agrees with C. Knessl, “Integral representations and asymptotic
161 | * expansions for Shannon and Renyi entropies,”)
162 | * Information only
163 | * For calculating real value use analyzer
164 | */
165 | get entropy(): number {
166 | return 0.5 * Math.log(2 * Math.PI * this.lambda) + 0.5 - 1 / (12 * this.lambda) - 1 / (24 * Math.pow(this.lambda, 2));
167 | }
168 |
169 | /**
170 | * All parameters of distribution in one object
171 | * Information only
172 | */
173 | get parameters(): {} {
174 | return {
175 | mean: this.mean,
176 | median: this.median,
177 | variance: this.variance,
178 | skewness: this.skewness,
179 | fisher: this.fisher,
180 | kurtosis: this.kurtosis,
181 | entropy: this.entropy
182 | };
183 | }
184 | }
185 |
186 | module.exports = Poisson;
187 |
--------------------------------------------------------------------------------
/core/methods/rayleigh.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Rayleigh distribution
5 | * This is continuous distribution
6 | * https://en.wikipedia.org/wiki/Rayleigh_distribution
7 | * @param sigma - scale parameter, sigma > 0
8 | * @returns Rayleigh distributed value
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | import prng from '../prng/prngProxy';
15 |
16 | class Rayleigh implements IDistribution {
17 | sigma: number;
18 |
19 | constructor(sigma: number): void {
20 | this.sigma = Number(sigma);
21 | }
22 |
23 | /**
24 | * Generates a random number
25 | * @returns a Rayleigh distributed number
26 | */
27 | random(): number {
28 | let epsilon = 0.00000001;
29 | let u: number = Math.min((prng.random(): any) + epsilon, 1 - epsilon);
30 | return this._random(u);
31 | }
32 |
33 | /**
34 | * Generates next seeded random number
35 | * @returns {number}
36 | */
37 | next(): number {
38 | let epsilon = 0.00000001;
39 | let u: number = Math.min((prng.next(): any) + epsilon, 1 - epsilon);
40 | return this._random(u);
41 | }
42 |
43 | _random(u: number): number {
44 | return this.sigma * Math.sqrt(-2 * Math.log(u));
45 | }
46 |
47 | /**
48 | * Generates Rayleigh distributed numbers
49 | * @param n: number - Number of elements in resulting array, n > 0
50 | * @returns Array - Rayleigh distributed numbers
51 | */
52 | distribution(n: number): RandomArray {
53 | let rayleighArray: RandomArray = [],
54 | epsilon = 0.00000001,
55 | random: RandomArray = (prng.random(n): any),
56 | u: number;
57 | for(let i: number = 0; i < n; i += 1){
58 | u = Math.min(random[i] + epsilon, 1 - epsilon);
59 | rayleighArray[i] = this._random(u);
60 | }
61 | return rayleighArray;
62 | }
63 |
64 | /**
65 | * Error handling
66 | * @returns {boolean}
67 | */
68 | isError(): MethodError {
69 | if(!this.sigma) {
70 | return {error: 'Rayleigh distribution: you should point "sigma" (scale) numerical value'};
71 | }
72 | if(this.sigma <= 0) {
73 | return {error: 'Rayleigh distribution: parameter "sigma" (scale) must be a positive value'};
74 | }
75 | return { error: false };
76 | }
77 |
78 | /**
79 | * Refresh method
80 | * @param newSigma: number - new parameter "sigma"
81 | * This method does not return values
82 | */
83 | refresh(newSigma: number): void {
84 | this.sigma = Number(newSigma);
85 | }
86 |
87 | /**
88 | * Class .toString method
89 | * @returns {string}
90 | */
91 | toString(): string {
92 | let info = [
93 | 'Rayleigh Distribution',
94 | `Usage: unirand.rayleigh(${this.sigma}).random()`
95 | ];
96 | return info.join('\n');
97 | }
98 |
99 | /**
100 | * Mean value
101 | * Information only
102 | * For calculating real mean value use analyzer
103 | */
104 | get mean(): number {
105 | return this.sigma * Math.sqrt(Math.PI / 2);
106 | }
107 |
108 | /**
109 | * Median value
110 | * Information only
111 | * For calculating real median value use analyzer
112 | */
113 | get median(): number {
114 | return this.sigma * Math.sqrt(2 * Math.log(2));
115 | }
116 |
117 | /**
118 | * Mode value - value, which appears most often
119 | * Information only
120 | * For calculating real mode value use analyzer
121 | */
122 | get mode(): number {
123 | return this.sigma;
124 | }
125 |
126 | /**
127 | * Variance value
128 | * Information only
129 | * For calculating real variance value use analyzer
130 | */
131 | get variance(): number {
132 | return Math.pow(this.sigma, 2) * (4 - Math.PI) / 2;
133 | }
134 |
135 | /**
136 | * Entropy value
137 | * Information only
138 | * For calculating real entropy value use analyzer
139 | */
140 | get entropy(): number {
141 | return 1.28860783245076643030325605 + Math.log(this.sigma / 1.4142135623730950488016887242097);
142 | }
143 |
144 | /**
145 | * Skewness value
146 | * Information only
147 | * For calculating real skewness value use analyzer
148 | */
149 | get skewness(): number {
150 | return 2 * Math.sqrt(Math.PI) * (Math.PI - 3) / Math.pow(4 - Math.PI, 1.5);
151 | }
152 |
153 | /**
154 | * Kurtosis value
155 | * Information only
156 | * For calculating real kurtosis value use analyzer
157 | */
158 | get kurtosis(): number {
159 | return - (6 * Math.pow(Math.PI, 2) - 24 * Math.PI + 16) / Math.pow(4 - Math.PI, 2);
160 | }
161 |
162 | /**
163 | * All parameters of distribution in one object
164 | * Information only
165 | */
166 | get parameters(): {} {
167 | return {
168 | mean: this.mean,
169 | median: this.median,
170 | mode: this.mode,
171 | variance: this.variance,
172 | entropy: this.entropy,
173 | skewness: this.skewness,
174 | kurtosis: this.kurtosis
175 | };
176 | }
177 | }
178 |
179 | module.exports = Rayleigh;
180 |
--------------------------------------------------------------------------------
/core/methods/uniform.js:
--------------------------------------------------------------------------------
1 | // @flow
2 |
3 | /**
4 | * Continuous Uniform Distribution
5 | * https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)
6 | * @param min: number - Minimal value of Uniform Distribution
7 | * @param max: number - Maximum value of Uniform Distribution
8 | * @returns Uniform Distributed value based on parameters
9 | * Created by Alexey S. Kiselev
10 | */
11 |
12 | import type { MethodError, RandomArray } from '../types';
13 | import type { IDistribution } from '../interfaces';
14 | import prng from '../prng/prngProxy';
15 |
16 | class Uniform implements IDistribution {
17 | min: number;
18 | max: number;
19 |
20 | constructor(min: number = 0, max: number = 1): void {
21 | this.min = Math.min(min, max);
22 | this.max = Math.max(min, max);
23 | }
24 |
25 | /**
26 | * Generates a random number
27 | * Uses core Math.random() method but with [min, max] range
28 | */
29 | random(): number {
30 | return this._random((prng.random(): any));
31 | }
32 |
33 | /**
34 | * Generates next seeded random number
35 | * @returns {number}
36 | */
37 | next(): number {
38 | return this._random(prng.next());
39 | }
40 |
41 | _random(u: number): number {
42 | return this.min + u * (this.max - this.min);
43 | }
44 |
45 | /**
46 | * Generates an array of uniformly distributed numbers
47 | * @param n: number - number of elements in resulting array
48 | */
49 | distribution(n: number): RandomArray {
50 | let uniformArray: RandomArray = [],
51 | random: RandomArray = (prng.random(n): any);
52 | for(let i = 0; i < n; i += 1) {
53 | uniformArray[i] = this._random(random[i]);
54 | }
55 | return uniformArray;
56 | }
57 |
58 | /**
59 | * Error handling
60 | * Check if min === max then throw Error
61 | * @returns {boolean}
62 | */
63 | isError(): MethodError {
64 | if((!this.min && this.min !== 0) || (!this.max && this.max !== 0)) {
65 | return {error: 'Uniform distribution: you should point "min" and "max" numerical values'};
66 | }
67 | if(this.min === this.max) {
68 | return {error: 'Uniform distribution: min and max values can\'t be the same'};
69 | }
70 | return { error: false };
71 | }
72 |
73 | /**
74 | * Refresh method
75 | * @param newMin
76 | * @param newMax
77 | */
78 | refresh(newMin: number = 0, newMax: number = 1): void {
79 | this.min = Math.min(newMin, newMax);
80 | this.max = Math.max(newMin, newMax);
81 | }
82 |
83 | /**
84 | * toString() method
85 | * Show the description of this distribution
86 | * @returns {string}
87 | */
88 | toString() {
89 | let info = [
90 | 'Continuous Uniform Distribution',
91 | `Usage: unirand.uniform(${this.min}, ${this.max}).random()`
92 | ];
93 | return info.join('\n');
94 | }
95 |
96 | /**
97 | * Mean value of this distribution
98 | * Information only
99 | * For calculating real mean value use analyzer
100 | */
101 | get mean(): number {
102 | return (this.min + this.max) / 2;
103 | }
104 |
105 | /**
106 | * Median value
107 | * Information only
108 | * For calculating real median value use analyzer
109 | */
110 | get median(): number {
111 | return (this.min + this.max) / 2;
112 | }
113 |
114 | /**
115 | * Variance value
116 | * Information only
117 | * For calculating real variance value use analyzer
118 | */
119 | get variance(): number {
120 | return Math.pow(this.max - this.min, 2) / 12;
121 | }
122 |
123 | /**
124 | * Skewness value
125 | * Information only
126 | */
127 | get skewness(): number {
128 | return 0;
129 | }
130 |
131 | /**
132 | * Entropy value
133 | * Information only
134 | */
135 | get entropy(): number {
136 | return Math.log(this.max - this.min);
137 | }
138 |
139 | /**
140 | * Kurtosis value
141 | * Information only
142 | */
143 | get kurtosis(): number {
144 | return - 6 / 5;
145 | }
146 |
147 | /**
148 | * All parameters of distribution in one object
149 | * Information only
150 | */
151 | get parameters(): {} {
152 | return {
153 | mean: this.mean,
154 | median: this.median,
155 | variance: this.variance,
156 | skewness: this.skewness,
157 | entropy: this.entropy,
158 | kurtosis: this.kurtosis
159 | };
160 | }
161 | }
162 |
163 | module.exports = Uniform;
164 |
--------------------------------------------------------------------------------
/core/prng/BasicPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Basic PRNG
4 | * Generates random numbers with seed
5 | */
6 | import type { IPRNG } from '../interfaces';
7 | import type {NumberString, RandomArray, RandomArrayNumber} from '../types';
8 | import hashProxy from '../utils/hash';
9 |
10 | class BasicPRNG implements IPRNG {
11 |
12 | _seed: ?NumberString;
13 |
14 | constructor() {
15 | this._seed = undefined;
16 | }
17 |
18 | /**
19 | * Random number generator with seed
20 | * @public
21 | * @returns {number} random number
22 | */
23 | random(n: ?number = 0): RandomArrayNumber {
24 | this._prepare_initial();
25 |
26 | if (typeof n !== 'number') {
27 | return this.next();
28 | }
29 |
30 | if (n < 1) {
31 | return this.next();
32 | }
33 |
34 | const random_array: RandomArray = [];
35 | for (let i = 0; i < n; i += 1) {
36 | random_array[i] = this.next();
37 | }
38 |
39 | return random_array;
40 | }
41 |
42 | /**
43 | * Next random value
44 | * Returns only single random value
45 | * Does not support seed
46 | * @abstract
47 | * @returns {number}
48 | */
49 | next(): number {
50 | throw new Error('Unassigned method');
51 | }
52 |
53 | /**
54 | * Next integer random value
55 | * Returns only single random value
56 | * Does not support seed
57 | * @public
58 | * @returns {number}
59 | */
60 | nextInt(): number {
61 | return this._nextInt() >>> 0; // returns only unsigned integers
62 | }
63 |
64 | /**
65 | * @abstract
66 | * @protected
67 | */
68 | _nextInt(): number {
69 | throw new Error('Unassigned method');
70 | }
71 |
72 | /**
73 | * Generates random integer [0, 2^32)
74 | * @public
75 | * @returns {number}
76 | */
77 | randomInt(n: ?number = 0): RandomArrayNumber {
78 | this._prepare_initial();
79 |
80 | if (typeof n !== 'number') {
81 | return this.nextInt();
82 | }
83 |
84 | if (n < 1) {
85 | return this.nextInt();
86 | }
87 |
88 | const random_array: RandomArray = [];
89 | for (let i = 0; i < n; i += 1) {
90 | random_array[i] = this.nextInt();
91 | }
92 |
93 | return random_array;
94 | }
95 |
96 | /**
97 | * Sets seed value for PRNG
98 | * @public
99 | */
100 | seed(seed_value: ?NumberString): void {
101 | this._seed = seed_value;
102 | }
103 |
104 | /**
105 | * Modulo for seed
106 | * @returns {number}
107 | */
108 | static get modulo(): number {
109 | return 2147483647;
110 | }
111 |
112 | /**
113 | * Sets random seed
114 | */
115 | static random_seed(): number {
116 | let _seed: number = hashProxy.hash(Date.now() + Math.floor(Math.random() * BasicPRNG.modulo));
117 | if (_seed >= BasicPRNG.modulo) {
118 | _seed = _seed % BasicPRNG.modulo;
119 | }
120 |
121 | if (_seed < 0) {
122 | _seed += BasicPRNG.modulo - 1;
123 | }
124 | return _seed;
125 | }
126 |
127 | /**
128 | * Prepare initial values for calculating random value
129 | * @private
130 | */
131 | _prepare_initial(): void {
132 | if (this._has_no_seed()) {
133 | this._initialize();
134 | this._set_random_seed();
135 | } else {
136 | this._get_from_state();
137 | }
138 | }
139 |
140 | /**
141 | * @abstract
142 | * @protected
143 | */
144 | _has_no_seed(): boolean {
145 | throw new Error('Unassigned method');
146 | }
147 |
148 | /**
149 | * @protected
150 | * @abstract
151 | */
152 | _initialize(): void {
153 | throw new Error('Unassigned method');
154 | }
155 |
156 | /**
157 | * @protected
158 | * @abstract
159 | */
160 | _get_from_state(): void {
161 | throw new Error('Unassigned method');
162 | }
163 |
164 | /**
165 | * @protected
166 | * @abstract
167 | */
168 | _set_random_seed(): void {
169 | throw new Error('Unassigned method');
170 | }
171 | }
172 |
173 | export default BasicPRNG;
174 |
--------------------------------------------------------------------------------
/core/prng/DefaultPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Default PRNG
4 | */
5 | import BasicPRNG from './BasicPRNG';
6 | import type { IPRNG } from '../interfaces';
7 | import type { RandomArrayNumber } from '../types';
8 |
9 | class DefaultPRNG extends BasicPRNG implements IPRNG {
10 | constructor() {
11 | super();
12 | }
13 |
14 | _random(): number {
15 | return Math.random();
16 | }
17 |
18 | _randomInt(): number {
19 | return Math.floor(this._random() * 0x100000000);
20 | }
21 |
22 | /**
23 | * @override
24 | * @returns {Array|number}
25 | */
26 | random(n: ?number = 0): RandomArrayNumber {
27 | if (typeof n !== 'number') {
28 | return this._random();
29 | }
30 |
31 | if (n < 1) {
32 | return this._random();
33 | }
34 |
35 | const random_array: Array = [];
36 | for (let i = 0; i < n; i += 1) {
37 | random_array[i] = this._random();
38 | }
39 | return random_array;
40 | }
41 |
42 | /**
43 | * @override
44 | * @returns {Array|number}
45 | */
46 | // eslint-disable-next-line
47 | randomInt(n: ?number = 0): RandomArrayNumber {
48 | if (typeof n !== 'number') {
49 | return this._randomInt();
50 | }
51 |
52 | if (n < 1) {
53 | return this._randomInt();
54 | }
55 |
56 | const random_array: Array = [];
57 | for (let i = 0; i < n; i += 1) {
58 | random_array[i] = this._randomInt();
59 | }
60 | return random_array;
61 | }
62 |
63 | /**
64 | * @override
65 | * @returns {number}
66 | */
67 | next(): number {
68 | return this._random();
69 | }
70 |
71 | /**
72 | * @override
73 | * @returns {number}
74 | */
75 | nextInt(): number {
76 | return this._randomInt();
77 | }
78 | }
79 |
80 | export default DefaultPRNG;
81 |
--------------------------------------------------------------------------------
/core/prng/KissPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Kiss32 PRNG
4 | * Period: 2^121
5 | */
6 |
7 | import BasicPRNG from './BasicPRNG';
8 | import type { IPRNG } from '../interfaces';
9 | import type {NumberString} from '../types';
10 |
11 | class KissPRNG extends BasicPRNG implements IPRNG {
12 |
13 | _x: number;
14 | _y: number;
15 | _z: number;
16 | _w: number;
17 | _c: number;
18 | _no_seed: boolean;
19 | _state: {[prop: string]: number}; // state after setting seed
20 |
21 | constructor() {
22 | super();
23 | this._no_seed = true;
24 | this._state = {};
25 | this._initialize();
26 | this._set_random_seed();
27 | }
28 |
29 | /**
30 | * Indicate whether seed is set up
31 | * @private
32 | * @override
33 | */
34 | _has_no_seed(): boolean {
35 | return this._no_seed;
36 | }
37 |
38 | /**
39 | * Initializes initial values and sets state for calculating random number
40 | * @private
41 | * @override
42 | */
43 | _initialize(): void {
44 | this._x = 0;
45 | this._y = 0;
46 | this._z = 0;
47 | this._w = 0;
48 | this._c = 0;
49 | }
50 |
51 | /**
52 | * Initializes initial values with seed and sets state for calculating random number
53 | * @private
54 | */
55 | _initialize_with_seed(): void {
56 | this._x = (this._seed: any) | 0;
57 | this._y = (this._seed: any) << 5 | 0;
58 | this._z = (this._seed: any) >> 7 | 0;
59 | this._w = (this._seed: any) << 22 | 0;
60 | this._c = 0;
61 | }
62 |
63 | /**
64 | * Sets state for random number generating
65 | * @param {number} x
66 | * @param {number} y
67 | * @param {number} z
68 | * @param {number} w
69 | * @param {number} c
70 | * @private
71 | */
72 | _setState(x: number, y: number, z: number, w: number, c: number): void {
73 | this._state._x = x;
74 | this._state._y = y;
75 | this._state._z = z;
76 | this._state._w = w;
77 | this._state._c = c;
78 | }
79 |
80 | /**
81 | * Gets values from state
82 | * @private
83 | * @override
84 | */
85 | _get_from_state(): void {
86 | this._x = this._state._x;
87 | this._y = this._state._y;
88 | this._z = this._state._z;
89 | this._w = this._state._w;
90 | this._c = this._state._c;
91 | }
92 |
93 | /**
94 | * Creates random seed
95 | * @private
96 | * @override
97 | */
98 | _set_random_seed(): void {
99 | this._seed = BasicPRNG.random_seed();
100 | this._initialize_with_seed();
101 | }
102 |
103 | /**
104 | * @override
105 | * @param {?NumberString} seed_value
106 | */
107 | seed(seed_value: ?NumberString): void {
108 | let _tempSeed;
109 | this._initialize();
110 | if (seed_value === undefined || seed_value === null) {
111 | this._no_seed = true;
112 | this._set_random_seed();
113 | } else if (typeof seed_value === 'number') {
114 | this._seed = Math.floor(seed_value);
115 | this._initialize_with_seed();
116 | this._setState(this._x, this._y, this._z, this._w, this._c);
117 | this._no_seed = false;
118 | } else if (typeof seed_value === 'string') {
119 | this._seed = seed_value;
120 | for (let i = 0; i < this._seed.length + 20; i += 1) {
121 | _tempSeed = this._seed.charCodeAt(i);
122 | this._x ^= _tempSeed | 0;
123 | this._y ^= _tempSeed << 5 | 0;
124 | this._z ^= _tempSeed >> 7 | 0;
125 | this._w ^= _tempSeed << 22 | 0;
126 | this._nextInt();
127 | }
128 | this._setState(this._x, this._y, this._z, this._w, this._c);
129 | this._no_seed = false;
130 | } else {
131 | this._no_seed = true;
132 | this._set_random_seed();
133 | throw new Error('You should point seed with types: "undefined", "number" or "string"');
134 | }
135 | }
136 |
137 | /**
138 | * @override
139 | * @returns {number}
140 | * @private
141 | */
142 | _nextInt(): number {
143 | let t: number;
144 | this._y ^= (this._y << 5);
145 | this._y ^= (this._y >> 7);
146 | this._y ^= (this._y << 22);
147 | t = this._z + this._w + this._c;
148 | this._z = this._w;
149 | this._c = ((t < 0): any);
150 | this._w = t & 0x7FFFFFFF;
151 | this._x += 0x542023AB;
152 |
153 | return this._x + this._y + this._w;
154 | }
155 |
156 | /**
157 | * @override
158 | * @returns {number}
159 | */
160 | next(): number {
161 | return (this._nextInt() >>> 0) / 0x100000000;
162 | }
163 | }
164 |
165 | export default KissPRNG;
166 |
--------------------------------------------------------------------------------
/core/prng/ParkMillerPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * ParkMiller PRNG
4 | * Period: 2^31 - 1
5 | */
6 |
7 | import BasicPRNG from './BasicPRNG';
8 | import type { IPRNG } from '../interfaces';
9 | import type { NumberString } from '../types';
10 |
11 | class ParkMillerPRNG extends BasicPRNG implements IPRNG {
12 |
13 | _M: number;
14 | _A: number;
15 | _Q: number;
16 | _R: number;
17 | _no_seed: boolean;
18 | _state: {[prop: string]: number}; // state after setting seed
19 | _x: number;
20 |
21 | constructor() {
22 | super();
23 | this._no_seed = true;
24 | this._state = {};
25 | this._M = 0x7fffffff;
26 | this._A = 48271;
27 | this._Q = 44488;
28 | this._R = 3399;
29 | this._initialize();
30 | this._set_random_seed();
31 | }
32 |
33 | /**
34 | * Indicate whether seed is set up
35 | * @private
36 | * @override
37 | */
38 | _has_no_seed(): boolean {
39 | return this._no_seed;
40 | }
41 |
42 | /**
43 | * Initializes initial values and sets state for calculating random number
44 | * @private
45 | * @override
46 | */
47 | _initialize(): void {
48 | this._x = 0;
49 | }
50 |
51 | /**
52 | * Sets state for random number generating
53 | * @param {number} x
54 | * @private
55 | */
56 | _setState(x: number): void {
57 | this._state._x = x;
58 | }
59 |
60 | /**
61 | * Gets values from state
62 | * @private
63 | * @override
64 | */
65 | _get_from_state(): void {
66 | this._x = this._state._x;
67 | }
68 |
69 | /**
70 | * Creates random seed
71 | * @private
72 | * @override
73 | */
74 | _set_random_seed(): void {
75 | this._seed = BasicPRNG.random_seed();
76 | this._x = this._seed | 0;
77 | }
78 |
79 | /**
80 | * @override
81 | * @param {?NumberString} seed_value
82 | */
83 | seed(seed_value: ?NumberString): void {
84 | this._initialize();
85 | if (seed_value === undefined || seed_value === null) {
86 | this._no_seed = true;
87 | this._set_random_seed();
88 | } else if (typeof seed_value === 'number') {
89 | this._seed = Math.floor(seed_value);
90 | this._x = this._seed | 0;
91 | this._setState(this._x);
92 | this._no_seed = false;
93 | } else if (typeof seed_value === 'string') {
94 | this._seed = seed_value;
95 | for (let i = 0; i < this._seed.length + 20; i += 1) {
96 | this._x ^= this._seed.charCodeAt(i) | 0;
97 | this._nextInt();
98 | }
99 | this._setState(this._x);
100 | this._no_seed = false;
101 | } else {
102 | this._no_seed = true;
103 | this._set_random_seed();
104 | throw new Error('You should point seed with types: "undefined", "number" or "string"');
105 | }
106 | }
107 |
108 | /**
109 | * @override
110 | * @returns {number}
111 | * @private
112 | */
113 | _nextInt(): number {
114 | const div: number = Math.floor(this._x / this._Q);
115 | const rem: number = this._x % this._Q;
116 | let res: number = rem * this._A - div * this._R;
117 |
118 | if (res < 0) {
119 | res += this._M;
120 | }
121 |
122 | return this._x = res;
123 | }
124 |
125 | /**
126 | * @override
127 | * @returns {number}
128 | */
129 | next(): number {
130 | return (this._nextInt() >>> 0) / this._M;
131 | }
132 | }
133 |
134 | export default ParkMillerPRNG;
135 |
--------------------------------------------------------------------------------
/core/prng/Taus113PRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Tausworthe PRNG
4 | * Period: 2^113
5 | * P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe Generators", Mathematics of Computation, 65, 213 (1996), 203--213
6 | */
7 |
8 | import BasicPRNG from './BasicPRNG';
9 | import type { IPRNG } from '../interfaces';
10 | import type {NumberString} from '../types';
11 |
12 | class Taus113PRNG extends BasicPRNG implements IPRNG {
13 |
14 | _s1: number;
15 | _s2: number;
16 | _s3: number;
17 | _s4: number;
18 | _no_seed: boolean;
19 | _state: {[prop: string]: number}; // state after setting seed
20 |
21 | constructor() {
22 | super();
23 | this._no_seed = true;
24 | this._state = {};
25 | this._initialize();
26 | this._set_random_seed();
27 | }
28 |
29 | /**
30 | * Indicate whether seed is set up
31 | * @private
32 | * @override
33 | */
34 | _has_no_seed(): boolean {
35 | return this._no_seed;
36 | }
37 |
38 | /**
39 | * Initializes initial values and sets state for calculating random number
40 | * @private
41 | * @override
42 | */
43 | _initialize(): void {
44 | this._s1 = 0;
45 | this._s2 = 0;
46 | this._s3 = 0;
47 | this._s4 = 0;
48 | }
49 |
50 | /**
51 | * Initializes initial values with seed and sets state for calculating random number
52 | * @private
53 | */
54 | _initialize_with_seed(): void {
55 | const seed: number = ((this._seed: any) << 10) ^ (this._seed: any);
56 | this._s1 = this._minSeed(seed, 2);
57 | this._s2 = this._minSeed(seed, 8);
58 | this._s3 = this._minSeed(seed, 16);
59 | this._s4 = this._minSeed(seed, 128);
60 | }
61 |
62 | /**
63 | * Sets state for random number generating
64 | * @param {number} s1
65 | * @param {number} s2
66 | * @param {number} s3
67 | * @param {number} s4
68 | * @private
69 | */
70 | _setState(s1: number, s2: number, s3: number, s4: number): void {
71 | this._state._s1 = s1;
72 | this._state._s2 = s2;
73 | this._state._s3 = s3;
74 | this._state._s4 = s4;
75 | }
76 |
77 | /**
78 | * Gets values from state
79 | * @private
80 | * @override
81 | */
82 | _get_from_state(): void {
83 | this._s1 = this._state._s1;
84 | this._s2 = this._state._s2;
85 | this._s3 = this._state._s3;
86 | this._s4 = this._state._s4;
87 | }
88 |
89 | /**
90 | * Creates random seed
91 | * @private
92 | * @override
93 | */
94 | _set_random_seed(): void {
95 | this._seed = BasicPRNG.random_seed();
96 | this._initialize_with_seed();
97 | }
98 |
99 | /**
100 | * @override
101 | * @param {?NumberString} seed_value
102 | */
103 | seed(seed_value: ?NumberString): void {
104 | let _tempSeed;
105 | this._initialize();
106 | if (seed_value === undefined || seed_value === null) {
107 | this._no_seed = true;
108 | this._set_random_seed();
109 | } else if (typeof seed_value === 'number') {
110 | this._seed = Math.floor(seed_value);
111 | this._initialize_with_seed();
112 | this._setState(this._s1, this._s2, this._s3, this._s4);
113 | this._no_seed = false;
114 | } else if (typeof seed_value === 'string') {
115 | this._seed = seed_value;
116 | for (let i = 0; i < this._seed.length + 20; i += 1) {
117 | _tempSeed = this._seed.charCodeAt(i);
118 | _tempSeed = (_tempSeed << 10) ^ _tempSeed;
119 | this._s1 ^= this._minSeed(_tempSeed, 2);
120 | this._s2 ^= this._minSeed(_tempSeed, 8);
121 | this._s3 ^= this._minSeed(_tempSeed, 16);
122 | this._s4 ^= this._minSeed(_tempSeed, 128);
123 | this._nextInt();
124 | }
125 |
126 | this._setState(this._s1, this._s2, this._s3, this._s4);
127 | this._no_seed = false;
128 | } else {
129 | this._no_seed = true;
130 | this._set_random_seed();
131 | throw new Error('You should point seed with types: "undefined", "number" or "string"');
132 | }
133 | }
134 |
135 | /**
136 | * @override
137 | * @returns {number}
138 | * @private
139 | */
140 | _nextInt(): number {
141 | let b: number = ((this._s1 << 6) ^ this._s1) >> 13;
142 | this._s1 = ((this._s1 & 4294967294) << 18) ^ b;
143 | b = ((this._s2 << 2) ^ this._s2) >> 27;
144 | this._s2 = ((this._s2 & 4294967288) << 2) ^ b;
145 | b = ((this._s3 << 13) ^ this._s3) >> 21;
146 | this._s3 = ((this._s3 & 4294967280) << 7) ^ b;
147 | b = ((this._s4 << 3) ^ this._s4) >> 12;
148 | this._s4 = ((this._s4 & 4294967168) << 13) ^ b;
149 |
150 | return this._s1 ^ this._s2 ^ this._s3 ^ this._s4;
151 | }
152 |
153 | /**
154 | * @override
155 | * @returns {number}
156 | */
157 | next(): number {
158 | return (this._nextInt() >>> 0) / 0x100000000;
159 | }
160 |
161 | /**
162 | * Defines minimum value for seed
163 | */
164 | _minSeed(seed: number, minValue: number): number {
165 | return (seed < minValue) ? seed + minValue : seed;
166 | }
167 |
168 | }
169 |
170 | export default Taus113PRNG;
171 |
--------------------------------------------------------------------------------
/core/prng/TucheiPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Tuchei PRNG
4 | * Period: ~2^32
5 | */
6 | import BasicPRNG from './BasicPRNG';
7 | import type { IPRNG } from '../interfaces';
8 | import type { NumberString } from '../types';
9 |
10 | class TucheiPRNG extends BasicPRNG implements IPRNG {
11 |
12 | _a: number;
13 | _b: number;
14 | _c: number;
15 | _d: number;
16 | _no_seed: boolean;
17 | _state: {[prop: string]: number}; // state after setting seed
18 |
19 | constructor() {
20 | super();
21 | this._no_seed = true;
22 | this._state = {};
23 | this._initialize();
24 | this._set_random_seed();
25 | }
26 |
27 | /**
28 | * Indicate whether seed is set up
29 | * @private
30 | * @override
31 | */
32 | _has_no_seed(): boolean {
33 | return this._no_seed;
34 | }
35 |
36 | /**
37 | * Initializes initial values and sets state for calculating random number
38 | * @private
39 | * @override
40 | */
41 | _initialize(): void {
42 | this._a = 0;
43 | this._b = 0;
44 | this._c = 0x7FFFFFFF | 0;
45 | this._d = 0x517CC1B7;
46 | }
47 |
48 | /**
49 | * Sets state for random number generating
50 | * @param {number} a
51 | * @param {number} b
52 | * @param {number} c
53 | * @param {number} d
54 | * @private
55 | */
56 | _setState(a: number, b: number, c: number, d: number): void {
57 | this._state._a = a;
58 | this._state._b = b;
59 | this._state._c = c;
60 | this._state._d = d;
61 | }
62 |
63 | /**
64 | * Gets values from state
65 | * @private
66 | * @override
67 | */
68 | _get_from_state(): void {
69 | this._a = this._state._a;
70 | this._b = this._state._b;
71 | this._c = this._state._c;
72 | this._d = this._state._d;
73 | }
74 |
75 | /**
76 | * Creates random seed
77 | * @private
78 | * @override
79 | */
80 | _set_random_seed(): void {
81 | this._seed = BasicPRNG.random_seed();
82 | this._a = (this._seed / 0x100000000) | 0;
83 | this._b = this._seed | 0;
84 | }
85 |
86 | /**
87 | * @override
88 | * @returns {number}
89 | */
90 | next(): number {
91 | return (this._nextInt() >>> 0) / 0x100000000;
92 | }
93 |
94 | /**
95 | * @override
96 | */
97 | seed(seed_value: ?NumberString): void {
98 | this._initialize();
99 | if (seed_value === undefined || seed_value === null) {
100 | this._no_seed = true;
101 | this._set_random_seed();
102 | } else if (typeof seed_value === 'number') {
103 | this._seed = Math.floor(seed_value);
104 | this._a = (this._seed / 0x100000000) | 0;
105 | this._b = this._seed | 0;
106 | this._setState(this._a, this._b, this._c, this._d);
107 | this._no_seed = false;
108 | } else if (typeof seed_value === 'string') {
109 | this._seed = seed_value;
110 | for (let i = 0; i < this._seed.length + 20; i += 1) {
111 | this._b ^= this._seed.charCodeAt(i) | 0;
112 | this._nextInt();
113 | }
114 | this._setState(this._a, this._b, this._c, this._d);
115 | this._no_seed = false;
116 | } else {
117 | this._no_seed = true;
118 | this._set_random_seed();
119 | throw new Error('You should point seed with types: "undefined", "number" or "string"');
120 | }
121 | }
122 |
123 | /**
124 | * @override
125 | * @returns {number}
126 | * @private
127 | */
128 | _nextInt(): number {
129 | let a = this._a,
130 | b = this._b,
131 | c = this._c,
132 | d = this._d;
133 |
134 | b = (b << 25) ^ (b >>> 7) ^ c;
135 | c = (c - d) | 0;
136 | d = (d << 24) ^ (d >>> 8) ^ a;
137 | a = (a - b) | 0;
138 | this._b = b = (b << 20) ^ (b >>> 12) ^ c;
139 | this._c = c = (c - d) | 0;
140 | this._d = (d << 16) ^ (c >>> 16) ^ a;
141 | return this._a = (a - b) | 0;
142 | }
143 | }
144 |
145 | export default TucheiPRNG;
146 |
--------------------------------------------------------------------------------
/core/prng/XorshiftPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Xorshift32 PRNG
4 | * Period: 2 ^ 32
5 | */
6 |
7 | import BasicPRNG from './BasicPRNG';
8 | import type { IPRNG } from '../interfaces';
9 | import type {NumberString} from '../types';
10 |
11 | class XorshiftPRNG extends BasicPRNG implements IPRNG {
12 |
13 | _x: number;
14 | _no_seed: boolean;
15 | _state: {[prop: string]: number}; // state after setting seed
16 |
17 | constructor() {
18 | super();
19 | this._no_seed = true;
20 | this._state = {};
21 | this._initialize();
22 | this._set_random_seed();
23 | }
24 |
25 | /**
26 | * Indicate whether seed is set up
27 | * @private
28 | * @override
29 | */
30 | _has_no_seed(): boolean {
31 | return this._no_seed;
32 | }
33 |
34 | /**
35 | * Initializes initial values and sets state for calculating random number
36 | * @private
37 | * @override
38 | */
39 | _initialize(): void {
40 | this._x = 0;
41 | }
42 |
43 | /**
44 | * Sets state for random number generating
45 | * @param {number} x
46 | * @private
47 | */
48 | _setState(x: number): void {
49 | this._state._x = x;
50 | }
51 |
52 | /**
53 | * Gets values from state
54 | * @private
55 | * @override
56 | */
57 | _get_from_state(): void {
58 | this._x = this._state._x;
59 | }
60 |
61 | /**
62 | * Creates random seed
63 | * @private
64 | * @override
65 | */
66 | _set_random_seed(): void {
67 | this._seed = BasicPRNG.random_seed();
68 | this._x = this._seed | 0;
69 | }
70 |
71 | /**
72 | * @override
73 | * @param {?NumberString} seed_value
74 | */
75 | seed(seed_value: ?NumberString): void {
76 | this._initialize();
77 | if (seed_value === undefined || seed_value === null) {
78 | this._no_seed = true;
79 | this._set_random_seed();
80 | } else if (typeof seed_value === 'number') {
81 | this._seed = Math.floor(seed_value);
82 | this._x = this._seed | 0;
83 | this._setState(this._x);
84 | this._no_seed = false;
85 | } else if (typeof seed_value === 'string') {
86 | this._seed = seed_value;
87 | for (let i = 0; i < this._seed.length + 20; i += 1) {
88 | this._x ^= this._seed.charCodeAt(i) | 0;
89 | this._nextInt();
90 | }
91 | this._setState(this._x);
92 | this._no_seed = false;
93 | } else {
94 | this._no_seed = true;
95 | this._set_random_seed();
96 | throw new Error('You should point seed with types: "undefined", "number" or "string"');
97 | }
98 | }
99 |
100 | /**
101 | * @override
102 | * @returns {number}
103 | * @private
104 | */
105 | _nextInt(): number {
106 | let x = this._x;
107 |
108 | x ^= x << 13;
109 | x ^= x >> 17;
110 | x ^= x << 5;
111 | return this._x = x;
112 | }
113 |
114 | /**
115 | * @override
116 | * @returns {number}
117 | */
118 | next(): number {
119 | return (this._nextInt() >>> 0) / 0x100000000;
120 | }
121 | }
122 |
123 | export default XorshiftPRNG;
124 |
--------------------------------------------------------------------------------
/core/prng/XorwowPRNG.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * xorwow PRNG
4 | * http://www.jstatsoft.org/v08/i14/paper
5 | * Period: ~10^38
6 | */
7 |
8 | import BasicPRNG from './BasicPRNG';
9 | import XorshiftPRNG from './XorshiftPRNG';
10 | import type { IPRNG } from '../interfaces';
11 | import type {NumberString} from '../types';
12 |
13 | const WORDS_NUMBER: number = 6;
14 | const RECALCULATE_FREQ: number = 65536;
15 |
16 | class XorwowPRNG extends BasicPRNG implements IPRNG {
17 |
18 | _localPrng: IPRNG;
19 | _words: Array; // [x, y, z, w, v, d]
20 | _recalculate_counter: number;
21 | _no_seed: boolean;
22 | _state: {[prop: string]: number}; // state after setting seed
23 | _M: number;
24 |
25 | constructor() {
26 | super();
27 | this._no_seed = true;
28 | this._state = {};
29 | this._localPrng = new XorshiftPRNG();
30 | this._recalculate_counter = 0;
31 | this._M = 0x100000000;
32 | this._initialize();
33 | this._set_random_seed();
34 | }
35 |
36 | /**
37 | * Indicate whether seed is set up
38 | * @private
39 | * @override
40 | */
41 | _has_no_seed(): boolean {
42 | return this._no_seed;
43 | }
44 |
45 | /**
46 | * Initializes initial values and sets state for calculating random number
47 | * @private
48 | * @override
49 | */
50 | _initialize(): void {
51 | this._localPrng.seed(this._seed);
52 | this._words = (this._localPrng.randomInt(WORDS_NUMBER): any);
53 | }
54 |
55 | /**
56 | * Sets state for random number generating
57 | * @param {Array} words
58 | * @private
59 | */
60 | _setState(words: Array): void {
61 | this._state._words = (words: any).slice();
62 | }
63 |
64 | /**
65 | * Gets values from state
66 | * @private
67 | * @override
68 | */
69 | _get_from_state(): void {
70 | this._words = (this._state._words: any).slice();
71 | }
72 |
73 | /**
74 | * Prepare initial values for calculating random value
75 | * @private
76 | * @override
77 | */
78 | _prepare_initial(): void {
79 | if (this._no_seed === true) {
80 | this._set_random_seed();
81 | } else {
82 | this._get_from_state();
83 | }
84 | }
85 |
86 | /**
87 | * Creates random seed
88 | * @private
89 | * @override
90 | */
91 | _set_random_seed(): void {
92 | this._seed = BasicPRNG.random_seed();
93 | if (this._recalculate_counter === 0) {
94 | this._initialize();
95 | }
96 | this._recalculate_counter += 1;
97 | if (this._recalculate_counter === RECALCULATE_FREQ) {
98 | this._recalculate_counter = 0;
99 | }
100 | }
101 |
102 | /**
103 | * @override
104 | * @param {?NumberString} seed_value
105 | */
106 | seed(seed_value: ?NumberString): void {
107 | if (seed_value === undefined || seed_value === null) {
108 | this._no_seed = true;
109 | this._set_random_seed();
110 | } else if (typeof seed_value === 'number') {
111 | this._seed = Math.floor(seed_value);
112 | this._initialize();
113 | this._setState(this._words);
114 | this._no_seed = false;
115 | } else if (typeof seed_value === 'string') {
116 | this._seed = seed_value;
117 | this._initialize();
118 | this._setState(this._words);
119 | this._no_seed = false;
120 | } else {
121 | this._no_seed = true;
122 | this._set_random_seed();
123 | throw new Error('You should point seed with types: "undefined", "number" or "string"');
124 | }
125 | }
126 |
127 | /**
128 | * @override
129 | * @returns {number}
130 | * @private
131 | */
132 | _nextInt(): number {
133 | const t: number = this._words[0] ^ (this._words[0] >> 2);
134 | this._words[0] = this._words[1];
135 | this._words[1] = this._words[2];
136 | this._words[2] = this._words[3];
137 | this._words[3] = this._words[4];
138 | this._words[4] = (this._words[4] ^ (this._words[4] << 4)) ^ (t ^ (t << 1));
139 | this._words[5] = this._words[5] + 362437;
140 | if (this._words[5] >= this._M) {
141 | this._words[5] = this._words[5] % this._M;
142 | }
143 |
144 | let res: number = this._words[5] + this._words[4];
145 | if (res >= this._M) {
146 | res = res % this._M;
147 | }
148 | return res;
149 | }
150 |
151 | /**
152 | * @override
153 | * @returns {number}
154 | */
155 | next(): number {
156 | return (this._nextInt() >>> 0) / this._M;
157 | }
158 | }
159 |
160 | export default XorwowPRNG;
161 |
--------------------------------------------------------------------------------
/core/types.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Project types
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | /**
8 | * Random Array type
9 | */
10 | export type RandomArray = Array;
11 |
12 | /**
13 | * Random Array of numbers or string
14 | */
15 | export type RandomArrayNumberString = Array;
16 |
17 | /**
18 | * Random Array or String type
19 | */
20 | export type RandomArrayString = RandomArrayNumberString | string;
21 |
22 | /**
23 | * Random Array or string type or Object
24 | */
25 | export type RandomArrayStringObject = RandomArrayString | Object;
26 |
27 | /**
28 | * Analyzer public methods
29 | */
30 | export type AnalyzerPublicMethods = { [method: string]: number | boolean };
31 |
32 | /**
33 | * Analyzer Public properties
34 | */
35 | export type AnalyzerPublicProperties = { [property: string]: any };
36 |
37 | /**
38 | * In-method error type
39 | */
40 | export type MethodError = { error: string | boolean };
41 |
42 | /**
43 | * Analyzer PDF type
44 | */
45 | export type AnalyzerPDF = {
46 | values: RandomArray,
47 | probabilities: RandomArray
48 | };
49 |
50 | /**
51 | * Sample options
52 | */
53 | export type SampleOptions = {
54 | shuffle: ?boolean
55 | };
56 |
57 | export type KFoldOptions = {
58 | type: 'list' | 'set',
59 | derange: ?boolean
60 | }
61 |
62 | export type HashOptions = {
63 | algorithm: string
64 | }
65 |
66 | /**
67 | * Percentile input
68 | */
69 | export type PercentileInput = Array | T;
70 |
71 | /**
72 | * Number or string type
73 | */
74 | export type NumberString = number | string;
75 |
76 | /**
77 | * Array or number
78 | */
79 | export type RandomArrayNumber = RandomArray | number;
80 |
81 | /**
82 | * kfold crossvalidation
83 | */
84 | export type KFoldCrossValidationItem = {
85 | id: number,
86 | test: RandomArrayStringObject,
87 | data: RandomArrayStringObject
88 | };
89 |
90 | export type KFoldCrossValidation = Array;
91 |
92 | export type SmoothData = RandomArray | {
93 | smoothData: RandomArray,
94 | diff: {[string]: any}
95 | };
96 |
--------------------------------------------------------------------------------
/core/uidFactory.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Factory method for generating UIDs using different algorithms
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type {IUIDGeneratorFactory, IUIDGenerator} from './interfaces';
8 |
9 | const uidGenerators: {[string]: IUIDGenerator} = require('./utils/string_utils/uid');
10 | const DEFAULT_GENERATOR: string = 'uuid';
11 |
12 | class UidFactory implements IUIDGeneratorFactory {
13 |
14 | _currentGenerator: string;
15 |
16 | constructor(): void {
17 | this.setGenerator(DEFAULT_GENERATOR);
18 | }
19 |
20 | setGenerator(generator: string): void {
21 | if (!uidGenerators[generator]) {
22 | throw new Error(`UID generator: "${generator}" is not allowed`);
23 | }
24 | this._currentGenerator = generator;
25 | }
26 |
27 | random(): string {
28 | return uidGenerators[this._currentGenerator].generateRandom();
29 | }
30 |
31 | next(): string {
32 | return uidGenerators[this._currentGenerator].generateNext();
33 | }
34 | }
35 |
36 | export default UidFactory;
37 |
--------------------------------------------------------------------------------
/core/utils/Readme.md:
--------------------------------------------------------------------------------
1 | # Special functions list
2 |
3 | Gamma function for real numbers
4 |
5 | ## Gamma function
6 | gamma(z: number): number
7 |
8 | Parameters:
9 | * z: positive real number
10 |
11 | Returns:
12 | * number
13 |
14 | Throws:
15 | * Error: if the argument `z` less or equals to zero
16 |
17 | ## Digamma function
18 | digamma(z: number): number
19 |
20 | Parameters:
21 | * z: positive real number
22 |
23 | Returns:
24 | * number
25 |
26 | Throws:
27 | * Error: if the argument `z` less or equals to zero
28 |
29 | ## Error function
30 | erf(z: number): number
31 |
32 | Parameters:
33 | * z: real number
34 |
35 | Returns:
36 | * number
37 |
--------------------------------------------------------------------------------
/core/utils/encoders/base58.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Base58 encoding
4 | * https://en.wikipedia.org/wiki/Base58
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IEncoder} from '../../interfaces';
9 | import CommonEncoder from './commonEncoder';
10 |
11 | const ALLOWED_ALPHABETS: {[string]: string} = {
12 | 'bitcoin-base58': '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
13 | 'flickr-base58': '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ',
14 | 'ripple-base58': 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'
15 | };
16 | const ALPHABET_LENGTH: number = 58;
17 |
18 | class CommonBase58 extends CommonEncoder implements IEncoder {
19 |
20 | _alphabet: string;
21 | _codesMap: {[string]: number};
22 |
23 | constructor(alphabet: string) {
24 | super();
25 | this.setAlphabet(alphabet);
26 | this._createCodesMap();
27 | }
28 |
29 | setAlphabet(alphabet: string): void {
30 | if (!ALLOWED_ALPHABETS[alphabet]) {
31 | throw new Error(`Base58 encoder: alphabet ${alphabet} is not allowed`);
32 | }
33 | this._alphabet = ALLOWED_ALPHABETS[alphabet];
34 | }
35 |
36 | _createCodesMap(): void {
37 | this._codesMap = {};
38 | for (let i = 0; i < this._alphabet.length; i += 1) {
39 | this._codesMap[this._alphabet[i]] = i;
40 | }
41 | }
42 |
43 | _getCodesMap(): {[string]: number} {
44 | return this._codesMap;
45 | }
46 |
47 | encodeFromByteArray(bytes: Array): string {
48 | const indexes: Array = [0];
49 | let carry: number;
50 | for (let i = 0; i < bytes.length; i += 1) {
51 | carry = (indexes[0] << 8) + bytes[i];
52 | indexes[0] = carry % ALPHABET_LENGTH;
53 | carry = Math.floor(carry / ALPHABET_LENGTH);
54 |
55 | for (let j = 1; j < indexes.length; j += 1) {
56 | carry += indexes[j] << 8;
57 | indexes[j] = carry % ALPHABET_LENGTH;
58 | carry = Math.floor(carry / ALPHABET_LENGTH);
59 | }
60 |
61 | while (carry > 0) {
62 | indexes.push(carry % ALPHABET_LENGTH);
63 | carry = Math.floor(carry / ALPHABET_LENGTH);
64 | }
65 | }
66 |
67 | // leading zeros
68 | for (let i = 0; bytes[i] === 0 && i < bytes.length - 1; i += 1) {
69 | indexes.push(0);
70 | }
71 |
72 | // reverse indexes and build the string
73 | let temp_letter: string;
74 | for (let i = 0, j = indexes.length - 1; i <= j; i += 1, j -= 1) {
75 | temp_letter = this._alphabet[indexes[i]];
76 | indexes[i] = this._alphabet[indexes[j]];
77 | indexes[j] = temp_letter;
78 | }
79 | return indexes.join('');
80 | }
81 |
82 | decodeToByteArray(str: string): Array {
83 | const res: Array = [0];
84 | let letterIndex: number;
85 | let carry: number;
86 |
87 | for (let i = 0; i < str.length; i += 1) {
88 | if (!this._getCodesMap()[str[i]]) {
89 | if (str[i] !== this._alphabet[0]) {
90 | throw new Error('Base58 decode: invalid input');
91 | }
92 | }
93 | letterIndex = this._getCodesMap()[str[i]];
94 |
95 | carry = res[0] * ALPHABET_LENGTH + letterIndex;
96 | res[0] = carry & 0xFF;
97 | carry >>= 8;
98 |
99 | for (let j = 1; j < res.length; j += 1) {
100 | carry += res[j] * ALPHABET_LENGTH;
101 | res[j] = carry & 0xFF;
102 | carry >>= 8;
103 | }
104 |
105 | while (carry > 0) {
106 | res.push(carry & 0xFF);
107 | carry >>= 8;
108 | }
109 | }
110 |
111 | // leading zeros
112 | for (let i = 0; str[i] === this._alphabet[0] && i < str.length - 1; i += 1) {
113 | res.push(0);
114 | }
115 |
116 | return res.reverse();
117 | }
118 | }
119 |
120 | class BitcoinBase58 extends CommonBase58 {
121 | constructor() {
122 | super('bitcoin-base58');
123 | }
124 | }
125 |
126 | class FlickrBase58 extends CommonBase58 {
127 | constructor() {
128 | super('flickr-base58');
129 | }
130 | }
131 |
132 | class RippleBase58 extends CommonBase58 {
133 | constructor() {
134 | super('ripple-base58');
135 | }
136 | }
137 |
138 | export {
139 | BitcoinBase58,
140 | FlickrBase58,
141 | RippleBase58
142 | };
143 |
--------------------------------------------------------------------------------
/core/utils/encoders/base62.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Base62 encoding
4 | * https://en.wikipedia.org/wiki/Base62
5 | * https://www.codeproject.com/Articles/1076295/Base-Encode
6 | * Created by Alexey S. Kiselev
7 | */
8 |
9 | import type {IEncoder} from '../../interfaces';
10 | import CommonEncoder from './commonEncoder';
11 |
12 | const CODES: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
13 | const CODES_INDEX_MAP: {[string]: number} = CODES
14 | .split('')
15 | .reduce((res, letter, index) => {res[letter] = index; return res;}, {});
16 | const CODE_FLAG: string = '9';
17 | const SPECIAL_CODE_MAP: {[string]: number} = {'A': 61, 'B': 62, 'C': 63};
18 |
19 | class Base62 extends CommonEncoder implements IEncoder {
20 |
21 | constructor() {
22 | super();
23 | }
24 |
25 | encodeFromByteArray(bytes: Array): string {
26 | let res: string = '';
27 | let b: number;
28 |
29 | // Iterate with 3 bytes as a group
30 | for (let i = 0; i < bytes.length; i += 3) {
31 | // #1 char
32 | b = (bytes[i] & 0xFC) >> 2;
33 | if (b < 61) {
34 | res += CODES[b];
35 | } else {
36 | res += CODE_FLAG + CODES[b - 61];
37 | }
38 |
39 | b = (bytes[i] & 0x03) << 4;
40 | if (i + 1 < bytes.length) {
41 | // #2 char
42 | b |= (bytes[i + 1] & 0xF0) >> 4;
43 | if (b < 61) {
44 | res += CODES[b];
45 | } else {
46 | res += CODE_FLAG + CODES[b - 61];
47 | }
48 |
49 | b = (bytes[i + 1] & 0x0F) << 2;
50 | if (i + 2 < bytes.length) {
51 | // #3 char
52 | b |= (bytes[i + 2] & 0xC0) >> 6;
53 | if (b < 61) {
54 | res += CODES[b];
55 | } else {
56 | res += CODE_FLAG + CODES[b - 61];
57 | }
58 |
59 | // #4 char
60 | b = bytes[i + 2] & 0x3F;
61 | if (b < 61) {
62 | res += CODES[b];
63 | } else {
64 | res += CODE_FLAG + CODES[b - 61];
65 | }
66 | } else {
67 | // #3 char, last char
68 | if (b < 61) {
69 | res += CODES[b];
70 | } else {
71 | res += CODE_FLAG + CODES[b - 61];
72 | }
73 | }
74 | } else {
75 | // #2 char, last char
76 | if (b < 61) {
77 | res += CODES[b];
78 | } else {
79 | res += CODE_FLAG + CODES[b - 61];
80 | }
81 | }
82 | }
83 |
84 | return res;
85 | }
86 |
87 | decodeToByteArray(str: string): Array {
88 | // Map for special code followed by CODE_FLAG '9' and its code index
89 | const decodedBytes: Array = [];
90 | const strLength: number = str.length;
91 | // 6 bits bytes
92 | const unit: Array = [0, 0, 0, 0];
93 | // char counter
94 | let n: number = 0;
95 | // unit counter
96 | let m: number = 0;
97 | // regular char
98 | let char1: string;
99 | // special char
100 | let char2: string;
101 | let b: number;
102 |
103 | while (n < strLength) {
104 | char1 = str[n];
105 | if (char1 !== CODE_FLAG) {
106 | // regular code
107 | unit[m] = CODES_INDEX_MAP[char1];
108 | m += 1;
109 | n += 1;
110 | } else {
111 | n += 1;
112 | if (n < strLength) {
113 | char2 = str[n];
114 | if (char2 !== CODE_FLAG) {
115 | // special code index 61, 62, 63
116 | unit[m] = SPECIAL_CODE_MAP[char2];
117 | m++;
118 | n++;
119 | }
120 | }
121 | }
122 | // Add regular bytes with 3 bytes group composed from 4 units with 6 bits
123 | if (m === 4) {
124 | b = (unit[0] << 2) | (unit[1] >> 4);
125 | decodedBytes.push(b);
126 | b = ((unit[1] & 0x0F) << 4) | (unit[2] >> 2);
127 | decodedBytes.push(b);
128 | b = ((unit[2] & 0x03) << 6) | unit[3];
129 | decodedBytes.push(b);
130 |
131 | m = 0;
132 | }
133 | }
134 |
135 | // Add tail bytes group less than 4 units
136 | if (m !== 0) {
137 | if (m === 1) {
138 | b = (unit[0] << 2);
139 | decodedBytes.push(b);
140 | } else if (m === 2) {
141 | b = (unit[0] << 2) | (unit[1] >> 4);
142 | decodedBytes.push(b);
143 | } else if (m === 3) {
144 | b = (unit[0] << 2) | (unit[1] >> 4);
145 | decodedBytes.push(b);
146 | b = ((unit[1] & 0x0F) << 4) | (unit[2] >> 2);
147 | decodedBytes.push(b);
148 | }
149 | }
150 | return decodedBytes;
151 | }
152 | }
153 |
154 | const base62: IEncoder = new Base62(); // need because of multiple usage
155 |
156 | export default base62;
157 |
--------------------------------------------------------------------------------
/core/utils/encoders/base64.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Base64 encoding
4 | * https://en.wikipedia.org/wiki/Base64
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IEncoder} from '../../interfaces';
9 | import CommonEncoder from './commonEncoder';
10 |
11 | const ALPHABET: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
12 | const ALPHABET_INDEX_MAP: {[string]: number} = ALPHABET
13 | .split('')
14 | .reduce((res, letter, index) => {res[letter] = index; return res;}, {});
15 | const PADDING: string = '=';
16 | const DOUBLE_PADDING: string = '==';
17 |
18 | class Base64 extends CommonEncoder implements IEncoder {
19 |
20 | constructor() {
21 | super();
22 | }
23 |
24 | encodeFromByteArray(bytes: Array): string {
25 | const bytesLength: number = bytes.length;
26 | if (bytesLength === 0) {
27 | return '';
28 | }
29 |
30 | const sizeMod3: number = bytesLength % 3;
31 | const div3Size: number = bytesLength - sizeMod3;
32 | let temp: number;
33 | const res: Array = [];
34 | for (let i = 0; i < div3Size; i += 3) {
35 | temp = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
36 | res.push(ALPHABET.charAt(temp >> 18));
37 | res.push(ALPHABET.charAt((temp >> 12) & 0x3F));
38 | res.push(ALPHABET.charAt((temp >> 6) & 0x3F));
39 | res.push(ALPHABET.charAt(temp & 0x3F));
40 | }
41 |
42 | if (sizeMod3 === 1) {
43 | temp = bytes[bytesLength - 1] << 16;
44 | res.push(ALPHABET.charAt(temp >> 18) + ALPHABET.charAt((temp >> 12) & 0x3F)
45 | + DOUBLE_PADDING);
46 | } else if (sizeMod3 === 2) {
47 | temp = bytes[bytesLength - 2] << 16 | bytes[bytesLength - 1] << 8;
48 | res.push(ALPHABET.charAt(temp >> 18) + ALPHABET.charAt((temp >> 12) & 0x3F)
49 | + ALPHABET.charAt((temp >> 6) & 0x3F) + PADDING);
50 | }
51 |
52 | return res.join('');
53 | }
54 |
55 | decodeToByteArray(str: string): Array {
56 | let strlength: number = str.length;
57 | if (strlength === 0) {
58 | return [];
59 | }
60 | if (strlength % 4 !== 0) {
61 | throw new Error('Base64 decode: wrong input');
62 | }
63 |
64 | let paddingSymbols: number = 0;
65 | if (str.charAt(strlength - 1) === PADDING) {
66 | paddingSymbols = 1;
67 | if (str.charAt(strlength - 2) === PADDING) {
68 | paddingSymbols = 2;
69 | }
70 | strlength -= 4;
71 | }
72 |
73 | const res: Array = [];
74 | let temp: number;
75 | for (let i = 0; i < strlength; i += 4) {
76 | temp = (ALPHABET_INDEX_MAP[str[i]] << 18) | (ALPHABET_INDEX_MAP[str[i + 1]] << 12)
77 | | (ALPHABET_INDEX_MAP[str[i + 2]] << 6) | ALPHABET_INDEX_MAP[str[i + 3]];
78 | res.push(temp >> 16);
79 | res.push((temp >> 8) & 0xFF);
80 | res.push(temp & 0xFF);
81 | }
82 |
83 | if (paddingSymbols === 1) {
84 | temp = (ALPHABET_INDEX_MAP[str[strlength]] << 18) | (ALPHABET_INDEX_MAP[str[strlength + 1]] << 12)
85 | | (ALPHABET_INDEX_MAP[str[strlength + 2]] << 6);
86 | res.push(temp >> 16);
87 | res.push((temp >> 8) & 0xFF);
88 | } else if (paddingSymbols === 2) {
89 | temp = (ALPHABET_INDEX_MAP[str[strlength]] << 18) | (ALPHABET_INDEX_MAP[str[strlength + 1]] << 12);
90 | res.push(temp >> 16);
91 | }
92 |
93 | return res;
94 | }
95 | }
96 |
97 | const base64: IEncoder = new Base64(); // need because of multiple usage
98 |
99 | export default base64;
100 |
--------------------------------------------------------------------------------
/core/utils/encoders/commonEncoder.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Common class for encoders
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type {IEncoder} from '../../interfaces';
8 |
9 | class CommonEncoder implements IEncoder {
10 |
11 | constructor() {}
12 |
13 | encode(str: string): string {
14 | const bytes: Array = [];
15 | for (let i = 0; i < str.length; i += 1) {
16 | bytes[i] = str[i].charCodeAt(0);
17 | }
18 | return this.encodeFromByteArray(bytes);
19 | }
20 |
21 | decode(str: string): string {
22 | const bytes: Array = this.decodeToByteArray(str);
23 | let res: string = '';
24 |
25 | for (let i = 0; i < bytes.length; i += 1) {
26 | res += String.fromCharCode(bytes[i]);
27 | }
28 |
29 | return res;
30 | }
31 |
32 | /**
33 | * @abstract
34 | */
35 | // eslint-disable-next-line no-unused-vars
36 | encodeFromByteArray(bytes: Array): string {
37 | throw new Error('encodeFromByteArray method not implemented');
38 | }
39 |
40 | /**
41 | * @abstract
42 | */
43 | // eslint-disable-next-line no-unused-vars
44 | decodeToByteArray(str: string): Array {
45 | throw new Error('decodeToByteArray method not implemented');
46 | }
47 | }
48 |
49 | export default CommonEncoder;
50 |
--------------------------------------------------------------------------------
/core/utils/encoders/encoderProxy.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Encoders proxy
4 | * Creates encoder due to type
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IEncoderProxy, IEncoder} from '../../interfaces';
9 |
10 | import base62 from './base62'; // object already created, because of multiple usage
11 | import base64 from './base64'; // object already created, because of multiple usage
12 | import {Base32, Base32Hex, ZBase32, CrockfordBase32} from './base32';
13 | import {BitcoinBase58, FlickrBase58, RippleBase58} from './base58';
14 |
15 | class EncoderProxy implements IEncoderProxy {
16 |
17 | _encoders: {[string]: IEncoder};
18 | _allowed_encoders: {[string]: any};
19 | _current_encoder_name: string;
20 | _current_encoder: IEncoder;
21 |
22 | constructor() {
23 | this._allowed_encoders = {
24 | 'base62': base62,
25 | 'base64': base64,
26 | 'base32': Base32,
27 | 'base32Hex': Base32Hex,
28 | 'z-base-32': ZBase32,
29 | 'crockford-base32': CrockfordBase32,
30 | 'base58': BitcoinBase58,
31 | 'bitcoin-base58': BitcoinBase58,
32 | 'flickr-base58': FlickrBase58,
33 | 'ripple-base58': RippleBase58
34 | };
35 | // encoders already created must be in list of encoders by default
36 | this._encoders = {
37 | 'base62': base62,
38 | 'base64': base64
39 | };
40 | }
41 |
42 | /**
43 | * A list of allowed encoders
44 | * @returns {Array} a list of encoders
45 | */
46 | get encoders(): Array {
47 | return Object.keys(this._allowed_encoders);
48 | }
49 |
50 | /**
51 | * Current Encoder name
52 | * @returns {string} name of current Encoder
53 | */
54 | get encoder_name(): string {
55 | return this._current_encoder_name;
56 | }
57 |
58 | setEncoder(encoder: string): void {
59 | if (!this._allowed_encoders[encoder]) {
60 | throw new Error(`Encoder ${encoder} is not allowed`);
61 | }
62 |
63 | // if current encoder is the same - do nothing
64 | if (this._current_encoder_name === encoder) {
65 | return;
66 | }
67 |
68 | if (!this._encoders[encoder]) {
69 | this._encoders[encoder] = new this._allowed_encoders[encoder]();
70 | }
71 |
72 | this._current_encoder_name = encoder;
73 | this._current_encoder = this._encoders[encoder];
74 | }
75 |
76 | encode(str: string): string {
77 | return this._current_encoder.encode(str);
78 | }
79 |
80 | encodeFromByteArray(bytes: Array): string {
81 | return this._current_encoder.encodeFromByteArray(bytes);
82 | }
83 |
84 | decode(str: string): string {
85 | return this._current_encoder.decode(str);
86 | }
87 |
88 | decodeToByteArray(str: string): Array {
89 | return this._current_encoder.decodeToByteArray(str);
90 | }
91 | }
92 |
93 | const encoderProxy: IEncoderProxy = new EncoderProxy();
94 |
95 | export default encoderProxy;
96 |
--------------------------------------------------------------------------------
/core/utils/hash/hashProxy.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Hash proxy
4 | * Creates hash using different hash algorithms
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type { NumberString, RandomArrayNumber } from '../../types';
9 | import type { IHash, IHashProxy } from '../../interfaces';
10 |
11 | import Murmur3Hash from './murmur3';
12 | import JenkinsHash from './jenkins';
13 |
14 | const DEFAULT_HASH_FUNCTION: string = 'murmur';
15 |
16 | class HashProxy implements IHashProxy {
17 |
18 | _hashFunctions: {[string]: IHash};
19 | _allowedHashFunctions: {[string]: IHash};
20 | _currentHashFunctionName: string;
21 | _currentHashFunction: IHash;
22 |
23 | constructor() {
24 | this._hashFunctions = {
25 | 'murmur': new Murmur3Hash()
26 | };
27 |
28 | this._allowedHashFunctions = {
29 | 'default': Murmur3Hash,
30 | 'murmur': Murmur3Hash,
31 | 'jenkins': JenkinsHash
32 | };
33 |
34 | this._currentHashFunctionName = DEFAULT_HASH_FUNCTION;
35 | this._currentHashFunction = this._hashFunctions[DEFAULT_HASH_FUNCTION];
36 | }
37 |
38 | /**
39 | * A list of allowed hash functions
40 | * @returns {Array} a list of hash functions
41 | */
42 | listHashFunctions(): Array {
43 | return Object.keys(this._allowedHashFunctions);
44 | }
45 |
46 | /**
47 | * Current hash function name
48 | * @returns {string} name of current hash function
49 | */
50 | get hashFunctionName(): string {
51 | return this._currentHashFunctionName;
52 | }
53 |
54 | /**
55 | * Default hash function name
56 | * @returns {string} name of default hash function
57 | */
58 | getDefaultHashFunctionName(): string {
59 | return DEFAULT_HASH_FUNCTION;
60 | }
61 |
62 | /**
63 | * Sets hash algorithm
64 | * @param {string} name - name from allowed hash functions list
65 | */
66 | setHashFunction(name: string = DEFAULT_HASH_FUNCTION): void {
67 | if (this._currentHashFunctionName === name || !this._allowedHashFunctions[name]) {
68 | return;
69 | }
70 |
71 | if (!this._hashFunctions[name]) {
72 | this._hashFunctions[name] = new this._allowedHashFunctions[name]();
73 | }
74 | this._currentHashFunctionName = name;
75 | this._currentHashFunction = this._hashFunctions[name];
76 | }
77 |
78 | /**
79 | * Return simple hash number
80 | * @param {NumberString} data
81 | * @param {number} seed
82 | * @param {number} modulo
83 | * @returns {number} hash number
84 | * @private
85 | */
86 | _hashNumber(data: NumberString, seed: number, modulo: ?number): number {
87 | if (!modulo) {
88 | return this._currentHashFunction.hash(data, seed || 0);
89 | }
90 | return this._mod(this._currentHashFunction.hash(data, seed || 0), modulo);
91 | }
92 |
93 | /**
94 | * Implement modulo by relying on the fact that the negative remainder
95 | * is always p numbers away from a positive reminder
96 | * Ex: -5 % 3 | -5 = -2 * 3 + 1 and -5 = -1 * 3 + (-2) | -2 + 3 = 1
97 | * @param {number} value
98 | * @param {number} p
99 | * @private
100 | */
101 | _mod(value: number, p: number): number {
102 | const r: number = value % p;
103 | return r < 0 ? r + p : r;
104 | }
105 |
106 | /**
107 | * Return array of hash numbers
108 | * @param {NumberString} data
109 | * @param {Array} seed - array of seed numbers
110 | * @param {number} modulo
111 | * @return {Array}
112 | * @private
113 | */
114 | _hashArray(data: NumberString, seed: Array, modulo: ?number): Array {
115 | const res: Array = [];
116 | for (let i = 0; i < seed.length; i += 1) {
117 | res[i] = this._hashNumber(data, seed[i], modulo);
118 | }
119 |
120 | return res;
121 | }
122 |
123 | /**
124 | * Hash
125 | * @param {NumberString} data - data to hash
126 | * @param {RandomArrayNumber} seed
127 | * @param {number} modulo
128 | * @returns {RandomArrayNumber} hash, can return array of hashes for different seeds
129 | */
130 | hash(data: NumberString, seed: RandomArrayNumber, modulo: ?number): RandomArrayNumber {
131 | if (Array.isArray(seed)) {
132 | return this._hashArray(data, seed, modulo);
133 | }
134 | return this._hashNumber(data, seed, modulo);
135 | }
136 | }
137 |
138 | export default HashProxy;
139 |
--------------------------------------------------------------------------------
/core/utils/hash/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Return hash proxy with given algorithm
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type { IHashProxy } from '../../interfaces';
8 | import HashProxy from './hashProxy';
9 |
10 | const hashProxy: IHashProxy = new HashProxy();
11 | const defaultHashFunctionName: string = hashProxy.getDefaultHashFunctionName();
12 | const hash: Function = (name: string = 'default'): IHashProxy => {
13 | hashProxy.setHashFunction(name || defaultHashFunctionName);
14 | return hashProxy;
15 | };
16 |
17 | export default hash(defaultHashFunctionName);
18 |
--------------------------------------------------------------------------------
/core/utils/hash/jenkins.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Jenkins algorithm
4 | * https://en.wikipedia.org/wiki/Jenkins_hash_function
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type { NumberString } from '../../types';
9 | import type { IHash } from '../../interfaces';
10 |
11 | class JenkinsHash implements IHash {
12 | constructor() {}
13 |
14 | /**
15 | * Calculates hash
16 | * @param {NumberString} data
17 | * @param {number} seed
18 | * @returns {number}
19 | */
20 | hash(data: NumberString, seed: number = 0): number {
21 | if (typeof data === 'string') {
22 | return this._hashFromString(data, data.length, seed);
23 | } else if (typeof data === 'number') {
24 | const s: string = data.toString();
25 | return this._hashFromString(s, s.length, seed);
26 | }
27 | throw new Error('You should point data as number or string');
28 | }
29 |
30 | _hashFromString(data: string, len: number, seed: number = 0): number {
31 | let a, b, c, offset = 0, length = len;
32 | a = b = c = (0xdeadbeef + (len << 2) + seed) | 0;
33 |
34 | while(length > 3) {
35 | a = (a + data.charCodeAt(offset)) | 0;
36 | b = (b + data.charCodeAt(offset + 1)) | 0;
37 | c = (c + data.charCodeAt(offset + 2)) | 0;
38 |
39 | // mixing
40 | a = (a - c) | 0; a ^= this._rotate(c, 4); c = (c + b) | 0;
41 | b = (b - a) | 0; b ^= this._rotate(a, 6); a = (a + c) | 0;
42 | c = (c - b) | 0; c ^= this._rotate(b, 8); b = (b + a) | 0;
43 | a = (a - c) | 0; a ^= this._rotate(c,16); c = (c + b) | 0;
44 | b = (b - a) | 0; b ^= this._rotate(a,19); a = (a + c) | 0;
45 | c = (c - b) | 0; c ^= this._rotate(b, 4); b = (b + a) | 0;
46 |
47 | length -= 3;
48 | offset += 3;
49 | }
50 |
51 | switch(length) {
52 | case 3: c = (c + data.charCodeAt(offset + 2)) | 0;
53 | // eslint-disable-next-line
54 | case 2: b = (b + data.charCodeAt(offset + 1)) | 0;
55 | // eslint-disable-next-line
56 | case 1: a = (a + data.charCodeAt(offset)) | 0;
57 |
58 | // final mixing
59 | c ^= b; c = (c - this._rotate(b,14)) | 0;
60 | a ^= c; a = (a - this._rotate(c,11)) | 0;
61 | b ^= a; b = (b - this._rotate(a,25)) | 0;
62 | c ^= b; c = (c - this._rotate(b,16)) | 0;
63 | a ^= c; a = (a - this._rotate(c, 4)) | 0;
64 | b ^= a; b = (b - this._rotate(a,14)) | 0;
65 | c ^= b; c = (c - this._rotate(b,24)) | 0;
66 | // eslint-disable-next-line
67 | case 0: break;
68 | }
69 |
70 | // return the result
71 | return c;
72 | }
73 |
74 | _rotate(v: number, k: number): number {
75 | return (v << k) | (v >>> (32 - k));
76 | }
77 | }
78 |
79 | export default JenkinsHash;
80 |
--------------------------------------------------------------------------------
/core/utils/hash/murmur3.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Fast hashing murmur3 algorithm
4 | * Austin Appleby algorithm
5 | * https://en.wikipedia.org/wiki/MurmurHash
6 | */
7 |
8 | import type { NumberString } from '../../types';
9 | import type { IHash } from '../../interfaces';
10 |
11 | class Murmur3Hash implements IHash {
12 |
13 | constructor() {}
14 |
15 | /**
16 | * Calculates hash
17 | * @param {NumberString} data
18 | * @param {number} seed
19 | * @returns {number}
20 | */
21 | hash(data: NumberString, seed: number = 0): number {
22 | if (typeof data === 'string') {
23 | return this._hashFromString(data, data.length, seed);
24 | } else if (typeof data === 'number') {
25 | const s: string = data.toString();
26 | return this._hashFromString(s, s.length, seed);
27 | }
28 | throw new Error('You should point data as number or string');
29 | }
30 |
31 | _mul32(m: number, n: number): number {
32 | const nlo: number = n & 0xffff;
33 | const nhi: number = n - nlo;
34 | return ((nhi * m | 0) + (nlo * m | 0)) | 0;
35 | }
36 |
37 | _hashFromString(data: string, len: number, seed: number): number {
38 | let c1: number = 0xcc9e2d51,
39 | c2: number = 0x1b873593,
40 | h1: number = seed,
41 | roundedEnd: number = len & ~0x1,
42 | k1: number;
43 |
44 | for (let i = 0; i < roundedEnd; i += 2) {
45 | k1 = data.charCodeAt(i) | (data.charCodeAt(i + 1) << 16);
46 |
47 | k1 = this._mul32(k1, c1);
48 | k1 = ((k1 & 0x1ffff) << 15) | (k1 >>> 17);
49 | k1 = this._mul32(k1, c2);
50 |
51 | h1 ^= k1;
52 | h1 = ((h1 & 0x7ffff) << 13) | (h1 >>> 19);
53 | h1 = (h1 * 5 + 0xe6546b64) | 0;
54 | }
55 |
56 | if (len % 2 === 1) {
57 | k1 = data.charCodeAt(roundedEnd);
58 | k1 = this._mul32(k1, c1);
59 | k1 = ((k1 & 0x1ffff) << 15) | (k1 >>> 17);
60 | k1 = this._mul32(k1, c2);
61 | h1 ^= k1;
62 | }
63 |
64 | h1 ^= (len << 1);
65 | h1 ^= h1 >>> 16;
66 | h1 = this._mul32(h1, 0x85ebca6b);
67 | h1 ^= h1 >>> 13;
68 | h1 = this._mul32(h1, 0xc2b2ae35);
69 | h1 ^= h1 >>> 16;
70 |
71 | return h1;
72 | }
73 | }
74 |
75 | export default Murmur3Hash;
76 |
--------------------------------------------------------------------------------
/core/utils/string_utils/randomBitStringHelper.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Helper for randomBitString
4 | * Using Binomial-Shuffle algorithm
5 | * https://arxiv.org/pdf/1808.05009.pdf
6 | * Created by Alexey S. Kiselev
7 | */
8 |
9 | import prng from '../../prng/prngProxy';
10 |
11 | module.exports = (length: number, p: number = 0.5, binomialM: number): string => {
12 | if (p === 1) {
13 | let _res = '';
14 | for (let i = 0; i < length; i += 1) {
15 | _res += '1';
16 | }
17 | return _res;
18 | }
19 |
20 | let res: Array = [];
21 | for (let i = 0; i < length; i += 1) {
22 | res[i] = 0;
23 | }
24 |
25 | let k: number, t: number;
26 | for (let i = length - binomialM; i < length; i += 1) {
27 | k = Math.floor(prng.next() * i);
28 | t = length - 1 - k;
29 | if (res[t] === 0) {
30 | res[t] = 1;
31 | } else {
32 | res[length - 1 - i] = 1;
33 | }
34 | }
35 |
36 | return res.join('');
37 | };
38 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/baseUid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Common class for all uid generators
4 | * Created by Alexey S. Kiselev
5 | */
6 | import type {RandomArray} from '../../../types';
7 |
8 | const TIMESTAMP_SIZE: number = 4;
9 | const UNIX_TIMESTAMP_SIZE: number = 8;
10 | const EPOCH: number = 1400000000;
11 |
12 | class BaseUid {
13 |
14 | constructor() {}
15 |
16 | getTimestampSize(): number {
17 | return TIMESTAMP_SIZE;
18 | }
19 |
20 | /**
21 | * Generates random time, then converts it to array of 4 bytes
22 | * @returns {RandomArray}
23 | * @private
24 | */
25 | _createTimestampBytesArray(): RandomArray {
26 | const res = [];
27 | // TODO: fix it after 2038-01-19T03:14:07+00:00
28 | let ts = Math.floor(Date.now() / 1000) - EPOCH;
29 | for (let i = 0; i < TIMESTAMP_SIZE; i += 1) {
30 | res[TIMESTAMP_SIZE - 1 - i] = (ts & 0xFF);
31 | ts >>= 8;
32 | }
33 |
34 | return res;
35 | }
36 |
37 | /**
38 | * Generates random time in millis, then converts it to array of 8 bytes
39 | * @returns {RandomArray}
40 | * @private
41 | */
42 | _createUnixTimestampBytesArray(alphabet: string, modulo: number): string {
43 | let res = '';
44 | let ts = Date.now();
45 | for (let i = 0; i < UNIX_TIMESTAMP_SIZE; i += 1) {
46 | res = alphabet[ts % modulo] + res;
47 | ts = Math.floor(ts / modulo);
48 | }
49 |
50 | return res;
51 | }
52 | }
53 |
54 | export default BaseUid;
55 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/betterguid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * betterguid UID generator
4 | * 8 bytes of time (milliseconds) + 9 random bytes
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IUIDGenerator} from '../../../interfaces';
9 | import type {RandomArray} from '../../../types';
10 | import BaseUid from './baseUid';
11 | import prng from '../../../prng/prngProxy';
12 |
13 | const CODES: string = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
14 | const MODULO: number = CODES.length;
15 | const PAYLOAD_SIZE: number = 9;
16 |
17 | class RandomBetterGuidGenerator extends BaseUid implements IUIDGenerator {
18 |
19 | constructor() {
20 | super();
21 | }
22 |
23 | generateRandom(): string {
24 | return this._getRandomString((prng.random(PAYLOAD_SIZE): any));
25 | }
26 |
27 | generateNext(): string {
28 | const randoms: RandomArray = [];
29 | for (let i = 0; i < PAYLOAD_SIZE; i += 1) {
30 | randoms[i] = (prng.next(): any);
31 | }
32 | return this._getRandomString(randoms);
33 | }
34 |
35 | _getRandomString(randoms: RandomArray): string {
36 | let res: string = this._createUnixTimestampBytesArray(CODES, MODULO);
37 | for (let i = 0; i < PAYLOAD_SIZE; i += 1) {
38 | res += CODES[Math.floor(randoms[i] * MODULO)];
39 | }
40 |
41 | return res;
42 | }
43 | }
44 |
45 | export default RandomBetterGuidGenerator;
46 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Random UID generators
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type {IUIDGenerator} from '../../../interfaces';
8 |
9 | import RandomKsuidGenerator from './ksuid';
10 | import RandomXidGenerator from './xid';
11 | import RandomBetterGuidGenerator from './betterguid';
12 | import RandomSonyflakeGenerator from './sonyflake';
13 | import RandomSnowflakeGenerator from './snowflake';
14 | import RandomUlidGenerator from './ulid';
15 | import RandomUuidGenerator from './uuid';
16 | import RandomShortUuidGenerator from './shortuuid';
17 | import RandomSnoGenerator from './sno';
18 |
19 | const ksuid: IUIDGenerator = new RandomKsuidGenerator();
20 | const xid: IUIDGenerator = new RandomXidGenerator();
21 | const betterguid: IUIDGenerator = new RandomBetterGuidGenerator();
22 | const sonyflake: IUIDGenerator = new RandomSonyflakeGenerator();
23 | const snowflake: IUIDGenerator = new RandomSnowflakeGenerator();
24 | const ulid: IUIDGenerator = new RandomUlidGenerator();
25 | const uuid: IUIDGenerator = new RandomUuidGenerator();
26 | const shortuuid: IUIDGenerator = new RandomShortUuidGenerator();
27 | const sno: IUIDGenerator = new RandomSnoGenerator();
28 |
29 | module.exports = {
30 | ksuid,
31 | xid,
32 | betterguid,
33 | sonyflake,
34 | snowflake,
35 | ulid,
36 | uuid,
37 | shortuuid,
38 | sno
39 | };
40 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/ksuid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * ksuid UID generator
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import prng from '../../../prng/prngProxy';
8 | import BaseUid from './baseUid';
9 | import base62 from '../../encoders/base62';
10 | import type {IUIDGenerator} from '../../../interfaces';
11 | import type {RandomArray} from '../../../types';
12 |
13 | const PAYLOAD_SIZE: number = 16;
14 | const MAX_ENCODED_SIZE: number = 27;
15 |
16 | class RandomKsuidGenerator extends BaseUid implements IUIDGenerator {
17 |
18 | codes: string;
19 | codeFlag: string;
20 | payloadSize: number;
21 | tsSize: number;
22 |
23 | constructor() {
24 | super();
25 | this.tsSize = this.getTimestampSize();
26 | }
27 |
28 | generateRandom(): string {
29 | let res: string = base62.encodeFromByteArray(this._randomBytesArray());
30 | if (res.length > MAX_ENCODED_SIZE) {
31 | return res.substring(0, MAX_ENCODED_SIZE);
32 | }
33 | return res;
34 | }
35 |
36 | generateNext(): string {
37 | let res: string = base62.encodeFromByteArray(this._nextBytesArray());
38 | if (res.length > MAX_ENCODED_SIZE) {
39 | return res.substring(0, MAX_ENCODED_SIZE);
40 | }
41 | return res;
42 | }
43 |
44 | _randomBytesArray(): RandomArray {
45 | const res: RandomArray = this._createTimestampBytesArray();
46 | const randoms: RandomArray = (prng.random(PAYLOAD_SIZE): any);
47 | for (let i = 0; i < PAYLOAD_SIZE; i += 1) {
48 | res[this.tsSize + i] = Math.floor(randoms[i] * 256);
49 | }
50 |
51 | return res;
52 | }
53 |
54 | _nextBytesArray(): RandomArray {
55 | const res: RandomArray = this._createTimestampBytesArray();
56 | for (let i = 0; i < PAYLOAD_SIZE; i += 1) {
57 | res[this.tsSize + i] = Math.floor(prng.next() * 256);
58 | }
59 |
60 | return res;
61 | }
62 | }
63 |
64 | export default RandomKsuidGenerator;
65 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/shortuuid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * shortuuid UID generator
4 | * Encoded UUID transformed to Flickr base58 alphabet
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IUIDGenerator} from '../../../interfaces';
9 | import RandomUuidGenerator from './uuid';
10 |
11 | const HEX_ALPHABET_INDEX_MAP: {[string]: number} = {
12 | '0': 0,
13 | '1': 1,
14 | '2': 2,
15 | '3': 3,
16 | '4': 4,
17 | '5': 5,
18 | '6': 6,
19 | '7': 7,
20 | '8': 8,
21 | '9': 9,
22 | 'a': 10,
23 | 'b': 11,
24 | 'c': 12,
25 | 'd': 13,
26 | 'e': 14,
27 | 'f': 15
28 | };
29 | const FLICKR_ALPHABET: string = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
30 | const FLICKR_ALPHABET_LENGTH: number = FLICKR_ALPHABET.length;
31 | const EXPECTED_LENGTH: number = 22;
32 |
33 | class RandomShortUuidGenerator extends RandomUuidGenerator implements IUIDGenerator {
34 |
35 | constructor() {
36 | super();
37 | }
38 |
39 | generateRandom(): string {
40 | return this._getShortUuid(super.generateRandom());
41 | }
42 |
43 | generateNext(): string {
44 | return this._getShortUuid(super.generateNext());
45 | }
46 |
47 | _getShortUuid(uid: string): string {
48 | let res: string = '';
49 | const indexMap: {[number]: number} = {};
50 | let j: number = 0;
51 | for (let i = 0; i < uid.length; i += 1) {
52 | if (uid[i] !== '-') {
53 | indexMap[j] = HEX_ALPHABET_INDEX_MAP[uid[i]];
54 | j += 1;
55 | }
56 | }
57 |
58 | let div: number, newLength: number;
59 | let length: number = Object.keys(indexMap).length;
60 | do {
61 | div = 0;
62 | newLength = 0;
63 | for (let i = 0; i < length; i += 1) {
64 | div = div * 16 + indexMap[i];
65 | if (div >= FLICKR_ALPHABET_LENGTH) {
66 | indexMap[newLength++] = parseInt(div / FLICKR_ALPHABET_LENGTH, 10);
67 | div = div % FLICKR_ALPHABET_LENGTH;
68 | } else if (newLength > 0) {
69 | indexMap[newLength++] = 0;
70 | }
71 | }
72 | length = newLength;
73 | res = FLICKR_ALPHABET[div] + res;
74 | } while (newLength !== 0);
75 |
76 | let startPaddingRes: string = '';
77 | for (let i = 0; i < Math.max(EXPECTED_LENGTH - res.length, 0); i += 1) {
78 | startPaddingRes += FLICKR_ALPHABET[0];
79 | }
80 | return startPaddingRes + res;
81 | }
82 | }
83 |
84 | export default RandomShortUuidGenerator;
85 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/sno.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * sno UID generator
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type {IUIDGenerator, IEncoder} from '../../../interfaces';
8 | import prng from '../../../prng/prngProxy';
9 | import {Base32Hex} from '../../encoders/base32';
10 |
11 | const MAX_SEQUENCE_VALUE: number = 0xFFFF;
12 | // use meta and partition random values
13 | const MAX_META_PARTITION_VALUE: number = 0xFFFFFF;
14 | const base32Hex: IEncoder = new Base32Hex(true);
15 |
16 | class RandomSnoGenerator implements IUIDGenerator {
17 |
18 | sequence: number;
19 |
20 | constructor() {
21 | this.sequence = 0;
22 | }
23 |
24 | generateRandom(): string {
25 | return this._getSno((prng.random(): any));
26 | }
27 |
28 | generateNext(): string {
29 | return this._getSno((prng.next(): any));
30 | }
31 |
32 | _getSno(random: number): string {
33 | const resBytes: Array = this._convertTimestampToBytesArray();
34 | let rand: number = Math.floor(random * MAX_META_PARTITION_VALUE);
35 |
36 | // generates rand as bytes array
37 | for (let i = 0; i < 3; i += 1) {
38 | resBytes[7 - i] = rand % 256;
39 | rand = Math.floor(rand / 256);
40 | }
41 |
42 | this.sequence = (this.sequence + 1) & MAX_SEQUENCE_VALUE;
43 | let seq: number = this.sequence | 0;
44 |
45 | for (let i = 0; i < 2; i += 1) {
46 | resBytes[9 - i] = seq % 256;
47 | seq = Math.floor(seq / 256);
48 | }
49 |
50 | return base32Hex.encodeFromByteArray(resBytes);
51 | }
52 |
53 | /**
54 | * @returns {string}
55 | * @private
56 | */
57 | _convertTimestampToBytesArray(): Array {
58 | const res: Array = [];
59 | let ts = Math.floor(Date.now() / 4);
60 | for (let i = 0; i < 5; i += 1) {
61 | res[4 - i] = ts % 256;
62 | ts = Math.floor(ts / 256);
63 | }
64 |
65 | return res;
66 | }
67 | }
68 |
69 | export default RandomSnoGenerator;
70 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/snowflake.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * snowflake UID generator - 41 bits of time in millis + 10 bit machine id + 12 sequence
4 | * highest bit is always 0
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IUIDGenerator, IHash} from '../../../interfaces';
9 | import prng from '../../../prng/prngProxy';
10 | import Murmur3Hash from '../../hash/murmur3';
11 |
12 | const murmur3Hash: IHash = new Murmur3Hash();
13 | const isRunningInNode: boolean = typeof process !== 'undefined'
14 | && process.versions != null && process.versions.node != null;
15 |
16 | const MAX_RANDOM1_VALUE: number = 0x3FF;
17 | const LEADING_ZEROS: Array = ['', '0', '00', '000', '0000', '00000', '000000',
18 | '0000000', '00000000', '000000000', '0000000000'];
19 | const EXPECTED_RIGHT_SIZE: number = 10;
20 |
21 | class RandomSnowflakeGenerator implements IUIDGenerator {
22 |
23 | random1: number;
24 | need_random1: boolean;
25 | sequence: number;
26 |
27 | constructor() {
28 | this.need_random1 = true;
29 | this.sequence = 0;
30 |
31 | if (isRunningInNode) {
32 | const os = require('os');
33 | if (os.hostname().length > 0) {
34 | this.need_random1 = false;
35 | this.random1 = murmur3Hash.hash((os.hostname(): any), 0) & MAX_RANDOM1_VALUE;
36 | }
37 | }
38 | }
39 |
40 | generateRandom(): string {
41 | this._generateRandom1IfNeeded((prng.random(): any));
42 | return this._getSnowflake();
43 | }
44 |
45 | generateNext(): string {
46 | this._generateRandom1IfNeeded((prng.next(): any));
47 | return this._getSnowflake();
48 | }
49 |
50 | _generateRandom1IfNeeded(random: number): void {
51 | if (this.need_random1) {
52 | this.random1 = Math.floor(random * MAX_RANDOM1_VALUE);
53 | }
54 | }
55 |
56 | _getSnowflake(): string {
57 | const res: {left: string, right: number} = this._getTimestampData();
58 | let right: number = res.right;
59 | right |= (this.random1 << 12);
60 | this.sequence = (this.sequence + 1) & 0xFFF;
61 | right |= this.sequence;
62 | const rightAsString: string = right.toString();
63 | return res.left + LEADING_ZEROS[EXPECTED_RIGHT_SIZE - rightAsString.length] + rightAsString;
64 | }
65 |
66 | _getTimestampData(): {left: string, right: number} {
67 | let left: number = 0;
68 | let right: number;
69 | let ts = Date.now();
70 |
71 | right = ts % 3;
72 | ts = Math.floor(ts / 3);
73 | right |= ((ts % 256) & 0x7F) << 2;
74 | ts = Math.floor(ts / 256);
75 | right <<= 22;
76 |
77 | for (let i = 0; i < 4; i += 1) {
78 | left = (ts % 256) << (8 * i);
79 | ts = Math.floor(ts / 256);
80 | }
81 | left &= 0x7FFFFFFF;
82 |
83 | return {
84 | left: left.toString(),
85 | right
86 | };
87 | }
88 | }
89 |
90 | export default RandomSnowflakeGenerator;
91 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/sonyflake.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * sonyflake UID generator - ~6 bytes of time (10 ms) + 1 byte sequence + 2 bytes machine id
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | import type {IUIDGenerator, IHash} from '../../../interfaces';
8 | import prng from '../../../prng/prngProxy';
9 | import Murmur3Hash from '../../hash/murmur3';
10 |
11 | const murmur3Hash: IHash = new Murmur3Hash();
12 | const isRunningInNode: boolean = typeof process !== 'undefined'
13 | && process.versions != null && process.versions.node != null;
14 | const MAX_RANDOM1_VALUE: number = 0xFFFF;
15 | const MAX_SEQUENCE_VALUE: number = 0xFF;
16 | const SONYFLAKE_TIMEUNIT: number = 10;
17 | const SONYFLAKE_START_TIME: number = 1409529600000;
18 | const SONYFLAKE_OVERFLOW_VALUE: number = 549755813887;
19 |
20 | class RandomSonyflakeGenerator implements IUIDGenerator {
21 |
22 | random1: number;
23 | need_random1: boolean;
24 | startTime: number;
25 | elapsedTime: number;
26 | sequence: number;
27 |
28 | constructor() {
29 | this.sequence = 0;
30 | this.startTime = this._toSonyflakeTime(SONYFLAKE_START_TIME);
31 | this.elapsedTime = 0;
32 | this.need_random1 = true;
33 | if (isRunningInNode) {
34 | const os = require('os');
35 | if (os.hostname().length > 0) {
36 | this.need_random1 = false;
37 | this.random1 = murmur3Hash.hash((os.hostname(): any), 0) & MAX_RANDOM1_VALUE;
38 | }
39 | }
40 | }
41 |
42 | generateRandom(): string {
43 | this._generateRandom1IfNeeded((prng.random(): any));
44 | return this._getSonyflake();
45 | }
46 |
47 | generateNext(): string {
48 | this._generateRandom1IfNeeded((prng.next(): any));
49 | return this._getSonyflake();
50 | }
51 |
52 | _getSonyflake(): string {
53 | let current: number = this._currentElapsedTime(this.startTime);
54 | if (this.elapsedTime < current) {
55 | this.elapsedTime = current;
56 | this.sequence = 0;
57 | } else {
58 | this.sequence = (this.sequence + 1) & MAX_SEQUENCE_VALUE;
59 | if (this.sequence === 0) {
60 | this.elapsedTime += 1;
61 | }
62 | }
63 |
64 | if (this.elapsedTime > SONYFLAKE_OVERFLOW_VALUE) {
65 | throw new Error('Sonyflake UID: time overflow');
66 | }
67 |
68 | let res: string = this._convertTimestampToString();
69 | res += this._sequenceToString(this.sequence);
70 |
71 | res += this._decToHex((this.random1 >> 8) & 0xFF);
72 | res += this._decToHex(this.random1 & 0xFF);
73 | return res;
74 | }
75 |
76 | /**
77 | * @returns {string}
78 | * @private
79 | */
80 | _convertTimestampToString(): string {
81 | let res: string = '';
82 | let ts = this.elapsedTime;
83 | for (let i = 0; i < 5; i += 1) {
84 | res = this._decToHex(ts % 256) + res;
85 | ts = Math.floor(ts / 256);
86 | }
87 |
88 | return res;
89 | }
90 |
91 | _generateRandom1IfNeeded(random: number): void {
92 | if (this.need_random1) {
93 | this.random1 = Math.floor(random * MAX_RANDOM1_VALUE);
94 | }
95 | }
96 |
97 | _toSonyflakeTime(utcTime: number): number {
98 | return Math.floor(utcTime / SONYFLAKE_TIMEUNIT);
99 | }
100 |
101 | _currentElapsedTime(startTime: number): number {
102 | return this._toSonyflakeTime(Date.now()) - startTime;
103 | }
104 |
105 | _decToHex(dec: number): string {
106 | if (dec < 16) {
107 | return '0' + dec.toString(16);
108 | }
109 | return dec.toString(16);
110 | }
111 |
112 | _sequenceToString(sequence: number): string {
113 | if (sequence < 10) {
114 | return '00' + sequence.toString();
115 | }
116 | if (sequence < 100) {
117 | return '0' + sequence.toString();
118 | }
119 | return sequence.toString();
120 | }
121 | }
122 |
123 | export default RandomSonyflakeGenerator;
124 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/ulid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * ulid UID generator
4 | * 6 bytes of time (milliseconds) + 8 random bytes
5 | * Created by Alexey S. Kiselev
6 | */
7 |
8 | import type {IUIDGenerator, IEncoder} from '../../../interfaces';
9 | import type {RandomArray} from '../../../types';
10 | import prng from '../../../prng/prngProxy';
11 | import {CrockfordBase32} from '../../encoders/base32';
12 |
13 | const crockfordBase32: IEncoder = new CrockfordBase32();
14 | const PAYLOAD_SIZE: number = 8;
15 | const MODULO: number = 256;
16 |
17 | class RandomUlidGenerator implements IUIDGenerator {
18 |
19 | constructor() {}
20 |
21 | generateRandom(): string {
22 | return crockfordBase32.encodeFromByteArray(this._getRandomByteArray((prng.random(PAYLOAD_SIZE): any)));
23 | }
24 |
25 | generateNext(): string {
26 | const randoms: RandomArray = [];
27 | for (let i = 0; i < PAYLOAD_SIZE; i += 1) {
28 | randoms[i] = (prng.next(): any);
29 | }
30 | return crockfordBase32.encodeFromByteArray(this._getRandomByteArray(randoms));
31 | }
32 |
33 | /**
34 | * Generates random time in millis, then converts it to array of 8 bytes
35 | * @returns {RandomArray}
36 | * @private
37 | */
38 | _createUnixTimestampBytesArray(): Array {
39 | const res: Array = [];
40 | let ts = Date.now();
41 | for (let i = 0; i < 6; i += 1) {
42 | res[5 - i] = ts % MODULO;
43 | ts = Math.floor(ts / MODULO);
44 | }
45 |
46 | return res;
47 | }
48 |
49 | _getRandomByteArray(randoms: RandomArray): RandomArray {
50 | const res: Array = this._createUnixTimestampBytesArray();
51 |
52 | for (let i = 0; i < PAYLOAD_SIZE; i += 1) {
53 | res[6 + i] = Math.floor(randoms[i] * MODULO);
54 | }
55 |
56 | return res;
57 | }
58 | }
59 |
60 | export default RandomUlidGenerator;
61 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/uuid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * uuid UID generator
4 | * https://en.wikipedia.org/wiki/Universally_unique_identifier
5 | * RFC 4122
6 | * Created by Alexey S. Kiselev
7 | */
8 |
9 | import type {IUIDGenerator} from '../../../interfaces';
10 | import type {RandomArray} from '../../../types';
11 | import prng from '../../../prng/prngProxy';
12 |
13 | class RandomUuidGenerator implements IUIDGenerator {
14 |
15 | constructor() {}
16 |
17 | generateRandom(): string {
18 | return this._getUuid((prng.random(23): any));
19 | }
20 |
21 | generateNext(): string {
22 | const randoms: RandomArray = [];
23 | for (let i = 0; i < 23; i += 1) {
24 | randoms[i] = (prng.next(): any);
25 | }
26 | return this._getUuid(randoms);
27 | }
28 |
29 | /**
30 | * randoms should be length of 23
31 | * @param {RandomArray} randoms
32 | * @returns {string}
33 | * @private
34 | */
35 | _getUuid(randoms: RandomArray): string {
36 | let res: string = this._createUnixTimestampString();
37 |
38 | // ********-xxxx
39 | res += Math.floor(randoms[0] * 0xF).toString(16) +
40 | Math.floor(randoms[1] * 0xF).toString(16) +
41 | Math.floor(randoms[2] * 0xF).toString(16) +
42 | Math.floor(randoms[3] * 0xF).toString(16) + '-';
43 |
44 | // ********-****-4xxx
45 | res += '4' + Math.floor(randoms[4] * 0xF).toString(16) +
46 | Math.floor(randoms[5] * 0xF).toString(16) +
47 | Math.floor(randoms[6] * 0xF).toString(16) + '-';
48 |
49 | // ********-****-****-yxxx
50 | res += ((Math.floor(randoms[7] * 0xF) & 0x3) | 0x8).toString(16) +
51 | Math.floor(randoms[8] * 0xF).toString(16) +
52 | Math.floor(randoms[9] * 0xF).toString(16) +
53 | Math.floor(randoms[10] * 0xF).toString(16) + '-';
54 | // ********-****-****-****-xxxxxxxxxxxx
55 | res += Math.floor(randoms[11] * 0xF).toString(16) +
56 | Math.floor(randoms[12] * 0xF).toString(16) +
57 | Math.floor(randoms[13] * 0xF).toString(16) +
58 | Math.floor(randoms[14] * 0xF).toString(16) +
59 | Math.floor(randoms[15] * 0xF).toString(16) +
60 | Math.floor(randoms[16] * 0xF).toString(16) +
61 | Math.floor(randoms[17] * 0xF).toString(16) +
62 | Math.floor(randoms[18] * 0xF).toString(16) +
63 | Math.floor(randoms[19] * 0xF).toString(16) +
64 | Math.floor(randoms[20] * 0xF).toString(16) +
65 | Math.floor(randoms[21] * 0xF).toString(16) +
66 | Math.floor(randoms[22] * 0xF).toString(16);
67 | return res;
68 | }
69 |
70 | /**
71 | * Generates random time in millis, then converts it <32 low bits>-<16 high bits> string
72 | * @private
73 | */
74 | _createUnixTimestampString(): string {
75 | let ts = Date.now();
76 | return (ts % 0xFFFFFFFF).toString(16) + '-';
77 | }
78 | }
79 |
80 | export default RandomUuidGenerator;
81 |
--------------------------------------------------------------------------------
/core/utils/string_utils/uid/xid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * xid UID generator
4 | * 4 bytes of time (seconds) + 3 byte machine id + 2 byte process id + 3 bytes random
5 | * for browser version will use random values
6 | * Created by Alexey S. Kiselev
7 | */
8 |
9 | import type {IUIDGenerator, IHash, IEncoder} from '../../../interfaces';
10 | import type {RandomArray} from '../../../types';
11 | import prng from '../../../prng/prngProxy';
12 | import Murmur3Hash from '../../hash/murmur3';
13 | import BaseUid from './baseUid';
14 | import {Base32Hex} from '../../encoders/base32';
15 |
16 | const MAX_RANDOM1_VALUE: number = 0xFFFFFF;
17 | const MAX_RANDOM2_VALUE: number = 0xFFFF;
18 | const MAX_RANDOM3_VALUE: number = 0xFFFFFF;
19 | const isRunningInNode: boolean = typeof process !== 'undefined'
20 | && process.versions != null && process.versions.node != null;
21 | const murmur3Hash: IHash = new Murmur3Hash();
22 | const base32Hex: IEncoder = new Base32Hex(true);
23 |
24 | class RandomXidGenerator extends BaseUid implements IUIDGenerator {
25 |
26 | random1: number;
27 | random2: number;
28 | need_random1: boolean;
29 | need_random2: boolean;
30 | tsSize: number;
31 |
32 | constructor() {
33 | super();
34 | this.tsSize = this.getTimestampSize();
35 | this.need_random1 = true;
36 | this.need_random2 = true;
37 | if (isRunningInNode) {
38 | const os = require('os');
39 | if (os.hostname().length > 0) {
40 | this.need_random1 = false;
41 | this.random1 = murmur3Hash.hash((os.hostname(): any), 0) & MAX_RANDOM1_VALUE;
42 | }
43 | this.need_random2 = false;
44 | this.random2 = process.pid & MAX_RANDOM2_VALUE;
45 | }
46 | }
47 |
48 | generateRandom(): string {
49 | return base32Hex.encodeFromByteArray(this._randomBytesArray());
50 | }
51 |
52 | generateNext(): string {
53 | return base32Hex.encodeFromByteArray(this._nextBytesArray());
54 | }
55 |
56 | _randomBytesArray(): RandomArray {
57 | return this._getBytesArray((prng.random(3): any));
58 | }
59 |
60 | _nextBytesArray(): RandomArray {
61 | const randoms: RandomArray = [];
62 | for (let i = 0; i < 3; i += 1) {
63 | randoms[i] = (prng.next(): any);
64 | }
65 | return this._getBytesArray(randoms);
66 | }
67 |
68 | _getBytesArray(randoms: RandomArray): RandomArray {
69 | const res: RandomArray = this._createTimestampBytesArray();
70 | if (this.need_random1) {
71 | this.random1 = Math.floor(randoms[0] * MAX_RANDOM1_VALUE);
72 | }
73 | res[this.tsSize] = this.random1 >> 16;
74 | res[this.tsSize + 1] = (this.random1 >> 8) & 0xFF;
75 | res[this.tsSize + 2] = this.random1 & 0xFF;
76 |
77 | if (this.need_random2) {
78 | this.random2 = Math.floor(randoms[1] * MAX_RANDOM2_VALUE);
79 | }
80 | res[this.tsSize + 3] = this.random2 >> 8;
81 | res[this.tsSize + 4] = this.random2 & 0xFF;
82 |
83 | let random3: number = Math.floor(randoms[2] * MAX_RANDOM3_VALUE);
84 | res[this.tsSize + 5] = random3 >> 16;
85 | res[this.tsSize + 6] = (random3 >> 8) & 0xFF;
86 | res[this.tsSize + 7] = random3 & 0xFF;
87 |
88 | return res;
89 | }
90 | }
91 |
92 | export default RandomXidGenerator;
93 |
--------------------------------------------------------------------------------
/core/utils/utils.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | /**
3 | * Class for additional functions (Gamma, Digamma, ...)
4 | * Created by Alexey S. Kiselev
5 | */
6 |
7 | class Utils {
8 | /**
9 | * Gamma function
10 | * Calculate function using polynomial approach
11 | * https://en.wikipedia.org/wiki/Gamma_function
12 | * @param z - for this project purpose z > 0 and real value
13 | * @returns {number}
14 | */
15 | static gamma(z: number): number {
16 | if(z <= 0)
17 | throw new Error('Argument of Gamma function must be positive');
18 | let coefs: Array = [
19 | 57.1562356658629235,
20 | -59.5979603554754912,
21 | 14.1360979747417471,
22 | -0.491913816097620199,
23 | 0.339946499848118887E-4,
24 | 0.465236289270485756E-4,
25 | -0.983744753048795646E-4,
26 | 0.158088703224912494E-3,
27 | -0.210264441724104883E-3,
28 | 0.217439618115212643E-3,
29 | -0.164318106536763890E-3,
30 | 0.844182239838527433E-4,
31 | -0.261908384015814087E-4,
32 | 0.368991826595316234E-5
33 | ];
34 | let denominator: number = z,
35 | series: number = 0.999999999999997092,
36 | temp: number = z + 5.24218750000000000;
37 | temp = (z + 0.5) * Math.log(temp) - temp;
38 |
39 | for(let i: number = 0; i < 14; i += 1){
40 | series += coefs[i] / ++denominator;
41 | }
42 |
43 | return Math.exp(temp + Math.log(2.5066282746310005*series/z));
44 | }
45 |
46 | /**
47 | * Digamma function
48 | * https://en.wikipedia.org/wiki/Digamma_function
49 | * Calculate function using polynomial approach
50 | * @param z: number, z > 0
51 | * @returns {number}
52 | */
53 | static digamma(z: number): number {
54 | if(z <= 0)
55 | throw new Error('Argument of Digamma function must be positive');
56 | if(z < 0.5) {
57 | return Utils.digamma(1 - z) + Math.PI * Math.cos(Math.PI * (1 -z)) / Math.sin(Math.PI * (1 - z));
58 | }
59 | return Math.log(z + 0.4849142940227510) - 1 / (z * 1.0271785180163817);
60 | }
61 |
62 | /**
63 | * Error function
64 | * https://en.wikipedia.org/wiki/Error_function
65 | * Use Maclaurin series approximation
66 | * @param z: number
67 | * @returns {number}
68 | */
69 | static erf(z: number): number {
70 | let series: number = z,
71 | factorial = 1;
72 |
73 | if(z >= 3) {
74 | return 1;
75 | }
76 |
77 | if(z <= -3) {
78 | return -1;
79 | }
80 |
81 | for(let i = 1; i <= 26; i += 1) {
82 | factorial *= i;
83 | series += Math.pow(-1, i) * Math.pow(z, 2 * i + 1) / (factorial * (2 * i + 1));
84 | }
85 | return 2 * series / Math.sqrt(Math.PI);
86 | }
87 | }
88 |
89 | module.exports = Utils;
90 |
91 | // TODO: implement Beta function
92 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gulp Settings
3 | * Created by Alexey S. Kiselev.
4 | */
5 |
6 | let gulp = require('gulp'),
7 | replace = require('gulp-replace-path'),
8 | babel = require('gulp-babel');
9 |
10 | // Bundle main Index File
11 | gulp.task('build:node', function(done) {
12 | gulp.src([
13 | './index.js',
14 | './core/**/*.js'
15 | ]).
16 | on('error', function(e){
17 | console.error('Error occurred during build: ' + e);
18 | this.emit('end');
19 | }).
20 | pipe(babel({
21 | presets: ['@babel/preset-env', '@babel/preset-flow'],
22 | plugins: [
23 | [
24 | '@babel/plugin-proposal-decorators',
25 | {
26 | 'legacy': true
27 | }
28 | ]
29 | ]
30 | })).
31 | on('error', function(e){
32 | console.error('Error occurred during build: ' + e);
33 | this.emit('end');
34 | }).
35 | pipe(replace(/(\/core)/g,'')).
36 | on('error', function(e){
37 | console.error('Error occurred during build: ' + e);
38 | this.emit('end');
39 | }).
40 | pipe(gulp.dest('./lib/')).
41 | on('error', function(e){
42 | console.error('Error occurred during build: ' + e);
43 | this.emit('end');
44 | });
45 | done();
46 | });
47 |
48 | // Default task
49 | gulp.task('default', gulp.series('build:node'));
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "unirand",
3 | "version": "2.12.2",
4 | "description": "Random numbers and Distributions generation",
5 | "main": "./lib/index.js",
6 | "scripts": {
7 | "mocha": "node node_modules/mocha/bin/mocha test/*.test.js",
8 | "lint": "node node_modules/eslint/bin/eslint --config .eslintrc.json ./",
9 | "build:node": "node node_modules/gulp/bin/gulp build:node",
10 | "test": "node node_modules/npm-run-all/bin/run-s lint build:node mocha",
11 | "test:analyzer": "node node_modules/mocha/bin/mocha test/analyzer.test.js",
12 | "test:array-manipulation": "node node_modules/mocha/bin/mocha test/array_manipulation*.test.js",
13 | "test:distribution": "node node_modules/mocha/bin/mocha test/distribution*.test.js",
14 | "test:encoder": "node node_modules/mocha/bin/mocha test/encoder.test.js",
15 | "test:hash": "node node_modules/mocha/bin/mocha test/hash.test.js",
16 | "test:index": "node node_modules/mocha/bin/mocha test/index.test.js",
17 | "test:prng": "node node_modules/mocha/bin/mocha test/prng.test.js",
18 | "test:random-color": "node node_modules/mocha/bin/mocha test/randomColor.test.js",
19 | "test:stringutils": "node node_modules/mocha/bin/mocha test/stringutils.test.js",
20 | "test:utils": "node node_modules/mocha/bin/mocha test/utils.test.js",
21 | "nyc-coverage": "node node_modules/nyc/bin/nyc --reporter=text mocha",
22 | "test-coverage": "node node_modules/npm-run-all/bin/run-s build:node nyc-coverage",
23 | "publish": "npm run build:node && npm login && npm publish"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/AlexeySKiselev/randomjs"
28 | },
29 | "bugs": {
30 | "url": "https://github.com/AlexeySKiselev/randomjs/issues",
31 | "email": "alexeys.kiselev@yahoo.com"
32 | },
33 | "keywords": [
34 | "random",
35 | "distribution",
36 | "uniform",
37 | "normal",
38 | "gaussian",
39 | "bernoulli",
40 | "shuffle",
41 | "derangement",
42 | "permutation",
43 | "sampling",
44 | "random number generator",
45 | "analyzis",
46 | "winsorize",
47 | "zipf",
48 | "murmur hash",
49 | "prng",
50 | "tuchei prng",
51 | "seed",
52 | "moving average",
53 | "roulette wheel",
54 | "uuid",
55 | "base64",
56 | "base62"
57 | ],
58 | "author": "Alexey S. Kiselev ",
59 | "license": "ISC",
60 | "devDependencies": {
61 | "@babel/core": "7.x",
62 | "@babel/plugin-proposal-decorators": "7.x",
63 | "@babel/preset-env": "7.x",
64 | "@babel/preset-flow": "7.x",
65 | "@babel/register": "7.x",
66 | "babel-eslint": "10.x",
67 | "chai": "4.x",
68 | "eslint": "7.x",
69 | "eslint-plugin-flowtype": "5.x",
70 | "eslint-plugin-mocha": "8.x",
71 | "flow": "^0.2.3",
72 | "flow-bin": "^0.141.0",
73 | "flowtype": "^2.0.0",
74 | "gulp": "4.x",
75 | "gulp-babel": "8.x",
76 | "gulp-replace-path": "^0.4.0",
77 | "mocha": "8.x",
78 | "npm-run-all": "4.x",
79 | "nyc": "15.x"
80 | },
81 | "dependencies": {}
82 | }
83 |
--------------------------------------------------------------------------------
/smooth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexeySKiselev/randomjs/ebe41d6e313d0854e6cfd5ea26542c7200fae8ec/smooth.png
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test for index
3 | */
4 |
5 | // Import Mocha tool for tests
6 | let chai = require('chai'),
7 | expect = chai.expect,
8 | {describe, it} = require('mocha');
9 |
10 | chai.should();
11 |
12 | const methods = ['analyze', 'utils', 'stringutils', 'sample', 'kfold', 'shuffle', 'derange', 'chance', 'winsorize',
13 | 'hash', 'smooth', 'smoothSync', 'seed', 'random', 'next', 'randomInt', 'nextInt',
14 | 'randomInRange', 'nextInRange', 'newRouletteWheel', 'newPrng', 'randomColor', 'nextColor', 'encoder', 'uid']; // check prng separately
15 |
16 | describe('Index', () => {
17 | it('unirand should have all supported methods', () => {
18 | const unirand = require('../lib');
19 |
20 | for (let m of methods) {
21 | expect(unirand).to.have.property(m);
22 | expect(unirand).to.respondsTo(m);
23 | }
24 |
25 | expect(unirand).to.have.property('prng');
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/test/testBuild.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test Build
3 | * I am using it for tests only
4 | * Created by Alexey S. Kiselev
5 | */
6 |
--------------------------------------------------------------------------------