├── .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 | --------------------------------------------------------------------------------