This file is indexed.

/usr/share/pyshared/scitools/StringFunction.py is in python-scitools 0.9.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
#!/usr/bin/env python
"""Make a string with a mathematical expression behave as a Python function."""
from __future__ import division

# Default import of mathematical functions (in case the user
# supplies expressions with math functions and does not provide
# a globals keyword to the constructor with appropriate modules
# defining these math functions):
# from math import *
math_functions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos',
                  'cosh', 'exp', 'fabs', 'floor', 'log', 'log10',
                  'pi', 'pow', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']
s = 'from math import ' + ', '.join(math_functions)
exec s
# Problem: vectorized expressions require NumPy versions
# of the math functions. We try to detect errors arising from
# such lacking imports.

# The first edition of the "Python for Computational Science" book
# introduced the classes StringFunction1x, StringFunction1, and
# StringFunction. These are now (for the second edition) named
# StringFunction_v3, StringFunction_v4, and StringFunction_v5,
# respectively.


# The following implementation of StringFunction is an
# improved version compared to the one explained in the
# first edition of the book "Python for Computational Science".
# The new version is created by Mario Pernici <Mario.Pernici@mi.infn.it>
# and Hans Petter Langtangen <hpl@simula.no>. The basic idea is to
# build a lambda function out of the string expression and define
# self.__call__ to be this lambda function.

import re

