The patch looks fine to me, however I'm not familiar with fortran. But no one objected so far, so I pushed it in, thanks.
Vinzent 2010/2/24 Toon Verstraelen <toon.verstrae...@gmail.com>: > --- > doc/src/modules/printing.txt | 96 +++++++++++ > sympy/__init__.py | 2 +- > sympy/printing/__init__.py | 1 + > sympy/printing/fcode.py | 326 > ++++++++++++++++++++++++++++++++++++ > sympy/printing/tests/test_fcode.py | 217 ++++++++++++++++++++++++ > 5 files changed, 641 insertions(+), 1 deletions(-) > create mode 100644 sympy/printing/fcode.py > create mode 100644 sympy/printing/tests/test_fcode.py > > diff --git a/doc/src/modules/printing.txt b/doc/src/modules/printing.txt > index d6178e3..d6714f0 100644 > --- a/doc/src/modules/printing.txt > +++ b/doc/src/modules/printing.txt > @@ -84,6 +84,102 @@ This class implements Python printing. Usage:: > x = Symbol('x') > e = sin(x) + 5*x**3 > > +fcode > +----- > + > +The fcode function translates a sympy expression into Fortran code. The main > +purpose is to take away the burden of manually translating long mathematical > +expressions. Therefore the resulting expression should also require no (or > very > +little) manual tweaking to make it compilable. The optional arguments of > fcode > +can be used to fine-tune the behavior of fcode in such a way that manual > changes > +in the result are no longer needed. > + > +.. module:: sympy.printing.fcode > +.. autofunction:: fcode > +.. autofunction:: print_fcode > + > +Two basic examples: > + > + >>> from sympy import * > + >>> x = symbols("x") > + >>> fcode(sqrt(1-x**2)) > + ' sqrt(1 - x**2)' > + >>> fcode((3 + 4*I)/(1 - conjugate(x))) > + ' (cmplx(3,4))/(1 - conjg(x))' > + > +An example where line wrapping is required: > + > + >>> expr = sqrt(1-x**2).series(x,n=20).removeO() > + >>> print fcode(expr) > + 1 - x**2/2 - x**4/8 - x**6/16 - 5*x**8/128 - 7*x**10/256 - 21*x > + @ **12/1024 - 33*x**14/2048 - 429*x**16/32768 - 715*x**18/65536 > + > +In case of line wrapping, it is handy to include the assignment so that lines > +are wrapped properly when the assignment part is added. > + > + >>> print fcode(expr, assign_to="var") > + var = 1 - x**2/2 - x**4/8 - x**6/16 - 5*x**8/128 - 7*x**10/256 - > + @ 21*x**12/1024 - 33*x**14/2048 - 429*x**16/32768 - 715*x**18/65536 > + > +Also for piecewise functions, the assign_to option can be helpful: > + > + >>> print fcode(Piecewise((x,x<1),(x**2,True)), assign_to="var") > + if (x < 1) then > + var = x > + else > + var = x**2 > + end if > + > +Note that only top-level piecewise functions are supported due to the lack of > +a conditional operator in Fortran. Nested piecewise functions would require > the > +introduction of temporary variables, which is a type of expression > manipulation > +that goes beyond the scope of fcode. > + > +By default, number symbols such as ``pi`` and ``E`` are detected and defined > as > +Fortran parameters. The precision of the constants can be tuned with the > +precision argument. Parameter definitions are easily avoided using the ``N`` > +function. > + > + >>> print fcode(x - pi**2 - E) > + parameter (E = 2.71828182845905) > + parameter (pi = 3.14159265358979) > + x - E - pi**2 > + >>> print fcode(x - pi**2 - E, precision=25) > + parameter (E = 2.718281828459045235360287) > + parameter (pi = 3.141592653589793238462643) > + x - E - pi**2 > + >>> print fcode(N(x - pi**2, 25)) > + -9.869604401089358618834491 + x > + > +When some functions are not part of the Fortran standard, it might be > desirable > +to introduce the names of user-defined functions in the Fortran expression. > + > + >>> print fcode(1 - gamma(x)**2, user_functions={gamma: 'mygamma'}) > + 1 - mygamma(x)**2 > + > +However, when the user_functions argument is not provided, fcode attempts to > +use a reasonable default and adds a comment to inform the user of the issue. > + > + >>> print fcode(1 - gamma(x)**2) > + C Not Fortran 77: > + C gamma(x) > + 1 - gamma(x)**2 > + > +By default the output is human readable code, ready for copy and paste. With > the > +option ``human=False``, the return value is suitable for post-processing with > +source code generators that write routines with multiple instructions. The > +return value is a three-tuple containing: (i) the list of number symbols that > +must be defined as 'Fortran parameters', (ii) a list functions that can not > be > +translated in pure Fortran and (iii) a string of Fortran code. A few > examples: > + > + >>> fcode(1 - gamma(x)**2, human=False) > + ([], set([gamma(x)]), ' 1 - gamma(x)**2') > + >>> fcode(1 - sin(x)**2, human=False) > + ([], set(), ' 1 - sin(x)**2') > + >>> fcode(x - pi**2, human=False) > + ([('pi', 3.14159265358979)], set(), ' x - pi**2') > + > + > Preview > ------- > > diff --git a/sympy/__init__.py b/sympy/__init__.py > index 2cc73f8..81cd4a5 100644 > --- a/sympy/__init__.py > +++ b/sympy/__init__.py > @@ -38,7 +38,7 @@ def __sympy_debug(): > from plotting import Plot, textplot > from printing import pretty, pretty_print, pprint, pprint_use_unicode, \ > pprint_try_use_unicode, print_gtk, print_tree > -from printing import ccode, latex, preview > +from printing import ccode, fcode, latex, preview > from printing import python, print_python, srepr, sstr, sstrrepr > > evalf._create_evalf_table() > diff --git a/sympy/printing/__init__.py b/sympy/printing/__init__.py > index 1ade76d..40065a0 100644 > --- a/sympy/printing/__init__.py > +++ b/sympy/printing/__init__.py > @@ -5,6 +5,7 @@ > from mathml import mathml, print_mathml > from python import python, print_python > from ccode import ccode, print_ccode > +from fcode import fcode, print_fcode > from gtk import * > > from preview import preview > diff --git a/sympy/printing/fcode.py b/sympy/printing/fcode.py > new file mode 100644 > index 0000000..6211920 > --- /dev/null > +++ b/sympy/printing/fcode.py > @@ -0,0 +1,326 @@ > +""" > +Fortran code printer > + > +The FCodePrinter converts single sympy expressions into single Fortran > +expressions, using the functions defined in the Fortran 77 standard where > +possible. Some useful pointers to Fortran can be found on wikipedia: > + > +http://en.wikipedia.org/wiki/Fortran > + > +Most of the code below is based on the "Professional Programmer\'s Guide to > +Fortran77" by Clive G. Page: > + > +http://www.star.le.ac.uk/~cgp/prof77.html > + > +Fortran is a case-insensitive language. This might cause trouble because > sympy > +is case sensitive. The implementation below does not care and leaves the > +responsibility for generating properly cased Fortran code to the user. > +""" > + > + > +from str import StrPrinter > +from sympy.printing.precedence import precedence > +from sympy.core import S, Add, I > +from sympy.core.numbers import NumberSymbol > +from sympy.functions import sin, cos, tan, asin, acos, atan, atan2, sinh, \ > + cosh, tanh, sqrt, log, exp, abs, sign, conjugate, Piecewise > +from sympy.utilities.iterables import postorder_traversal > + > + > +implicit_functions = set([ > + sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, sqrt, log, exp, > + abs, sign, conjugate > +]) > + > + > +class FCodePrinter(StrPrinter): > + """A printer to convert sympy expressions to strings of Fortran code""" > + printmethod = "_fcode_" > + > + def doprint(self, expr): > + """Returns Fortran code for expr (as a string)""" > + # keep a set of expressions that are not strictly translatable to > + # Fortran. > + self.not_fortran = set([]) > + > + lines = [] > + if isinstance(expr, Piecewise): > + # support for top-level Piecewise function > + for i, (e, c) in enumerate(expr.args): > + if i == 0: > + lines.append(" if (%s) then" % self._print(c)) > + elif i == len(expr.args)-1 and c == True: > + lines.append(" else") > + else: > + lines.append(" else if (%s) then" % self._print(c)) > + if self._settings["assign_to"] is None: > + lines.append(" %s" % self._print(e)) > + else: > + lines.append(" %s = %s" % > (self._settings["assign_to"], self._print(e))) > + lines.append(" end if") > + return "\n".join(lines) > + else: > + line = StrPrinter.doprint(self, expr) > + if self._settings["assign_to"] is None: > + return " %s" % line > + else: > + return " %s = %s" % (self._settings["assign_to"], line) > + > + def _print_Add(self, expr): > + # purpose: print complex numbers nicely in Fortran. > + # collect the purely real and purely imaginary parts: > + pure_real = [] > + pure_imaginary = [] > + mixed = [] > + for arg in expr.args: > + if arg.is_real and arg.is_number: > + pure_real.append(arg) > + elif arg.is_imaginary and arg.is_number: > + pure_imaginary.append(arg) > + else: > + mixed.append(arg) > + if len(pure_imaginary) > 0: > + if len(mixed) > 0: > + PREC = precedence(expr) > + term = Add(*mixed) > + t = self._print(term) > + if t.startswith('-'): > + sign = "-" > + t = t[1:] > + else: > + sign = "+" > + if precedence(term) < PREC: > + t = "(%s)" % t > + > + return "cmplx(%s,%s) %s %s" % ( > + self._print(Add(*pure_real)), > + self._print(-I*Add(*pure_imaginary)), > + sign, t, > + ) > + else: > + return "cmplx(%s,%s)" % ( > + self._print(Add(*pure_real)), > + self._print(-I*Add(*pure_imaginary)), > + ) > + else: > + return StrPrinter._print_Add(self, expr) > + > + def _print_Function(self, expr): > + name = self._settings["user_functions"].get(expr.__class__) > + if name is None: > + if expr.func == conjugate: > + name = "conjg" > + else: > + name = expr.func.__name__ > + if expr.func not in implicit_functions: > + self.not_fortran.add(expr) > + return "%s(%s)" % (name, self.stringify(expr.args, ", ")) > + > + _print_Factorial = _print_Function > + > + def _print_ImaginaryUnit(self, expr): > + # purpose: print complex numbers nicely in Fortran. > + return "cmplx(0,1)" > + > + def _print_int(self, expr): > + return str(expr) > + > + def _print_Mul(self, expr): > + # purpose: print complex numbers nicely in Fortran. > + if expr.is_imaginary and expr.is_number: > + return "cmplx(0,%s)" % ( > + self._print(-I*expr) > + ) > + else: > + return StrPrinter._print_Mul(self, expr) > + > + def _print_NumberSymbol(self, expr): > + # Standard Fortran has no predefined constants. Write their string > + # representation, and assume parameter statements are defined > elsewhere > + # in the code to make this work. > + return str(expr) > + > + _print_Catalan = _print_NumberSymbol > + _print_EulerGamma = _print_NumberSymbol > + _print_Exp1 = _print_NumberSymbol > + _print_GoldenRatio = _print_NumberSymbol > + _print_Pi = _print_NumberSymbol > + > + def _print_Pow(self, expr): > + PREC = precedence(expr) > + if expr.exp is S.NegativeOne: > + return '1.0/%s'%(self.parenthesize(expr.base, PREC)) > + elif expr.exp == 0.5: > + return 'sqrt(%s)' % self._print(expr.base) > + else: > + return StrPrinter._print_Pow(self, expr) > + > + def _print_Rational(self, expr): > + p, q = int(expr.p), int(expr.q) > + return '%d.0/%d.0' % (p, q) > + > + def _print_not_fortran(self, expr): > + self.not_fortran.add(expr) > + return StrPrinter.emptyPrinter(self, expr) > + > + # The following can not be simply translated into Fortran. > + _print_Basic = _print_not_fortran > + _print_ComplexInfinity = _print_not_fortran > + _print_Derivative = _print_not_fortran > + _print_dict = _print_not_fortran > + _print_Dummy = _print_not_fortran > + _print_ExprCondPair = _print_not_fortran > + _print_GeometryEntity = _print_not_fortran > + _print_Infinity = _print_not_fortran > + _print_Integral = _print_not_fortran > + _print_Interval = _print_not_fortran > + _print_Limit = _print_not_fortran > + _print_list = _print_not_fortran > + _print_Matrix = _print_not_fortran > + _print_DeferredVector = _print_not_fortran > + _print_NaN = _print_not_fortran > + _print_NegativeInfinity = _print_not_fortran > + _print_Normal = _print_not_fortran > + _print_Order = _print_not_fortran > + _print_PDF = _print_not_fortran > + _print_RootOf = _print_not_fortran > + _print_RootsOf = _print_not_fortran > + _print_RootSum = _print_not_fortran > + _print_Sample = _print_not_fortran > + _print_SMatrix = _print_not_fortran > + _print_tuple = _print_not_fortran > + _print_Uniform = _print_not_fortran > + _print_Unit = _print_not_fortran > + _print_Wild = _print_not_fortran > + _print_WildFunction = _print_not_fortran > + > + > +def wrap_fortran(lines): > + """Wrap long Fortran lines > + > + Argument: > + lines -- a list of lines (without \\n character) > + > + A comment line is split at white space. Code lines are split with a > more > + complex rule to give nice results. > + """ > + # routine to find split point in a code line > + my_alnum = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_") > + my_white = set(" \t()") > + def split_pos_code(line, endpos): > + if len(line) <= endpos: > + return len(line) > + pos = endpos > + split = lambda pos: \ > + (line[pos] in my_alnum and line[pos-1] not in my_alnum) or \ > + (line[pos] not in my_alnum and line[pos-1] in my_alnum) or \ > + (line[pos] in my_white and line[pos-1] not in my_white) or \ > + (line[pos] not in my_white and line[pos-1] in my_white) > + while not split(pos): > + pos -= 1 > + if pos == 0: > + return endpos > + return pos > + # split line by line and add the splitted lines to result > + result = [] > + for line in lines: > + if line.startswith(" "): > + # code line > + pos = split_pos_code(line, 72) > + hunk = line[:pos].rstrip() > + line = line[pos:].lstrip() > + result.append(hunk) > + while len(line) > 0: > + pos = split_pos_code(line, 65) > + hunk = line[:pos].rstrip() > + line = line[pos:].lstrip() > + result.append(" @ %s" % hunk) > + elif line.startswith("C"): > + # comment line > + if len(line) > 72: > + pos = line.rfind(" ", 6, 72) > + if pos == -1: > + pos = 72 > + hunk = line[:pos] > + line = line[pos:].lstrip() > + result.append(hunk) > + while len(line) > 0: > + pos = line.rfind(" ", 0, 66) > + if pos == -1: > + pos = 66 > + hunk = line[:pos] > + line = line[pos:].lstrip() > + result.append("C %s" % hunk) > + else: > + result.append(line) > + else: > + result.append(line) > + return result > + > + > +def fcode(expr, assign_to=None, precision=15, user_functions={}, human=True): > + """Converts an expr to a string of Fortran 77 code > + > + Arguments: > + expr -- a sympy expression to be converted > + > + Optional arguments: > + assign_to -- When given, the argument is used as the name of the > + variable to which the Fortran expression is assigned. > + (This is helpful in case of line-wrapping.) > + precision -- the precision for numbers such as pi [default=15] > + user_functions -- A dictionary where keys are FunctionClass > instances > + and values are there string representations. > + human -- If True, the result is a single string that may contain > + some parameter statements for the number symbols. If > + False, the same information is returned in a more > + programmer-friendly data structure. > + > + >>> from sympy import fcode, symbols, Rational, pi, sin > + >>> x, tau = symbols(["x", "tau"]) > + >>> fcode((2*tau)**Rational(7,2)) > + ' 8*sqrt(2)*tau**(7.0/2.0)' > + >>> fcode(sin(x), assign_to="s") > + ' s = sin(x)' > + >>> print fcode(pi) > + parameter (pi = 3.14159265358979) > + pi > + > + """ > + # find all number symbols > + number_symbols = set([]) > + for sub in postorder_traversal(expr): > + if isinstance(sub, NumberSymbol): > + number_symbols.add(sub) > + number_symbols = [(str(ns), ns.evalf(precision)) for ns in > sorted(number_symbols)] > + # run the printer > + profile = { > + "full_prec": False, # programmers don't care about trailing zeros. > + "assign_to": assign_to, > + "user_functions": user_functions, > + } > + printer = FCodePrinter(profile) > + result = printer.doprint(expr) > + # format the output > + if human: > + lines = [] > + if len(printer.not_fortran) > 0: > + lines.append("C Not Fortran 77:") > + for expr in sorted(printer.not_fortran): > + lines.append("C %s" % expr) > + for name, value in number_symbols: > + lines.append(" parameter (%s = %s)" % (name, value)) > + lines.extend(result.split("\n")) > + lines = wrap_fortran(lines) > + return "\n".join(lines) > + else: > + return number_symbols, printer.not_fortran, result > + > + > +def print_fcode(expr, assign_to=None, precision=15, user_functions={}): > + """Prints the Fortran representation of the given expression. > + > + See fcode for the meaning of the optional arguments. > + """ > + print fcode(expr, assign_to, precision, user_functions) > + > diff --git a/sympy/printing/tests/test_fcode.py > b/sympy/printing/tests/test_fcode.py > new file mode 100644 > index 0000000..44cb24c > --- /dev/null > +++ b/sympy/printing/tests/test_fcode.py > @@ -0,0 +1,217 @@ > +from sympy import sin, cos, atan2, gamma, conjugate, sqrt, Factorial, \ > + Integral, Piecewise, Add, diff, symbols, raises > +from sympy import Catalan, EulerGamma, E, GoldenRatio, I, pi > +from sympy import Function, Rational, Integer > + > +from sympy.printing.fcode import fcode, wrap_fortran > + > + > +def test_printmethod(): > + x = symbols('x') > + class nint(Function): > + def _fcode_(self, printer): > + return "nint(%s)" % printer._print(self.args[0]) > + assert fcode(nint(x)) == " nint(x)" > + > +def test_fcode_Pow(): > + x, y = symbols('xy') > + assert fcode(x**3) == " x**3" > + assert fcode(x**(y**3)) == " x**(y**3)" > + assert fcode(1/(sin(x)*3.5)**(x - y**x)/(x**2 + y)) == \ > + " (3.5*sin(x))**(-x + y**x)/(y + x**2)" > + assert fcode(sqrt(x)) == ' sqrt(x)' > + assert fcode(x**0.5) == ' sqrt(x)' > + assert fcode(x**Rational(1,2)) == ' sqrt(x)' > + > +def test_fcode_Rational(): > + assert fcode(Rational(3,7)) == " 3.0/7.0" > + assert fcode(Rational(18,9)) == " 2" > + assert fcode(Rational(3,-7)) == " -3.0/7.0" > + assert fcode(Rational(-3,-7)) == " 3.0/7.0" > + > +def test_fcode_Integer(): > + assert fcode(Integer(67)) == " 67" > + assert fcode(Integer(-1)) == " -1" > + > +def test_fcode_functions(): > + x, y = symbols('xy') > + assert fcode(sin(x) ** cos(y)) == " sin(x)**cos(y)" > + > +def test_fcode_NumberSymbol(): > + assert fcode(Catalan) == ' parameter (Catalan = > 0.915965594177219)\n Catalan' > + assert fcode(EulerGamma) == ' parameter (EulerGamma = > 0.577215664901533)\n EulerGamma' > + assert fcode(E) == ' parameter (E = 2.71828182845905)\n E' > + assert fcode(GoldenRatio) == ' parameter (GoldenRatio = > 1.61803398874989)\n GoldenRatio' > + assert fcode(pi) == ' parameter (pi = 3.14159265358979)\n pi' > + assert fcode(pi,precision=5) == ' parameter (pi = 3.1416)\n pi' > + assert fcode(Catalan,human=False) == ([('Catalan', Catalan.evalf(15))], > set([]), ' Catalan') > + assert fcode(EulerGamma,human=False) == ([('EulerGamma', > EulerGamma.evalf(15))], set([]), ' EulerGamma') > + assert fcode(E,human=False) == ([('E', E.evalf(15))], set([]), ' E') > + assert fcode(GoldenRatio,human=False) == ([('GoldenRatio', > GoldenRatio.evalf(15))], set([]), ' GoldenRatio') > + assert fcode(pi,human=False) == ([('pi', pi.evalf(15))], set([]), ' > pi') > + assert fcode(pi,precision=5,human=False) == ([('pi', pi.evalf(5))], > set([]), ' pi') > + > +def test_fcode_complex(): > + assert fcode(I) == " cmplx(0,1)" > + x = symbols('x') > + assert fcode(4*I) == " cmplx(0,4)" > + assert fcode(3+4*I) == " cmplx(3,4)" > + assert fcode(3+4*I+x) == " cmplx(3,4) + x" > + assert fcode(I*x) == " cmplx(0,1)*x" > + assert fcode(3+4*I-x) == " cmplx(3,4) - x" > + x = symbols('x', imaginary=True) > + assert fcode(5*x) == " 5*x" > + assert fcode(I*x) == " cmplx(0,1)*x" > + assert fcode(3+x) == " 3 + x" > + > +def test_implicit(): > + x, y = symbols('xy') > + assert fcode(sin(x)) == " sin(x)" > + assert fcode(atan2(x,y)) == " atan2(x, y)" > + assert fcode(conjugate(x)) == " conjg(x)" > + > +def test_not_fortran(): > + x = symbols('x') > + g = Function('g') > + assert fcode(gamma(x)) == "C Not Fortran 77:\nC gamma(x)\n > gamma(x)" > + assert fcode(Integral(sin(x))) == "C Not Fortran 77:\nC > Integral(sin(x), x)\n Integral(sin(x), x)" > + assert fcode(g(x)) == "C Not Fortran 77:\nC g(x)\n g(x)" > + > +def test_user_functions(): > + x = symbols('x') > + assert fcode(sin(x), user_functions={sin: "zsin"}) == " zsin(x)" > + x = symbols('x') > + assert fcode(gamma(x), user_functions={gamma: "mygamma"}) == " > mygamma(x)" > + g = Function('g') > + assert fcode(g(x), user_functions={g: "great"}) == " great(x)" > + n = symbols('n', integer=True) > + assert fcode(Factorial(n), user_functions={Factorial: "fct"}) == " > fct(n)" > + > +def test_assign_to(): > + x = symbols('x') > + assert fcode(sin(x), assign_to="s") == " s = sin(x)" > + > +def test_line_wrapping(): > + x, y = symbols('xy') > + assert fcode(((x+y)**10).expand(), assign_to="var") == ( > + " var = 45*x**8*y**2 + 120*x**7*y**3 + 210*x**6*y**4 + > 252*x**5*y**5\n" > + " @ + 210*x**4*y**6 + 120*x**3*y**7 + 45*x**2*y**8 + 10*x*y**9 + > 10*y\n" > + " @ *x**9 + x**10 + y**10" > + ) > + e = [x**i for i in range(11)] > + assert fcode(Add(*e)) == ( > + " 1 + x + x**2 + x**3 + x**4 + x**5 + x**6 + x**7 + x**8 + x**9 > + x\n" > + " @ **10" > + ) > + > +def test_fcode_Piecewise(): > + x = symbols('x') > + assert fcode(Piecewise((x,x<1),(x**2,True))) == ( > + " if (x < 1) then\n" > + " x\n" > + " else\n" > + " x**2\n" > + " end if" > + ) > + assert fcode(Piecewise((x,x<1),(x**2,True)), assign_to="var") == ( > + " if (x < 1) then\n" > + " var = x\n" > + " else\n" > + " var = x**2\n" > + " end if" > + ) > + a = cos(x)/x > + b = sin(x)/x > + for i in xrange(10): > + a = diff(a, x) > + b = diff(b, x) > + assert fcode(Piecewise((a,x<0),(b,True)), assign_to="weird_name") == ( > + " if (x < 0) then\n" > + " weird_name = -cos(x)/x - 1814400*cos(x)/x**9 - > 604800*sin(x)/x\n" > + " @ **8 - 5040*cos(x)/x**5 - 720*sin(x)/x**4 + 10*sin(x)/x**2 + > 90*\n" > + " @ cos(x)/x**3 + 30240*sin(x)/x**6 + 151200*cos(x)/x**7 + > 3628800*\n" > + " @ cos(x)/x**11 + 3628800*sin(x)/x**10\n" > + " else\n" > + " weird_name = -sin(x)/x - 3628800*cos(x)/x**10 - > 1814400*sin(x)/x\n" > + " @ **9 - 30240*cos(x)/x**6 - 5040*sin(x)/x**5 - 10*cos(x)/x**2 > + 90*\n" > + " @ sin(x)/x**3 + 720*cos(x)/x**4 + 151200*sin(x)/x**7 + > 604800*cos(x\n" > + " @ )/x**8 + 3628800*sin(x)/x**11\n" > + " end if" > + ) > + assert fcode(Piecewise((x,x<1),(x**2,x>1),(sin(x),True))) == ( > + " if (x < 1) then\n" > + " x\n" > + " else if (1 < x) then\n" > + " x**2\n" > + " else\n" > + " sin(x)\n" > + " end if" > + ) > + assert fcode(Piecewise((x,x<1),(x**2,x>1),(sin(x),x>0))) == ( > + " if (x < 1) then\n" > + " x\n" > + " else if (1 < x) then\n" > + " x**2\n" > + " else if (0 < x) then\n" > + " sin(x)\n" > + " end if" > + ) > + > +def test_wrap_fortran(): > + # > "########################################################################" > + lines = [ > + "C This is a long comment on a single line that must be wrapped > properly", > + " this = is + a + long + and + nasty + fortran + statement + > that * must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that * must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that * must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that*must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that*must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that*must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that*must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + > statement(that)/must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + > statement(that)/must + be + wrapped + properly", > + ] > + wrapped_lines = wrap_fortran(lines) > + expected_lines = [ > + "C This is a long comment on a single line that must be wrapped", > + "C properly", > + " this = is + a + long + and + nasty + fortran + statement + > that *", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that *", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that", > + " @ * must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that*", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that*", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that", > + " @ *must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement +", > + " @ that*must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that**", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that", > + " @ **must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement + > that", > + " @ **must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + statement +", > + " @ that**must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + > statement(that)/", > + " @ must + be + wrapped + properly", > + " this = is + a + long + and + nasty + fortran + > statement(that)", > + " @ /must + be + wrapped + properly", > + ] > + for line in wrapped_lines: > + assert len(line) <= 72 > + for w, e in zip(wrapped_lines, expected_lines): > + assert w == e > + assert len(wrapped_lines) == len(expected_lines) > + > -- > 1.6.3.3 -- You received this message because you are subscribed to the Google Groups "sympy-patches" group. To post to this group, send email to sympy-patc...@googlegroups.com. To unsubscribe from this group, send email to sympy-patches+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sympy-patches?hl=en.