-
Notifications
You must be signed in to change notification settings - Fork 124
/
Function.js
96 lines (83 loc) · 2.67 KB
/
Function.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import math from 'mathjs'
import {jStat} from 'jstat'
var Finance = require('financejs')
const finance = new Finance()
const jStatDistributions = {
beta: jStat.beta.sample,
centralF: jStat.centralF.sample,
cauchy: jStat.cauchy.sample,
chisquare: jStat.chisquare.sample,
exponential: jStat.exponential.sample,
invgamma: jStat.invgamma.sample,
lognormal: jStat.lognormal.sample,
normal: jStat.normal.sample,
studentt: jStat.studentt.sample,
weibull: jStat.weibull.sample,
uniform: jStat.uniform.sample,
gamma: jStat.gamma.sample
}
const financeFunctions = {
PV: finance.PV,
FV: finance.FV,
NPV: finance.NPV,
//IRR: finance.IRR, Too slow.
PP: finance.PP,
ROI: finance.ROI,
AM: finance.AM,
PI: finance.PI,
DF: finance.DF,
CI: finance.CI,
CAGR: finance.CAGR,
LR: finance.LR,
R72: finance.R72,
WACC: finance.WACC
}
// Here, we extend the math.js parser and library with the jStat sample functions. We override any default math.js
// functions because we want the jStat distributions to have priority.
math.import(jStatDistributions, {override: true})
// Here, we extend the math.js parser with financial functions.
math.import(financeFunctions, {override: true})
// All of jStat's functions are impure as they require sampling on pure inputs.
const IMPURE_FUNCTIONS = ['pickRandom', 'randomInt', 'random'].concat(Object.keys(jStatDistributions))
export var Sampler = {
sample({text}, n, inputs) {
try {
const compiled = math.compile(text)
const sampleCount = requiredSampleCount(text, inputs, n)
return sample(compiled, inputs, sampleCount)
} catch (exception) {
return [{errors: [exception.message]}];
}
}
}
const requiredSampleCount = (text, inputs, n) => (isPure(text, inputs) ? 1 : n)
export function sampleInputs(inputs) {
const sample = {}
for (let key of Object.keys(inputs)){
sample[key] = _.sample(inputs[key])
}
return sample
}
export function sample(compiled, inputs, n){
let samples = []
for (let i = 0; i < n; i++) {
const sampledInputs = sampleInputs(inputs)
const newSample = compiled.eval(sampledInputs)
if (_.isFinite(newSample)) {
samples = samples.concat(newSample)
} else {
return {values: samples, errors: ['Invalid sample']}
}
}
return {values: samples}
}
export function isPure(text, inputs) {
const impureInputs = _.some(inputs, (i) => someDifferent(i))
const impureFunction = hasImpureFunction(text)
return (!impureInputs) && (!impureFunction)
}
const hasImpureFunction = (text) => _.some(IMPURE_FUNCTIONS, e => text.indexOf(e) !== -1)
function someDifferent(array) {
const firstElement = array[0]
return _.some(array, e => (e !== firstElement))
}