Implemented tests of the generated strings, and a script in test_external/ to
test resulting binaries.

The existing CCodeGen and related tests was used as templates for the new
functionality.
---
 sympy/test_external/test_codegen_f95_ifort.py |  240 ++++++++++++++++
 sympy/utilities/codegen.py                    |  164 +++++++++++-
 sympy/utilities/tests/test_codegen.py         |  370 ++++++++++++++++++++++++-
 3 files changed, 770 insertions(+), 4 deletions(-)
 create mode 100644 sympy/test_external/test_codegen_f95_ifort.py

diff --git a/sympy/test_external/test_codegen_f95_ifort.py 
b/sympy/test_external/test_codegen_f95_ifort.py
new file mode 100644
index 0000000..0030cc0
--- /dev/null
+++ b/sympy/test_external/test_codegen_f95_ifort.py
@@ -0,0 +1,240 @@
+# This tests the compilation and execution of the source code generated with
+# utilities.codegen. The compilation takes place in a temporary directory that
+# is removed after the test. By default the test directory is always removed,
+# but this behavior can be changed by setting the environment variable
+# SYMPY_TEST_CLEAN_TEMP to:
+#   export SYMPY_TEST_CLEAN_TEMP=always   : the default behavior.
+#   export SYMPY_TEST_CLEAN_TEMP=success  : only remove the directories of 
working tests.
+#   export SYMPY_TEST_CLEAN_TEMP=never    : never remove the directories with 
the test code.
+# When a directory is not removed, the necessary information is printed on
+# screen to find the files that belong to the (failed) tests. If a test does
+# not fail, py.test captures all the output and you will not see the 
directories
+# corresponding to the successful tests. Use the --nocapture option to see all
+# the output.
+
+# All tests below have a counterpart in utilities/test/test_codegen.py. In the
+# latter file, the resulting code is compared with predefined strings, without
+# compilation or execution.
+
+# All the generated Fortran code should conform with the Fortran 95 standard,
+# which facilitates the incorporation in various projects. The tests below
+# assume that the binary ifort is somewhere in the path and that it can compile
+# standard Fortran 95 code.
+
+
+from sympy import symbols
+from sympy.utilities.codegen import codegen, FCodeGen, Routine, InputArgument, 
\
+    Result
+import sys, os, tempfile
+
+
+# templates for the main program that will test the generated code.
+
+main_template = """
+program main
+  include "codegen.h"
+  integer :: result;
+  result = 0
+
+  %s
+
+  call exit(result)
+end program
+"""
+
+numerical_test_template = """
+  if (abs(%(call)s)>%(threshold)s) then
+    write(6,"('Numerical validation failed:')")
+    write(6,"('%(call)s=',e15.5,'threshold=',e15.5)") %(call)s, %(threshold)s
+    result = -1;
+  end if
+"""
+
+
+def try_run(commands):
+    """Run a series of commands and only return True if all ran fine."""
+    for command in commands:
+        retcode = os.system(command)
+        if retcode != 0:
+            return False
+    return True
+
+
+def run_f95_test(label, routines, numerical_tests, friendly=True):
+    """A driver for the codegen tests.
+
+       This driver assumes that a compiler ifort is present in the PATH and 
that
+       ifort is (at least) a Fortran 90 compiler. The generated code is 
written in
+       a temporary directory, together with a main program that validates the
+       generated code. The test passes when the compilation and the validation
+       run correctly.
+    """
+    # Do all the magic to compile, run and validate the test code
+    # 1) prepare the temporary working directory, switch to that dir
+    work = tempfile.mkdtemp("_sympy_f95_test", "%s_" % label)
+    oldwork = os.getcwd()
+    os.chdir(work)
+    # 2) write the generated code
+    if friendly:
+        # interpret the routines as a name_expr list and call the friendly
+        # function codegen
+        codegen(routines, 'F95', "codegen", to_files=True)
+    else:
+        code_gen = FCodeGen()
+        code_gen.write(routines, "codegen", to_files=True)
+    # 3) write a simple main program that links to the generated code, and that
+    #    includes the numerical tests
+    test_strings = []
+    for fn_name, args, expected, threshold in numerical_tests:
+        call_string = "%s(%s)-(%s)" % (fn_name, ",".join(str(arg) for arg in 
args), expected)
+        call_string = fortranize_double_constants(call_string)
+        threshold = fortranize_double_constants(str(threshold))
+        test_strings.append(numerical_test_template % {
+            "call": call_string,
+            "threshold": threshold,
+        })
+    f = file("main.f90", "w")
+    f.write(main_template % "".join(test_strings))
+    f.close()
+    # 4) Compile and link
+    compiled = try_run([
+        "ifort -c codegen.f90 -o codegen.o",
+        "ifort -c main.f90 -o main.o",
+        "ifort main.o codegen.o -o test.exe"
+    ])
+    # 5) Run if compiled
+    if compiled:
+        executed = try_run(["./test.exe"])
+    else:
+        executed = False
+    # 6) Clean up stuff
+    clean = os.getenv('SYMPY_TEST_CLEAN_TEMP', 'always').lower()
+    if clean not in ('always', 'success', 'never'):
+        raise ValueError("SYMPY_TEST_CLEAN_TEMP must be one of the following: 
'always', 'success' or 'never'.")
+    if clean == 'always' or (clean == 'success' and compiled and executed):
+        def safe_remove(filename):
+            if os.path.isfile(filename):
+                os.remove(filename)
+        safe_remove("codegen.f90")
+        safe_remove("codegen.h")
+        safe_remove("codegen.o")
+        safe_remove("main.f90")
+        safe_remove("main.o")
+        safe_remove("test.exe")
+        os.chdir(oldwork)
+        os.rmdir(work)
+    else:
+        print >> sys.stderr, "TEST NOT REMOVED: %s" % work
+        os.chdir(oldwork)
+    # 7) Do the assertions in the end
+    assert compiled
+    assert executed
+
+def fortranize_double_constants(code_string):
+    """
+    Replaces every literal float with literal doubles
+    """
+    import re
+    pattern_exp = re.compile('\d+(\.)?\d*[eE]-?\d+')
+    pattern_float = re.compile('\d+\.\d*(?!\d*d)')
+
+    def subs_exp(matchobj):
+        return re.sub('[eE]','d',matchobj.group(0))
+
+    def subs_float(matchobj):
+        return "%sd0" % matchobj.group(0)
+
+    code_string = pattern_exp.sub(subs_exp, code_string)
+    code_string = pattern_float.sub(subs_float, code_string)
+
+    return code_string
+
+
+
+def is_feasible():
+    # This test should always work, otherwise the f95 compiler is not present.
+    x,y,z = symbols('xyz')
+    expr = (x+y)*z
+    routine = Routine("test", [InputArgument(symbol) for symbol in x,y,z], 
[Result(expr)])
+    numerical_tests = [
+        ("test", (1.0, 6.0, 3.0), 21.0, 1e-15),
+        ("test", (-1.0, 2.0, -2.5), -2.5, 1e-15),
+    ]
+    run_f95_test("is_feasible", [routine], numerical_tests, friendly=False)
+
+
+try:
+    is_feasible()
+except AssertionError:
+    disabled = True
+
+
+def test_basic():
+    is_feasible()
+
+
+def test_basic_codegen():
+    x,y,z = symbols('xyz')
+    numerical_tests = [
+        ("test", (1.0, 6.0, 3.0), 21.0, 1e-15),
+        ("test", (-1.0, 2.0, -2.5), -2.5, 1e-15),
+    ]
+    run_f95_test("basic_codegen", [("test", (x+y)*z)], numerical_tests)
+
+def test_intrinsic_math1_codegen():
+    # not included: log10
+    from sympy import acos, asin, atan, ceiling, cos, cosh, floor, log, ln, \
+        sin, sinh, sqrt, tan, tanh, N
+    x = symbols('x')
+    name_expr = [
+        ("test_fabs", abs(x)),
+        ("test_acos", acos(x)),
+        ("test_asin", asin(x)),
+        ("test_atan", atan(x)),
+        # ("test_ceil", ceiling(x)),
+        ("test_cos", cos(x)),
+        ("test_cosh", cosh(x)),
+        # ("test_floor", floor(x)),
+        ("test_log", log(x)),
+        ("test_ln", ln(x)),
+        ("test_sin", sin(x)),
+        ("test_sinh", sinh(x)),
+        ("test_sqrt", sqrt(x)),
+        ("test_tan", tan(x)),
+        ("test_tanh", tanh(x)),
+    ]
+    numerical_tests = []
+    for name, expr in name_expr:
+        for xval in 0.2, 0.5, 0.8:
+            expected = N(expr.subs(x, xval))
+            numerical_tests.append((name, (xval,), expected, 1e-14))
+    run_f95_test("intrinsic_math1", name_expr, numerical_tests)
+
+def test_instrinsic_math2_codegen():
+    # not included: frexp, ldexp, modf, fmod
+    from sympy import atan2, N
+    x, y = symbols('xy')
+    name_expr = [
+        ("test_atan2", atan2(x,y)),
+        ("test_pow", x**y),
+    ]
+    numerical_tests = []
+    for name, expr in name_expr:
+        for xval,yval in (0.2, 1.3), (0.5, -0.2), (0.8, 0.8):
+            expected = N(expr.subs(x, xval).subs(y, yval))
+            numerical_tests.append((name, (xval,yval), expected, 1e-14))
+    run_f95_test("intrinsic_math2", name_expr, numerical_tests)
+
+def test_complicated_codegen():
+    from sympy import sin, cos, tan, N
+    x,y,z = symbols('xyz')
+    name_expr = [
+        ("test1", ((sin(x)+cos(y)+tan(z))**7).expand()),
+        ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x+y+z))))))))),
+    ]
+    numerical_tests = []
+    for name, expr in name_expr:
+        for xval,yval,zval in (0.2, 1.3, -0.3), (0.5, -0.2, 0.0), (0.8, 2.1, 
0.8):
+            expected = N(expr.subs(x, xval).subs(y, yval).subs(z, zval))
+            numerical_tests.append((name, (xval,yval,zval), expected, 1e-12))
+    run_f95_test("complicated_codegen", name_expr, numerical_tests)
diff --git a/sympy/utilities/codegen.py b/sympy/utilities/codegen.py
index 23b0e0b..8e701c4 100644
--- a/sympy/utilities/codegen.py
+++ b/sympy/utilities/codegen.py
@@ -76,6 +76,7 @@
 from sympy.core.basic import Basic
 from sympy.utilities.iterables import postorder_traversal
 from sympy.printing.ccode import ccode
