package org.broadinstitute.sting.playground.indels;

/**
 * Created by IntelliJ IDEA.
 * User: asivache
 * Date: Mar 22, 2009
 * Time: 3:43:05 PM
 * To change this template use File | Settings | File Templates.
 */
public class SymmetricMatrix {

    protected double [] data;
    protected int size;

    public SymmetricMatrix(int dimension) {
        assert dimension >= 0 : "Matrix size can not be negative";
        if ( dimension % 2 == 0 ) {
                int k = dimension >> 1; // dimension/2
                data = new double[k*(dimension+1)];
        } else {
                int k = ( dimension + 1 ) >> 1; // (dimension + 1)/2
                data = new double[k*dimension];
        }
        size = dimension;
    }

    public double get(int i, int j) {
        assert (i < size) && ( j < size) : "Out of bound index into matrix detected";
        if ( i >= j ) return data[linearOffset(j,i)]; // we store only the upper triangle in memory		
        return data[ linearOffset(i,j) ];
    }

    public void set(int i, int j, double value) {
        assert (i < size) && (j < size) : "Out of bound index into matrix detected";

        if ( i >= j ) data[ linearOffset(j,i) ] = value;
        else data[ linearOffset(i,j) ] = value;
    }

    public int size() { return size; }

    public int nRows() { return size; }
    public int nCols() { return size; }

    /** Returns ready-to-print string representing the full matrix (don't use for 1000x1000 matrices!!);
     *  each element is formatted according to a default format.
     * @return
     * @see #format(String f)
     */
    public String format() {
        return format("%6.3f ");
    }


    /** Returns ready-to-print string representing the full matrix (don't use for 1000x1000 matrices!!);
     *  each element is formatted according to a specified format string (note: format string must include all desired
     *  whitespaces before and/or after an element, as this method itself does not add any spaces between the formatted elements).
     * @return
     * @see #format()
     */
    public String format(String f) {
        StringBuilder b = new StringBuilder();
        java.util.Formatter frm = new java.util.Formatter(b);
        for ( int i = 0 ; i < size ; i++ ) {
            for ( int j = 0 ; j < size ; j++ ) {
                frm.format(f, get(i,j));
            }
            b.append('\n');
        }
        return b.toString();
    }


    /** Computes linear offset into the internal array that keeps actual data, given "row" and "column" indices
     * into the matrix; this method is unchecked, but it expects i <= j otherwise the result is unspecified.
     * @param i row index
     * @param j column index
     * @return linear offset into the data[] member of this class
     */
    private int linearOffset(int i, int j) {
        int k = (( size << 1 ) - i + 1)*i; // [ 2*d - (i+1) ] * i
        k >>= 1; // k/=2
        // now k is the offset of the first stored element in row i
        return ( k + j - i );
    }

}
