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

Reply via email to