class StringFunction:
    """
    Representation of a string formula as a function of one or
    more variables, optionally with parameters.

    Example on usage:

    >>> from scitools.StringFunction import StringFunction
    >>> f = StringFunction('1+sin(2*x)')
    >>> f(1.2)
    1.6754631805511511

    >>> f = StringFunction('1+sin(2*t)', independent_variable='t')
    >>> f(1.2)
    1.6754631805511511

    >>> f = StringFunction('1+A*sin(w*t)', independent_variable='t', \
                           A=0.1, w=3.14159)
    >>> f(1.2)
    0.94122173238695939
    >>> f.set_parameters(A=1, w=1)
    >>> f(1.2)
    1.9320390859672263

    >>> f(1.2, A=2, w=1)   # can also set parameters in the call
    2.8640781719344526

    >>> # function of two variables:
    >>> f = StringFunction('1+sin(2*x)*cos(y)', \
                           independent_variables=('x','y'))
    >>> f(1.2,-1.1)
    1.3063874788637866

    >>> f = StringFunction('1+V*sin(w*x)*exp(-b*t)', \
                           independent_variables=('x','t'))
    >>> f.set_parameters(V=0.1, w=1, b=0.1)
    >>> f(1.0,0.1)
    1.0833098208613807
    >>> str(f)  # print formula with parameters substituted by values
    '1+0.1*sin(1*x)*exp(-0.1*t)'
    >>> repr(f)
    "StringFunction('1+V*sin(w*x)*exp(-b*t)', independent_variables=('x', 't'), b=0.10000000000000001, w=1, V=0.10000000000000001)"

    >>> # vector field of x and y:
    >>> f = StringFunction('[a+b*x,y]', \
                           independent_variables=('x','y'))
    >>> f.set_parameters(a=1, b=2)
    >>> f(2,1)  # [1+2*2, 1]
    [5, 1]

    StringFunction expressions may contain fractions like 1/2 and these
    always result in float division (not integer division). Here is
    an example:

    >>> from scitools.StringFunction import StringFunction
    >>> f = StringFunction('1/4 + 1/2*x')
    >>> f(2)
    1.25


    The string parameter can, instead of a valid Python expression,
    be a function in a file (module). The string is then the
    complete path to the function, typically of the form
    somepackage.somemodule.function_name. This functionality is useful
    when simple string formulas cannot describe the function, e.g., when
    there are multiple if-branches inside the function expression.

    As an example, there is a function called _test_function::

        def _test_function(x, c=0, a=1, b=2):
            if x > c:
                return a*(x-c) + b
            else:
                return -a*(x-c) + b

    in the module scitools.misc (i.e., the misc.py file in the scitools
    package). We can then specify the complete path of this function
    as "string expression":

    >>> f = StringFunction('scitools.misc._test_function', independent_variable='x', a=10)
    >>> f(4)  # 10*(4-0) + 2 = 42
    42

    (Note that in Python 2.5 the _test_function can be coded as a
    simple string expression a*(x-c)+b if x > c else -a*(x-c)+b.)

    Giving the name of a function in a file (module) is convenient in
    user interfaces because the user can then write the name of
    the function as a standard Python module.function path. StringFunction
    turns this name, as a string, into a working module.function path.

    Troubleshooting:

    1)
    The StringFunction class can work with sin, cos, exp, and other
    mathematical functions if the argument is a scalar (float or int) type.
    If the argument is a vector, the NumPy versions of sin, cos,
    exp, etc., are needed. A common error message in the latter case is::

       TypeError: only rank-0 arrays can be converted to Python scalars.

    Make something like::

       from numpy import *
       # or
       from scitools.std import *
       # or
       from numpy import sin, cos

    in the calling code and supply globals=globals() as argument to
    the constructor::

       f = StringFunction('1+x*y', independent_variables=('x', 'y'),
                          globals=globals())
       # f(p,q) will now work for NumPy arrays p and q.

    You can also omit the globals argument when constructing the
    StringFunction and later call
    f.vectorize(globals())
    to allow array arguments.

    2) StringFunction builds a lambda function and evaluates this.
    You can see the lambda function as a string by accessing the
    _lambda attribute.
    """
    def __init__(self, expression, **kwargs):
        self._f = str(expression)  # ensure a string

        # check if expression is a function in a module:
        self._function_in_module = None

        # a module function specification is on the form
        # [A-Za-z_][A-Za-z0-9_.]x where x+1 is the len(expression)
        # but there MUST be a dot in there
        pattern = r'[A-Za-z_][A-Za-z0-9_.]{%d}' % (len(expression)-1)
        if "." in expression:
            if re.search(pattern, expression):
                parts = expression.split('.')
                module = '.'.join(parts[:-1])
                function = parts[-1]
                self._function_in_module = (module, function)

        # self._var holds the independent variables in a tuple:
        if 'independent_variable' in kwargs:  # allow "variable" and "variables"
            # note that tuple(string) gives a tuple of the characters
            # so we need to be careful (if the indep. var has more than
            # one character)
            name = kwargs['independent_variable'] # 'x', 't', 'dudt' etc.
            if not isinstance(name, str):
                raise ValueError(
                    'name "%s" of independent variable is illegal' % name)
            self._var = (name,)
        else:
            names = kwargs.get('independent_variables', ('x',))
            if isinstance(names, str):
                names = (names,)
            elif not isinstance(names, (list,tuple)):
                raise ValueError(
                    'independent variables=%s is invalid' % names)
            self._var = tuple(names)

        # user's globals() array (with relevant imported modules/functions):
        if 'globals' in kwargs:
            if kwargs['globals'] is None:
                self._globals = globals()
            else:
                self._globals = kwargs['globals']
        else:
            self._globals = globals()

        self._prms = kwargs.copy()
	try:    del self._prms['independent_variable']
        except: pass
        try:    del self._prms['independent_variables']
        except: pass
        try:    del self._prms['globals']
        except: pass
        try:
            # may fail if not all parameters are defined yet
            self._build_lambda()
        except NameError, e:
            #print e
            pass # ok at this stage: parameters might be missing

    def _build_lambda(self):
        """
        Translate the expression to a lambda function taking the
        independent variables as positional arguments and the
        parameters as keyword arguments.
        The idea is due to Mario Pernici <Mario.Pernici@mi.infn.it>.
        """
        args = ', '.join(self._var)
        s = 'lambda ' + args

        # add parameters as keyword arguments:
        if self._prms:
            kwargs = ', '.join(['%s=%s' % (k, self._prms[k]) \
                                for k in self._prms])
            s += ', ' + kwargs
        else:
            kwargs = ''

        if self._function_in_module is None:
            # insert string expression as body in the lambda function:
            s += ': ' + self._f
        else:
            exec('import ' + self._function_in_module[0])
            # let lambda call a function in a file (module):
            s += ', module=%s: module.%s(%s, %s)' % \
                 (self._function_in_module[0],
                  self._function_in_module[1],
                  args, kwargs)
            # note: we could use self._f directly here (giving the
            # full module path), but then we need to do import first,
            # all this is done in the __init__ and then it is simpler
            # to just let self_function_in_module point to the imported
            # function

        self._lambda = s # store lambda function code; just for convenience

        try:
            if self._function_in_module is None:
                try:
                    self.__call__ = eval(s, self._globals)
                except Exception, e:
                    print """
Making StringFunction with formula %s failed!
Tried to build a lambda function:\n %s""" % (self._f, s)
                    raise e

                ## the following makes all instances have the same function :-(
                ##StringFunction.__call__ = eval(s, self._globals)
            else:
                # didn't work with self._globals...and we don't need it...????
                self.__call__ = eval(s, globals(), locals())
                #self.__call__ = eval(s)
                #print 'call is', self.__call__

        except NameError, e:
            prm = str(e).split()[1]
            raise NameError, 'name "%s" is not defined - if it is '\
                  'a parameter,\nset it in the constructor or the '\
                  'set_parameters method, or provide\nglobals=globals() '\
                  'in the constructor if "%s" is a global name in the '\
                  'calling code.' % (prm, prm)


    def set_parameters(self, **kwargs):
        """Set keyword parameters in the function."""
        self._prms.update(kwargs)
        self._build_lambda()

    def vectorize(self, globals_dict):
        """
        Allow the StringFunction object to take NumPy array
        arguments. The calling code must have done a
        from numpy import * or similar and send the globals()
        dictionary as the argument globals_dict.
        Alternatively, the globals() dictionary can be supplied
        as a globals keyword argument to the constructor.
        """
        self._globals = globals_dict
        self._build_lambda()

    def troubleshoot(self, *args, **kwargs):
        """
        Perform function evaluation call with lots of testing to
        try to help the user with problems.
        """
        try:
            v = self(*args, **kwargs)
        except TypeError, e:
            if str(e).find('only rank-0 arrays can be converted to Python scalars') != -1:

                print '\nThe call resulted in the exception TypeError:'
                print e

                # *args contains NumPy arrays and the operations in
                # the string formula are not compatible

                # do we have intrinsic math functions of wrong type?
                math_funcs = False; not_NumPy = False
                for f in math_functions:
                    if self._f.find(f) != -1:
                        math_funcs = True
                        if str(type(f))[7:-2] != 'ufunc':
                            not_NumPy = True
                        break
                if math_funcs and not_NumPy:
                    print '\nThis message is caused by using scalar math\n'\
                          'functions (like %s) with array arguments.\n'\
                          'Make some\nfrom numarray import *\n'\
                          'or similar in the calling code and '\
                          'supply the globals=globals() constructor\n'\
                          'argument when creating the StringFunction instance.'\
                          % f
                else:
                    print 'Internal error - this should not happen...'
            else:
                print e
        except NameError, e:
            print e
        else:
            print 'This call worked perfectly!'

    def __str__(self):
        """
        Return the string function formula as a string, with
        parameters substituted by their values.

        The return value can be used when creating Fortran or C/C++
        functions out of the string formula:
        f = StringFunction('a + p*x', a=1, p=0)
        somefile.write('double somefunc(double x) { return %s; }' % str(f))
        """
        s = self._f  # formula with parameter names and indep. variables

        # Substitute parameter names by their numerical values.
        # Start with the most complicated parameter names
        # (this algorithm may fail).
        prm_names = self._prms.keys()
        prm_names.sort(lambda a, b: cmp(len(a), len(b)))
        prm_names.reverse()
        for name in prm_names:
            s = s.replace(name, str(self._prms[name]))
        return s

    def __repr__(self):
        """Return the code required to reconstruct this instance."""
        kwargs = ', '.join(['%s=%s' % (key, repr(value)) \
                            for key, value in self._prms.items()])
        return """StringFunction(%s, independent_variables=%s, %s)""" % \
               (repr(self._f), repr(self._var), kwargs)

    # The next code generation functions work only for scalar
    # function values, not vector values (can extend to vectors by
    # checking if self._f has a list form, and then include the
    # return value as an array argument in the functions).

    def _no_of_vector_components(self):
        """
        Return the number of vector components in the string
        expression.
        """
        ni = len(self._var)
        if ni == 1:
            return 1
        # function of more than one variable may be a vector field:
        args = tuple([0]*ni)  # try (0,0,...) as indep. variables
        try:
            v = self(args)
            return ni
        except:
            # try another argument (in case (0,0,...) caused wrong calculations:
            args = tuple([1.105]*ni)
            v = self(args)
            return ni


    def Cpp_code(self, function_name='somefunc'):
        """
        Dump the string expression to C++.
        In C++ we use a plain function if there are no parameters,
        otherwise we use a function object with operator() for
        the function evaluation.
        """
        varlist = ', '.join(['double %s' % var for var in self._var])
        expr = self._f
        # ** does not work in C, instead of doing sophisticated
        # parsing and edit, provide an error message
        self._pow_check()
        if self._prms:
            decl = ' '.join(['double %s;' % name for name in self._prms])
            prms = ', '.join(['double %s_=%s' % (name, self._prms[name]) \
                    for name in self._prms])
            setp = ' '.join(['%s = %s_;' % (name, name) \
                             for name in self._prms])
            # function object:
            s = """
class %(function_name)s
{
  // parameters:
  %(decl)s
 public:
  %(function_name)s (%(prms)s)
    { %(setp)s }
  double operator() const (%(varlist)s)
    { return %(expr)s; }
};
""" % vars()
        else:
            s = """
double %(function_name)s (%(varlist)s)
{ return %(expr)s; }
"""
        return s

    def F77_code(self, function_name='somefunc'):
        """
        Dump the string expressions as a Fortran 77 function or subroutine.

        Note: if pow(x,a) is used in the expression, this is
        translated to x**a by a simple regex, which may fail if
        there are function calls inside pow(.,.).
        """
        expr = self._f
        real = 'real*8'
        varlist = ', '.join(self._var)
        varlist_decl = '\n'.join(['      %s %s' % (real, name) \
                                  for name in self._var])
        decl = '\n'.join(['      %s %s' % (real,name) for name in self._prms])
        import re
        if re.search(r'pow\s*\(', expr):
            # try to replace pow(x,a) by x**a
            expr = re.sub(r'pow\(([^,]+),([^)]+)\)', '((\g<1>)**(\g<2>))',
                          expr)
        # set parameter values:
        setp = '\n'.join(['      %s = %s' % (name, self._prms[name]) \
                          for name in self._prms])
        s = """
      %(real)s function %(function_name)s(%(varlist)s)
C     independent variables:
%(varlist_decl)s
""" % vars()
        if self._prms:
            s += """
C     parameters:
%(decl)s
%(setp)s
""" % vars()
        s += """
      %(function_name)s = %(expr)s

      return
      end
""" % vars()
        return s

    def F77_pow(self):
        """
        Generate an F77 function pow(x,a) (x**a). In some
        string expressions that are to be translated to C/C++,
        pow(x,a) must be used instead of x**a, but pow is not
        defined in F77. This code dumpes the necessary F77 version
        of pow such that string functions can be seamlessly translated
        to C, C++, and F77.

        Note: this function is difficult to use since it expects
        exactly a single-precision power. We now rely on regular
        expressions to replace pow(x,a) by x**a in F77_code instead.
        """
        s = """

      real*8 function pow(x, a)
      real*8 x
C     the power a is usually a number (single precision),
C     note: it cannot be int
      real*4 a
      pow = x**a
      return
      end
"""
        return s

    def C_code(self, function_name='somefunc', inline=False):
        """
        Dump the string expressions as a C function.
        If inline is true, the C++ inline keyword is inserted
        to make the function inline.
        """
        if inline:
            s = 'inline '
        else:
            s = ''
        expr = self._f
        # ** does not work in C, instead of doing sophisticated
        # parsing and edit, provide an error message
        self._pow_check()

        varlist = ', '.join(['double %s' % var for var in self._var])
        s += 'double %s (%s)\n{\n' % (function_name, varlist)
        if self._prms:
            # declare variables for parameters:
            decl = ' '.join(['double %s;' % name for name in self._prms])
            # set parameter values:
            prms = ' '.join(['%s = %s;' % (name, self._prms[name]) \
                            for name in self._prms])
            s += '  %s\n  %s\n' % (decl, prms)
        # evaluate math expression:
        s += '  return ' + expr + ';\n}\n'
        return s

    def _pow_check(self):
        """
        Raise a SyntaxError exception if the ** power operator is used
        in the string formula.
        """
        if self._f.find('**') != -1:
            raise SyntaxError, \
                  'use pow(a,b) instead of a**b in the expression'\
                  '\n%s\n(since you demand translation to C/C++)' % self._f