+from sympy.printing.fcode import fcode
 
 from StringIO import StringIO
 import sympy, os
@@ -86,7 +87,7 @@
     "Routine", "DataType", "default_datatypes", "get_default_datatype",
     "Argument", "InputArgument", "Result",
     # routines -> code
-    "CodeGen", "CCodeGen",
+    "CodeGen", "CCodeGen", "FCodeGen"
     # friendly functions
     "codegen",
 ]
@@ -263,8 +264,9 @@ class CodeGenError(Exception):
 
 
 class CCodeGen(CodeGen):
+
     def _dump_header(self, f):
-        """Writes a common header for the .c and the .h file."""
+        """Writes a common header for the generated files."""
         print >> f, 
"/******************************************************************************
 "
         tmp = header_comment % {"version": sympy.__version__, "project": 
self.project}
         for line in tmp.splitlines():
@@ -372,6 +374,162 @@ def dump_h(self, routines, f, prefix, header=True, 
empty=True):
     # functions it has to call.
     dump_fns = [dump_c, dump_h]
 
+class FCodeGen(CodeGen):
+    """
+    Generator for Fortran 95 code
+    """
+
+    def _dump_header(self, f):
+        """Writes a common header for the generated files."""
+        print >> f, 
"!******************************************************************************
 "
