def print_2x2_table(table, row_labels, col_labels, fmt="%d"):
    """
    Prints a table used for Fisher's exact test. Adds row, column, and grand
    totals.

    :param table: The four cells of a 2x2 table: [r1c1, r1c2, r2c1, r2c2]
    :param row_labels: A length-2 list of row names
    :param col_labels: A length-2 list of column names

    """
    grand = sum(table)

    # Separate table into components and get row/col sums
    t11, t12, t21, t22 = table

    # Row sums, col sums, and grand total
    r1 = t11 + t12
    r2 = t21 + t22
    c1 = t11 + t21
    c2 = t12 + t22

    # Re-cast everything as the appropriate format
    t11, t12, t21, t22, c1, c2, r1, r2, grand = [
        fmt % i for i in [t11, t12, t21, t22, c1, c2, r1, r2, grand]]

    # Construct rows and columns the long way...
    rows = [
        [""] + col_labels + ['total'],
        [row_labels[0], t11, t12, r1],
        [row_labels[1], t21, t22, r2],
        ['total', c1, c2, grand],
    ]

    cols = [
        [row[0] for row in rows],
        [col_labels[0], t11, t21, c1],
        [col_labels[1], t12, t22, c2],
        ['total', r1, r2, grand],
    ]

    # Get max column width for each column; need this for nice justification
    widths = []
    for col in cols:
        widths.append(max(len(i) for i in col))

    # ReST-formatted header
    sep = ['=' * i for i in widths]

    # Construct the table one row at a time with nice justification
    s = []
    s.append(' '.join(sep))
    s.append(' '.join(i.ljust(j) for i, j in zip(rows[0], widths)))
    s.append(' '.join(sep))
    for row in rows[1:]:
        s.append(' '.join(i.ljust(j) for i, j in zip(row, widths)))
    s.append(' '.join(sep) + '\n')
    return "\n".join(s)
Prints a table used for Fisher's exact test. Adds row, column, and grand
    totals.

    :param table: The four cells of a 2x2 table: [r1c1, r1c2, r2c1, r2c2]
    :param row_labels: A length-2 list of row names
    :param col_labels: A length-2 list of column names