def _doctest():
    import doctest, StringFunction
    return doctest.testmod(StringFunction)

def _demo():
    f = StringFunction('a+b*sin(x)', a=1, b=4)
    print f(2)
    f.set_parameters(a=-1, b=pi)
    print f(1)
    print 'internals:', str(f), repr(f), f._lambda, f._prms
    f = StringFunction('amp*sin(a*t)*exp(-6.211*x)',
                       independent_variables=('x','t'))
    f.set_parameters(amp=0.1, a=1)
    print f(0,pi/2.0,a=2)
    print 'internals:', str(f), repr(f), f._lambda, f._prms
    print f.C_code()
    print f.Cpp_code()
    print f.F77_code()


# simplified "pedagogical" versions from the
# "Python for Computational Science" book:

class StringFunction_v1:
    def __init__(self, expression):
        self._f = expression

    def __call__(self, x):
        return eval(self._f)  # evaluate function expression

# compile eval expression:
class StringFunction_v2:
    def __init__(self, expression):
        self._f_compiled = compile(expression, '<string>', 'eval')

    def __call__(self, x):
        return eval(self._f_compiled)

# allow parameters and an arbitrary name of the independent variable:
class StringFunction_v3:
    def __init__(self, expression,
                 independent_variable='x',
                 set_parameters=''):
        self._f_compiled = compile(expression, '<string>', 'eval')
        self._var = independent_variable  # 'x', 't' etc.
        self._code = set_parameters
        self.__name__ = expression  # name of func is expression

    def set_parameters(self, code):
        self._code = code

    def __call__(self, x):
        # assign value to independent variable:
        exec '%s = %g' % (self._var, x)
        # execute some user code (defining parameters etc.):
        if self._code:  exec(self._code)
        return eval(self._f_compiled)

