Exemplo n.º 1
0
def write_functions(info_map, group, template_map, base_dir):
    #
    # group.keys() is a vector of different grouped function names
    # like gees, dgesv, etc.
    #
    for group_name, subroutines in group.iteritems():

        filename = group_name.lower() + '.hpp'
        includes = [
            #'#include <boost/numeric/bindings/detail/void_ptr.hpp>',
            #'#include <boost/numeric/bindings/traits/traits.hpp>',
            #'#include <boost/numeric/bindings/traits/type_traits.hpp>',
            '#include <boost/numeric/bindings/remove_imaginary.hpp>',
            '#include <boost/numeric/bindings/is_mutable.hpp>',
            '#include <boost/numeric/bindings/value_type.hpp>',
            '#include <boost/numeric/bindings/stride.hpp>',
            '#include <boost/numeric/bindings/size.hpp>',
            '#include <boost/numeric/bindings/begin.hpp>',
            #'#include <boost/mpl/bool.hpp>',
            '#include <boost/type_traits/is_same.hpp>',
            '#include <boost/type_traits/remove_const.hpp>',
            '#include <boost/static_assert.hpp>',
            '#include <boost/assert.hpp>',
        ]

        for subroutine in subroutines:
            group_name_l = info_map[subroutine]['group_name'].lower()
            if template_map.has_key(group_name_l + '.includes'):
                includes += template_map[group_name_l +
                                         '.includes'].splitlines()

        #
        # LEVEL 0 HANDLING
        #
        overloads = template_map['backend_blas_overloads']
        for select_backend in [
                'blas_overloads', 'cblas_overloads', 'cublas_overloads'
        ]:
            sub_overloads = ''
            for subroutine in subroutines:
                group_name_l = info_map[subroutine]['group_name'].lower()

                # stuff like float, complex<double>, etc.
                subroutine_value_type = documentation. \
                    subroutine_value_type[ ",".join( [
                        info_map[ subroutine ][ 'value_type' ],
                        info_map[ subroutine ][ 'precision' ] ] ) ]

                sub_template = template_map[select_backend]
                # add the argument list here
                arg_list = []
                typename_list = []
                blas_arg_list = []
                cblas_arg_list = []
                cublas_arg_list = []
                level0_static_asserts = []
                check_for_unused = []

                for arg in info_map[subroutine]['arguments']:
                    print "Subroutine ", subroutine, " arg ", arg
                    arg_list += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['level_0']
                    ]
                    blas_arg_list += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['call_blas_header']
                    ]

                    #
                    # Find potential arguments that may cause warnings because they are not used, and
                    # store these in check_for_unused
                    #
                    if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None and \
                       info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None:
                        keyword = info_map[subroutine]['argument_map'][arg][
                            'code']['level_0'].split(' ')[-1]
                        check_for_unused += [keyword]

                    if 'call_cblas_header' in info_map[subroutine][
                            'argument_map'][arg]['code']:
                        cblas_arg_list += [
                            info_map[subroutine]['argument_map'][arg]['code']
                            ['call_cblas_header']
                        ]
                    else:
                        print "WARNING: couldn't find cblas call."

                    if info_map[subroutine]['argument_map'][arg]['code'][
                            'level_0_typename'] != None:
                        typename_list += [
                            info_map[subroutine]['argument_map'][arg]['code']
                            ['level_0_typename']
                        ]

                if "has_cblas_order_arg" in info_map[subroutine]:
                    check_for_unused.append('order')
                    if info_map[subroutine]["has_cblas_order_arg"] == True:
                        arg_list.insert(0, "const Order order")
                        cblas_arg_list.insert(0,
                                              "cblas_option< Order >::value")
                        typename_list.insert(0, "typename Order")
                        level0_static_asserts.append(
                            "BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );"
                        )
                        includes += [
                            "#include <boost/type_traits/is_same.hpp>"
                        ]

                sub_template = sub_template.replace("$TYPES",
                                                    ", ".join(typename_list))
                sub_template = sub_template.replace("template<  >\n", "")
                sub_template = sub_template.replace("$LEVEL0",
                                                    ", ".join(arg_list))
                sub_template = sub_template.replace("$CALL_BLAS_HEADER",
                                                    ", ".join(blas_arg_list))
                sub_template = sub_template.replace("$CALL_CBLAS_HEADER",
                                                    ", ".join(cblas_arg_list))
                sub_template = sub_template.replace("$SUBROUTINE", subroutine)
                sub_template = sub_template.replace("$SPECIALIZATION",
                                                    subroutine_value_type)
                sub_template = sub_template.replace(
                    '$STATIC_ASSERTS', "\n    ".join(level0_static_asserts))

                if select_backend == 'blas_overloads':
                    sub_template = sub_template.replace(
                        '$LIBRARY_INT_TYPE', "fortran_int_t")
                else:
                    # the C integer-type defaults to 'int'
                    sub_template = sub_template.replace(
                        '$LIBRARY_INT_TYPE', "int")

                # CBLAS stuff
                if 'cblas_routine' in info_map[subroutine]:
                    cblas_routine = info_map[subroutine]['cblas_routine']
                else:
                    cblas_routine = '// NOT FOUND'
                sub_template = sub_template.replace("$CBLAS_ROUTINE",
                                                    cblas_routine)

                # CUBLAS stuff
                if 'cublas_routine' in info_map[subroutine]:
                    cublas_routine = info_map[subroutine]['cublas_routine']
                    for arg in info_map[subroutine]['arguments']:
                        cublas_arg_list += [
                            info_map[subroutine]['argument_map'][arg]['code']
                            ['call_cublas_header']
                        ]
                else:
                    cublas_routine = '// NOT FOUND'

                #
                # Count potentially unused arguments. If the count is 1; it is only present in the
                # parameter list. In that case, the argument may be removed from the code
                #
                print "Check for unused:", check_for_unused
                for parameter in check_for_unused:
                    if sub_template.count(parameter) == 1:
                        sub_template = sub_template.replace(
                            ' ' + parameter, '')

                sub_template = sub_template.replace("$CALL_CUBLAS_HEADER",
                                                    ", ".join(cublas_arg_list))
                sub_template = sub_template.replace("$CUBLAS_ROUTINE",
                                                    cublas_routine)

                sub_template = sub_template.replace('$groupname',
                                                    group_name.lower())
                sub_template = sub_template.replace(
                    '$RESULT_TYPE', info_map[subroutine]['return_value_type'])
                sub_template = sub_template.replace(
                    '$RETURN_STATEMENT',
                    info_map[subroutine]['return_statement'])
                sub_template = bindings.search_replace(
                    sub_template, group_name_l + '.level0.gsub', template_map)

                sub_overloads += bindings.proper_indent(sub_template)

            overloads = overloads.replace('$' + select_backend.upper(),
                                          sub_overloads)

        #
        # Prepare for levels 1 and 2
        #
        cases = {}
        # first, see what kind of functions we have
        # needed for argument check etc.
        for subroutine in subroutines:
            if info_map[subroutine]['value_type'] == 'real':
                if not cases.has_key('real'):
                    cases['real'] = {}
                    cases['real']['subroutines'] = []
                cases['real']['subroutines'] += [subroutine]
            if info_map[subroutine]['value_type'][:7] == 'complex':
                if not cases.has_key('complex'):
                    cases['complex'] = {}
                    cases['complex']['subroutines'] = []
                cases['complex']['subroutines'] += [subroutine]

        #
        # LEVEL 1 and 2 HANDLING
        #
        level1_map = {}
        level2_map = {}
        for value_type, case_map in cases.iteritems():
            level1_template = ''
            level2_template = ''
            level1_template = template_map['blas_level1']
            level2_template = template_map['blas_level2']

            subroutine = case_map['subroutines'][0]
            group_name_l = info_map[subroutine]['group_name'].lower()

            # take this subroutine for arguments etc.
            # (last entry -> complex version if available, which can be important)
            subroutine = subroutines[-1]

            # include templates come before anything else; they can hold any
            # $ID
            my_include_key = group_name_l + '.' + value_type + '.include_templates'
            if netlib.my_has_key(my_include_key, template_map):
                include_template_list = template_map[netlib.my_has_key(
                    my_include_key,
                    template_map)].strip().replace(' ', '').split(",")
                include_templates = ''
                for template in include_template_list:
                    include_templates += template_map['template_' + template]
                level1_template = level1_template.replace(
                    '$INCLUDE_TEMPLATES',
                    bindings.proper_indent(include_templates))
                level2_template = level2_template.replace(
                    '$INCLUDE_TEMPLATES',
                    bindings.proper_indent(include_templates))
            else:
                level1_template = level1_template.replace(
                    '\n$INCLUDE_TEMPLATES', '')
                level2_template = level2_template.replace(
                    '\n$INCLUDE_TEMPLATES', '')

            level0_arg_list = []
            level1_arg_list = []
            level2_arg_list = []
            call_level1_arg_list = []
            level1_type_arg_list = []
            level1_assert_list = []
            level1_static_assert_list = []
            keyword_type_list = []
            typedef_list = []

            #
            # Are we dealing with a transpose option here?
            # Because CBLAS allows to pass the order of the matrices, here we
            # inject code that determines the default data order.
            #
            if 'matrix' in info_map[subroutine]['grouped_arguments'][
                    'by_type']:
                has_trans = False
                matrix_wo_trans = []
                matrix_with_trans = []
                for matrix_arg in info_map[subroutine]['grouped_arguments'][
                        'by_type']['matrix']:
                    if 'ref_trans' in info_map[subroutine]['argument_map'][
                            matrix_arg]:
                        has_trans = True
                        matrix_type = info_map[subroutine]['argument_map'][
                            matrix_arg]['code']['level_1_static_assert']
                        matrix_with_trans += [matrix_type]
                    else:
                        matrix_wo_trans.append(
                            info_map[subroutine]['argument_map'][matrix_arg]
                            ['code']['level_1_static_assert'])

                #
                # Matrices have trans options in this case. If there is one without,
                # that one will determine the order of the call
                #
                if has_trans:
                    includes += [
                        '#include <boost/numeric/bindings/trans_tag.hpp>'
                    ]
                    if len(matrix_wo_trans) > 0:
                        typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \
                            ' >::type order;' )
                        includes += [
                            '#include <boost/numeric/bindings/data_order.hpp>'
                        ]
                    else:
                        typedef_list.insert( 0, 'typedef typename detail::default_order< ' + matrix_with_trans[0] + \
                            ' >::type order;' )
                        includes += [
                            '#include <boost/numeric/bindings/blas/detail/default_order.hpp>'
                        ]
                else:
                    # so, there's no trans option
                    # but, what if there's an order? (e.g., syr) -- then use `
                    if "has_cblas_order_arg" in info_map[subroutine]:
                        typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \
                          ' >::type order;' )
                        includes += [
                            '#include <boost/numeric/bindings/data_order.hpp>'
                        ]

            #
            # Add an include in case of the uplo or diag options
            #
            if 'UPLO' in info_map[subroutine]['arguments']:
                includes += ['#include <boost/numeric/bindings/uplo_tag.hpp>']
            if 'DIAG' in info_map[subroutine]['arguments']:
                includes += ['#include <boost/numeric/bindings/diag_tag.hpp>']

            #
            # Create static assertions, first by value type
            #
            has_comment = False
            for value_type_tmp_key in info_map[subroutine][
                    'grouped_arguments']['by_value_type'].keys():
                # look up whether they are template params
                static_asserts = []
                for arg in info_map[subroutine]['grouped_arguments'][
                        'by_value_type'][value_type_tmp_key]:
                    if info_map[subroutine]['argument_map'][arg]['code'][
                            'level_1_type'] != None:
                        static_asserts.append(arg)
                if len(static_asserts) > 1:
                    arg_A = static_asserts[0]
                    for arg_B in static_asserts[1:]:
                        print "Adding static assert for argA", arg_A, " argb", arg_B
                        arg_left = info_map[subroutine]['argument_map'][arg_A][
                            'code']['level_1_static_assert']
                        arg_right = info_map[subroutine]['argument_map'][
                            arg_B]['code']['level_1_static_assert']
                        if arg_left != None and arg_right != None:
                            assert_line = 'BOOST_STATIC_ASSERT( (is_same< ' + \
                                'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_left + ' >::type >::type, ' + \
                                'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_right + ' >::type >::type' \
                                ' >::value) );'
                            if not has_comment:
                                #level1_static_assert_list += [ '// Here, we assert... ' ]
                                has_comment = True
                            level1_static_assert_list += [assert_line]

            #
            # Matrices should adhere to their storage scheme
            #
            if 'matrix' in info_map[subroutine]['grouped_arguments'][
                    'by_type']:
                for matrix_id in info_map[subroutine]['grouped_arguments'][
                        'by_type']['matrix']:
                    info_map_item = info_map[subroutine]['argument_map'][
                        matrix_id]
                    if 'banded' in info_map_item and info_map_item[ 'banded' ] == True and \
                              info_map_item[ 'code' ][ 'level_1_type' ] != None:
                        assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_band_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                          ' >::value) );'
                        level1_static_assert_list += [assert_line]
                        includes += [
                            "#include <boost/numeric/bindings/has_band_array.hpp>"
                        ]
                    elif 'packed' in info_map_item and info_map_item[ 'packed' ] == True and \
                              info_map_item[ 'code' ][ 'level_1_type' ] != None:
                        assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_triangular_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                          ' >::value) );'
                        level1_static_assert_list += [assert_line]
                        includes += [
                            "#include <boost/numeric/bindings/has_triangular_array.hpp>"
                        ]
                    elif info_map_item['code']['level_1_type'] != None:
                        assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                          ' >::value) );'
                        level1_static_assert_list += [assert_line]
                        includes += [
                            "#include <boost/numeric/bindings/has_linear_array.hpp>"
                        ]

            #
            # Vectors should have linear arrays
            #
            if 'vector' in info_map[subroutine]['grouped_arguments'][
                    'by_type']:
                for vector_id in info_map[subroutine]['grouped_arguments'][
                        'by_type']['vector']:
                    info_map_item = info_map[subroutine]['argument_map'][
                        vector_id]
                    if 'ref_stride' in info_map_item and info_map_item['code'][
                            'level_1_type'] != None:
                        assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                          ' >::value) );'
                        level1_static_assert_list += [assert_line]
                        includes += [
                            "#include <boost/numeric/bindings/has_linear_array.hpp>"
                        ]

            # Make sure the mutable stuff is mutable
            if 'output' in info_map[subroutine]['grouped_arguments']['by_io']:
                for arg in info_map[subroutine]['grouped_arguments']['by_io'][
                        'output']:
                    if info_map[subroutine]['argument_map'][arg]['code'][
                            'level_1_type'] != None:
                        assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEis_mutable< ' + \
                            info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );'
                        level1_static_assert_list += [assert_line]

            # import the code by argument
            for arg in info_map[subroutine]['arguments']:
                level0_arg_list += [
                    info_map[subroutine]['argument_map'][arg]['code']
                    ['call_level_0']
                ]
                if info_map[subroutine]['argument_map'][arg]['code'][
                        'level_1'] != None:
                    level1_arg_list += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['level_1']
                    ]
                if info_map[subroutine]['argument_map'][arg]['code'][
                        'call_level_1'] != None:
                    call_level1_arg_list += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['call_level_1']
                    ]
                if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \
                  info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] not in level1_type_arg_list:
                    level1_type_arg_list += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['level_1_type']
                    ]
                if info_map[subroutine]['argument_map'][arg]['code'][
                        'level_1_assert'] != []:
                    level1_assert_list += info_map[subroutine]['argument_map'][
                        arg]['code']['level_1_assert']
                if info_map[subroutine]['argument_map'][arg]['code'][
                        'typedef'] != None:
                    # make sure trans tags always preceed other tags, as they may be dependant
                    if 'TRANS' in arg:
                        at_i = 0
                        if len(typedef_list
                               ) > 0 and '_order<' in typedef_list[0]:
                            at_i = 1
                        typedef_list.insert(
                            at_i, info_map[subroutine]['argument_map'][arg]
                            ['code']['typedef'])
                    else:
                        typedef_list.append(
                            info_map[subroutine]['argument_map'][arg]['code']
                            ['typedef'])
                if info_map[subroutine]['argument_map'][arg]['code'][
                        'level_2'] != None:
                    level2_arg_list += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['level_2']
                    ]
                if 'banded' in info_map[subroutine]['argument_map'][arg]:
                    includes += [
                        '#include <boost/numeric/bindings/bandwidth.hpp>'
                    ]

            # Insert the order_type() if appropriate
            if "has_cblas_order_arg" in info_map[ subroutine ] and \
                   info_map[ subroutine ][ 'has_cblas_order_arg' ]:
                level0_arg_list.insert(0, "order()")

            # Level 1 replacements
            level1_template = level1_template.replace(
                "$TYPEDEFS", "\n        ".join(typedef_list))
            level1_template = level1_template.replace(
                "$CALL_LEVEL0", ", ".join(level0_arg_list))
            level1_template = level1_template.replace(
                "$CALL_LEVEL1", ", ".join(call_level1_arg_list))
            level1_template = level1_template.replace(
                "$LEVEL1", ", ".join(level1_arg_list))
            level1_template = level1_template.replace(
                "$TYPES", ", ".join(level1_type_arg_list))
            level1_template = level1_template.replace(
                "$ASSERTS", "\n        ".join(sorted(level1_assert_list)))
            level1_template = level1_template.replace(
                '$RESULT_TYPE', info_map[subroutine]['level1_result_type'])
            level1_template = level1_template.replace(
                '$RETURN_STATEMENT', info_map[subroutine]['return_statement'])
            level1_template = level1_template.replace(
                "$KEYWORDS", ", ".join(keyword_type_list))

            if len(level1_static_assert_list) > 0:
                level1_template = level1_template.replace(
                    "$STATIC_ASSERTS",
                    "\n        ".join(level1_static_assert_list))
            else:
                level1_template = level1_template.replace(
                    "\n        $STATIC_ASSERTS", "")

            # Level 2 replacements
            # some special stuff is done here, such as replacing real_type with a
            # type-traits deduction, etc..
            # more important: all non-const and const variants of functions are written here
            level2_functions = []
            level2_arg_lists, level2_comments = \
                  bindings.generate_const_variants( group_name.lower() + '.' + value_type, \
                      level2_arg_list, template_map )
            for level2_idx in range(0, len(level2_arg_lists)):
                level2_function = level2_template.replace( "$LEVEL2", \
                        ", ".join( level2_arg_lists[ level2_idx ] ) )
                if len("".join(level2_comments[level2_idx])) > 0:
                    level2_function = level2_function.replace( "$COMMENTS", \
                          "\n".join( level2_comments[ level2_idx ] ) )
                level2_functions.append(level2_function)

            level2_template = "\n".join(level2_functions)
            level2_template = level2_template.replace("$COMMENTS\n", "")

            #level2_template = level2_template.replace( "$LEVEL2", ", ".join( level2_arg_list ) )

            if len(level1_type_arg_list) > 0:
                my_key = group_name_l + '.' + value_type + '.first_typename'
                if netlib.my_has_key(my_key, template_map):
                    first_typename = template_map[ netlib.my_has_key( \
                        my_key, template_map ) ].strip()
                else:
                    first_typename = ''
                    for tn in level1_type_arg_list:
                        bare_type = tn.split(" ")[-1]
                        if first_typename == '' and bare_type[:6].lower() in [
                                'matrix', 'vector'
                        ]:
                            first_typename = bare_type
                first_typename_datatype = first_typename[:6].lower(
                )  # 'matrix' or 'vector' or 'scalar'
            else:
                level1_type_arg_list.insert(0, 'typename Value')
                first_typename = 'Value'
                first_typename_datatype = 'typename Value'

            level2_template = level2_template.replace("$FIRST_TYPENAME",
                                                      first_typename)
            level2_template = level2_template.replace("$TYPEOF_FIRST_TYPENAME",
                                                      first_typename_datatype)
            level2_template = level2_template.replace(
                "$CALL_LEVEL1", ", ".join(call_level1_arg_list))
            level2_template = level2_template.replace(
                "$TYPES", ", ".join(level1_type_arg_list))
            level2_template = level2_template.replace(
                '$RETURN_STATEMENT', info_map[subroutine]['return_statement'])
            level2_template = level2_template.replace('    $STATIC_ASSERTS\n',
                                                      '')

            if first_typename == 'Value':
                level2_template = level2_template.replace(
                    'typename $NAMESPACEvalue_type< Value >::type', 'Value')
                level2_template = level2_template.replace(
                    'typename remove_imaginary< Value >::type', 'Value')

            level1_map[value_type] = bindings.proper_indent(level1_template)
            level2_map[value_type] = bindings.proper_indent(level2_template)

        #
        # LEVEL 1 and 2 FINALIZATION
        #
        for mapping in [level1_map, level2_map]:
            if len(mapping) > 1:
                # compare real and complex cases
                all_keys = mapping.keys()
                if mapping[all_keys[0]] == mapping[all_keys[1]]:
                    print "literally everything is the same!!, falling back to 1 case"
                    del mapping[all_keys[1]]

        level1 = ''
        for value_type in level1_map.keys():
            level1 += level1_map[value_type]

        level2 = ''
        for value_type in level2_map.keys():
            level2 += level2_map[value_type]

        #
        # handle addition of includes
        #
        includes_code = ''
        unique_includes = []
        for include in includes:
            if include not in unique_includes:
                unique_includes += [include]
        sorted_includes = sorted(unique_includes,
                                 lambda x, y: cmp(x.lower(), y.lower()))
        if len(sorted_includes) > 0:
            includes_code = "\n".join(sorted_includes)

        result = template_map['blas.hpp']
        result = result.replace('$INCLUDES', includes_code)
        result = result.replace('$OVERLOADS', overloads)
        result = result.replace('$LEVEL1', level1)
        result = result.replace('$LEVEL2', level2)
        result = result.replace('$GROUPNAME', group_name)
        result = result.replace('$groupname', group_name.lower())
        result = result.replace('$DIRNAME', base_dir.split("/")[-1].upper())
        result = result.replace('$dirname', base_dir.split("/")[-1].lower())
        result = result.replace('$INTEGER_TYPE', netlib.generic_integer_type)
        result = result.replace('$LIBRARY_INT_TYPE',
                                netlib.generic_integer_type)
        result = result.replace('$NAMESPACE', "bindings::")
        result = result.replace('template<  >', '')
        result = result.replace('\n\n\n', '\n\n')
        result = result.replace("\n    \n", "\n")
        result = result.replace('\n        \n', '\n')
        result = result.replace('\n        \n', '\n')

        # replace the global variables as last (this is convenient)
        #result = result.replace( '$INDENT', '    ' )
        #result = result.replace( '$groupname', group_name.lower() )
        #result = result.replace( '$DESCRIPTION', info_map[ group[g][0] ][ 'description' ] )

        open(os.path.join(base_dir, filename), 'w').write(result)
