r"""
A binary matrix datatype in Cython

It's almost a copy of the bitset datatype, but allows a differentiation of the
rows of the matrix. That's the only advantage compared to storing all the rows
of a matrix in a loooooonng bitset.

a ``binary_matrix_t`` structure contains :

- ``long n_cols`` -- number of columns

- ``long n_rows`` -- number of rows

- ``long width`` -- number of ``unsigned long`` per row

- ``unsigned long ** rows`` -- ``rows[i]`` points toward the ``width`` blocks of
  type ``unsigned long`` containing the bits of row `i`.

.. NOTE::

    The rows are stored contiguously in memory, i.e. ``row[i] = row[i-1] +
    width``.
"""
include "binary_matrix_pxd.pxi"
include "sage/ext/stdsage.pxi"

cdef inline binary_matrix_init(binary_matrix_t m, long n_rows, long n_cols):
    r"""
    Allocates the binary matrix.
    """
    cdef int i

    m.n_cols = n_cols
    m.n_rows = n_rows
    m.width = (n_cols - 1)/(8*sizeof(unsigned long)) + 1
    m.rows = <unsigned long**>sage_malloc(n_rows * sizeof(unsigned long *))
    if m.rows == NULL:
        raise MemoryError

    m.rows[0] = <unsigned long*>sage_malloc(n_rows * m.width * sizeof(unsigned long))
    if m.rows[0] == NULL:
        sage_free(m.rows)
        raise MemoryError

    for i in range(1,n_rows):
        m.rows[i] = m.rows[i-1] + m.width
        m.rows[i][m.width-1] = 0

cdef inline binary_matrix_free(binary_matrix_t m):
    r"""
    Frees the memory allocated by the matrix
    """
    sage_free(m.rows[0])
    sage_free(m.rows)

cdef inline binary_matrix_fill(binary_matrix_t m, bint bit):
    r"""
    Fill the whole matrix with a bit
    """
    memset(m.rows[0],-(<unsigned char> bit),m.width * m.n_rows * sizeof(unsigned long))

cdef inline binary_matrix_complement(binary_matrix_t m):
    r"""
    Complements all of the matrix' bits.
    """
    cdef int i,j
    for i in range(m.n_rows):
        for j in range(m.width):
            m.rows[i][j] = ~m.rows[i][j]

        # Set the "useless" bits to 0 too.
        if ((m.n_cols)  & offset_mask):
            m.rows[i][m.width-1] &= (<unsigned long> 1<<((m.n_cols)  & offset_mask))-1

cdef inline binary_matrix_set1(binary_matrix_t m, long row, long col):
    r"""
    Sets an entry to 1
    """
    m.rows[row][col >> index_shift] |= (<unsigned long>1) << (col & offset_mask)

cdef inline binary_matrix_set0(binary_matrix_t m, long row, long col):
    r"""
    Sets an entry to 0
    """
    m.rows[row][col >> index_shift] &= ~((<unsigned long>1) << (col & offset_mask))

cdef inline binary_matrix_set(binary_matrix_t m, long row, long col, bint value):
    r"""
    Sets an entry
    """
    binary_matrix_set0(m,row,col)
    m.rows[row][col >> index_shift] |= (<unsigned long>1) << (value & offset_mask)

cdef inline bint binary_matrix_get(binary_matrix_t m, long row, long col):
    r"""
    Returns the value of a given entry
    """
    return (m.rows[row][col >> index_shift] >> (col & offset_mask)) & 1

cdef inline binary_matrix_print(binary_matrix_t m):
    r"""
    Prints the binary matrix
    """
    cdef int i,j
    import sys
    for i in range(m.n_rows):
        # If you want to print the *whole* matrix, including the useless bits,
        # use the following line instead
        #
        # for j in (m.width*8*sizeof(unsigned long)):
        for j in range(m.n_cols):
            sys.stdout.write("1" if binary_matrix_get(m, i, j) else ".",)
        print ""