# let parameters be keyword arguments:
class StringFunction_v4:
    def __init__(self, expression, **kwargs):
        self._f = expression
        self._var = kwargs.get('independent_variable', 'x') # 'x', 't' etc.
        self._globals = kwargs.get('globals', globals())
        self.__name__ = self._f  # class name = function expression
        self._f_compiled = compile(self._f, '<string>', 'eval')
        self._prms = kwargs.copy()
	try:
            del self._prms['independent_variable']
            del self._prms['globals']
        except: pass

    def set_parameters(self, **kwargs):
        self._prms.update(kwargs)

    def __call__(self, x):
        # include indep. variable in dictionary of function parameters:
        self._prms[self._var] = x
        # evaluate function expression:
        return eval(self._f_compiled, self._globals, self._prms)

    def test(self, x=1.4325):
        # test that all parameters are defined
        try:
            self(x)  # sample call
            return True
        except NameError, e:
            prm = str(e).split()[2]
            raise NameError, 'Parameter "%s" is not defined,\nneither in '\
                  'the constructor nor set_parameters.\n'\
                  'Update the constructor call or call set_parameters'\
                  '(%s=...)' % (prm, prm)
        else:
            return True  # accept other errors

    def __str__(self):
        s = self._f
        # Substitute parameter names by their numerical values.
        # Start with the most complicated parameter names
        # (this algorithm may fail).

        # first remove indep. variables possibly inserted in self._prms
        # by the self.__call__ method:
        try:
            del self._prms[self._var]
        except:
            pass
        prm_names = self._prms.keys()
        prm_names.sort(lambda a, b: cmp(len(a), len(b)))
        prm_names.reverse()
        for name in prm_names:
            s = s.replace(name, str(self._prms[name]))
        return s

    def __repr__(self):
        # first remove indep. variables possibly inserted in self._prms
        # by the self.__call__ method:
        try:
            del self._prms[self._var]
        except:
            pass
        kwargs = ', '.join(['%s=%s' % (key, repr(value)) \
                            for key, value in self._prms.items()])
        return """StringFunction1(%s, independent_variable=%s, %s)""" % \
               (repr(self._f), repr(self._var), kwargs)

