mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
0c5f7cf0a7
Summary: * Adds a `SELF_HYPERLINK()` python function, with optional keyword arguments to set a label, the page, and link parameters. * Adds a `UUID()` python function, since using python's uuid.uuidv4 hits a problem accessing /dev/urandom in the sandbox. UUID makes no particular quality claims since it doesn't use an audited implementation. A difficult to guess code is convenient for some use cases that `SELF_HYPERLINK()` enables. The canonical URL for a document is mutable, but older versions generally forward. So for implementation simplicity the document url is passed it on sandbox creation and remains fixed throughout the lifetime of the sandbox. This could and should be improved in future. The URL is passed into the sandbox as a `DOC_URL` environment variable. The code for creating the URL is factored out of `Notifier.ts`. Since the url is a function of the organization as well as the document, some rejiggering is needed to make that information available to DocManager. On document imports, the new document is registered in the database slightly earlier now, in order to keep the procedure for constructing the URL in different starting conditions more homogeneous. Test Plan: updated test Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2759
841 lines
16 KiB
Python
841 lines
16 KiB
Python
# pylint: disable=unused-argument
|
|
|
|
from __future__ import absolute_import
|
|
import itertools
|
|
import math as _math
|
|
import operator
|
|
import random
|
|
import uuid
|
|
|
|
from functions.info import ISNUMBER, ISLOGICAL
|
|
from functions.unimplemented import unimplemented
|
|
import roman
|
|
|
|
# Iterates through elements of iterable arguments, or through individual args when not iterable.
|
|
def _chain(*values_or_iterables):
|
|
for v in values_or_iterables:
|
|
try:
|
|
for x in v:
|
|
yield x
|
|
except TypeError:
|
|
yield v
|
|
|
|
|
|
# Iterates through iterable or other arguments, skipping non-numeric ones.
|
|
def _chain_numeric(*values_or_iterables):
|
|
for v in _chain(*values_or_iterables):
|
|
if ISNUMBER(v) and not ISLOGICAL(v):
|
|
yield v
|
|
|
|
|
|
# Iterates through iterable or other arguments, replacing non-numeric ones with 0 (or True with 1).
|
|
def _chain_numeric_a(*values_or_iterables):
|
|
for v in _chain(*values_or_iterables):
|
|
yield int(v) if ISLOGICAL(v) else v if ISNUMBER(v) else 0
|
|
|
|
|
|
def _round_toward_zero(value):
|
|
return _math.floor(value) if value >= 0 else _math.ceil(value)
|
|
|
|
def _round_away_from_zero(value):
|
|
return _math.ceil(value) if value >= 0 else _math.floor(value)
|
|
|
|
def ABS(value):
|
|
"""
|
|
Returns the absolute value of a number.
|
|
|
|
>>> ABS(2)
|
|
2
|
|
>>> ABS(-2)
|
|
2
|
|
>>> ABS(-4)
|
|
4
|
|
"""
|
|
return abs(value)
|
|
|
|
def ACOS(value):
|
|
"""
|
|
Returns the inverse cosine of a value, in radians.
|
|
|
|
>>> round(ACOS(-0.5), 9)
|
|
2.094395102
|
|
>>> round(ACOS(-0.5)*180/PI(), 10)
|
|
120.0
|
|
"""
|
|
return _math.acos(value)
|
|
|
|
def ACOSH(value):
|
|
"""
|
|
Returns the inverse hyperbolic cosine of a number.
|
|
|
|
>>> ACOSH(1)
|
|
0.0
|
|
>>> round(ACOSH(10), 7)
|
|
2.9932228
|
|
"""
|
|
return _math.acosh(value)
|
|
|
|
def ARABIC(roman_numeral):
|
|
"""
|
|
Computes the value of a Roman numeral.
|
|
|
|
>>> ARABIC("LVII")
|
|
57
|
|
>>> ARABIC('mcmxii')
|
|
1912
|
|
"""
|
|
return roman.fromRoman(roman_numeral.upper())
|
|
|
|
def ASIN(value):
|
|
"""
|
|
Returns the inverse sine of a value, in radians.
|
|
|
|
>>> round(ASIN(-0.5), 9)
|
|
-0.523598776
|
|
>>> round(ASIN(-0.5)*180/PI(), 10)
|
|
-30.0
|
|
>>> round(DEGREES(ASIN(-0.5)), 10)
|
|
-30.0
|
|
"""
|
|
return _math.asin(value)
|
|
|
|
def ASINH(value):
|
|
"""
|
|
Returns the inverse hyperbolic sine of a number.
|
|
|
|
>>> round(ASINH(-2.5), 9)
|
|
-1.647231146
|
|
>>> round(ASINH(10), 9)
|
|
2.99822295
|
|
"""
|
|
return _math.asinh(value)
|
|
|
|
def ATAN(value):
|
|
"""
|
|
Returns the inverse tangent of a value, in radians.
|
|
|
|
>>> round(ATAN(1), 9)
|
|
0.785398163
|
|
>>> ATAN(1)*180/PI()
|
|
45.0
|
|
>>> DEGREES(ATAN(1))
|
|
45.0
|
|
"""
|
|
return _math.atan(value)
|
|
|
|
def ATAN2(x, y):
|
|
"""
|
|
Returns the angle between the x-axis and a line segment from the origin (0,0) to specified
|
|
coordinate pair (`x`,`y`), in radians.
|
|
|
|
>>> round(ATAN2(1, 1), 9)
|
|
0.785398163
|
|
>>> round(ATAN2(-1, -1), 9)
|
|
-2.35619449
|
|
>>> ATAN2(-1, -1)*180/PI()
|
|
-135.0
|
|
>>> DEGREES(ATAN2(-1, -1))
|
|
-135.0
|
|
>>> round(ATAN2(1,2), 9)
|
|
1.107148718
|
|
"""
|
|
return _math.atan2(y, x)
|
|
|
|
def ATANH(value):
|
|
"""
|
|
Returns the inverse hyperbolic tangent of a number.
|
|
|
|
>>> round(ATANH(0.76159416), 9)
|
|
1.00000001
|
|
>>> round(ATANH(-0.1), 9)
|
|
-0.100335348
|
|
"""
|
|
return _math.atanh(value)
|
|
|
|
def CEILING(value, factor=1):
|
|
"""
|
|
Rounds a number up to the nearest multiple of factor, or the nearest integer if the factor is
|
|
omitted or 1.
|
|
|
|
>>> CEILING(2.5, 1)
|
|
3
|
|
>>> CEILING(-2.5, -2)
|
|
-4
|
|
>>> CEILING(-2.5, 2)
|
|
-2
|
|
>>> CEILING(1.5, 0.1)
|
|
1.5
|
|
>>> CEILING(0.234, 0.01)
|
|
0.24
|
|
"""
|
|
return int(_math.ceil(float(value) / factor)) * factor
|
|
|
|
def COMBIN(n, k):
|
|
"""
|
|
Returns the number of ways to choose some number of objects from a pool of a given size of
|
|
objects.
|
|
|
|
>>> COMBIN(8,2)
|
|
28
|
|
>>> COMBIN(4,2)
|
|
6
|
|
>>> COMBIN(10,7)
|
|
120
|
|
"""
|
|
# From http://stackoverflow.com/a/4941932/328565
|
|
k = min(k, n-k)
|
|
if k == 0:
|
|
return 1
|
|
numer = reduce(operator.mul, xrange(n, n-k, -1))
|
|
denom = reduce(operator.mul, xrange(1, k+1))
|
|
return numer//denom
|
|
|
|
def COS(angle):
|
|
"""
|
|
Returns the cosine of an angle provided in radians.
|
|
|
|
>>> round(COS(1.047), 7)
|
|
0.5001711
|
|
>>> round(COS(60*PI()/180), 10)
|
|
0.5
|
|
>>> round(COS(RADIANS(60)), 10)
|
|
0.5
|
|
"""
|
|
return _math.cos(angle)
|
|
|
|
def COSH(value):
|
|
"""
|
|
Returns the hyperbolic cosine of any real number.
|
|
|
|
>>> round(COSH(4), 6)
|
|
27.308233
|
|
>>> round(COSH(EXP(1)), 7)
|
|
7.6101251
|
|
"""
|
|
return _math.cosh(value)
|
|
|
|
def DEGREES(angle):
|
|
"""
|
|
Converts an angle value in radians to degrees.
|
|
|
|
>>> round(DEGREES(ACOS(-0.5)), 10)
|
|
120.0
|
|
>>> DEGREES(PI())
|
|
180.0
|
|
"""
|
|
return _math.degrees(angle)
|
|
|
|
def EVEN(value):
|
|
"""
|
|
Rounds a number up to the nearest even integer, rounding away from zero.
|
|
|
|
>>> EVEN(1.5)
|
|
2
|
|
>>> EVEN(3)
|
|
4
|
|
>>> EVEN(2)
|
|
2
|
|
>>> EVEN(-1)
|
|
-2
|
|
"""
|
|
return int(_round_away_from_zero(float(value) / 2)) * 2
|
|
|
|
def EXP(exponent):
|
|
"""
|
|
Returns Euler's number, e (~2.718) raised to a power.
|
|
|
|
>>> round(EXP(1), 8)
|
|
2.71828183
|
|
>>> round(EXP(2), 7)
|
|
7.3890561
|
|
"""
|
|
return _math.exp(exponent)
|
|
|
|
def FACT(value):
|
|
"""
|
|
Returns the factorial of a number.
|
|
|
|
>>> FACT(5)
|
|
120
|
|
>>> FACT(1.9)
|
|
1
|
|
>>> FACT(0)
|
|
1
|
|
>>> FACT(1)
|
|
1
|
|
>>> FACT(-1)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: factorial() not defined for negative values
|
|
"""
|
|
return _math.factorial(int(value))
|
|
|
|
def FACTDOUBLE(value):
|
|
"""
|
|
Returns the "double factorial" of a number.
|
|
|
|
>>> FACTDOUBLE(6)
|
|
48
|
|
>>> FACTDOUBLE(7)
|
|
105
|
|
>>> FACTDOUBLE(3)
|
|
3
|
|
>>> FACTDOUBLE(4)
|
|
8
|
|
"""
|
|
return reduce(operator.mul, xrange(value, 1, -2))
|
|
|
|
def FLOOR(value, factor=1):
|
|
"""
|
|
Rounds a number down to the nearest integer multiple of specified significance.
|
|
|
|
>>> FLOOR(3.7,2)
|
|
2
|
|
>>> FLOOR(-2.5,-2)
|
|
-2
|
|
>>> FLOOR(2.5,-2)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: factor argument invalid
|
|
>>> FLOOR(1.58,0.1)
|
|
1.5
|
|
>>> FLOOR(0.234,0.01)
|
|
0.23
|
|
"""
|
|
if (factor < 0) != (value < 0):
|
|
raise ValueError("factor argument invalid")
|
|
return int(_math.floor(float(value) / factor)) * factor
|
|
|
|
def _gcd(a, b):
|
|
while a != 0:
|
|
if a > b:
|
|
a, b = b, a
|
|
a, b = b % a, a
|
|
return b
|
|
|
|
def GCD(value1, *more_values):
|
|
"""
|
|
Returns the greatest common divisor of one or more integers.
|
|
|
|
>>> GCD(5, 2)
|
|
1
|
|
>>> GCD(24, 36)
|
|
12
|
|
>>> GCD(7, 1)
|
|
1
|
|
>>> GCD(5, 0)
|
|
5
|
|
>>> GCD(0, 5)
|
|
5
|
|
>>> GCD(5)
|
|
5
|
|
>>> GCD(14, 42, 21)
|
|
7
|
|
"""
|
|
values = [v for v in (value1,) + more_values if v]
|
|
if not values:
|
|
return 0
|
|
if any(v < 0 for v in values):
|
|
raise ValueError("gcd requires non-negative values")
|
|
return reduce(_gcd, map(int, values))
|
|
|
|
def INT(value):
|
|
"""
|
|
Rounds a number down to the nearest integer that is less than or equal to it.
|
|
|
|
>>> INT(8.9)
|
|
8
|
|
>>> INT(-8.9)
|
|
-9
|
|
>>> 19.5-INT(19.5)
|
|
0.5
|
|
"""
|
|
return int(_math.floor(value))
|
|
|
|
def _lcm(a, b):
|
|
return a * b / _gcd(a, b)
|
|
|
|
def LCM(value1, *more_values):
|
|
"""
|
|
Returns the least common multiple of one or more integers.
|
|
|
|
>>> LCM(5, 2)
|
|
10
|
|
>>> LCM(24, 36)
|
|
72
|
|
>>> LCM(0, 5)
|
|
0
|
|
>>> LCM(5)
|
|
5
|
|
>>> LCM(10, 100)
|
|
100
|
|
>>> LCM(12, 18)
|
|
36
|
|
>>> LCM(12, 18, 24)
|
|
72
|
|
"""
|
|
values = (value1,) + more_values
|
|
if any(v < 0 for v in values):
|
|
raise ValueError("gcd requires non-negative values")
|
|
if any(v == 0 for v in values):
|
|
return 0
|
|
return reduce(_lcm, map(int, values))
|
|
|
|
def LN(value):
|
|
"""
|
|
Returns the the logarithm of a number, base e (Euler's number).
|
|
|
|
>>> round(LN(86), 7)
|
|
4.4543473
|
|
>>> round(LN(2.7182818), 7)
|
|
1.0
|
|
>>> round(LN(EXP(3)), 10)
|
|
3.0
|
|
"""
|
|
return _math.log(value)
|
|
|
|
def LOG(value, base=10):
|
|
"""
|
|
Returns the the logarithm of a number given a base.
|
|
|
|
>>> LOG(10)
|
|
1.0
|
|
>>> LOG(8, 2)
|
|
3.0
|
|
>>> round(LOG(86, 2.7182818), 7)
|
|
4.4543473
|
|
"""
|
|
return _math.log(value, base)
|
|
|
|
def LOG10(value):
|
|
"""
|
|
Returns the the logarithm of a number, base 10.
|
|
|
|
>>> round(LOG10(86), 9)
|
|
1.934498451
|
|
>>> LOG10(10)
|
|
1.0
|
|
>>> LOG10(100000)
|
|
5.0
|
|
>>> LOG10(10**5)
|
|
5.0
|
|
"""
|
|
return _math.log10(value)
|
|
|
|
def MOD(dividend, divisor):
|
|
"""
|
|
Returns the result of the modulo operator, the remainder after a division operation.
|
|
|
|
>>> MOD(3, 2)
|
|
1
|
|
>>> MOD(-3, 2)
|
|
1
|
|
>>> MOD(3, -2)
|
|
-1
|
|
>>> MOD(-3, -2)
|
|
-1
|
|
"""
|
|
return dividend % divisor
|
|
|
|
def MROUND(value, factor):
|
|
"""
|
|
Rounds one number to the nearest integer multiple of another.
|
|
|
|
>>> MROUND(10, 3)
|
|
9
|
|
>>> MROUND(-10, -3)
|
|
-9
|
|
>>> round(MROUND(1.3, 0.2), 10)
|
|
1.4
|
|
>>> MROUND(5, -2)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: factor argument invalid
|
|
"""
|
|
if (factor < 0) != (value < 0):
|
|
raise ValueError("factor argument invalid")
|
|
return int(_round_toward_zero(float(value) / factor + 0.5)) * factor
|
|
|
|
def MULTINOMIAL(value1, *more_values):
|
|
"""
|
|
Returns the factorial of the sum of values divided by the product of the values' factorials.
|
|
|
|
>>> MULTINOMIAL(2, 3, 4)
|
|
1260
|
|
>>> MULTINOMIAL(3)
|
|
1
|
|
>>> MULTINOMIAL(1,2,3)
|
|
60
|
|
>>> MULTINOMIAL(0,2,4,6)
|
|
13860
|
|
"""
|
|
s = value1
|
|
res = 1
|
|
for v in more_values:
|
|
s += v
|
|
res *= COMBIN(s, v)
|
|
return res
|
|
|
|
def ODD(value):
|
|
"""
|
|
Rounds a number up to the nearest odd integer.
|
|
|
|
>>> ODD(1.5)
|
|
3
|
|
>>> ODD(3)
|
|
3
|
|
>>> ODD(2)
|
|
3
|
|
>>> ODD(-1)
|
|
-1
|
|
>>> ODD(-2)
|
|
-3
|
|
"""
|
|
return int(_round_away_from_zero(float(value + 1) / 2)) * 2 - 1
|
|
|
|
def PI():
|
|
"""
|
|
Returns the value of Pi to 14 decimal places.
|
|
|
|
>>> round(PI(), 9)
|
|
3.141592654
|
|
>>> round(PI()/2, 9)
|
|
1.570796327
|
|
>>> round(PI()*9, 8)
|
|
28.27433388
|
|
"""
|
|
return _math.pi
|
|
|
|
def POWER(base, exponent):
|
|
"""
|
|
Returns a number raised to a power.
|
|
|
|
>>> POWER(5,2)
|
|
25.0
|
|
>>> round(POWER(98.6,3.2), 3)
|
|
2401077.222
|
|
>>> round(POWER(4,5.0/4), 9)
|
|
5.656854249
|
|
"""
|
|
return _math.pow(base, exponent)
|
|
|
|
|
|
def PRODUCT(factor1, *more_factors):
|
|
"""
|
|
Returns the result of multiplying a series of numbers together. Each argument may be a number or
|
|
an array.
|
|
|
|
>>> PRODUCT([5,15,30])
|
|
2250
|
|
>>> PRODUCT([5,15,30], 2)
|
|
4500
|
|
>>> PRODUCT(5,15,[30],[2])
|
|
4500
|
|
|
|
More tests:
|
|
>>> PRODUCT([2, True, None, "", False, "0", 5])
|
|
10
|
|
>>> PRODUCT([2, True, None, "", False, 0, 5])
|
|
0
|
|
"""
|
|
return reduce(operator.mul, _chain_numeric(factor1, *more_factors))
|
|
|
|
def QUOTIENT(dividend, divisor):
|
|
"""
|
|
Returns one number divided by another.
|
|
|
|
>>> QUOTIENT(5, 2)
|
|
2
|
|
>>> QUOTIENT(4.5, 3.1)
|
|
1
|
|
>>> QUOTIENT(-10, 3)
|
|
-3
|
|
"""
|
|
return TRUNC(float(dividend) / divisor)
|
|
|
|
def RADIANS(angle):
|
|
"""
|
|
Converts an angle value in degrees to radians.
|
|
|
|
>>> round(RADIANS(270), 6)
|
|
4.712389
|
|
"""
|
|
return _math.radians(angle)
|
|
|
|
def RAND():
|
|
"""
|
|
Returns a random number between 0 inclusive and 1 exclusive.
|
|
"""
|
|
return random.random()
|
|
|
|
def RANDBETWEEN(low, high):
|
|
"""
|
|
Returns a uniformly random integer between two values, inclusive.
|
|
"""
|
|
return random.randrange(low, high + 1)
|
|
|
|
def ROMAN(number, form_unused=None):
|
|
"""
|
|
Formats a number in Roman numerals. The second argument is ignored in this implementation.
|
|
|
|
>>> ROMAN(499,0)
|
|
'CDXCIX'
|
|
>>> ROMAN(499.2,0)
|
|
'CDXCIX'
|
|
>>> ROMAN(57)
|
|
'LVII'
|
|
>>> ROMAN(1912)
|
|
'MCMXII'
|
|
"""
|
|
# TODO: Maybe we should support the second argument.
|
|
return roman.toRoman(int(number))
|
|
|
|
def ROUND(value, places=0):
|
|
"""
|
|
Rounds a number to a certain number of decimal places according to standard rules.
|
|
|
|
>>> ROUND(2.15, 1) # Excel actually gives the more correct 2.2
|
|
2.1
|
|
>>> ROUND(2.149, 1)
|
|
2.1
|
|
>>> ROUND(-1.475, 2)
|
|
-1.48
|
|
>>> ROUND(21.5, -1)
|
|
20.0
|
|
>>> ROUND(626.3,-3)
|
|
1000.0
|
|
>>> ROUND(1.98,-1)
|
|
0.0
|
|
>>> ROUND(-50.55,-2)
|
|
-100.0
|
|
"""
|
|
# TODO: Excel manages to round 2.15 to 2.2, but Python sees 2.149999... and rounds to 2.1
|
|
# (see Python notes in documentation of `round()`).
|
|
return round(value, places)
|
|
|
|
def ROUNDDOWN(value, places=0):
|
|
"""
|
|
Rounds a number to a certain number of decimal places, always rounding down towards zero.
|
|
|
|
>>> ROUNDDOWN(3.2, 0)
|
|
3
|
|
>>> ROUNDDOWN(76.9,0)
|
|
76
|
|
>>> ROUNDDOWN(3.14159, 3)
|
|
3.141
|
|
>>> ROUNDDOWN(-3.14159, 1)
|
|
-3.1
|
|
>>> ROUNDDOWN(31415.92654, -2)
|
|
31400
|
|
"""
|
|
factor = 10**-places
|
|
return int(_round_toward_zero(float(value) / factor)) * factor
|
|
|
|
def ROUNDUP(value, places=0):
|
|
"""
|
|
Rounds a number to a certain number of decimal places, always rounding up away from zero.
|
|
|
|
>>> ROUNDUP(3.2,0)
|
|
4
|
|
>>> ROUNDUP(76.9,0)
|
|
77
|
|
>>> ROUNDUP(3.14159, 3)
|
|
3.142
|
|
>>> ROUNDUP(-3.14159, 1)
|
|
-3.2
|
|
>>> ROUNDUP(31415.92654, -2)
|
|
31500
|
|
"""
|
|
factor = 10**-places
|
|
return int(_round_away_from_zero(float(value) / factor)) * factor
|
|
|
|
def SERIESSUM(x, n, m, a):
|
|
"""
|
|
Given parameters x, n, m, and a, returns the power series sum a_1*x^n + a_2*x^(n+m)
|
|
+ ... + a_i*x^(n+(i-1)m), where i is the number of entries in range `a`.
|
|
|
|
>>> SERIESSUM(1,0,1,1)
|
|
1
|
|
>>> SERIESSUM(2,1,0,[1,2,3])
|
|
12
|
|
>>> SERIESSUM(-3,1,1,[2,4,6])
|
|
-132
|
|
>>> round(SERIESSUM(PI()/4,0,2,[1,-1./FACT(2),1./FACT(4),-1./FACT(6)]), 6)
|
|
0.707103
|
|
"""
|
|
return sum(coef*pow(x, n+i*m) for i, coef in enumerate(_chain(a)))
|
|
|
|
def SIGN(value):
|
|
"""
|
|
Given an input number, returns `-1` if it is negative, `1` if positive, and `0` if it is zero.
|
|
|
|
>>> SIGN(10)
|
|
1
|
|
>>> SIGN(4.0-4.0)
|
|
0
|
|
>>> SIGN(-0.00001)
|
|
-1
|
|
"""
|
|
return 0 if value == 0 else int(_math.copysign(1, value))
|
|
|
|
def SIN(angle):
|
|
"""
|
|
Returns the sine of an angle provided in radians.
|
|
|
|
>>> round(SIN(PI()), 10)
|
|
0.0
|
|
>>> SIN(PI()/2)
|
|
1.0
|
|
>>> round(SIN(30*PI()/180), 10)
|
|
0.5
|
|
>>> round(SIN(RADIANS(30)), 10)
|
|
0.5
|
|
"""
|
|
return _math.sin(angle)
|
|
|
|
def SINH(value):
|
|
"""
|
|
Returns the hyperbolic sine of any real number.
|
|
|
|
>>> round(2.868*SINH(0.0342*1.03), 7)
|
|
0.1010491
|
|
"""
|
|
return _math.sinh(value)
|
|
|
|
def SQRT(value):
|
|
"""
|
|
Returns the positive square root of a positive number.
|
|
|
|
>>> SQRT(16)
|
|
4.0
|
|
>>> SQRT(-16)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: math domain error
|
|
>>> SQRT(ABS(-16))
|
|
4.0
|
|
"""
|
|
return _math.sqrt(value)
|
|
|
|
|
|
def SQRTPI(value):
|
|
"""
|
|
Returns the positive square root of the product of Pi and the given positive number.
|
|
|
|
>>> round(SQRTPI(1), 6)
|
|
1.772454
|
|
>>> round(SQRTPI(2), 6)
|
|
2.506628
|
|
"""
|
|
return _math.sqrt(_math.pi * value)
|
|
|
|
@unimplemented
|
|
def SUBTOTAL(function_code, range1, range2):
|
|
"""
|
|
Returns a subtotal for a vertical range of cells using a specified aggregation function.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
|
|
def SUM(value1, *more_values):
|
|
"""
|
|
Returns the sum of a series of numbers. Each argument may be a number or an array.
|
|
Non-numeric values are ignored.
|
|
|
|
>>> SUM([5,15,30])
|
|
50
|
|
>>> SUM([5.,15,30], 2)
|
|
52.0
|
|
>>> SUM(5,15,[30],[2])
|
|
52
|
|
|
|
More tests:
|
|
>>> SUM([10.25, None, "", False, "other", 20.5])
|
|
30.75
|
|
>>> SUM([True, "3", 4], True)
|
|
6
|
|
"""
|
|
return sum(_chain_numeric_a(value1, *more_values))
|
|
|
|
|
|
@unimplemented
|
|
def SUMIF(records, criterion, sum_range):
|
|
"""
|
|
Returns a conditional sum across a range.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@unimplemented
|
|
def SUMIFS(sum_range, criteria_range1, criterion1, *args):
|
|
"""
|
|
Returns the sum of a range depending on multiple criteria.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def SUMPRODUCT(array1, *more_arrays):
|
|
"""
|
|
Multiplies corresponding components in the given arrays, and returns the sum of those products.
|
|
|
|
>>> SUMPRODUCT([3,8,1,4,6,9], [2,6,5,7,7,3])
|
|
156
|
|
>>> SUMPRODUCT([], [], [])
|
|
0
|
|
>>> SUMPRODUCT([-0.25], [-2], [-3])
|
|
-1.5
|
|
>>> SUMPRODUCT([-0.25, -0.25], [-2, -2], [-3, -3])
|
|
-3.0
|
|
"""
|
|
return sum(reduce(operator.mul, values) for values in itertools.izip(array1, *more_arrays))
|
|
|
|
@unimplemented
|
|
def SUMSQ(value1, value2):
|
|
"""
|
|
Returns the sum of the squares of a series of numbers and/or cells.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def TAN(angle):
|
|
"""
|
|
Returns the tangent of an angle provided in radians.
|
|
|
|
>>> round(TAN(0.785), 8)
|
|
0.99920399
|
|
>>> round(TAN(45*PI()/180), 10)
|
|
1.0
|
|
>>> round(TAN(RADIANS(45)), 10)
|
|
1.0
|
|
"""
|
|
return _math.tan(angle)
|
|
|
|
def TANH(value):
|
|
"""
|
|
Returns the hyperbolic tangent of any real number.
|
|
|
|
>>> round(TANH(-2), 6)
|
|
-0.964028
|
|
>>> TANH(0)
|
|
0.0
|
|
>>> round(TANH(0.5), 6)
|
|
0.462117
|
|
"""
|
|
return _math.tanh(value)
|
|
|
|
def TRUNC(value, places=0):
|
|
"""
|
|
Truncates a number to a certain number of significant digits by omitting less significant
|
|
digits.
|
|
|
|
>>> TRUNC(8.9)
|
|
8
|
|
>>> TRUNC(-8.9)
|
|
-8
|
|
>>> TRUNC(0.45)
|
|
0
|
|
"""
|
|
# TRUNC seems indistinguishable from ROUNDDOWN.
|
|
return ROUNDDOWN(value, places)
|
|
|
|
def UUID():
|
|
"""Generate a random UUID-formatted string identifier."""
|
|
return str(uuid.UUID(bytes=[chr(random.randrange(0, 256)) for _ in xrange(0, 16)], version=4))
|