def write_documentation( info_map, level, group, template_map, base_dir ):

    parsermode = template_map[ 'PARSERMODE' ].lower()

    for group_name, subroutines in group.iteritems():
        filename = group_name.lower() + '.qbk'

        result = template_map[ parsermode + '.qbk' ]

        prototypes = []
        first_typename = None
        originating_sources = []
        no_arguments_level0 = []
        no_arguments_level2 = []

        for subroutine in subroutines:
            if parsermode == 'blas':
                originating_sources.append( "[@http://www.netlib.org/blas/" +
                    subroutine.lower() + ".f " + subroutine.lower() + ".f]" )
            if parsermode == 'lapack':
                originating_sources.append( "[@http://www.netlib.org/lapack/" +
                    lapack_value_type[subroutine[0]] + "/" +
                    subroutine.lower() + ".f " + subroutine.lower() + ".f]" )

            level2_arg_list_pre = []
            for arg in info_map[ subroutine ][ 'arguments' ]:
                if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] != None:
                    level2_arg_list_pre += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] ]

            # Keep track of the numbers of arguments
            if len( info_map[ subroutine ][ 'arguments' ] ) not in no_arguments_level0:
                no_arguments_level0.append( len( info_map[ subroutine ][ 'arguments' ] ) )

            if len( level2_arg_list_pre ) not in no_arguments_level2:
                no_arguments_level2.append( len( level2_arg_list_pre ) )

            level2_arg_list = []
            for arg in level2_arg_list_pre:
                readable_arg = ''
                if '::real_type' in arg:
                    splitted = arg.split( "::real_type" )
                    readable_arg = "Scalar" + splitted[1]
                    if 'const' in arg:
                        readable_arg = 'const ' + readable_arg
                elif '::type' in arg:
                    splitted = arg.split( "::type" )
                    readable_arg = "Scalar" + splitted[1]
                    if 'const' in arg:
                        readable_arg = 'const ' + readable_arg
                else:
                    readable_arg = arg

                if (first_typename == None ) and ('Vector' in arg or 'Matrix' in arg):
                    first_typename = arg.replace( "const ", "" ).replace( "&", "" ).split(" ")[0]

                level2_arg_list.append( readable_arg )


            prototype = template_map[ parsermode + '_prototype.qbk' ]
            prototype = prototype.replace( "$LEVEL2", ", ".join( level2_arg_list ) )

            if prototype not in prototypes:
                prototypes.append( prototype )

        prototype_overloads = ''
        if len(prototypes) == 1:
            prototype_overloads = 'is one prototype of'
        else:
            prototype_overloads = 'are ' + number_to_text[ len(prototypes) ] + ' prototypes of'

        prototypes = bindings.proper_indent( "\n".join( prototypes ) )

        #prototypes.append( prototype )
        result = result.replace( "$PROTOTYPES", prototypes )
        result = result.replace( "$PROTOTYPE_OVERLOADS", prototype_overloads )
        result = result.replace( "$ORIGINATING_SOURCES", readable_join( originating_sources ) )
        result = result.replace( '$PURPOSE', combine_purposes( subroutines, info_map ) )
        result = result.replace( '$LIBRARY_INT_TYPE', "int_t" )

        result = result.replace( '$SUBROUTINES', readable_join( subroutines ) )
        result = result.replace( '$header', 'boost/numeric/bindings/' + parsermode + '/' + level + '/' + group_name.lower() + '.hpp' )

        if len( no_arguments_level0 ) == 1:
            result = result.replace( '$NO_ARGUMENTS_LEVEL0', number_to_text[ no_arguments_level0[0] ] )
        else:
            result = result.replace( '$NO_ARGUMENTS_LEVEL0', "TODO" )

        if len( no_arguments_level2 ) == 1:
            result = result.replace( '$NO_ARGUMENTS_LEVEL2', number_to_text[ no_arguments_level2[0] ] )
        else:
            result = result.replace( '$NO_ARGUMENTS_LEVEL2', "TODO" )

        if parsermode == 'blas':
            result = result.replace( '$DISPATCH_TABLE', write_blas_dispatch_table( subroutines, info_map ) )
            result = result.replace( '$FRIENDLY_NAME', blas_friendly_name( group_name, info_map, template_map ) )
        if parsermode == 'lapack':
            result = result.replace( '$DISPATCH_TABLE', write_lapack_dispatch_table( subroutines, info_map ) )
            #result = result.replace( '$FRIENDLY_NAME', lapack_friendly_name( group_name, info_map, template_map ) )

        if first_typename != None:
            result = result.replace( "$FIRST_TYPENAME", first_typename )
        result = result.replace( '$GROUPNAME', group_name )
        result = result.replace( '$groupname', group_name.lower() )
        print "Writing " + base_dir + "/" + filename
        open( os.path.join( base_dir, filename ), 'w' ).write( result )