class StringFunction_v5(StringFunction_v4):
    """
    Extension of class StringFunction_v4 to an arbitrary
    number of independent variables.
    """
    def __init__(self, expression, **kwargs):
        StringFunction_v4.__init__(self, expression, **kwargs)
        self._var = tuple(kwargs.get('independent_variables', 'x'))
        try:    del self._prms['independent_variables']
        except: pass

    def __call__(self, *args):
        # add independent variables to self._prms:
        for name, value in zip(self._var, args):
            self._prms[name] = value
        return eval(self._f_compiled, self._globals, self._prms)

    def __str__(self):
        # remove the independent variables from self._prms such that
        # this dict contains parameters (to be subsituted by values) only:
        try:
            for v in self._var:
                del self._prms[v]
        except:
            pass
        return StringFunction1.__str__(self)


    def __repr__(self):
        # first remove indep. variables possibly inserted in self._prms
        # by the self.__call__ method:
        try:
            for v in self._var:
                del self._prms[v]
        except:
            pass
        kwargs = ', '.join(['%s=%s' % (key, repr(value)) \
                            for key, value in self._prms.items()])
        return """StringFunction(%s, independent_variables=%s, %s)""" % \
               (repr(self._f), repr(self._var), kwargs)



def _efficiency():
    print '\nPerform some efficiency tests (this might take some time...):'
    formula = 'sin(x) + x**3 + 2*x'
    formula_wprm = formula + ' + A*B'
    def s0(x):
        return sin(x) + x**3 + 2*x
    s1 = StringFunction_v1(formula)
    s2 = StringFunction_v2(formula)  # compiled
    s3 = StringFunction_v3(formula_wprm, set_parameters='A=0; B=0')
    s4 = StringFunction_v4(formula)
    s5 = StringFunction_v5(formula, independent_variables=('x',),
                           A=0, B=0)
    s6 = StringFunction(formula, independent_variables=('x',),
                        A=0, B=0)
    s7 = s6.__call__
    x = 0.9
    # verification first:
    values = [s(x) for s in s0, s1, s2, s3, s4, s5, s6, s7]
    print 'values of %s for x=%s: %s' % (formula, x, values)

    n = 400000
    from scitools.misc import timer
    from scitools.EfficiencyTable import EfficiencyTable as ET
    e = ET('Efficiency check of StringFunction implementations; '
           'formula=%s, n=%d' % (formula, n))
    import inspect
    for s in s0, s1, s2, s3, s4, s5, s6, s7:
        if inspect.isfunction(s) or inspect.ismethod(s):
            name = s.__name__
        else:
            name = s.__class__.__name__
        t = timer(s, args=(x,), repetitions=100000,
                  comment=name)
        e.add(name, t)
    print e
    print 'End of efficiency tests\n'

if __name__ == '__main__':
    _doctest()
    _demo()
    _efficiency()