Formats a table represented as an iterable of iterable into a nice big string
    suitable for printing.

    Parameters:
    -----------
    align : string or list of strings

            Alignment of cell contents. Each character in a string specifies
            the alignment of one column.

            * ``<`` - Left aligned (default)
            * ``^`` - Centered
            * ``>`` - Right aligned

            The last alignment is repeated for unspecified columns.

            If it's a list of strings, each string specifies the alignment of
            one row. The last string is used repeatedly for unspecified rows.

    format : string/function, or (nested) list of string/function

             Formats the contents of the cells using the specified function(s)
             or format string(s).

             If it's a list of strings/functions each entry specifies formatting
             for one column, the last entry being used repeatedly for
             unspecified columns.

             If it's a list of lists, each sub-list specifies one row, the last
             sub-list being used repeatedly for unspecified rows.

    colwidth : int, list of ints or None

               The width of each column. The last width is used repeatedly for
               unspecified columns. If ``None`` the width is fitted to the
               contents.

    maxwidth : int or None

               The maximum width of the table. Defaults to terminal width minus
               1 if ``None``. If the table would be wider than ``maxwidth`` one
               of the columns is truncated.

    spacing : int

              The spacing between columns

    truncate : int

               Which column to truncate if table width would exceed ``maxwidth``.

    Beware that no columns can have zero or negative width. If for instance
    'maxwidth' is 80 and 'colwidth' is [10, 30, 30, 30] with spacing 2 the total
    width will initially be 10+2+30+2+30+2+30=106. That's 26 characters too
    much, so a width of 26 will be removed from the truncated column. If
    'truncate' is 0, column 0 will have a width of -16 which is not permitted.

def format_table(table,
                 align='<',
                 format='{:.3g}',
                 colwidth=None,
                 maxwidth=None,
                 spacing=2,
                 truncate=0,
                 suffix="..."
                ):
    """
    Formats a table represented as an iterable of iterable into a nice big string
    suitable for printing.

    Parameters:
    -----------
    align : string or list of strings

            Alignment of cell contents. Each character in a string specifies
            the alignment of one column.

            * ``<`` - Left aligned (default)
            * ``^`` - Centered
            * ``>`` - Right aligned

            The last alignment is repeated for unspecified columns.

            If it's a list of strings, each string specifies the alignment of
            one row. The last string is used repeatedly for unspecified rows.

    format : string/function, or (nested) list of string/function

             Formats the contents of the cells using the specified function(s)
             or format string(s).

             If it's a list of strings/functions each entry specifies formatting
             for one column, the last entry being used repeatedly for
             unspecified columns.

             If it's a list of lists, each sub-list specifies one row, the last
             sub-list being used repeatedly for unspecified rows.

    colwidth : int, list of ints or None

               The width of each column. The last width is used repeatedly for
               unspecified columns. If ``None`` the width is fitted to the
               contents.

    maxwidth : int or None

               The maximum width of the table. Defaults to terminal width minus
               1 if ``None``. If the table would be wider than ``maxwidth`` one
               of the columns is truncated.

    spacing : int

              The spacing between columns

    truncate : int

               Which column to truncate if table width would exceed ``maxwidth``.

    Beware that no columns can have zero or negative width. If for instance
    'maxwidth' is 80 and 'colwidth' is [10, 30, 30, 30] with spacing 2 the total
    width will initially be 10+2+30+2+30+2+30=106. That's 26 characters too
    much, so a width of 26 will be removed from the truncated column. If
    'truncate' is 0, column 0 will have a width of -16 which is not permitted.
    """

    table = list(deepcopy(table))

    if not isinstance(align, list):
        align = [align]

    if not isinstance(format, list):
        format = [format]

    if not isinstance(format[0], list):
        format = [format]

    num_cols = len(table[0])
    if len(set([len(row) for row in table]))>1:
        raise ValueError("All rows must have the same number of columns")

    for i in range(len(table)):
        table[i] = list(table[i])
        colformat = format[min(i,len(format)-1)]
        for j, cell in enumerate(table[i]):
            f = colformat[min(j,len(colformat)-1)]
            if isinstance(f, str):
                fun = lambda x: f.format(x)
            else:
                fun = f
            try:
                table[i][j] = fun(cell)
            except:
                table[i][j] = str(cell)

    if colwidth==None:
        cellwidth = [[len(cell) for cell in row] for row in table]
        colwidth = list(map(max, zip(*cellwidth)))

    elif not isinstance(colwidth, list):
        colwidth = [colwidth]


    colwidth.extend([colwidth[-1]]*(num_cols-len(colwidth)))

    if maxwidth==None:
        maxwidth = get_terminal_size().columns-1

    width = sum(colwidth)+spacing*(num_cols-1)
    if width>maxwidth:
        colwidth[truncate] -= (width-maxwidth)

    for j, cw in enumerate(colwidth):
        if cw<1:
            raise RuntimeError("Column {} in format_table() has width {}. "
                               "Make sure all columns have width >0. "
                               "Read docstring for further details."
                               .format(j,cw)
                              )

    s = ''
    for i, row in enumerate(table):
        if i != 0: s += "\n"
        colalign = align[min(i,len(align)-1)]
        colformat = format[min(i,len(format)-1)]
        for j, col in enumerate(row):
            a = colalign[min(j,len(colalign)-1)]
            f = colformat[min(j,len(colformat)-1)]
            w = colwidth[j]
            if j!=0: s+= ' '*spacing
            s += format_fit(format_time(col), w, a, suffix)

    return s