Exemplo n.º 3
0
def write_functions( info_map, group, template_map, base_dir ):
  #
  # group.keys() is a vector of different grouped function names
  # like gees, dgesv, etc.
  #
  for group_name, subroutines in group.iteritems():

    filename = group_name.lower() + '.hpp'
    includes = [
      #'#include <boost/numeric/bindings/detail/void_ptr.hpp>',
      #'#include <boost/numeric/bindings/traits/traits.hpp>',
      #'#include <boost/numeric/bindings/traits/type_traits.hpp>', 
      '#include <boost/numeric/bindings/remove_imaginary.hpp>', 
      '#include <boost/numeric/bindings/is_mutable.hpp>', 
      '#include <boost/numeric/bindings/value_type.hpp>', 
      '#include <boost/numeric/bindings/stride.hpp>',
      '#include <boost/numeric/bindings/size.hpp>',
      '#include <boost/numeric/bindings/begin.hpp>',
      #'#include <boost/mpl/bool.hpp>',
      '#include <boost/type_traits/is_same.hpp>',
      '#include <boost/type_traits/remove_const.hpp>',
      '#include <boost/static_assert.hpp>',
      '#include <boost/assert.hpp>',
    ]
      
    for subroutine in subroutines:
      group_name_l = info_map[ subroutine ][ 'group_name' ].lower()
      if template_map.has_key( group_name_l + '.includes' ):
        includes += template_map[ group_name_l + '.includes' ].splitlines()

    #
    # LEVEL 0 HANDLING
    #
    overloads = template_map[ 'backend_blas_overloads' ]
    for select_backend in [ 'blas_overloads', 'cblas_overloads', 'cublas_overloads' ]:
      sub_overloads = ''
      for subroutine in subroutines:
        group_name_l = info_map[ subroutine ][ 'group_name' ].lower()

        # stuff like float, complex<double>, etc.
        subroutine_value_type = documentation. \
            subroutine_value_type[ ",".join( [
                info_map[ subroutine ][ 'value_type' ],
                info_map[ subroutine ][ 'precision' ] ] ) ]

        sub_template = template_map[ select_backend ]
        # add the argument list here
        arg_list = []
        typename_list = []
        blas_arg_list = []
        cblas_arg_list = []
        cublas_arg_list = []
        level0_static_asserts = []
        check_for_unused = []

        for arg in info_map[ subroutine ][ 'arguments' ]:
            print "Subroutine ", subroutine, " arg ", arg
            arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] ]
            blas_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_blas_header' ] ]

            #
            # Find potential arguments that may cause warnings because they are not used, and
            # store these in check_for_unused
            #
            if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None and \
               info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None:
                keyword = info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ].split( ' ')[-1]
                check_for_unused += [ keyword ]
            
            if 'call_cblas_header' in info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ]:
                cblas_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_cblas_header' ] ]
            else:
                print "WARNING: couldn't find cblas call."

            if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None:
                typename_list +=  [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] ]

        if "has_cblas_order_arg" in info_map[ subroutine ]:
            check_for_unused.append( 'order' )
            if info_map[ subroutine ][ "has_cblas_order_arg" ] == True:
                arg_list.insert( 0, "const Order order" )
                cblas_arg_list.insert( 0, "cblas_option< Order >::value" )
                typename_list.insert( 0, "typename Order" )
                level0_static_asserts.append( "BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );" )
                includes += [ "#include <boost/type_traits/is_same.hpp>" ]

        sub_template = sub_template.replace( "$TYPES", ", ".join( typename_list ) )
        sub_template = sub_template.replace( "template<  >\n", "" )
        sub_template = sub_template.replace( "$LEVEL0", ", ".join( arg_list ) )
        sub_template = sub_template.replace( "$CALL_BLAS_HEADER", ", ".join( blas_arg_list ) )
        sub_template = sub_template.replace( "$CALL_CBLAS_HEADER", ", ".join( cblas_arg_list ) )
        sub_template = sub_template.replace( "$SUBROUTINE", subroutine )
        sub_template = sub_template.replace( "$SPECIALIZATION", subroutine_value_type )
        sub_template = sub_template.replace( '$STATIC_ASSERTS', "\n    ".join( level0_static_asserts ) )

        if select_backend == 'blas_overloads':
            sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "fortran_int_t" )
        else:
            # the C integer-type defaults to 'int'
            sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "int" )

        # CBLAS stuff
        if 'cblas_routine' in info_map[ subroutine ]:
            cblas_routine = info_map[ subroutine ][ 'cblas_routine' ]
        else:
            cblas_routine = '// NOT FOUND'
        sub_template = sub_template.replace( "$CBLAS_ROUTINE", cblas_routine )

        # CUBLAS stuff
        if 'cublas_routine' in info_map[ subroutine ]:
            cublas_routine = info_map[ subroutine ][ 'cublas_routine' ]
            for arg in info_map[ subroutine ][ 'arguments' ]:
                cublas_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_cublas_header' ] ]
        else:
            cublas_routine = '// NOT FOUND'

        #
        # Count potentially unused arguments. If the count is 1; it is only present in the 
        # parameter list. In that case, the argument may be removed from the code
        # 
        print "Check for unused:" , check_for_unused
        for parameter in check_for_unused:
            if sub_template.count( parameter ) == 1:
                sub_template = sub_template.replace( ' ' + parameter, '' )
            
        sub_template = sub_template.replace( "$CALL_CUBLAS_HEADER", ", ".join( cublas_arg_list ) )
        sub_template = sub_template.replace( "$CUBLAS_ROUTINE", cublas_routine )

        sub_template = sub_template.replace( '$groupname', group_name.lower() )
        sub_template = sub_template.replace( '$RESULT_TYPE', info_map[ subroutine ][ 'return_value_type' ] )
        sub_template = sub_template.replace( '$RETURN_STATEMENT', info_map[ subroutine ][ 'return_statement' ] )
        sub_template = bindings.search_replace( sub_template, group_name_l + '.level0.gsub', template_map )

        sub_overloads += bindings.proper_indent( sub_template )

      overloads = overloads.replace( '$' + select_backend.upper(),
            sub_overloads )

    #
    # Prepare for levels 1 and 2
    #
    cases = {}
    # first, see what kind of functions we have
    # needed for argument check etc.
    for subroutine in subroutines:
      if info_map[ subroutine ][ 'value_type' ] == 'real':
        if not cases.has_key( 'real' ):
          cases[ 'real' ] = {}
          cases[ 'real' ][ 'subroutines' ] = []
        cases[ 'real' ][ 'subroutines' ] += [ subroutine ]
      if info_map[ subroutine ][ 'value_type' ][ :7 ] == 'complex':
        if not cases.has_key( 'complex' ):
          cases[ 'complex' ] = {}
          cases[ 'complex' ][ 'subroutines' ] = []
        cases[ 'complex' ][ 'subroutines' ] += [ subroutine ]


    #
    # LEVEL 1 and 2 HANDLING
    #
    level1_map = {}
    level2_map = {}
    for value_type, case_map in cases.iteritems():
      level1_template = ''
      level2_template = ''
      level1_template = template_map[ 'blas_level1' ]
      level2_template = template_map[ 'blas_level2' ]

      subroutine = case_map[ 'subroutines' ][ 0 ]
      group_name_l = info_map[ subroutine ][ 'group_name' ].lower()

      # take this subroutine for arguments etc.
      # (last entry -> complex version if available, which can be important)
      subroutine = subroutines[-1]

      # include templates come before anything else; they can hold any
      # $ID
      my_include_key = group_name_l + '.' + value_type + '.include_templates'
      if netlib.my_has_key( my_include_key, template_map ):
        include_template_list = template_map[ netlib.my_has_key( my_include_key, template_map ) ].strip().replace(' ','').split(",")
        include_templates = ''
        for template in include_template_list:
          include_templates += template_map[ 'template_' + template ]
        level1_template = level1_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) )
        level2_template = level2_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) )
      else:
        level1_template = level1_template.replace( '\n$INCLUDE_TEMPLATES', '' )
        level2_template = level2_template.replace( '\n$INCLUDE_TEMPLATES', '' )

      level0_arg_list = []
      level1_arg_list = []
      level2_arg_list = []
      call_level1_arg_list = []
      level1_type_arg_list = []
      level1_assert_list = []
      level1_static_assert_list = []
      keyword_type_list = []
      typedef_list = []

      #
      # Are we dealing with a transpose option here?
      # Because CBLAS allows to pass the order of the matrices, here we
      # inject code that determines the default data order.
      #
      if 'matrix' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]:
        has_trans = False
        matrix_wo_trans = []
        matrix_with_trans = []
        for matrix_arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'matrix' ]:
            if 'ref_trans' in info_map[ subroutine ][ 'argument_map' ][ matrix_arg ]:
                has_trans = True
                matrix_type = info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ]
                matrix_with_trans += [ matrix_type ]
            else:
                matrix_wo_trans.append( info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] )

        #
        # Matrices have trans options in this case. If there is one without,
        # that one will determine the order of the call
        #
        if has_trans:
          includes += [ '#include <boost/numeric/bindings/trans_tag.hpp>' ]
          if len( matrix_wo_trans )>0:
            typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \
                ' >::type order;' )
            includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ]
          else:
            typedef_list.insert( 0, 'typedef typename detail::default_order< ' + matrix_with_trans[0] + \
                ' >::type order;' )
            includes += [ '#include <boost/numeric/bindings/blas/detail/default_order.hpp>' ]
        else:
            # so, there's no trans option
            # but, what if there's an order? (e.g., syr) -- then use `
            if "has_cblas_order_arg" in info_map[ subroutine ]:
              typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \
                ' >::type order;' )
              includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ]

      #
      # Add an include in case of the uplo or diag options
      #
      if 'UPLO' in info_map[ subroutine ][ 'arguments' ]:
        includes += [ '#include <boost/numeric/bindings/uplo_tag.hpp>' ]
      if 'DIAG' in info_map[ subroutine ][ 'arguments' ]:
        includes += [ '#include <boost/numeric/bindings/diag_tag.hpp>' ]

      #
      # Create static assertions, first by value type
      #
      has_comment = False
      for value_type_tmp_key in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ].keys():
        # look up whether they are template params
        static_asserts = []
        for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ][ value_type_tmp_key ]:
          if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None:
            static_asserts.append( arg )
        if len(static_asserts)>1:
          arg_A = static_asserts[0]
          for arg_B in static_asserts[1:]:
            print "Adding static assert for argA", arg_A, " argb", arg_B
            arg_left = info_map[ subroutine ][ 'argument_map' ][ arg_A ][ 'code' ][ 'level_1_static_assert' ]
            arg_right = info_map[ subroutine ][ 'argument_map' ][ arg_B ][ 'code' ][ 'level_1_static_assert' ]
            if arg_left != None and arg_right != None:
                assert_line = 'BOOST_STATIC_ASSERT( (is_same< ' + \
                    'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_left + ' >::type >::type, ' + \
                    'typename remove_const< typename $NAMESPACEvalue_type< ' + arg_right + ' >::type >::type' \
                    ' >::value) );'
                if not has_comment:
                    #level1_static_assert_list += [ '// Here, we assert... ' ]
                    has_comment = True
                level1_static_assert_list += [ assert_line ]

      #
      # Matrices should adhere to their storage scheme
      #
      if 'matrix' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]:
          for matrix_id in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'matrix' ]:
              info_map_item = info_map[ subroutine ][ 'argument_map' ][ matrix_id ]
              if 'banded' in info_map_item and info_map_item[ 'banded' ] == True and \
                        info_map_item[ 'code' ][ 'level_1_type' ] != None:
                  assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_band_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                    ' >::value) );'
                  level1_static_assert_list += [ assert_line ]
                  includes += [ "#include <boost/numeric/bindings/has_band_array.hpp>" ]
              elif 'packed' in info_map_item and info_map_item[ 'packed' ] == True and \
                        info_map_item[ 'code' ][ 'level_1_type' ] != None:
                  assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_triangular_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                    ' >::value) );'
                  level1_static_assert_list += [ assert_line ]
                  includes += [ "#include <boost/numeric/bindings/has_triangular_array.hpp>" ]
              elif info_map_item[ 'code' ][ 'level_1_type' ] != None:
                  assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                    ' >::value) );'
                  level1_static_assert_list += [ assert_line ]
                  includes += [ "#include <boost/numeric/bindings/has_linear_array.hpp>" ]
                  
      #
      # Vectors should have linear arrays
      #
      if 'vector' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]:
          for vector_id in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'vector' ]:
              info_map_item = info_map[ subroutine ][ 'argument_map' ][ vector_id ]
              if 'ref_stride' in info_map_item and info_map_item[ 'code' ][ 'level_1_type' ] != None:
                  assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEhas_linear_array< ' + info_map_item[ 'code' ][ 'level_1_static_assert' ] + \
                    ' >::value) );'
                  level1_static_assert_list += [ assert_line ]
                  includes += [ "#include <boost/numeric/bindings/has_linear_array.hpp>" ]
                
      # Make sure the mutable stuff is mutable
      if 'output' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ]:
        for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'output' ]:
          if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None:
            assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEis_mutable< ' + \
                info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );'
            level1_static_assert_list += [ assert_line ]

      # import the code by argument
      for arg in info_map[ subroutine ][ 'arguments' ]:
        level0_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_0' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] != None:
          level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] != None:
          call_level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \
          info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] not in level1_type_arg_list:
          level1_type_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ] != []:
          level1_assert_list += info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] != None:
          # make sure trans tags always preceed other tags, as they may be dependant
          if 'TRANS' in arg:
              at_i = 0
              if len(typedef_list)>0 and '_order<' in typedef_list[0]:
                at_i = 1
              typedef_list.insert( at_i, info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] )
          else:
              typedef_list.append( info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] )
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] != None:
          level2_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] ]
        if 'banded' in info_map[ subroutine ][ 'argument_map' ][ arg ]:
            includes += [ '#include <boost/numeric/bindings/bandwidth.hpp>' ]

      # Insert the order_type() if appropriate
      if "has_cblas_order_arg" in info_map[ subroutine ] and \
             info_map[ subroutine ][ 'has_cblas_order_arg' ]:
          level0_arg_list.insert( 0, "order()" )

      # Level 1 replacements
      level1_template = level1_template.replace( "$TYPEDEFS", "\n        ".join( typedef_list ) )
      level1_template = level1_template.replace( "$CALL_LEVEL0", ", ".join( level0_arg_list ) )
      level1_template = level1_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) )
      level1_template = level1_template.replace( "$LEVEL1", ", ".join( level1_arg_list ) )
      level1_template = level1_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) )
      level1_template = level1_template.replace( "$ASSERTS", "\n        ".join( sorted( level1_assert_list ) ) )
      level1_template = level1_template.replace( '$RESULT_TYPE', info_map[ subroutine ][ 'level1_result_type' ] )
      level1_template = level1_template.replace( '$RETURN_STATEMENT', info_map[ subroutine ][ 'return_statement' ] )
      level1_template = level1_template.replace( "$KEYWORDS", ", ".join( keyword_type_list ) )

      if len( level1_static_assert_list ) > 0:
        level1_template = level1_template.replace( "$STATIC_ASSERTS", "\n        ".join( level1_static_assert_list ) )
      else:
        level1_template = level1_template.replace( "\n        $STATIC_ASSERTS", "" )

      # Level 2 replacements
      # some special stuff is done here, such as replacing real_type with a 
      # type-traits deduction, etc..
      # more important: all non-const and const variants of functions are written here
      level2_functions = []
      level2_arg_lists, level2_comments = \
            bindings.generate_const_variants( group_name.lower() + '.' + value_type, \
                level2_arg_list, template_map )
      for level2_idx in range( 0, len( level2_arg_lists ) ):
        level2_function = level2_template.replace( "$LEVEL2", \
                ", ".join( level2_arg_lists[ level2_idx ] ) )
        if len( "".join(level2_comments[ level2_idx ] ) ) > 0:
          level2_function = level2_function.replace( "$COMMENTS", \
                "\n".join( level2_comments[ level2_idx ] ) )
        level2_functions.append( level2_function )

      level2_template = "\n".join( level2_functions )
      level2_template = level2_template.replace( "$COMMENTS\n", "" )

      #level2_template = level2_template.replace( "$LEVEL2", ", ".join( level2_arg_list ) )

      if len(level1_type_arg_list)>0:
        my_key = group_name_l + '.' + value_type + '.first_typename'
        if netlib.my_has_key( my_key, template_map ):
            first_typename = template_map[ netlib.my_has_key( \
                my_key, template_map ) ].strip()
        else:
            first_typename = ''
            for tn in level1_type_arg_list:
                bare_type = tn.split(" ")[-1]
                if first_typename == '' and bare_type[:6].lower() in [ 'matrix', 'vector' ]:
                    first_typename = bare_type
        first_typename_datatype = first_typename[:6].lower() # 'matrix' or 'vector' or 'scalar'
      else:
        level1_type_arg_list.insert( 0, 'typename Value' )
        first_typename = 'Value'
        first_typename_datatype = 'typename Value'


      level2_template = level2_template.replace( "$FIRST_TYPENAME", first_typename )
      level2_template = level2_template.replace( "$TYPEOF_FIRST_TYPENAME", first_typename_datatype )
      level2_template = level2_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) )
      level2_template = level2_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) )
      level2_template = level2_template.replace( '$RETURN_STATEMENT', info_map[ subroutine ][ 'return_statement' ] )
      level2_template = level2_template.replace( '    $STATIC_ASSERTS\n', '' )

      if first_typename == 'Value':
          level2_template = level2_template.replace( 'typename $NAMESPACEvalue_type< Value >::type', 'Value' )
          level2_template = level2_template.replace( 'typename remove_imaginary< Value >::type', 'Value' )

      level1_map[ value_type ] = bindings.proper_indent( level1_template )
      level2_map[ value_type ] = bindings.proper_indent( level2_template )


    #
    # LEVEL 1 and 2 FINALIZATION
    #
    for mapping in [ level1_map, level2_map ]:
      if len(mapping) > 1:
        # compare real and complex cases
        all_keys = mapping.keys()
        if mapping[ all_keys[0] ] == mapping[ all_keys[1] ]:
          print "literally everything is the same!!, falling back to 1 case"
          del mapping[ all_keys[ 1 ] ]

    level1 = ''
    for value_type in level1_map.keys():
      level1 += level1_map[ value_type ]

    level2 = ''
    for value_type in level2_map.keys():
      level2 += level2_map[ value_type ]

    #
    # handle addition of includes
    #
    includes_code = ''
    unique_includes = []
    for include in includes:
      if include not in unique_includes:
        unique_includes += [ include ]
    sorted_includes = sorted( unique_includes, lambda x, y: cmp( x.lower(), y.lower() ) )
    if len( sorted_includes ) > 0:
      includes_code = "\n".join( sorted_includes )

    result = template_map[ 'blas.hpp' ]
    result = result.replace( '$INCLUDES', includes_code )
    result = result.replace( '$OVERLOADS', overloads )
    result = result.replace( '$LEVEL1', level1 )
    result = result.replace( '$LEVEL2', level2 )
    result = result.replace( '$GROUPNAME', group_name )
    result = result.replace( '$groupname', group_name.lower() )
    result = result.replace( '$DIRNAME', base_dir.split("/")[-1].upper() )
    result = result.replace( '$dirname', base_dir.split("/")[-1].lower() )
    result = result.replace( '$INTEGER_TYPE', netlib.generic_integer_type )
    result = result.replace( '$LIBRARY_INT_TYPE', netlib.generic_integer_type )
    result = result.replace( '$NAMESPACE', "bindings::" )
    result = result.replace( 'template<  >', '' )
    result = result.replace( '\n\n\n', '\n\n' )
    result = result.replace( "\n    \n", "\n" )
    result = result.replace( '\n        \n', '\n' )
    result = result.replace( '\n        \n', '\n' )

    # replace the global variables as last (this is convenient)
    #result = result.replace( '$INDENT', '    ' )
    #result = result.replace( '$groupname', group_name.lower() )
    #result = result.replace( '$DESCRIPTION', info_map[ group[g][0] ][ 'description' ] )

    open( os.path.join( base_dir, filename ), 'w' ).write( result )