+        tmp = header_comment % {"version": sympy.__version__, "project": 
self.project}
+        for line in tmp.splitlines():
+            print >> f, "!*%s* " % line.center(76)
+        print >> f, 
"!******************************************************************************/"
+
+    def _get_routine_opening(self, routine):
+        """
+        Returns the opening statements of the fortran routine
+        """
+        code_list = []
+        if len(routine.results) > 1:
+            raise CodeGenError("Fortran only supports a single or no return 
value.")
+        elif len(routine.results) == 1:
+            result = routine.results[0]
+            code_list.append(result.datatype.fname)
+            code_list.append("function")
+        else:
+            code_list.append("subroutine")
+
+        # name of the routine + arguments
+        code_list.append("%s(%s)\n" % (routine.name,
+            ", ".join("%s" % arg.name for arg in routine.arguments)
+            ))
+
+        code_list.append('implicit none\n')
+
+        # argument type declarations
+        code_list.append("\n ".join(
+            ["%s :: %s" % (arg.datatype.fname, arg.name)
+                for arg in routine.arguments])
+            + '\n')
+
+        return code_list
+
+    def _get_routine_ending(self, routine):
+        """
+        Returns the closing statements of the fortran routine
+        """
+        if len(routine.results) == 1:
+            return ["end function\n"]
+        else:
+            return ["end subroutine\n"]
+
+    def get_interface(self, routine):
+        """Returns a string for the function interface for the given routine 
and
+           a single result object, which can be None.
+
+           If the routine has multiple result objects, a CodeGenError is
+           raised.
+
+           See: http://en.wikipedia.org/wiki/Function_prototype
+
+        """
+        prototype = [ "interface\n" ]
+        prototype.extend(self._get_routine_opening(routine))
+        prototype.extend(self._get_routine_ending(routine))
+        prototype.append("end interface\n")
+
+        return " ".join(prototype)
+
+    def _get_result(self, routine):
+        """Returns a single result object, which can be None.
+
+           If the routine has multiple result objects, an CodeGenError is
+           raised.
+
+           See: http://en.wikipedia.org/wiki/Function_prototype
+        """
+
+        if len(routine.results) > 1:
+            raise CodeGenError("Fortran only supports a single or no return 
value.")
+        elif len(routine.results) == 1:
+            result = routine.results[0]
+        else:
+            result = None
+
+        return result
+
+    def dump_f95(self, routines, f, prefix, header=True, empty=True):
+        """Write the F95 code file.
+
+           This file contains all the definitions of the routines in f95 code 
and
+           refers to the header file.
+
+           Arguments:
+             routines  --  a list of Routine instances
+             f  --  a file-like object to write the file to
+             prefix  --  the filename prefix, used to refer to the proper 
header
+                         file. Only the basename of the prefix is used.
+
+           Optional arguments:
+             header  --  When True, a header comment is included on top of each
+                         source file. [DEFAULT=True]
+             empty  --  When True, empty lines are included to structure the
+                        source files. [DEFAULT=True]
+        """
+        if header:
+            self._dump_header(f)
+        if empty: print >> f
+
+        for routine in routines:
+            code_lines = self._get_routine_opening(routine)
+
+            result = self._get_result(routine)
+            if result is not None:
+                code_lines.append("%s = %s\n" %(routine.name,
+                    fcode(result.expr, source_format='free')))
+            print >> f, ' '.join(code_lines),
+
+            if empty: print >> f
+            code_lines = self._get_routine_ending(routine)
+            print >> f, ' '.join(code_lines),
+
+            if empty: print >> f
+        if empty: print >> f
+    dump_f95.extension = "f90"
+
+    def dump_h(self, routines, f, prefix, header=True, empty=True):
+        """Writes the interface  header file.
+
+           This file contains all the function declarations.
+
+           Arguments:
+             routines  --  a list of Routine instances
+             f  --  a file-like object to write the file to
+             prefix  --  the filename prefix, used to construct the include
+                         guards.
+
+           Optional arguments:
+             header  --  When True, a header comment is included on top of each
+                         source file. [DEFAULT=True]
+             empty  --  When True, empty lines are included to structure the
+                        source files. [DEFAULT=True]
+        """
+        if header:
+            self._dump_header(f)
+        if empty: print >> f
+        # declaration of the function prototypes
+        for routine in routines:
+            prototype  = self.get_interface(routine)
+            print >> f, prototype,
+        if empty: print >> f
+    dump_h.extension = "h"
+
+    # This list of dump functions is used by CodeGen.write to know which dump
+    # functions it has to call.
+    dump_fns = [dump_f95, dump_h]
+
 
 #
 # Friendly functions
@@ -427,7 +585,7 @@ def codegen(name_expr, language, prefix, project="project", 
to_files=False, head
     """
 
     # Initialize the code generator.
-    CodeGenClass = {"C": CCodeGen}.get(language.upper())
+    CodeGenClass = {"C": CCodeGen, "F95": FCodeGen}.get(language.upper())
     if CodeGenClass is None:
         raise ValueError("Language '%s' is not supported." % language)
     code_gen = CodeGenClass(project)
diff --git a/sympy/utilities/tests/test_codegen.py 
b/sympy/utilities/tests/test_codegen.py
index 771d023..0a102a3 100644
--- a/sympy/utilities/tests/test_codegen.py
+++ b/sympy/utilities/tests/test_codegen.py
@@ -1,6 +1,6 @@
 from sympy import symbols, raises
 from sympy.utilities.codegen import CCodeGen, Routine, InputArgument, Result, \
-    codegen, CodeGenError
+    codegen, CodeGenError, FCodeGen
 from StringIO import StringIO
 
 def get_string(dump_fn, routines, prefix="file"):
@@ -229,3 +229,371 @@ def test_complicated_codegen():
         'double test2(double x, double y, double z);\n'
         '#endif\n'
     )
+
+def test_empty_f_code():
+    code_gen = FCodeGen()
+    source = get_string(code_gen.dump_f95, [])
+    assert source == ""
+
+def test_empty_f_header():
+    code_gen = FCodeGen()
+    source = get_string(code_gen.dump_h, [])
+    assert source == ""
+
+def test_simple_f_code():
+    x,y,z = symbols('xyz')
+    expr = (x+y)*z
+    routine = Routine("test", [InputArgument(symbol) for symbol in x,y,z], 
[Result(expr)])
+    code_gen = FCodeGen()
+    source = get_string(code_gen.dump_f95, [routine])
+    expected = (
+            "REAL*8 function test(x, y, z)\n"
+            " implicit none\n"
+            " REAL*8 :: x\n"
+            " REAL*8 :: y\n"
+            " REAL*8 :: z\n"
+            " test =  z*(x + y)\n"
+            "end function\n"
+    )
+    assert source == expected
+
+def test_simple_f_header():
+    x,y,z = symbols('xyz')
+    expr = (x+y)*z
+    routine = Routine("test", [InputArgument(symbol) for symbol in x,y,z], 
[Result(expr)])
+    code_gen = FCodeGen()
+    source = get_string(code_gen.dump_h, [routine])
+    expected = (
+            "interface\n"
+            " REAL*8 function test(x, y, z)\n"
+            " implicit none\n"
+            " REAL*8 :: x\n"
+            " REAL*8 :: y\n"
+            " REAL*8 :: z\n"
+            " end function\n"
+            " end interface\n"
+    )
+    assert source == expected
+
+def test_simple_f_codegen():
+    x,y,z = symbols('xyz')
+    expr = (x+y)*z
+    result = codegen(("test", (x+y)*z), "F95", "file", header=False, 
empty=False)
+    expected = [
+       ("file.f90",
+        "REAL*8 function test(x, y, z)\n"
+        " implicit none\n"
+        " REAL*8 :: x\n"
+        " REAL*8 :: y\n"
+        " REAL*8 :: z\n"
+        " test =  z*(x + y)\n"
+        "end function\n"),
+       ("file.h",
+        "interface\n"
+        " REAL*8 function test(x, y, z)\n"
+        " implicit none\n"
+        " REAL*8 :: x\n"
+        " REAL*8 :: y\n"
+        " REAL*8 :: z\n"
+        " end function\n"
+        " end interface\n")
+    ]
+    assert result == expected
+
+def test_multiple_results_f():
+    x,y,z = symbols('xyz')
+    expr1 = (x+y)*z
+    expr2 = (x-y)*z
+    routine = Routine(
+        "test",
+        [InputArgument(symbol) for symbol in x,y,z],
+        [Result(expr1),Result(expr2)]
+    )
+    code_gen = FCodeGen()
+    raises(CodeGenError, 'get_string(code_gen.dump_h, [routine])')
+
+def test_no_results_f():
+    x = symbols('x')
+    raises(ValueError, 'Routine("test", [InputArgument(x)], [])')
+
+def test_intrinsic_math_codegen():
+    # not included: log10
+    from sympy import acos, asin, atan, ceiling, cos, cosh, floor, log, ln, \
+            sin, sinh, sqrt, tan, tanh, N
+    x = symbols('x')
+    name_expr = [
+            ("test_abs", abs(x)),
+            ("test_acos", acos(x)),
+            ("test_asin", asin(x)),
+            ("test_atan", atan(x)),
+            # ("test_ceil", ceiling(x)),
+            ("test_cos", cos(x)),
+            ("test_cosh", cosh(x)),
+            # ("test_floor", floor(x)),
+            ("test_log", log(x)),
+            ("test_ln", ln(x)),
+            ("test_sin", sin(x)),
+            ("test_sinh", sinh(x)),
+            ("test_sqrt", sqrt(x)),
+            ("test_tan", tan(x)),
+            ("test_tanh", tanh(x)),
+            ]
+    result = codegen(name_expr, "F95", "file", header=False, empty=False)
+    assert result[0][0] == "file.f90"
+    expected = (
+            'REAL*8 function test_abs(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_abs =  abs(x)\n'
+            'end function\n'
+            'REAL*8 function test_acos(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_acos =  acos(x)\n'
+            'end function\n'
+            'REAL*8 function test_asin(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_asin =  asin(x)\n'
+            'end function\n'
+            'REAL*8 function test_atan(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_atan =  atan(x)\n'
+            'end function\n'
+            'REAL*8 function test_cos(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_cos =  cos(x)\n'
+            'end function\n'
+            'REAL*8 function test_cosh(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_cosh =  cosh(x)\n'
+            'end function\n'
+            'REAL*8 function test_log(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_log =  log(x)\n'
+            'end function\n'
+            'REAL*8 function test_ln(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_ln =  log(x)\n'
+            'end function\n'
+            'REAL*8 function test_sin(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_sin =  sin(x)\n'
+            'end function\n'
+            'REAL*8 function test_sinh(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_sinh =  sinh(x)\n'
+            'end function\n'
+            'REAL*8 function test_sqrt(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_sqrt =  sqrt(x)\n'
+            'end function\n'
+            'REAL*8 function test_tan(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_tan =  tan(x)\n'
+            'end function\n'
+            'REAL*8 function test_tanh(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' test_tanh =  tanh(x)\n'
+            'end function\n'
+        )
+    assert result[0][1] == expected
+
+    assert result[1][0] == "file.h"
+    expected =  (
+            'interface\n'
+            ' REAL*8 function test_abs(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_acos(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_asin(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_atan(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_cos(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_cosh(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_log(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_ln(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_sin(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_sinh(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_sqrt(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_tan(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_tanh(x)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' end function\n'
+            ' end interface\n'
+    )
+    assert result[1][1] == expected
+
+def test_intrinsic_math2_codegen():
+    # not included: frexp, ldexp, modf, fmod
+    from sympy import atan2, N
+    x, y = symbols('xy')
+    name_expr = [
+        ("test_atan2", atan2(x,y)),
+        ("test_pow", x**y),
+    ]
+    result = codegen(name_expr, "F95", "file", header=False, empty=False)
+    assert result[0][0] == "file.f90"
+    expected = (
+            'REAL*8 function test_atan2(x, y)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' test_atan2 =  atan2(x, y)\n'
+            'end function\n'
+            'REAL*8 function test_pow(x, y)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' test_pow =  x**y\n'
+            'end function\n'
+            )
+    assert result[0][1] == expected
+
+    assert result[1][0] == "file.h"
+    expected = (
+            'interface\n'
+            ' REAL*8 function test_atan2(x, y)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test_pow(x, y)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' end function\n'
+            ' end interface\n'
+    )
+    assert result[1][1] == expected
+
+def test_complicated_codegen_f95():
+    from sympy import sin, cos, tan, N
+    x,y,z = symbols('xyz')
+    name_expr = [
+        ("test1", ((sin(x)+cos(y)+tan(z))**7).expand()),
+        ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x+y+z))))))))),
+    ]
+    result = codegen(name_expr, "F95", "file", header=False, empty=False)
+    assert result[0][0] == "file.f90"
+    expected = (
+            'REAL*8 function test1(x, y, z)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' REAL*8 :: z\n'
+            ' test1 =  7*cos(y)**6*sin(x) + 7*cos(y)**6*tan(z) + 
7*sin(x)**6*cos(y) + 7*sin(x) &\n'
+            '      **6*tan(z) + 7*tan(z)**6*cos(y) + 7*tan(z)**6*sin(x) + 
42*cos(y) &\n'
+            '      **5*sin(x)*tan(z) + 42*sin(x)**5*cos(y)*tan(z) + 
42*tan(z)**5*cos &\n'
+            '      (y)*sin(x) + 105*cos(y)**2*sin(x)**4*tan(z) + 
105*cos(y)**2*tan(z &\n'
+            '      )**4*sin(x) + 105*cos(y)**4*sin(x)**2*tan(z) + 
105*cos(y)**4*tan( &\n'
+            '      z)**2*sin(x) + 105*sin(x)**2*tan(z)**4*cos(y) + 
105*sin(x)**4*tan &\n'
+            '      (z)**2*cos(y) + 140*cos(y)**3*sin(x)**3*tan(z) + 
140*cos(y)**3* &\n'
+            '      tan(z)**3*sin(x) + 140*sin(x)**3*tan(z)**3*cos(y) + 
21*cos(y)**5* &\n'
+            '      sin(x)**2 + 21*cos(y)**5*tan(z)**2 + 21*sin(x)**5*tan(z)**2 
+ &\n'
+            '      210*cos(y)**2*sin(x)**3*tan(z)**2 + 
210*cos(y)**3*sin(x)**2*tan(z &\n'
+            '      )**2 + 35*cos(y)**4*sin(x)**3 + 35*cos(y)**4*tan(z)**3 + 
35*sin(x &\n'
+            '      )**4*tan(z)**3 + 210*cos(y)**2*sin(x)**2*tan(z)**3 + 
35*cos(y) &\n'
+            '      **3*sin(x)**4 + 35*cos(y)**3*tan(z)**4 + 
35*sin(x)**3*tan(z)**4 + &\n'
+            '      21*cos(y)**2*sin(x)**5 + 21*cos(y)**2*tan(z)**5 + 
21*sin(x)**2* &\n'
+            '      tan(z)**5 + cos(y)**7 + sin(x)**7 + tan(z)**7\n'
+            'end function\n'
+            'REAL*8 function test2(x, y, z)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' REAL*8 :: z\n'
+            ' test2 =  cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))))\n'
+            'end function\n'
+    )
+    assert result[0][1] == expected
+    assert result[1][0] == "file.h"
+    expected = (
+            'interface\n'
+            ' REAL*8 function test1(x, y, z)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' REAL*8 :: z\n'
+            ' end function\n'
+            ' end interface\n'
+            'interface\n'
+            ' REAL*8 function test2(x, y, z)\n'
+            ' implicit none\n'
+            ' REAL*8 :: x\n'
+            ' REAL*8 :: y\n'
+            ' REAL*8 :: z\n'
+            ' end function\n'
+            ' end interface\n'
+    )
+    assert result[1][1] == expected
-- 
1.6.5

-- 
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.

Reply via email to