#ifndef _SINGULAR_DIAGONAL_MATRIX_H
#define _SINGULAR_DIAGONAL_MATRIX_H

#include "singular.h"



//#define L(M,N) (M < N ? M : N)
#define L(M,N) (M*N)
#if 1

typedef struct class_DiagonalMatrix DiagonalMatrix_t;
struct class_DiagonalMatrix {
	double *pBlock;
	
	double (*operator)(struct class_DiagonalMatrix *p, int i, int j);
	void (*release)(struct class_DiagonalMatrix *p);
};

#else
#include <algorithm>
#include <cassert>
#include <cstring>

//namespace singular {

	/**
	 * Diagonal matrix.
	 */
	template < int M, int N >
	class DiagonalMatrix {
	public:
		enum {
			/** Number of diagonal elements. */
			L = M < N ? M : N
		};
	private:
		/**
		 * Memory block for the diagonal elements.
		 * The ith row and ith column is given by `elements[i]`.
		 */
		double* pBlock;
	public:
		/** Initializes a diagonal matrix filled with 0. */
		DiagonalMatrix() {
			this->pBlock = new double[L];
			std::fill(this->pBlock, this->pBlock + L, 0.0);
		}

		/**
		 * Initializes a diagonal matrix with given diagonal values.
		 *
		 * The diagonal matrix will look like,
		 * \f[
		 * \begin{bmatrix}
		 *   \text{values[0]} &        &                            \\
		 *                    & \ddots &                            \\
		 *                    &        & \text{values[min(M, N)-1]}
		 * \end{bmatrix}
		 * \f]
		 *
		 * The behavior is undefined if `values` has less than `min(M, N)`
		 * elements.
		 *
		 * @param values
		 *     Diagonal values of the matrix.
		 */
		explicit DiagonalMatrix(const double values[]) {
			this->pBlock = new double[L];
			memcpy(this->pBlock, values, sizeof(double) * L);
		}

		/**
		 * Steals the memory block from a given diagonal matrix.
		 *
		 * @param[in,out] copyee
		 *     Diagonal matrix from which the memory block is to be stolen.
		 *     No loger valid after this call.
		 */
#if SINGULAR_RVALUE_REFERENCE_SUPPORTED
		DiagonalMatrix(DiagonalMatrix&& copyee) : pBlock(copyee.pBlock) {
			copyee.pBlock = nullptr;
		}
#else
		DiagonalMatrix(const DiagonalMatrix& copyee) : pBlock(copyee.pBlock) {
			const_cast< DiagonalMatrix& >(copyee).pBlock = nullptr;
		}
#endif

		/** Releases the memory block of this diagonal matrix. */
		~DiagonalMatrix() {
			this->release();
		}

		/**
		 * Steals the memory block from a given diagonal matrix.
		 *
		 * @param[in,out] copyee
		 *     Diagonal matrix from which the memory block is to be stolen.
		 *     No longer valid after this call.
		 * @return
		 *     Reference to this diagonal matrix.
		 */
#if SINGULAR_RVALUE_REFERENCE_SUPPORTED
		DiagonalMatrix& operator =(DiagonalMatrix&& copyee) {
#else
		DiagonalMatrix& operator =(const DiagonalMatrix& copyee) {
#endif
			this->release();
			this->pBlock = copyee.pBlock;
#if SINGULAR_RVALUE_REFERENCE_SUPPORTED
			copyee.pBlock = nullptr;
#else
			const_cast< DiagonalMatrix& >(copyee).pBlock = nullptr;
#endif
			return *this;
		}

		/**
		 * Returns a clone of this matrix.
		 *
		 * @return
		 *     Clone of this matrix.
		 */
		inline DiagonalMatrix clone() const {
			return DiagonalMatrix(this->pBlock);
		}

		/**
		 * Returns the element at a given row and column.
		 *
		 * The behavior is undefined,
		 *  - if `i < 0` or `i >= M`,
		 *  - or if `j < 0` or `j >= N`
		 *
		 * @param i
		 *     Index of the row to be obtained.
		 * @param j
		 *     Index of the column to be obtained.
		 * @return
		 *     Element at the ith row and jth column.
		 *     0 if `i != j`.
		 */
		double operator ()(int i, int j) const {
			assert(i >= 0 && i < M);
			assert(j >= 0 && j < N);
			if (i == j) {
				return this->pBlock[i];
			} else {
				return 0.0;
			}
		}

		/**
		 * Transposes this matrix.
		 *
		 * @return
		 *     Transposed matrix.
		 */
		DiagonalMatrix< N, M > transpose() const {
			return DiagonalMatrix< N, M >(this->pBlock);
		}
	private:
#if SINGULAR_FUNCTION_DELETION_SUPPORTED
		/** Copy constructor is not allowed. */
		DiagonalMatrix(const DiagonalMatrix& copyee) = delete;

		/** Copy assignment is not allowed. */
		DiagonalMatrix& operator =(const DiagonalMatrix& copyee) = delete;
#elif SINGULAR_RVALUE_REFERENCE_SUPPORTED
		/** Copy constructor is not allowed. */
		DiagonalMatrix(const DiagonalMatrix& copyee) {}

		/** Copy assignment is not allowed. */
		DiagonalMatrix& operator =(const DiagonalMatrix& copyee) {
			return *this;
		}
#endif

		/**
		 * Releases the memory block of this matrix.
		 * Has no effect if the memory block has already been released.
		 */
		inline void release() {
			delete[] this->pBlock;
			this->pBlock = nullptr;
		}
	};

//}
#endif
#endif
