On 3 June 2010 16:00, James Cameron <qu...@laptop.org> wrote: > On Thu, Jun 03, 2010 at 03:51:46PM +1200, Tim McNamara wrote: > > Thanks for taking the time to review that patch. > > But wait, there's more. > > Tested-by: James Cameron <qu...@laptop.org> > > shift_right() also mentions self on the argument list, which stops it > from working right. After that is fixed, shift_left() and shift_right() > are usable in Calculate. > > Applied.
Tim
diff --git a/functions.py b/functions.py index ddc1088..b101a4c 100644 --- a/functions.py +++ b/functions.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # functions.py, functions available in Calculate, # by Reinier Heeres <rein...@heeres.eu> # @@ -14,14 +16,20 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +''' +Functions library for the Calculate Activity. + +The functions module is designed to be added as a plugin for the +Calculate Activity's astparser.AstParser class. -# Any variable or function in this module that does not start with an -# underscore ('_') will be available in Calculate through astparser.py. -# Docstrings will automatically be added to the help index for that function. -# However, instead of setting the docstring on a function in the simple way we -# add it after the function definition so that they can more easily be -# localized through gettext. +Variables and functions that do not start with an underscore ('_') +will be available in Calculate through astparser.py. +Docstrings will automatically be added to the help index for that function. +However, instead of setting the docstring on a function in the simple way we +add it after the function definition so that they can more easily be +localized through gettext. +''' import types import math import random @@ -67,10 +75,10 @@ _FUNCTIONS = [ _('tan'), _('tanh'), _('xor'), - ] + ] def _d(val): - '''Return a _Decimal object.''' + '''Returns a _Decimal object, an alias of decimal.Decimal.''' if isinstance(val, _Decimal): return val @@ -94,79 +102,180 @@ def _inv_scale_angle(x): return x / _angle_scaling def abs(x): + """ + Calculates the absolute value of x in floating point. + When x is less than 0, it returns -x. + + >>> abs(10) + 10.0 + >>> abs(_Decimal(-10)) + 10.0 + """ return math.fabs(x) -abs.__doc__ = _( -'abs(x), return absolute value of x, which means -x for x < 0') +abs.__doc__ = _(abs.__doc__) def acos(x): + """ + Calculates the arc cosine of x. + + The arc cosine of x is the angle for which the cosine is x. + + >>> acos(-0.2) + 1.7721542475852274 + >>> acos(_Decimal('-0.20')) + 1.7721542475852274 + + x must be between -1 and 1. + >>> acos(20) + Traceback (most recent call last): + ... + ValueError: math domain error + """ return _inv_scale_angle(math.acos(x)) -acos.__doc__ = _( -'acos(x), return the arc cosine of x. This is the angle for which the cosine \ -is x. Defined for -1 <= x < 1') +acos.__doc__ = _(acos.__doc__) def acosh(x): + """ + Calculates the arc hyperbolic cosine of x. + + This is the value y for which the hyperbolic cosine equals x.' + + >>> acosh(20) + 3.6882538673612966 + >>> acosh(20.0) + 3.6882538673612966 + >>> acosh(_Decimal('20.0')) + 3.6882538673612966 + """ return math.acosh(x) -acosh.__doc__ = _( -'acosh(x), return the arc hyperbolic cosine of x. This is the value y for \ -which the hyperbolic cosine equals x.') +acosh.__doc__ = _(acosh.__doc__) def And(x, y): + """ + Logical Boolean operator "and". + + Returns True if both x and y evaluate to True, + otherwise it returns False. + + >>> And(True, True) + True + >>> And(False, False) + False + >>> And(True, False) + False + >>> And(False, True) + False + """ return x & y -And.__doc__ = _( -'And(x, y), logical and. Returns True if x and y are True, else returns False') +And.__doc__ = _(And.__doc__) def add(x, y): + """ + Adds x and y, returning their sum. + + If either of the varibles is an instance of decimal.Decimal, + the function will coerce the other to be that type. + """ if isinstance(x, _Decimal) or isinstance(y, _Decimal): x = _d(x) y = _d(y) return x + y -add.__doc__ = _('add(x, y), return x + y') +add.__doc__ = _(add.__doc__) def asin(x): + """ + Calculates the arc sine of x. + + This is the angle for which the sine is ix. + Accepts an x value of -1 to 1. + + >>> asin(_Decimal('-0.20')) + -0.2013579207903308 + >>> asin(_Decimal('-020')) + Traceback (most recent call last): + ... + ValueError: math domain error + """ return _inv_scale_angle(math.asin(x)) -asin.__doc__ = _( -'asin(x), return the arc sine of x. This is the angle for which the sine is ix. \ -Defined for -1 <= x <= 1') +asin.__doc__ = _(asin.__doc__) def asinh(x): + """ + Calculates the arc hyperbolic sine of x. + + This is the value y for which the hyperbolic sine equals x. + """ return math.asinh(x) -asinh.__doc__ = _( -'asinh(x), return the arc hyperbolic sine of x. This is the value y for \ -which the hyperbolic sine equals x.') +asinh.__doc__ = _(__doc__) def atan(x): + """ + Calculates the arc tangent of x. + + The arc tangent is the angle for which the tangent is x. + + x can be any number. + """ return _inv_scale_angle(math.atan(x)) -atan.__doc__ = _( -'atan(x), return the arc tangent of x. This is the angle for which the tangent \ -is x. Defined for all x') +atan.__doc__ = _(atan.__doc__) def atanh(x): + """ + Calculates the arc hyperbolic tangent of x. + + This is the value y for which the hyperbolic tangent equals x. + """ return math.atanh(x) -atanh.__doc__ = _( -'atanh(x), return the arc hyperbolic tangent of x. This is the value y for \ -which the hyperbolic tangent equals x.') +atanh.__doc__ = _(atanh.__doc__) def ceil(x): - return math.ceil(float(x)) -ceil.__doc__ = _('ceil(x), return the smallest integer larger than x.') + """ + Finds the smallest integer larger than x. + + >>> ceil(1.1) + 2 + >>> ceil(-1.1) + -1 + """ + return int(math.ceil(float(x))) +ceil.__doc__ = _(ceil.__doc__) def cos(x): + """ + Calculates the cosine of x + + This is the x-coordinate on the unit circle at the angle x. + See also http://en.wikipedia.org/wiki/Cosine + """ return math.cos(_scale_angle(x)) -cos.__doc__ = _( -'cos(x), return the cosine of x. This is the x-coordinate on the unit circle \ -at the angle x') +cos.__doc__ = _(cos.__doc__) def cosh(x): + """ + Return the hyperbolic cosine of x. + + Given by (exp(x) + exp(-x)) / 2. + """ return math.cosh(x) -cosh.__doc__ = _( -'cosh(x), return the hyperbolic cosine of x. Given by (exp(x) + exp(-x)) / 2') +cosh.__doc__ = _(cosh.__doc__) def div(x, y): + """ + Divides x by y. + Returns the result of the division, or a Rational + number when both are integers with absolute values + of less than 1e12 (1,000,000,000,000). + + >>> div(10, 2.5) + 4.0 + >>> div(10, 2) # doctest: +ELLIPSIS + <rational.Rational instance at ...> + """ if y == 0 or y == 0.0: raise ValueError(_('Can not divide by zero')) if is_int(x) and float(abs(x)) < 1e12 and \ - is_int(y) and float(abs(y)) < 1e12: + is_int(y) and float(abs(y)) < 1e12: return _Rational(x, y) if isinstance(x, _Decimal) or isinstance(y, _Decimal): @@ -174,52 +283,107 @@ def div(x, y): y = _d(y) return x / y +div.__doc__ = _(div.__doc__) def _do_gcd(a, b): + """Computes greatest common denominator of a & b""" if b == 0: return a else: - return self._do_gcd(b, a % b) - -def gcd(self, a, b): - TYPES = (types.IntType, types.LongType) - if type(a) not in TYPES or type(b) not in types: - raise ValueError(_('Invalid argument')) - return self._do_gcd(a, b) -gcd.__doc__ = _( -'gcd(a, b), determine the greatest common denominator of a and b. \ -For example, the biggest factor that is shared by the numbers 15 and 18 is 3.') + return _do_gcd(b, a % b) + +def gcd(a, b): + """ + Determine the greatest common denominator of a and b. + + For example, the biggest factor that is shared by the + numbers 15 and 18 is 3. + >>> gcd(15, 18) + 3 + >>> gcd(15, 1.8) + Traceback (most recent call last): + ... + TypeError: Must be integers: Try gcd(15,1), not gcd(15,1.8) + """ + # cheap type testing + if is_int(a) and is_int(b): + return _do_gcd(a, b) + else: + #FIXME gettext strings in exceptions generate errors :/ + raise TypeError( + 'Must be integers: Try gcd(%s,%s), not gcd(%s,%s)' % ( + int(a), int(b), a, b)) +gcd.__doc__ = _(gcd.__doc__) def exp(x): - return math.exp(float(x)) -exp.__doc__ = _('exp(x), return the natural exponent of x. Given by e^x') + """ + Calculates the natural exponent of x. -def factorial(n): - if type(n) not in (types.IntType, types.LongType): - raise ValueError(_('Factorial only defined for integers')) + Given by e^x. + """ + return math.exp(x) +exp.__doc__ = _(exp.__doc__) +def factorial(n): + """ + Calculates the factorial of n. + + Given by n * (n - 1) * (n - 2) * ... + + Adapted from http://en.wikipedia.org/wiki/Factorial: + the factorial of a positive integer n, + denoted by n!, is the product of all + positive integers less than or equal to n. + + For example, + 5! = 1 * 2 * 3 * 4 * 5 = 120 + + 0! is a special case that is explicitly defined to be 1. + + >>> factorial(5) + 120L + >>> factorial(0.5) + Traceback (most recent call last): + ... + TypeError: Must be an integer: try 0 + """ + if not is_int(n): + raise TypeError('Must be an integer: try %s' % int(n)) if n == 0: return 1 - n = long(n) res = long(n) while n > 2: res *= n - 1 n -= 1 - return res -factorial.__doc__ = _( -'factorial(n), return the factorial of n. \ -Given by n * (n - 1) * (n - 2) * ...') +factorial.__doc__ = _(factorial.__doc__) def fac(x): + """Alias of functions.factorial()""" return factorial(x) -fac.__doc__ = _( -'fac(x), return the factorial of x. Given by x * (x - 1) * (x - 2) * ...') - -def factorize(x): +fac.__doc__ = _(fac.__doc__) + +def factorize(x, mode='string'): + """ + Determine the prime factors that together form x. + + If mode='string', return a string representing the factors: + For example: 15 => "3 * 5". + + >>> factorize(15) + '3 * 5' + >>> factorize(90, mode='list') + [2, 3, 3, 5] + >>> factorize(90.90, 'list') + Traceback (most recent call last): + ... + TypeError: Must be an integer: try 90 + """ + if mode not in ['string','list']: + raise ValueError('Mode must be either string or list.') if not is_int(x): - return 0 + raise TypeError('Must be an integer: try %s' % int(x)) factors = [] num = x @@ -235,22 +399,42 @@ def factorize(x): i += 2 factors.append(num) - if len(factors) == 1: - return "1 * %d" % x + if mode == 'string': + if len(factors) == 1: + return "1 * %d" % x + else: + ret = "%d" % factors[0] + for fac in factors[1:]: + ret += " * %d" % fac else: - ret = "%d" % factors[0] - for fac in factors[1:]: - ret += " * %d" % fac - return ret -factorize.__doc__ = ( -'factorize(x), determine the prime factors that together form x. \ -For examples: 15 = 3 * 5.') + factors.sort() + ret = factors + return ret +factorize.__doc__ = _(factorize.__doc__) def floor(x): - return math.floor(float(x)) -floor.__doc__ = _('floor(x), return the largest integer smaller than x.') + """ + Calculates the largest integer that is smaller than x. + + >>> floor(90.90) + 90 + """ + return int(math.floor(x)) +floor.__doc__ = _(floor.__doc__) def is_int(n): + """ + Determines whether n is an integer. + + Returns True if it is, or False if not. + + >>> is_int(1) + True + >>> is_int(_Decimal('1')) + True + >>> is_int(_Rational(1,1)) + True + """ if type(n) in (types.IntType, types.LongType): return True @@ -264,49 +448,89 @@ def is_int(n): (sign, d, e) = n.normalize().as_tuple() return e >= 0 -is_int.__doc__ = ('is_int(n), determine whether n is an integer.') +is_int.__doc__ = _(is_int.__doc__) def ln(x): + """ + Returns the natural logarithm of x as a float. + + This is the value for which the exponent exp() + equals x. Non-negative values are acceptable, + """ if float(x) > 0: return math.log(float(x)) else: raise ValueError(_('Logarithm(x) only defined for x > 0')) -ln.__doc__ = _( -'ln(x), return the natural logarithm of x. This is the value for which the \ -exponent exp() equals x. Defined for x >= 0.') +ln.__doc__ = _(ln.__doc__) def log10(x): + """ + Returns the base 10 logarithm of x as a float. + + This is the value for which 10^y equals x. Non-negative + values are acceptable. + """ if float(x) > 0: return math.log(float(x)) else: raise ValueError(_('Logarithm(x) only defined for x > 0')) -log10.__doc__ = _( -'log10(x), return the base 10 logarithm of x. This is the value y for which \ -10^y equals x. Defined for x >= 0.') +log10.__doc__ = _(log10.__doc__) def mod(x, y): - if self.is_int(y): + if is_int(y): return x % y else: - raise ValueError(_('Can only calculate x modulo <integer>')) + raise TypeError(_('Can only calculate x modulo <integer>')) def mul(x, y): + """ + Multiplies x by y. + + >>> mul(10,5) + 50 + """ if isinstance(x, _Decimal) or isinstance(y, _Decimal): x = _d(x) y = _d(y) return x * y -mul.__doc__ = _('mul(x, y), return x * y') +mul.__doc__ = _(mul.__doc__) def negate(x): + """ + Negates x. + + >>> negate(1) + -1 + >>> negate(-0.1) + 0.10000000000000001 + """ return -x -negate.__doc__ = _('negate(x), return -x') +negate.__doc__ = _(negate.__doc__) def Or(x, y): + """ + Logical Boolean operator "or" + + Returns True if either x or y is true, + otherwise it returns False.' + + >>> Or(True, True) + True + >>> Or(True, False) + True + >>> Or(False, False) + False + """ return x | y -Or.__doc__ = _( -'Or(x, y), logical or. Returns True if x or y is True, else returns False') +Or.__doc__ = _(Or.__doc__) def pow(x, y): + """ + Calculates x to the power of y (x**y). + + >>> pow(10,5) + 100000L + """ if is_int(y): if is_int(x): return long(x) ** int(y) @@ -319,85 +543,159 @@ def pow(x, y): x = _d(x) y = _d(y) return _d(math.pow(float(x), float(y))) -pow.__doc__ = _('pow(x, y), return x to the power y (x**y)') +pow.__doc__ = _(pow.__doc__) def rand_float(): + """ + Selects a random floating point number between 0.0 and 1.0. + """ return random.random() -rand_float.__doc__ = _( -'rand_float(), return a random floating point number between 0.0 and 1.0') +rand_float.__doc__ = _(rand_float.__doc__) def rand_int(maxval=65535): + """Return a random integer between 0 and <maxval>. + + <maxval> is an optional argument set to 65535 by default. + """ return random.randint(0, maxval) -rand_int.__doc__ = _( -'rand_int([<maxval>]), return a random integer between 0 and <maxval>. \ -<maxval> is an optional argument and is set to 65535 by default.') +rand_int.__doc__ = _(rand_int.__doc__) def round(x): - return math.round(float(x)) -round.__doc__ = _('round(x), return the integer nearest to x.') + """ + Returns the integer nearest to x. + """ + return int(math.round(float(x))) +round.__doc__ = _(round.__doc__) def shift_left(x, y): + """ + Shifts x by y bits to the left. + + (Multiply by 2 per bit) + """ if is_int(x) and is_int(y): - return d(int(x) << int(y)) + return _d(int(x) << int(y)) else: - raise ValueError(_('Bitwise operations only apply to integers')) -shift_left.__doc__ = _( -'shift_left(x, y), shift x by y bits to the left (multiply by 2 per bit)') + raise TypeError(_('Bitwise operations only apply to integers')) +shift_left.__doc__ = _(shift_left.__doc__) + +def shift_right(x, y): + """ + Shifts x by y bits to the right. -def shift_right(self, x, y): + (Multiply by 2 per bit) + """ if is_int(x) and is_int(y): - return d(int(x) >> int(y)) + return _d(int(x) >> int(y)) else: - raise ValueError(_('Bitwise operations only apply to integers')) -shift_right.__doc__ = _( -'shift_right(x, y), shift x by y bits to the right (divide by 2 per bit)') + raise TypeError(_('Bitwise operations only apply to integers')) +shift_right.__doc__ = _(shift_right.__doc__) def sin(x): + """ + Calculates the sine of x. + This is the y-coordinate on the unit circle at the angle x. + + See also http://en.wikipedia.org/wiki/Sine + """ return math.sin(_scale_angle(x)) -sin.__doc__ = _( -'sin(x), return the sine of x. This is the y-coordinate on the unit circle at \ -the angle x') +sin.__doc__ = _(sin.__doc__) def sinh(x): + """ + Calculates the hyperbolic sine of x, given by + (exp(x) - exp(-x)) / 2 + + See also http://en.wikipedia.org/wiki/Hyperbolic_sine + """ return math.sinh(x) sinh.__doc__ = _( -'sinh(x), return the hyperbolic sine of x. Given by (exp(x) - exp(-x)) / 2') +'sinh(x), ') def sinc(x): + """ + Calculates the sinc of x, given by sin(x) / x. + + From http://en.wikipedia.org/wiki/Sinc + The term "sinc" is a contraction of the function's + full Latin name, the sinus cardinalis (cardinal sine). + """ if float(x) == 0.0: return 1 return sin(x) / x -sinc.__doc__ = _( -'sinc(x), return the sinc of x. This is given by sin(x) / x.') +sinc.__doc__ = _(sinc.__doc__) def sqrt(x): - return math.sqrt(float(x)) -sqrt.__doc__ = _( -'sqrt(x), return the square root of x. This is the value for which the square \ -equals x. Defined for x >= 0.') + """ + Find the square root of x. + + The square root is the number whose square equals x. + + See also http://en.wikipedia.org/wiki/Square_root + + >>> sqrt(16) + 4.0 + """ + return math.sqrt(x) +sqrt.__doc__ = _(sqrt.__doc__) def sub(x, y): + """ + Subtract y from x. + + >>> sub(10,9.0) + 1.0 + """ if isinstance(x, _Decimal) or isinstance(y, _Decimal): x = _d(x) y = _d(y) return x - y -sub.__doc__ = _('sub(x, y), return x - y') +sub.__doc__ = _(sub.__doc__) def tan(x): + """ + Calculates the tangent of x, by sin(x) / cos(x). + + This is the slope of the line from the origin of the unit + circle to the point on the unit circle defined by the angle x. + + >>> tan(0.1) + 0.10033467208545055 + """ return math.tan(_scale_angle(x)) -tan.__doc__ = _( -'tan(x), return the tangent of x. This is the slope of the line from the origin \ -of the unit circle to the point on the unit circle defined by the angle x. Given \ -by sin(x) / cos(x)') +tan.__doc__ = _(tan.__doc__) def tanh(x): + """ + Calculates the hyperbolic tangent of x. Given by sinh(x) / cosh(x) + + See also http://en.wikipedia.org/wiki/Hyperbolic_tangent + + >>> tanh(0.1) + 0.099667994624955819 + """ return math.tanh(x) -tanh.__doc__ = _( -'tanh(x), return the hyperbolic tangent of x. Given by sinh(x) / cosh(x)') +tanh.__doc__ = _(tanh.__doc__) def xor(x, y): + """ + XOR tests whether x or y is true, but doesn'700t allow both. + + >>> xor(x=True, y=False) + True + >>> xor(x=False, y=True) + True + >>> xor(x=True, y=True) + False + >>> xor(x=False, y=False) + False + + Adapted from http://en.wikipedia.org/wiki/Exclusive_or: + The logical operation "exclusive disjunction", also called + exclusive or (symbolized as XOR, EOR and EXOR), is a type + of logical disjunction on two operands that results in a + value of true if exactly one of the operands has a value of true. + """ return x ^ y -xor.__doc__ = _( -'xor(x, y), logical xor. Returns True if either x is True (and y is False) \ -or y is True (and x is False), else returns False') +xor.__doc__ = _(xor.__doc__)
_______________________________________________ Sugar-devel mailing list Sugar-devel@lists.sugarlabs.org http://lists.sugarlabs.org/listinfo/sugar-devel