//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;

//namespace McGuffinsOfflineNetworkWorkshop {
    // Stores the lower triangular half of a symmetric matrix
    // (with or without the diagonal),
    // allowing the client to access either the upper or lower triangular half transparently.
    // When the client sets (or reads) the value of element (i,j),
    // the effect is the same as setting (or reading) element (j,i).
    // The client sees the matrix as a 2D N*N array, but internally it is stored as a 1D array of length
    // approximately N*N/2 (the exact size depends on whether we are storing the diagonal or not).
    //
    // Because the upper triangular half is not stored,
    // this reduces by a factor of 2 the memory that would
    // be required by a full matrix.
    // However, there is a slightly increased cost in access time to elements,
    // because this class must check which of the indices (i,j) is bigger
    // and then perform some arithmetic to compute the index into the 1D array.
    //
    // Note that this class stores a symmetric matrix of FLOAT values.
    // To make the code more generic, this class could be made into a generic template on type <T>.
    //
    public class MSymmetricMatrix {
        public int N = 0; // the 2D matrix is N*N
        private int NN = 0; // the 1D array used internally has this size
        private bool storeDiagonal = false;
        private float[] array = null;

        public MSymmetricMatrix(int N, bool storeDiagonal) {
            if (N < 0) N = 0;
            this.N = N;
            this.storeDiagonal = storeDiagonal;
            if (N > 0) {
                NN = storeDiagonal ? (N * (N + 1) / 2) : (N * (N - 1) / 2);
                array = new float[NN];
            }
        }

        // i is row index (from 0 to N-1), j is column index (from 0 to N-1)
        public float get(int i, int j) {
            if (i < 0 || i >= N || j < 0 || j >= N) {
                // client trying to access out-of-bounds element
                return 0;
            }
            if (i == j && !storeDiagonal) {
                // client wants to access diagonal, but we are not storing any diagonal
                return 0;
            }
            if (j > i) {
                // swap indices so they will map to lower triangular half
                int temp = i;
                i = j;
                j = temp;
            }
            int ii = storeDiagonal ? (i * (i + 1) / 2 + j) : (i * (i - 1) / 2 + j);
            MLogger.Assert(0 <= ii && ii < NN, "43fth7txy2");
            return array[ii];
        }

        // i is row index (from 0 to N-1), j is column index (from 0 to N-1)
        public void set(int i, int j, float value) {
            if (i < 0 || i >= N || j < 0 || j >= N) {
                // client trying to access out-of-bounds element
                return;
            }
            if (i == j && !storeDiagonal) {
                // client wants to access diagonal, but we are not storing any diagonal
                return;
            }
            if (j > i) {
                // swap indices so they will map to lower triangular half
                int temp = i;
                i = j;
                j = temp;
            }
            int ii = storeDiagonal ? (i * (i + 1) / 2 + j) : (i * (i - 1) / 2 + j);
            MLogger.Assert(0 <= ii && ii < NN, "5q45shlkzq");
            array[ii] = value;
        }

        // this is for testing and debugging
        /*
        public void print() {
            System.Console.WriteLine("N==" + N + ", NN==" + NN );
            for ( int i = 0; i < N; ++i ) {
                System.Console.Write("[ ");
                for ( int j = 0; j < N; ++j ) {
                    float v = get(i,j);
                    System.Console.Write( (j>0?", ":"") + v );
                }
                System.Console.WriteLine(" ]");
            }
        }
        */

    }



//}