def write_functions( info_map, group, template_map, base_dir ):
  #
  # group.keys() is a vector of different grouped function names
  # like gees, dgesv, etc.
  #
  for group_name, subroutines in group.iteritems():
    
    filename = group_name.lower() + '.hpp'
    includes = [ '#include <boost/assert.hpp>',
      '#include <boost/numeric/bindings/remove_imaginary.hpp>',
      '#include <boost/numeric/bindings/value_type.hpp>',
      '#include <boost/numeric/bindings/begin.hpp>',
      '#include <boost/numeric/bindings/size.hpp>',
      #'#include <boost/numeric/bindings/tag.hpp>',
      '#include <boost/numeric/bindings/stride.hpp>',
      '#include <boost/numeric/bindings/is_mutable.hpp>',
      #'#include <boost/numeric/bindings/traits/traits.hpp>',
      #'#include <boost/numeric/bindings/traits/type_traits.hpp>', 
      #'#include <boost/mpl/bool.hpp>',
      '#include <boost/type_traits/is_same.hpp>',
      '#include <boost/type_traits/remove_const.hpp>',
      '#include <boost/static_assert.hpp>' ]

    for subroutine in subroutines:
      group_name_l = info_map[ subroutine ][ 'group_name' ].lower()
      if template_map.has_key( group_name_l + '.includes' ):
        includes += template_map[ group_name_l + '.includes' ].splitlines()

    #
    #
    # LEVEL 0 HANDLING
    #
    #
    # If the first subroutine has a clapack_ routine, assume we are going
    # to provide specialisations
    #
    provide_clapack_backend = 'clapack_routine' in info_map[ subroutines[0] ]
    overloads = ''
    backend_includes = ''
    if provide_clapack_backend:
        overloads = template_map[ 'backend_lapack_with_clapack' ]
        backend_includes = template_map[ 'lapack_backend_includes_with_clapack' ]
    else:
        overloads = template_map[ 'backend_lapack_default' ]
        backend_includes = template_map[ 'lapack_backend_includes_default' ]

    for select_backend in [ 'lapack_overloads', 'clapack_overloads' ]:

      sub_overloads = ''

      for subroutine in subroutines:

        sub_template = template_map[ select_backend ]
        have_clapack = 'clapack_routine' in info_map[ subroutine ]
        # add the argument list here
        arg_list = []
        lapack_arg_list = []
        clapack_arg_list = []
        typename_list = []
        level0_static_asserts = []
        check_for_unused = []

        for arg in info_map[ subroutine ][ 'arguments' ]:
            print "Subroutine ", subroutine, " arg ", arg
            if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None:
                arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] ]
            #
            # Find potential arguments that may cause warnings because they are not used, and
            # store these in check_for_unused
            #
            if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ] != None and \
               info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None:
                keyword = info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0' ].split( ' ')[-1]
                check_for_unused += [ keyword ]
            if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_lapack_header' ] != None:
                lapack_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_lapack_header' ] ]
            if have_clapack and info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_clapack_header' ] != None:
                clapack_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_clapack_header' ] ]
            if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] != None:
                typename_list +=  [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_0_typename' ] ]

        if "has_clapack_order_arg" in info_map[ subroutine ]:
            if info_map[ subroutine ][ "has_clapack_order_arg" ] == True:
                arg_list.insert( 0, "Order" )
                clapack_arg_list.insert( 0, "clapack_option< Order >::value" )
                typename_list.insert( 0, "typename Order" )
                level0_static_asserts.append( "BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );" )
                includes += [ "#include <boost/type_traits/is_same.hpp>" ]

        sub_template = sub_template.replace( "$TYPES", ", ".join( typename_list ) )
        sub_template = sub_template.replace( "template<  >\n", "" )
        sub_template = sub_template.replace( "$LEVEL0", ", ".join( arg_list ) )
        sub_template = sub_template.replace( "$CALL_LAPACK_HEADER", ", ".join( lapack_arg_list ) )
        sub_template = sub_template.replace( "$CALL_CLAPACK_HEADER", ", ".join( clapack_arg_list ) )
        sub_template = sub_template.replace( "$SUBROUTINE", subroutine )
        sub_template = sub_template.replace( '$groupname', group_name.lower() )
        sub_template = sub_template.replace( "$SPECIALIZATION", documentation.routine_value_type[ subroutine[0] ] )
        sub_template = sub_template.replace( '$STATIC_ASSERTS', "\n    ".join( level0_static_asserts ) )

        if select_backend == 'lapack_overloads':
            sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "fortran_int_t" )
        else:
            sub_template = sub_template.replace( '$LIBRARY_INT_TYPE', "int" )

        # CLAPACK stuff
        if 'clapack_routine' in info_map[ subroutine ]:
            clapack_routine = info_map[ subroutine ][ 'clapack_routine' ]
        else:
            clapack_routine = '// NOT FOUND'
        sub_template = sub_template.replace( "$CLAPACK_ROUTINE", clapack_routine )
        
        #
        # Count potentially unused arguments. If the count is 1; it is only present in the 
        # parameter list. In that case, the argument may be removed from the code
        # 
        for parameter in check_for_unused:
            if sub_template.count( parameter ) == 1:
                sub_template = sub_template.replace( ' ' + parameter, '' )

        # Finalize this sub_overload
        sub_overloads += bindings.proper_indent( sub_template )

      # fill in for the appropriate back-end
      print "Replacing ", '$' + select_backend.upper()
      overloads = overloads.replace( '$' + select_backend.upper(),
        sub_overloads )

    cases = {}
    # first, see what kind of functions we have
    # needed for argument check etc.
    for subroutine in subroutines:
      if info_map[ subroutine ][ 'value_type' ] == 'real':
        if not cases.has_key( 'real' ):
          cases[ 'real' ] = {}
          cases[ 'real' ][ 'subroutines' ] = []
        cases[ 'real' ][ 'subroutines' ] += [ subroutine ]
      if info_map[ subroutine ][ 'value_type' ] == 'complex':
        if not cases.has_key( 'complex' ):
          cases[ 'complex' ] = {}
          cases[ 'complex' ][ 'subroutines' ] = []
        cases[ 'complex' ][ 'subroutines' ] += [ subroutine ]

    # Figure out what the real/complex type selector argument might be
    type_selector_candidates = []
    if cases.has_key( 'real' ) and cases.has_key( 'complex' ):
      # we have real and complex scenarios, these keys only exist
      # if we also have associated routines
      for arg in info_map[ cases[ 'real' ][ 'subroutines' ][0] ][ 'arguments' ]:
        if arg in info_map[ cases[ 'complex' ][ 'subroutines' ][0] ][ 'arguments' ]:
          if info_map[ cases[ 'real' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \
             info_map[ cases[ 'complex' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \
             info_map[ cases[ 'real' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'value_type_variant' ] == 'real' and \
             info_map[ cases[ 'complex' ][ 'subroutines' ][0] ][ 'argument_map' ][ arg ][ 'value_type_variant' ] == 'complex':
            type_selector_candidates += [ arg ]

    #
    #
    # LEVEL 1 and 2 HANDLING
    #
    #
    level1_map = {}
    level2_map = {}
    for value_type, case_map in cases.iteritems():

      # take this subroutine for arguments etc.
      subroutine = case_map[ 'subroutines' ][ 0 ]
      group_name_l = info_map[ subroutine ][ 'group_name' ].lower()
      print "taking",subroutine

      level1_template = ''
      level2_template = ''
      if info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ].has_key( 'workspace' ):
        level1_template = template_map[ 'level1_workspace' ]
        level2_template = template_map[ 'level2_workspace' ]
      else:
        level1_template = template_map[ 'level1_noworkspace' ]
        level2_template = template_map[ 'level2_noworkspace' ]

      # include templates come before anything else; they can hold any
      # $ID
      my_include_key = group_name_l + '.' + value_type + '.include_templates'
      if netlib.my_has_key( my_include_key, template_map ):
        include_template_list = template_map[ netlib.my_has_key( my_include_key, template_map ) ].strip().replace(' ','').split(",")
        include_templates = ''
        for template in include_template_list:
          include_templates += template_map[ 'template_' + template ]
        level1_template = level1_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) )
        level2_template = level2_template.replace( '$INCLUDE_TEMPLATES', bindings.proper_indent(include_templates) )
      else:
        level1_template = level1_template.replace( '\n$INCLUDE_TEMPLATES', '' )
        level2_template = level2_template.replace( '\n$INCLUDE_TEMPLATES', '' )

      # continue with first replacements
      level1_template = level1_template.replace( '$groupname', group_name.lower() )
      level1_template = level1_template.replace( "$SPECIALIZATION", value_type )

      level0_arg_list = []
      level1_arg_list = []
      level2_arg_list = []
      level1_type_arg_list = []
      level1_assert_list = []
      level1_static_assert_list = []
      call_level1_arg_list = []
      workspace_query_arg_list = []
      user_defined_arg_list = []
      user_defined_opt_arg_list = []
      keyword_type_list = []
      typedef_list = []

      #
      # Are we dealing with a transpose option here?
      # Because CLAPACK allows to pass the order of the matrices, here we
      # inject code that determines the default data order.
      #
      if 'matrix' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ]:
        has_trans = False
        matrix_wo_trans = []
        matrix_wo_trans_arg = []
        matrix_with_trans = []
        matrix_wo_trans_arg_removed = []
        for matrix_arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_type' ][ 'matrix' ]:
            if 'ref_trans' in info_map[ subroutine ][ 'argument_map' ][ matrix_arg ]:
                has_trans = True
                matrix_type = info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ]
                matrix_with_trans += [ matrix_type ]
            else:
                matrix_wo_trans.append( info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] )
                matrix_wo_trans_arg.append( matrix_arg )

        #
        # Matrices have trans options in this case. If there is one without,
        # that one will determine the order of the call
        #
        if has_trans:
          includes += [ '#include <boost/numeric/bindings/trans_tag.hpp>' ]
          if len( matrix_wo_trans )>0:
            # Take the first from the matrix_wo_trans list for the order argument
            # remove this item from that list, so we have a correct list for static asserting
            # on column major data order later on
            typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \
                ' >::type order;' )
            includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ]
            del matrix_wo_trans[0]
            matrix_wo_trans_arg_removed = [ matrix_wo_trans_arg[0] ]
            del matrix_wo_trans_arg[0]
          else:
            typedef_list.insert( 0, 'typedef typename blas::detail::default_order< ' + matrix_with_trans[0] + \
                ' >::type order;' )
            includes += [ '#include <boost/numeric/bindings/blas/detail/default_order.hpp>' ]
        else:
            # so, there's no trans option
            # but, what if there's an order? (e.g., syr) -- then use `
            if "has_clapack_order_arg" in info_map[ subroutine ]:
              typedef_list.insert( 0, 'typedef typename result_of::data_order< ' + matrix_wo_trans[0] + \
                ' >::type order;' )
              includes += [ '#include <boost/numeric/bindings/data_order.hpp>' ]
              del matrix_wo_trans[0]
              del matrix_wo_trans_arg[0]
            elif 'TRANS' in info_map[ subroutine ][ 'arguments' ]:
              # FIXME: workaround such that tridiagonal stuff has a chance to pass compile test even if a 'TRANS' argument is present.
              typedef_list.insert( 0, 'typedef tag::column_major order;' )

        # in LAPACK, every matrix that is not
        # * transposeable
        # * used for order determination (CLAPACK). In this case, the wrong order will
        #   be caught in the detail/overload function (static assert on column_major order)
        # and a matrix that has
        # * a leading dimension trait
        # should be column major, even in case of a row_major order used by CLAPACK
        # See http://math-atlas.sourceforge.net/faq.html#RowSolve
        # This effectively says every RHS matrix should be column major
        if len( matrix_wo_trans_arg ) > 0:
            for matrix_arg in matrix_wo_trans_arg:
                # In some cases, level1 assert stuff isn't set due to a workspace array
                # being passed as a matrix. Test for None in this case. Make sure the matrix has
                # a leading dimension trait. 
                # TODO reconsider if the leading dimension trait is needed
                if 'ref_lda' in info_map[ subroutine ][ 'argument_map' ][ matrix_arg ] and \
                        info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] != None:
                    assert_line = 'BOOST_STATIC_ASSERT( (bindings::is_column_major< ' + \
                        info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );'
                    level1_static_assert_list += [ assert_line ]
                    # this looks like we're adding lots of includes, but it will be cleaned up later,
                    # and this makes sure we're only adding an include if the function is really used.
                    includes += [ '#include <boost/numeric/bindings/is_column_major.hpp>' ]

        # In case of, e.g., getrs, MatrixB should be column major for CLAPACK, too.
        # This is not detected because MatrixA may be row major and transposed to column_major ordering.
        # So, MatrixB should be column_major although it is determining the order. 
        # I.e., the order should be column_major. Look in the template system for these overrides.
        for matrix_arg in matrix_wo_trans_arg_removed:
            my_key = group_name_l + '.' + value_type + '.' + matrix_arg + '.is_column_major'
            print "Looking for column_major override ", my_key
            if netlib.my_has_key( my_key, template_map ):
                assert_line = 'BOOST_STATIC_ASSERT( (bindings::is_column_major< ' + \
                    info_map[ subroutine ][ 'argument_map' ][ matrix_arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );'
                level1_static_assert_list += [ assert_line ]
                includes += [ '#include <boost/numeric/bindings/is_column_major.hpp>' ]

      #
      # Add an include in case of the uplo or diag options
      #
      if 'UPLO' in info_map[ subroutine ][ 'arguments' ]:
        includes += [ '#include <boost/numeric/bindings/uplo_tag.hpp>' ]
      if 'DIAG' in info_map[ subroutine ][ 'arguments' ]:
        includes += [ '#include <boost/numeric/bindings/diag_tag.hpp>' ]

      #
      # Create static assertions, first by value type
      #
      for value_type_tmp_key in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ].keys():
        # look up whether they are template params
        static_asserts = []
        for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_value_type' ][ value_type_tmp_key ]:
          if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None:
            static_asserts.append( arg )
        if len(static_asserts)>1:
          arg_A = static_asserts[0]
          for arg_B in static_asserts[1:]:
            print "Adding static assert for argA", arg_A, " argb", arg_B
            assert_line = 'BOOST_STATIC_ASSERT( (boost::is_same< ' + \
                'typename remove_const< typename $NAMESPACEvalue_type< ' + info_map[ subroutine ][ 'argument_map' ][ arg_A ][ 'code' ][ 'level_1_static_assert' ] + ' >::type >::type, ' + \
                'typename remove_const< typename $NAMESPACEvalue_type< ' + info_map[ subroutine ][ 'argument_map' ][ arg_B ][ 'code' ][ 'level_1_static_assert' ] + ' >::type >::type' \
                ' >::value) );'
            level1_static_assert_list += [ assert_line ]

      #
      # Make sure the mutable stuff is mutable
      #
      if 'output' in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ]:
        for arg in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'output' ]:
          if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None:
            assert_line = 'BOOST_STATIC_ASSERT( ($NAMESPACEis_mutable< ' + \
                info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_static_assert' ] + ' >::value) );'
            level1_static_assert_list += [ assert_line ]

      #
      # import the code, by argument
      #
      for arg in info_map[ subroutine ][ 'arguments' ]:
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_0' ] != None:
          level0_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_0' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] != None:
          level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] != None:
          level2_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_2' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] != None and \
          info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] not in level1_type_arg_list:
          level1_type_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_type' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ] != []:
          level1_assert_list += info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'level_1_assert' ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] != None:
          call_level1_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'call_level_1' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'opt_workspace_query' ] != None:
          workspace_query_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'opt_workspace_query' ] ]
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] != None:
          # make sure trans tags always preceed other tags, as they may be dependant
          if 'TRANS' in arg:
              at_i = 0
              if len(typedef_list)>0 and ' order;' in typedef_list[0]:
                at_i = 1
              typedef_list.insert( at_i, info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] )
          else:
              typedef_list.append( info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'typedef' ] )
        if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'keyword_type' ] != None:
          keyword_type_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'keyword_type' ] ]
        if 'banded' in info_map[ subroutine ][ 'argument_map' ][ arg ]:
            includes += [ '#include <boost/numeric/bindings/bandwidth.hpp>' ]

      if info_map[ subroutine ][ 'user_defined_variables' ] != None:
        for arg in info_map[ subroutine ][ 'user_defined_variables' ]:
          print arg
          if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] != None:
            user_defined_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] ]

      if info_map[ subroutine ][ 'user_defined_opt_variables' ] != None:
        for arg in info_map[ subroutine ][ 'user_defined_opt_variables' ]:
          print arg
          if info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] != None:
            user_defined_opt_arg_list += [ info_map[ subroutine ][ 'argument_map' ][ arg ][ 'code' ][ 'user_defined_init' ] ]

      #
      # Insert the order_type() if appropriate
      #
      if "has_clapack_order_arg" in info_map[ subroutine ]:
          level0_arg_list.insert( 0, "order()" )
          workspace_query_arg_list.insert( 0, "order()" )

      #
      # LEVEL 1 REPLACEMENTS
      #
      level1_template = level1_template.replace( "$TYPEDEFS", "\n        ".join( typedef_list ) )
      level1_template = level1_template.replace( "$CALL_LEVEL0", ", ".join( level0_arg_list ) )
      level1_template = level1_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) )
      level1_template = level1_template.replace( "$LEVEL1", ", ".join( level1_arg_list ) )
      level1_template = level1_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) )
      level1_template = level1_template.replace( "$ASSERTS", "\n        ".join( sorted( level1_assert_list ) ) )
      level1_template = level1_template.replace( "$KEYWORDS", ", ".join( keyword_type_list ) )
      
      if len( level1_static_assert_list ) > 0:
        level1_template = level1_template.replace( "$STATIC_ASSERTS", "\n        ".join( level1_static_assert_list ) )
      else:
        level1_template = level1_template.replace( "\n        $STATIC_ASSERTS", "" )

      if len( user_defined_arg_list ) > 0:
        level1_template = level1_template.replace( "$INIT_USER_DEFINED_VARIABLES", indent_lines( "\n".join(user_defined_arg_list), 8 ) )
      else:
        level1_template = level1_template.replace( "\n        $INIT_USER_DEFINED_VARIABLES", "" )

      #
      # LEVEL 2 REPLACEMENTS
      #
      # some special stuff is done here, such as replacing real_type with a 
      # type-traits deduction, etc..
      # more important: all non-const and const variants of functions are written here
      #
      level2_functions = []
      level2_arg_lists, level2_comments = \
            bindings.generate_const_variants( \
                group_name.lower() + '.' + value_type, level2_arg_list, template_map )
      for level2_idx in range( 0, len( level2_arg_lists ) ):
        level2_function = level2_template.replace( "$LEVEL2", \
                ", ".join( level2_arg_lists[ level2_idx ] ) )
        if len( "".join(level2_comments[ level2_idx ] ) ) > 0:
          level2_function = level2_function.replace( "$COMMENTS", \
                "\n".join( level2_comments[ level2_idx ] ) )
        level2_functions.append( level2_function )

      level2_template = "\n".join( level2_functions )
      level2_template = level2_template.replace( "$COMMENTS\n", "" )

      #
      # Determine a right type to select for real or complex variants
      #
      first_typename = ''
      print "Type selectors: ", type_selector_candidates
      if len( type_selector_candidates ) > 0:
        first_typename_arg = type_selector_candidates[0]
        first_typename_code = info_map[ subroutine ][ 'argument_map' ][ first_typename_arg ][ 'code' ][ 'level_1_type' ]
        first_typename = first_typename_code.split(" ")[-1]
      else:
        for tn in level1_type_arg_list:
            bare_type = tn.split(" ")[-1]
            if first_typename == '' and bare_type[:6].lower() in [ 'matrix', 'vector' ]:
                first_typename = bare_type

      # generate the word "matrix" or "vector", to select the right traits
      first_typename_datatype = first_typename[0:6].lower()
      level2_template = level2_template.replace( "$FIRST_TYPENAME", first_typename )
      level2_template = level2_template.replace( "$LAST_TYPENAME", level1_type_arg_list[-1][9:] )
      level2_template = level2_template.replace( "$TYPEOF_FIRST_TYPENAME", first_typename_datatype )
      level2_template = level2_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) )
      level2_template = level2_template.replace( "$TYPES", ", ".join( level1_type_arg_list ) )

      #
      # Workspace stuff
      #
      if info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ].has_key( 'workspace' ):
        # Add an include for the workspace stuff
        includes += [ '#include <boost/numeric/bindings/lapack/workspace.hpp>' ]
        includes += [ '#include <boost/numeric/bindings/detail/array.hpp>' ]

        # Continue
        workspace_size = len( info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ] )
        workspace_args = info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]
        level1_template = level1_template.replace( "$WORKSPACE_SIZE", str(workspace_size) )
        level1_template = level1_template.replace( "$WORKSPACE_TYPENAMES", "typename " + ", typename ".join( workspace_args ) )
        level1_template = level1_template.replace( "$WORKSPACE_TYPES", ", ".join( workspace_args ) )

        # $TMP_WORKARRAYS is something like "tmp_work, tmp_rwork"
        tmp_workspace_args = []
        for name in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]:
          tmp_workspace_args += [ 'tmp_' + name.lower() ]
        level1_template = level1_template.replace( "$TMP_WORKARRAYS", ", ".join( tmp_workspace_args ) )
        
        # $SETUP_WORKARRAYS looks like 
        #    traits::detail::array< value_type > $TMP_NAME .... 
        setup_min_workarrays = ''
        setup_opt_workarrays_pre = []
        setup_opt_workarrays_post = []
        for name in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]:
          # minimal case
          sub_min_template = template_map[ 'setup_min_workspace' ]
          sub_min_template = sub_min_template.replace( '$WORKSPACE_FUNC', name.lower() )
          sub_min_template = sub_min_template.replace( '$WORKSPACE_TYPE', info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'workspace_type' ] )
          if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_call' ] != None:
            sub_min_template = sub_min_template.replace( "$CALL_MIN_SIZE", info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_call' ] )
          
          setup_min_workarrays += sub_min_template

          # optimal case
          if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_pre' ] != None:
            setup_opt_workarrays_pre += [ info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_pre' ] ]
          if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_post' ] != None:
            setup_opt_workarrays_post += [ info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'opt_workspace_post' ] ]


        # if the length of setup_opt_workarrays_post equals 0, it's equal to the minimal_case
        opt_workspace_template = ''
        if len( setup_opt_workarrays_post ) == 0:
          print "EQUAL to MINIMAL CASE!"
          opt_workspace_template = template_map[ 'level1_opt_workspace_is_min' ]
        else:
          includes += [ '#include <boost/numeric/bindings/traits/detail/utils.hpp>' ]
          opt_workspace_template = template_map[ 'level1_opt_workspace' ]

        opt_workspace_template = opt_workspace_template.replace( "$WORKSPACE_QUERY", ", ".join( workspace_query_arg_list ) )
        if provide_clapack_backend:
          # special workaround for clapack frontend of 'getri'
          opt_workspace_template = opt_workspace_template.replace( "$SETUP_OPT_WORKARRAYS_POST",
                "\n        ".join( setup_opt_workarrays_post ) +
                '\n#endif')
          opt_workspace_template = opt_workspace_template.replace( "        $SETUP_OPT_WORKARRAYS_PRE",
                '#if defined BOOST_NUMERIC_BINDINGS_LAPACK_CLAPACK\n' +
                '        $NAMESPACEdetail::array< ' +
                info_map[ subroutine ][ 'argument_map' ][ 'WORK' ][ 'code' ][ 'workspace_type' ] +
                ' > tmp_work( 0 );\n#else\n        ' +
                "\n        ".join( setup_opt_workarrays_pre ) )
        else:
          opt_workspace_template = opt_workspace_template.replace( "$SETUP_OPT_WORKARRAYS_POST", "\n        ".join( setup_opt_workarrays_post ) )
          opt_workspace_template = opt_workspace_template.replace( "$SETUP_OPT_WORKARRAYS_PRE", "\n        ".join( setup_opt_workarrays_pre ) )
        opt_workspace_template = opt_workspace_template.replace( "$CALL_LEVEL1", ", ".join( call_level1_arg_list ) )
        opt_workspace_template = opt_workspace_template.replace( "$TMP_WORKARRAYS", ", ".join( tmp_workspace_args ) )
        
      
        if len( user_defined_opt_arg_list ) > 0:
          opt_workspace_template = opt_workspace_template.replace( "$INIT_USER_DEFINED_OPT_VARIABLES", indent_lines( "\n".join(user_defined_arg_list), 8 ) )
        else:
          print "removing $INIT_USER_DEFINED_OPT_VARIABLES"
          opt_workspace_template = opt_workspace_template.replace( "        $INIT_USER_DEFINED_OPT_VARIABLES\n", "" )
        
        
        level1_template = level1_template.replace( "$OPT_WORKSPACE_FUNC", opt_workspace_template.rstrip() )
        level1_template = level1_template.replace( "$SETUP_MIN_WORKARRAYS_POST", setup_min_workarrays.rstrip() )
        
        
        #
        # INSERT THE MINIMAL WORKSPACE FUNCTIONS
        #
        min_size_funcs = ''
        for name in info_map[ subroutine ][ 'grouped_arguments' ][ 'by_io' ][ 'workspace' ]:
          sub_template = template_map[ 'min_size_func' ]
          sub_template = sub_template.replace( "$WORKSPACE_FUNC", name.lower() )

          # first: user-defined stuff (overrules any auto-detected stuff)

          resulting_code = ''
          my_key = group_name_l + '.' + value_type + '.min_size_' + name.lower()
          if netlib.my_has_key( my_key, template_map ):
            resulting_code = indent_lines( template_map[ netlib.my_has_key( my_key, template_map ) ].rstrip(), 8 )

          elif info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace' ] != None:
            resulting_code = 'return ' + info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace' ] + ';'

          if resulting_code != '' and provide_clapack_backend:
            # special workaround for clapack frontend of 'getri'
            sub_template = sub_template.replace( "        $MIN_SIZE_IMPLEMENTATION",
                                                 '#if defined BOOST_NUMERIC_BINDINGS_LAPACK_CLAPACK\n        return 0;\n' +
                                                 '#else\n        ' + resulting_code + '\n#endif' )
          elif resulting_code != '':
            sub_template = sub_template.replace( "$MIN_SIZE_IMPLEMENTATION", resulting_code.rstrip() )
            
          # Do about the same for the argument stuff.  
          if info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_args' ] != None:
            sub_template = sub_template.replace( "$TYPES", info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_args' ][ 'types' ] )
            sub_template = sub_template.replace( "$ARGUMENTS", info_map[ subroutine ][ 'argument_map' ][ name ][ 'code' ][ 'min_workspace_args' ][ 'code' ] )

            #sub_template += 'FOUND'
          min_size_funcs += sub_template

        min_size_funcs = min_size_funcs.rstrip()
        level1_template = level1_template.replace( "$MIN_SIZE_FUNCS", min_size_funcs )

      level1_map[ value_type ] = bindings.proper_indent( level1_template )
      level2_map[ value_type ] = bindings.proper_indent( level2_template )

    #
    # LEVEL 1 and 2 FINALIZATION
    #
    for mapping in [ level1_map, level2_map ]:
      if len(mapping) > 1:
        # compare real and complex cases
        all_keys = mapping.keys()
        if mapping[ all_keys[0] ] == mapping[ all_keys[1] ]:
          print "literally everything is the same!!, falling back to 1 case"
          del mapping[ all_keys[ 1 ] ]

    level1 = ''
    if len( level1_map ) > 1:
      level1 = template_map[ 'level1_pre_header' ]
      includes += [ '#include <boost/utility/enable_if.hpp>' ]
      includes += [ '#include <boost/numeric/bindings/is_real.hpp>' ]
      includes += [ '#include <boost/numeric/bindings/is_complex.hpp>' ]

    for value_type in level1_map.keys():
      if len( level1_map ) == 1:
        header = template_map[ 'level1_header1' ]
      else:
        header = template_map[ 'level1_header2' ]

      level1 += header.replace( "$SPECIALIZATION", value_type )
      level1 += level1_map[ value_type ]

    level2 = ''
    for value_type in level2_map.keys():
      level2 += level2_map[ value_type ]

    #
    # handle addition of includes
    #
    includes_code = ''
    unique_includes = []
    for include in includes:
      if include not in unique_includes:
        unique_includes += [ include ]
    sorted_includes = sorted( unique_includes, lambda x, y: cmp( x.lower(), y.lower() ) )
    if len( sorted_includes ) > 0:
      includes_code = "\n".join( sorted_includes )

    result = template_map[ 'lapack.hpp' ]
    result = result.replace( '$INCLUDES', includes_code )
    result = result.replace( '$BACKEND_INCLUDES', backend_includes )
    result = result.replace( '$OVERLOADS', overloads )
    result = result.replace( '$LEVEL1', level1 )
    result = result.replace( '$LEVEL2', level2 )
    result = result.replace( '$GROUPNAME', group_name )
    result = result.replace( '$groupname', group_name.lower() )
    result = result.replace( '$DIRNAME', base_dir.split("/")[-1].upper() )
    result = result.replace( '$dirname', base_dir.split("/")[-1].lower() )
    result = result.replace( '$INTEGER_TYPE', netlib.generic_integer_type )
    result = result.replace( '$LIBRARY_INT_TYPE', "fortran_int_t" )
    result = result.replace( '$NAMESPACE', "bindings::" )
    result = result.replace( '    template<  >\n', '' )
    result = result.replace( '\n\n\n', '\n\n' )
    result = result.replace( "\n    \n", "\n" )
    result = result.replace( "\n        \n", "\n" )
    result = result.replace( "\n        \n", "\n" )

    # replace the global variables as last (this is convenient)
    #result = result.replace( '$INDENT', '    ' )
    #result = result.replace( '$groupname', group_name.lower() )
    #result = result.replace( '$DESCRIPTION', info_map[ group[g][0] ][ 'description' ] )

    open( os.path.join( base_dir, filename ), 'w' ).write( result )
def write_documentation(info_map, level, group, template_map, base_dir):

    parsermode = template_map['PARSERMODE'].lower()

    for group_name, subroutines in group.iteritems():
        filename = group_name.lower() + '.qbk'

        result = template_map[parsermode + '.qbk']

        prototypes = []
        first_typename = None
        originating_sources = []
        no_arguments_level0 = []
        no_arguments_level2 = []

        for subroutine in subroutines:
            if parsermode == 'blas':
                originating_sources.append("[@http://www.netlib.org/blas/" +
                                           subroutine.lower() + ".f " +
                                           subroutine.lower() + ".f]")
            if parsermode == 'lapack':
                originating_sources.append("[@http://www.netlib.org/lapack/" +
                                           lapack_value_type[subroutine[0]] +
                                           "/" + subroutine.lower() + ".f " +
                                           subroutine.lower() + ".f]")

            level2_arg_list_pre = []
            for arg in info_map[subroutine]['arguments']:
                if info_map[subroutine]['argument_map'][arg]['code'][
                        'level_2'] != None:
                    level2_arg_list_pre += [
                        info_map[subroutine]['argument_map'][arg]['code']
                        ['level_2']
                    ]

            # Keep track of the numbers of arguments
            if len(info_map[subroutine]
                   ['arguments']) not in no_arguments_level0:
                no_arguments_level0.append(
                    len(info_map[subroutine]['arguments']))

            if len(level2_arg_list_pre) not in no_arguments_level2:
                no_arguments_level2.append(len(level2_arg_list_pre))

            level2_arg_list = []
            for arg in level2_arg_list_pre:
                readable_arg = ''
                if '::real_type' in arg:
                    splitted = arg.split("::real_type")
                    readable_arg = "Scalar" + splitted[1]
                    if 'const' in arg:
                        readable_arg = 'const ' + readable_arg
                elif '::type' in arg:
                    splitted = arg.split("::type")
                    readable_arg = "Scalar" + splitted[1]
                    if 'const' in arg:
                        readable_arg = 'const ' + readable_arg
                else:
                    readable_arg = arg

                if (first_typename == None) and ('Vector' in arg
                                                 or 'Matrix' in arg):
                    first_typename = arg.replace("const ",
                                                 "").replace("&",
                                                             "").split(" ")[0]

                level2_arg_list.append(readable_arg)

            prototype = template_map[parsermode + '_prototype.qbk']
            prototype = prototype.replace("$LEVEL2",
                                          ", ".join(level2_arg_list))

            if prototype not in prototypes:
                prototypes.append(prototype)

        prototype_overloads = ''
        if len(prototypes) == 1:
            prototype_overloads = 'is one prototype of'
        else:
            prototype_overloads = 'are ' + number_to_text[len(
                prototypes)] + ' prototypes of'

        prototypes = bindings.proper_indent("\n".join(prototypes))

        #prototypes.append( prototype )
        result = result.replace("$PROTOTYPES", prototypes)
        result = result.replace("$PROTOTYPE_OVERLOADS", prototype_overloads)
        result = result.replace("$ORIGINATING_SOURCES",
                                readable_join(originating_sources))
        result = result.replace('$PURPOSE',
                                combine_purposes(subroutines, info_map))
        result = result.replace('$LIBRARY_INT_TYPE', "int_t")

        result = result.replace('$SUBROUTINES', readable_join(subroutines))
        result = result.replace(
            '$header', 'boost/numeric/bindings/' + parsermode + '/' + level +
            '/' + group_name.lower() + '.hpp')

        if len(no_arguments_level0) == 1:
            result = result.replace('$NO_ARGUMENTS_LEVEL0',
                                    number_to_text[no_arguments_level0[0]])
        else:
            result = result.replace('$NO_ARGUMENTS_LEVEL0', "TODO")

        if len(no_arguments_level2) == 1:
            result = result.replace('$NO_ARGUMENTS_LEVEL2',
                                    number_to_text[no_arguments_level2[0]])
        else:
            result = result.replace('$NO_ARGUMENTS_LEVEL2', "TODO")

        if parsermode == 'blas':
            result = result.replace(
                '$DISPATCH_TABLE',
                write_blas_dispatch_table(subroutines, info_map))
            result = result.replace(
                '$FRIENDLY_NAME',
                blas_friendly_name(group_name, info_map, template_map))
        if parsermode == 'lapack':
            result = result.replace(
                '$DISPATCH_TABLE',
                write_lapack_dispatch_table(subroutines, info_map))
            #result = result.replace( '$FRIENDLY_NAME', lapack_friendly_name( group_name, info_map, template_map ) )

        if first_typename != None:
            result = result.replace("$FIRST_TYPENAME", first_typename)
        result = result.replace('$GROUPNAME', group_name)
        result = result.replace('$groupname', group_name.lower())
        print "Writing " + base_dir + "/" + filename
        open(os.path.join(base_dir, filename), 'w').write(result)