809 lines
21 KiB
JavaScript
809 lines
21 KiB
JavaScript
/*
|
|
* Copyright (C) 2011 - present Instructure, Inc.
|
|
*
|
|
* This file is part of Canvas.
|
|
*
|
|
* Canvas is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, version 3 of the License.
|
|
*
|
|
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import I18n from 'i18n!calculator.command'
|
|
|
|
const calcCmd = {}
|
|
|
|
;(function() {
|
|
const methods = {}
|
|
const predefinedVariables = {}
|
|
let variables = {}
|
|
let lastComputedResult
|
|
const expressions = [
|
|
{regex: /\s+/, token: 'whitespace'},
|
|
{regex: /[a-zA-Z][a-zA-Z0-9_\.]*/, token: 'variable'},
|
|
{regex: /[0-9]*\.?[0-9]+/, token: 'number'},
|
|
{regex: /\+/, token: 'add'},
|
|
{regex: /\-/, token: 'subtract'},
|
|
{regex: /\*/, token: 'multiply'},
|
|
{regex: /\//, token: 'divide'},
|
|
{regex: /\(/, token: 'open_paren'},
|
|
{regex: /\)/, token: 'close_paren'},
|
|
{regex: /\,/, token: 'comma'},
|
|
{regex: /\^/, token: 'power'},
|
|
{regex: /\=/, token: 'equals'}
|
|
]
|
|
const parseToken = function(command, index) {
|
|
const value = command.substring(index)
|
|
const item = {}
|
|
for (const idx in expressions) {
|
|
const expression = expressions[idx]
|
|
const match = value.match(expression.regex)
|
|
if (match && match[0] && value.indexOf(match[0]) == 0) {
|
|
item.token = expression.token
|
|
item.value = match[0]
|
|
item.newIndex = index + match[0].length
|
|
return item
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
const parseSyntax = function(command) {
|
|
let index = 0
|
|
const result = []
|
|
while (index < command.length) {
|
|
const item = parseToken(command, index)
|
|
if (!item) {
|
|
throw 'unrecognized token at ' + index
|
|
}
|
|
index = item.newIndex
|
|
result.push(item)
|
|
}
|
|
return result
|
|
}
|
|
let syntaxIndex = 0
|
|
const parseArgument = function(syntax) {
|
|
let result = null
|
|
switch (syntax[syntaxIndex].token) {
|
|
case 'number':
|
|
result = syntax[syntaxIndex]
|
|
break
|
|
case 'subtract':
|
|
if (
|
|
syntax[syntaxIndex + 1] &&
|
|
(syntax[syntaxIndex + 1].token == 'number' || syntax[syntaxIndex + 1].token == 'variable')
|
|
) {
|
|
syntax[syntaxIndex + 1].value = '-' + syntax[syntaxIndex + 1].value
|
|
syntaxIndex++
|
|
result = syntax[syntaxIndex]
|
|
} else {
|
|
throw 'expecting a number at ' + syntax[syntaxIndex].newIndex
|
|
}
|
|
break
|
|
case 'variable':
|
|
if (syntax[syntaxIndex + 1] && syntax[syntaxIndex + 1].token == 'open_paren') {
|
|
result = syntax[syntaxIndex]
|
|
result.token = 'method'
|
|
result.arguments = []
|
|
let ender = 'comma'
|
|
syntaxIndex += 2
|
|
if (syntax[syntaxIndex].token == 'close_paren') {
|
|
ender = 'close_paren'
|
|
syntaxIndex++
|
|
}
|
|
while (ender == 'comma') {
|
|
result.arguments.push(parseExpression(syntax, ['comma', 'close_paren']))
|
|
ender = syntax[syntaxIndex].token
|
|
syntaxIndex++
|
|
}
|
|
syntaxIndex--
|
|
if (ender != 'close_paren') {
|
|
throw 'expecting close parenthesis at ' + syntax[syntaxIndex].newIndex
|
|
}
|
|
} else {
|
|
result = syntax[syntaxIndex]
|
|
}
|
|
break
|
|
case 'open_paren':
|
|
result = syntax[syntaxIndex]
|
|
result.token = 'parenthesized_expression'
|
|
syntaxIndex++
|
|
result.expression = parseExpression(syntax, ['close_paren'])
|
|
break
|
|
}
|
|
if (!result) {
|
|
const index = (syntax && syntax[syntaxIndex] && syntax[syntaxIndex].newIndex) || 0
|
|
const type = (syntax && syntax[syntaxIndex] && syntax[syntaxIndex].token) || 'nothing'
|
|
throw 'expecting a value at ' + index + ', got a ' + type
|
|
}
|
|
syntaxIndex++
|
|
return result
|
|
}
|
|
const parseModifier = function(syntax) {
|
|
switch (syntax[syntaxIndex].token) {
|
|
case 'add':
|
|
return syntax[syntaxIndex++]
|
|
break
|
|
case 'subtract':
|
|
return syntax[syntaxIndex++]
|
|
break
|
|
case 'multiply':
|
|
return syntax[syntaxIndex++]
|
|
break
|
|
case 'divide':
|
|
return syntax[syntaxIndex++]
|
|
break
|
|
case 'power':
|
|
return syntax[syntaxIndex++]
|
|
break
|
|
}
|
|
const value = (syntax && syntax[syntaxIndex] && syntax[syntaxIndex].token) || 'value'
|
|
const index = (syntax && syntax[syntaxIndex] && syntax[syntaxIndex].newIndex) || 0
|
|
throw 'unexpected ' + value + ' at ' + index
|
|
}
|
|
|
|
var parseExpression = function(syntax, enders) {
|
|
const result = {
|
|
token: 'expression',
|
|
newIndex: syntax[syntaxIndex].newIndex
|
|
}
|
|
result.expressionItems = []
|
|
result.expressionItems.push(parseArgument(syntax))
|
|
if (syntaxIndex > syntax.length) {
|
|
return result
|
|
}
|
|
let ended = false
|
|
while (syntaxIndex < syntax.length && !ended) {
|
|
for (const idx in enders) {
|
|
if (syntax[syntaxIndex].token == enders[idx]) {
|
|
ended = true
|
|
}
|
|
}
|
|
if (!ended) {
|
|
result.expressionItems.push(parseModifier(syntax))
|
|
result.expressionItems.push(parseArgument(syntax))
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
const parseFullExpression = function(syntax) {
|
|
const newSyntax = []
|
|
for (const idx in syntax) {
|
|
if (syntax[idx].token != 'whitespace') {
|
|
newSyntax.push(syntax[idx])
|
|
}
|
|
}
|
|
syntax = newSyntax
|
|
let result = null
|
|
syntaxIndex = 0
|
|
if (
|
|
syntax[syntaxIndex].token == 'variable' &&
|
|
syntax.length > 1 &&
|
|
syntax[syntaxIndex + 1].token == 'equals'
|
|
) {
|
|
result = {
|
|
token: 'variable_assignment',
|
|
newIndex: syntax[syntaxIndex].newIndex
|
|
}
|
|
result.variable = syntax[syntaxIndex]
|
|
if (syntax.length > 2) {
|
|
syntaxIndex = 2
|
|
result.assignmentExpression = parseExpression(syntax)
|
|
} else {
|
|
throw 'Expecting value at ' + syntax[syntaxIndex + 1].newIndex
|
|
}
|
|
} else {
|
|
result = parseExpression(syntax)
|
|
}
|
|
return result
|
|
}
|
|
const computeExpression = function(tree) {
|
|
const round0 = tree.expressionItems
|
|
const round1 = [round0[0]]
|
|
for (var idx = 1; idx < round0.length; idx += 2) {
|
|
var item = round0[idx]
|
|
if (item.token == 'power') {
|
|
var left = round1.pop()
|
|
var right = round0[idx + 1]
|
|
round1.push(numberItem(Math.pow(compute(left), compute(right))))
|
|
} else {
|
|
round1.push(round0[idx])
|
|
round1.push(round0[idx + 1])
|
|
}
|
|
}
|
|
const round2 = [round1[0]]
|
|
for (var idx = 1; idx < round1.length; idx += 2) {
|
|
var item = round1[idx]
|
|
if (item.token == 'multiply') {
|
|
var left = round2.pop()
|
|
var right = round1[idx + 1]
|
|
round2.push(numberItem(compute(left) * compute(right)))
|
|
} else if (item.token == 'divide') {
|
|
var left = round2.pop()
|
|
var right = round1[idx + 1]
|
|
round2.push(numberItem(compute(left) / compute(right)))
|
|
} else {
|
|
round2.push(round1[idx])
|
|
round2.push(round1[idx + 1])
|
|
}
|
|
}
|
|
const round3 = [round2[0]]
|
|
for (var idx = 1; idx < round2.length; idx += 2) {
|
|
var item = round2[idx]
|
|
if (item.token == 'add') {
|
|
var left = round3.pop()
|
|
var right = round2[idx + 1]
|
|
round3.push(numberItem(compute(left) + compute(right)))
|
|
} else if (item.token == 'subtract') {
|
|
var left = round3.pop()
|
|
var right = round2[idx + 1]
|
|
round3.push(numberItem(compute(left) - compute(right)))
|
|
} else {
|
|
round3.push(round2[idx])
|
|
round3.push(round2[idx + 1])
|
|
}
|
|
}
|
|
if (round3.length === 0) {
|
|
throw 'expressions should have at least one value'
|
|
} else if (round3.length > 1) {
|
|
throw 'unexpected modifier: ' + round3[1].token
|
|
} else {
|
|
return compute(round3[0])
|
|
}
|
|
}
|
|
var numberItem = function(number) {
|
|
return {
|
|
token: 'number',
|
|
value: number,
|
|
calculatedValue: number
|
|
}
|
|
}
|
|
var compute = function(tree) {
|
|
switch (tree.token) {
|
|
case 'number':
|
|
return parseFloat(tree.value)
|
|
break
|
|
case 'expression':
|
|
return computeExpression(tree)
|
|
break
|
|
case 'parenthesized_expression':
|
|
return compute(tree.expression)
|
|
break
|
|
case 'variable_assignment':
|
|
if (tree.variable.value == '_') {
|
|
throw "the variable '_' is reserved"
|
|
}
|
|
variables[tree.variable.value] = compute(tree.assignmentExpression)
|
|
return variables[tree.variable.value]
|
|
break
|
|
case 'variable':
|
|
if (tree.value == '_') {
|
|
return lastComputedResult || 0
|
|
}
|
|
if (tree.value.indexOf('-') == 0) {
|
|
// the variable is negative, e.g. '-x'
|
|
const absolute = tree.value.replace(/^\-/, '')
|
|
var value = predefinedVariables && predefinedVariables[absolute]
|
|
value = value || (variables && variables[absolute])
|
|
value = -value
|
|
} else {
|
|
var value = predefinedVariables && predefinedVariables[tree.value]
|
|
value = value || (variables && variables[tree.value])
|
|
}
|
|
if (value == undefined) {
|
|
throw 'undefined variable ' + tree.value
|
|
}
|
|
return value
|
|
break
|
|
case 'method':
|
|
var args = []
|
|
for (const idx in tree.arguments) {
|
|
var value = compute(tree.arguments[idx])
|
|
tree.arguments[idx].computedValue = value
|
|
args.push(value)
|
|
}
|
|
if (methods[tree.value]) {
|
|
return methods[tree.value].apply(null, args)
|
|
} else {
|
|
throw 'unrecognized method ' + tree.value
|
|
}
|
|
break
|
|
}
|
|
throw 'Unexpected token type: ' + tree.token
|
|
}
|
|
calcCmd.clearMemory = function() {
|
|
variables = {}
|
|
lastComputedResult = null
|
|
}
|
|
const cached_trees = {}
|
|
calcCmd.compute = function(command) {
|
|
const result = {}
|
|
command = command.toString()
|
|
result.command = command
|
|
const tree = cached_trees[command]
|
|
if (tree) {
|
|
result.syntax = tree.syntax
|
|
result.tree = tree.tree
|
|
} else {
|
|
result.syntax = parseSyntax(command)
|
|
result.tree = parseFullExpression(result.syntax)
|
|
cached_trees[command] = result
|
|
}
|
|
result.computedValue = compute(result.tree)
|
|
lastComputedResult = result.computedValue
|
|
return result
|
|
}
|
|
calcCmd.computeValue = function(command) {
|
|
return calcCmd.compute(command).computedValue
|
|
}
|
|
const isFunction = function(arg) {
|
|
return true
|
|
}
|
|
calcCmd.addFunction = function(methodName, method, description, examples) {
|
|
if (typeof methodName === 'string' && isFunction(method)) {
|
|
method.friendlyName = methodName
|
|
method.description = description
|
|
if (typeof examples === 'string') {
|
|
examples = [examples]
|
|
}
|
|
method.examples = examples
|
|
methods[methodName] = method
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
calcCmd.addPredefinedVariable = function(variableName, value, description) {
|
|
value = parseFloat(value)
|
|
if (typeof variableName === 'string' && (value || value == 0)) {
|
|
predefinedVariables[variableName] = value
|
|
}
|
|
}
|
|
calcCmd.functionDescription = function(method) {
|
|
if (methods[method]) {
|
|
return (
|
|
methods[method].description ||
|
|
I18n.t('no_description', 'No description found for the function, %{functionName}', {
|
|
functionName: method
|
|
})
|
|
)
|
|
} else {
|
|
return I18n.t('unrecognized', '%{functionName} is not a recognized function', {
|
|
functionName: method
|
|
})
|
|
}
|
|
}
|
|
calcCmd.functionExamples = function(method) {
|
|
if (methods[method]) {
|
|
return methods[method].examples || []
|
|
} else {
|
|
return []
|
|
}
|
|
}
|
|
calcCmd.functionList = function() {
|
|
const result = []
|
|
for (const idx in methods) {
|
|
const method = methods[idx]
|
|
result.push([
|
|
idx,
|
|
method.description || I18n.t('default_description', 'No description given')
|
|
])
|
|
}
|
|
result.sort(function(a, b) {
|
|
if (a[0] > b[0]) {
|
|
return 1
|
|
} else if (a[0] < b[0]) {
|
|
return -1
|
|
} else {
|
|
return 0
|
|
}
|
|
})
|
|
return result
|
|
}
|
|
})()
|
|
;(function() {
|
|
const p = function(name, value, description) {
|
|
calcCmd.addPredefinedVariable(name, value, description)
|
|
}
|
|
const f = function(name, func, description, example) {
|
|
calcCmd.addFunction(name, func, description, example)
|
|
}
|
|
|
|
p('pi', Math.PI)
|
|
p('e', Math.exp(1))
|
|
|
|
f(
|
|
'abs',
|
|
function(val) {
|
|
return Math.abs(val)
|
|
},
|
|
I18n.t('abs.description', 'Returns the absolute value of the given value'),
|
|
'abs(x)'
|
|
)
|
|
f(
|
|
'asin',
|
|
function(x) {
|
|
return Math.asin(x)
|
|
},
|
|
I18n.t('asin.description', 'Returns the arcsin of the given value'),
|
|
'asin(x)'
|
|
)
|
|
f(
|
|
'acos',
|
|
function(x) {
|
|
return Math.acos(x)
|
|
},
|
|
I18n.t('acos.description', 'Returns the arccos of the given value'),
|
|
'acos(x)'
|
|
)
|
|
f(
|
|
'atan',
|
|
function(x) {
|
|
return Math.atan(x)
|
|
},
|
|
I18n.t('atan.description', 'Returns the arctan of the given value'),
|
|
'atan(x)'
|
|
)
|
|
f(
|
|
'log',
|
|
function(x, base) {
|
|
return Math.log(x) / Math.log(base || 10)
|
|
},
|
|
I18n.t('log.description', 'Returns the log of the given value with an optional base'),
|
|
'log(x, [base])'
|
|
)
|
|
f(
|
|
'ln',
|
|
function(x) {
|
|
return Math.log(x)
|
|
},
|
|
I18n.t('ln.description', 'Returns the natural log of the given value'),
|
|
'ln(x)'
|
|
)
|
|
f(
|
|
'rad_to_deg',
|
|
function(x) {
|
|
return (x * 180) / Math.PI
|
|
},
|
|
I18n.t('rad_to_deg.description', 'Returns the given value converted from radians to degrees'),
|
|
'rad_to_deg(radians)'
|
|
)
|
|
f(
|
|
'deg_to_rad',
|
|
function(x) {
|
|
return (x * Math.PI) / 180
|
|
},
|
|
I18n.t('deg_to_rad.description', 'Returns the given value converted from degrees to radians'),
|
|
'deg_to_rad(degrees)'
|
|
)
|
|
f(
|
|
'sin',
|
|
function(x) {
|
|
return Math.sin(x)
|
|
},
|
|
I18n.t('sin.description', 'Returns the sine of the given value'),
|
|
'sin(radians)'
|
|
)
|
|
f(
|
|
'cos',
|
|
function(x) {
|
|
return Math.cos(x)
|
|
},
|
|
I18n.t('cos.description', 'Returns the cosine of the given value'),
|
|
'cos(radians)'
|
|
)
|
|
f(
|
|
'tan',
|
|
function(x) {
|
|
return Math.tan(x)
|
|
},
|
|
I18n.t('tan.description', 'Returns the tangent of the given value'),
|
|
'tan(radians)'
|
|
)
|
|
|
|
f(
|
|
'sec',
|
|
function(x) {
|
|
return 1 / Math.cos(x)
|
|
},
|
|
I18n.t('sec.description', 'Returns the secant of the given value'),
|
|
'sec(radians)'
|
|
)
|
|
f(
|
|
'cosec',
|
|
function(x) {
|
|
return 1 / Math.sin(x)
|
|
},
|
|
I18n.t('cosec.description', 'Returns the cosecant of the given value'),
|
|
'cosec(radians)'
|
|
)
|
|
f(
|
|
'cotan',
|
|
function(x) {
|
|
return 1 / Math.tan(x)
|
|
},
|
|
I18n.t('cotan.description', 'Returns the cotangent of the given value'),
|
|
'cotan(radians)'
|
|
)
|
|
|
|
f(
|
|
'pi',
|
|
function(x) {
|
|
return Math.PI
|
|
},
|
|
I18n.t('pi.description', 'Returns the computed value of pi'),
|
|
'pi()'
|
|
)
|
|
f(
|
|
'if',
|
|
function(bool, pass, fail) {
|
|
return bool ? pass : fail
|
|
},
|
|
I18n.t(
|
|
'if.description',
|
|
'Evaluates the first argument, returns the second argument if it evaluates to a non-zero value, otherwise returns the third value'
|
|
),
|
|
'if(bool,success,fail)'
|
|
)
|
|
const make_list = function(args) {
|
|
if (args.length == 1 && args[0] instanceof Array) {
|
|
return args[0]
|
|
} else {
|
|
return args
|
|
}
|
|
}
|
|
f(
|
|
'max',
|
|
function() {
|
|
const args = make_list(arguments)
|
|
let max = args[0]
|
|
for (let idx = 0; idx < args.length; idx++) {
|
|
// in arguments) {
|
|
max = Math.max(max, args[idx])
|
|
}
|
|
return max
|
|
},
|
|
I18n.t('max.description', 'Returns the highest value in the list'),
|
|
['max(a,b,c...)', 'max(list)']
|
|
)
|
|
f(
|
|
'min',
|
|
function() {
|
|
const args = make_list(arguments)
|
|
let min = args[0]
|
|
for (let idx = 0; idx < args.length; idx++) {
|
|
// in arguments) {
|
|
min = Math.min(min, args[idx])
|
|
}
|
|
return min
|
|
},
|
|
I18n.t('min.description', 'Returns the lowest value in the list'),
|
|
['min(a,b,c...)', 'min(list)']
|
|
)
|
|
f(
|
|
'sqrt',
|
|
function(x) {
|
|
return Math.sqrt(x)
|
|
},
|
|
I18n.t('sqrt.description', 'Returns the square root of the given value'),
|
|
'sqrt(x)'
|
|
)
|
|
f(
|
|
'sort',
|
|
function(x) {
|
|
const args = make_list(arguments)
|
|
const list = []
|
|
for (let idx = 0; idx < args.length; idx++) {
|
|
list.push(args[idx])
|
|
}
|
|
return list.sort(function(a, b) {
|
|
return a - b
|
|
})
|
|
},
|
|
I18n.t('sort.description', 'Returns the list of values, sorted from lowest to highest'),
|
|
['sort(a,b,c...)', 'sort(list)']
|
|
)
|
|
f(
|
|
'reverse',
|
|
function(x) {
|
|
const args = make_list(arguments)
|
|
const list = []
|
|
for (let idx = 0; idx < args.length; idx++) {
|
|
list.unshift(args[idx])
|
|
}
|
|
return list
|
|
},
|
|
I18n.t('reverse.description', 'Reverses the order of the list of values'),
|
|
['reverse(a,b,c...)', 'reverse(list)']
|
|
)
|
|
f(
|
|
'first',
|
|
function() {
|
|
return make_list(arguments)[0]
|
|
},
|
|
I18n.t('first.description', 'Returns the first value in the list'),
|
|
['first(a,b,c...)', 'first(list)']
|
|
)
|
|
f(
|
|
'last',
|
|
function() {
|
|
const args = make_list(arguments)
|
|
return args[args.length - 1]
|
|
},
|
|
I18n.t('last.description', 'Returns the last value in the list'),
|
|
['last(a,b,c...)', 'last(list)']
|
|
)
|
|
f(
|
|
'at',
|
|
function(list, x) {
|
|
return list[x]
|
|
},
|
|
I18n.t('at.description', 'Returns the indexed value in the given list'),
|
|
'at(list,index)'
|
|
)
|
|
f(
|
|
'rand',
|
|
function(x) {
|
|
return Math.random() * (x || 1)
|
|
},
|
|
I18n.t(
|
|
'rand.description',
|
|
'Returns a random number between zero and the range specified, or one if no number is given'
|
|
),
|
|
'rand(x)'
|
|
)
|
|
f(
|
|
'length',
|
|
function() {
|
|
return make_list(arguments).length
|
|
},
|
|
I18n.t('length.description', 'Returns the number of arguments in the given list'),
|
|
['length(a,b,c...)', 'length(list)']
|
|
)
|
|
const sum = function(list) {
|
|
let total = 0
|
|
for (let idx = 0; idx < list.length; idx++) {
|
|
// in list) {
|
|
if (list[idx]) {
|
|
total += list[idx]
|
|
}
|
|
}
|
|
return total
|
|
}
|
|
f(
|
|
'mean',
|
|
function() {
|
|
const args = make_list(arguments)
|
|
return sum(args) / args.length
|
|
},
|
|
I18n.t('mean.description', 'Returns the average mean of the values in the list'),
|
|
['mean(a,b,c...)', 'mean(list)']
|
|
)
|
|
f(
|
|
'median',
|
|
function() {
|
|
const args = make_list(arguments)
|
|
var list = []
|
|
for (let idx = 0; idx < args.length; idx++) {
|
|
list.push(args[idx])
|
|
}
|
|
var list = list.sort(function(a, b) {
|
|
return parseFloat(a) - parseFloat(b)
|
|
})
|
|
if (list.length % 2 == 1) {
|
|
return list[Math.floor(list.length / 2)]
|
|
} else {
|
|
return (list[Math.round(list.length / 2)] + list[Math.round(list.length / 2) - 1]) / 2
|
|
}
|
|
},
|
|
I18n.t('median.description', 'Returns the median for the list of values'),
|
|
['median(a,b,c...)', 'median(list)']
|
|
)
|
|
f(
|
|
'range',
|
|
function() {
|
|
const args = make_list(arguments)
|
|
var list = []
|
|
for (let idx = 0; idx < args.length; idx++) {
|
|
list.push(args[idx])
|
|
}
|
|
var list = list.sort()
|
|
return list[list.length - 1] - list[0]
|
|
},
|
|
I18n.t('range.description', 'Returns the range for the list of values'),
|
|
['range(a,b,c...)', 'range(list)']
|
|
)
|
|
f(
|
|
'count',
|
|
function() {
|
|
return make_list(arguments).length
|
|
},
|
|
I18n.t('count.description', 'Returns the number of items in the list'),
|
|
['count(a,b,c...)', 'count(list)']
|
|
)
|
|
f(
|
|
'sum',
|
|
function() {
|
|
return sum(make_list(arguments))
|
|
},
|
|
I18n.t('sum.description', 'Returns the sum of the list of values'),
|
|
['sum(a,b,c...)', 'sum(list)']
|
|
)
|
|
const factorials = {}
|
|
var fact = function(n) {
|
|
n = Math.max(parseInt(n), 0)
|
|
if (n == 0 || n == 1) {
|
|
return 1
|
|
} else if (n > 170) {
|
|
return Infinity
|
|
} else if (factorials[n]) {
|
|
return factorials[n]
|
|
} else {
|
|
return n * fact(n - 1)
|
|
}
|
|
}
|
|
f(
|
|
'fact',
|
|
function(n) {
|
|
return fact(n)
|
|
},
|
|
I18n.t('fact.description', 'Returns the factorial of the given number'),
|
|
'fact(n)'
|
|
)
|
|
f(
|
|
'perm',
|
|
function(n, k) {
|
|
return fact(n) / fact(n - k)
|
|
},
|
|
I18n.t('perm.description', 'Returns the permutation result for the given values'),
|
|
'perm(n, k)'
|
|
)
|
|
f(
|
|
'comb',
|
|
function(n, k) {
|
|
return fact(n) / (fact(k) * fact(n - k))
|
|
},
|
|
I18n.t('comb.description', 'Returns the combination result for the given values'),
|
|
'comb(n, k)'
|
|
)
|
|
f(
|
|
'ceil',
|
|
function(x) {
|
|
return Math.ceil(x)
|
|
},
|
|
I18n.t('ceil.description', 'Returns the ceiling for the given value'),
|
|
'ceil(x)'
|
|
)
|
|
f(
|
|
'floor',
|
|
function(x) {
|
|
return Math.floor(x)
|
|
},
|
|
I18n.t('floor.description', 'Returns the floor for the given value'),
|
|
'floor(x)'
|
|
)
|
|
f(
|
|
'round',
|
|
function(x) {
|
|
return Math.round(x)
|
|
},
|
|
I18n.t('round.description', 'Returns the given value rounded to the nearest whole number'),
|
|
'round(x)'
|
|
)
|
|
f(
|
|
'e',
|
|
function(x) {
|
|
return Math.exp(x || 1)
|
|
},
|
|
I18n.t('e.description', 'Returns the value for e'),
|
|
'e()'
|
|
)
|
|
})()
|
|
|
|
export default calcCmd
|