/*
Matrix

Author: Darcy Cook

Description: This is the header file for the matrix operation functions

*/




#ifndef MATRIX_H
#define MATRIX_H

#define CHECK_DIMENSIONS

typedef double Matrix_Unit;

typedef struct
{
    Matrix_Unit *data;
    unsigned int rows, cols;
} Matrix;


void create_matrix(Matrix *matrix_ptr, unsigned int rows, unsigned int cols);
void free_matrix(Matrix *matrix_ptr);

/* this retreives a single element froma matrix. This is a define rather than a function to speed up 
   access to the data.  The bounds on the elements are not checked to also allow for speedy access */
#define get_matrix_element(matrix_ptr, row, col) ((matrix_ptr)->data[((row) * (matrix_ptr)->cols) + (col)])

//this function sets all elements of the matrix to 0
void all_zeros(Matrix *matrix_ptr);

//this function sets all elements of a matrix to a particular value
void set_all_elements(Matrix *matrix_ptr, Matrix_Unit value);

//this function sets all elements of a matrix to a particular value
void set_block_elements(Matrix *matrix_ptr, Matrix_Unit value, int start_row, int start_col, int end_row, int end_col);

//this function performs matrix addition on m1 + m2, and saves the result to result.  m1, m2, and result must have the same dimensions
void matrix_addition(Matrix *m1_ptr, Matrix *m2_ptr, Matrix *result_ptr);

//this function performs matrix addition on particular blocks of m1 and m2, and saves the result to a block in result.  
void matrix_block_addition(int block_rows, int block_cols, 
						   	Matrix *m1_ptr, int m1_start_row, int m1_start_col, 
						 	Matrix *m2_ptr, int m2_start_row, int m2_start_col,
						 	Matrix *result_ptr, int result_start_row, int result_start_col);
						 	
//this function performs matrix subtraction on particular blocks of m1 and m2, and saves the result to a block in result.  
void matrix_block_subtraction(int block_rows, int block_cols, 
						   	Matrix *m1_ptr, int m1_start_row, int m1_start_col, 
						 	Matrix *m2_ptr, int m2_start_row, int m2_start_col,
						 	Matrix *result_ptr, int result_start_row, int result_start_col);

						 	
//this function divides all elements in a matrix by a scalar						 	
void matrix_division(Matrix *m1_ptr, double divisor, Matrix *result_ptr);

//this function creates the autocorrelation matrix of m1 given a matrix block and adds it to m2 and saves the result in result
void matrix_block_autocorrelation_sum(int block_rows, int block_cols, 
								  	Matrix *m1_ptr, int m1_start_row, int m1_start_col, 
								  	Matrix *m2_ptr, int m2_start_row, int m2_start_col,
						 		  	Matrix *result_ptr, int result_start_row, int result_start_col);
						 		  	
//this function copies the elements from the original matrix to the copy matrix.  The two matricies must be the same size.						 		  	
void copy_matrix(Matrix *original_ptr, Matrix *copy_ptr);

//this function assigns the identity matrix to the matrix passed as a pointer.						 		  	
void identity_matrix(Matrix *matrix_ptr);

//this function performs matrix multiplication on m1 and m2, and saves the result to result.  
//m1 and m2 must have the same inner dimensions (n by m and m by k), and result must have the outer
//dimensions of m1 and m2 (i.e. result is n by k) 
void matrix_mult(Matrix *m1_ptr, Matrix *m2_ptr, Matrix *result_ptr);

//this function performs matrix multiplication on the transpose of m1 and m2, and saves the result to result.  
//the transpose m1 and m2 must have the same inner dimensions (if m1 is m by n and then m2 must be m by k), and result must have the outer
//dimensions of the transpose of m1 and m2 (i.e. result is n by k) 
void matrix_transpose_mult(Matrix *m1_ptr, Matrix *m2_ptr, Matrix *result_ptr);

//this function creates a rotator matrix, Q, such that A is rotated in the j-i plane.  This means that 
//the result of Q'*A will have a 0 in the i, j element.  Q needs to have the same dimensions as A.
//Both A and Q must be square. The rotator is then multiplied by the current value of Q. 
void get_rotator_and_mult(Matrix *A, Matrix *Q, unsigned int i, unsigned int j);

//this function performs matrix multiplication on the transpose of certain blocks in m1 and m2, and saves the result to a block in result.  
// this function is for square blocks matrices only, so a block_dim by block_dim block in m1 will be transposed and multiplied
// by a block_dim by block_dim block in m2, and the result will be saved to a block_dim by block_dim block in result
void square_matrix_block_transpose_mult(int block_dim, Matrix *m1_ptr, int m1_start_row, int m1_start_col, 
						 	Matrix *m2_ptr, int m2_start_row, int m2_start_col,
						 	Matrix *result_ptr, int result_start_row, int result_start_col);
						 	
//this function performs matrix multiplication on certain blocks in m1 and m2, and saves the result to a block in result.  
// this function is for square blocks matrices only, so a block_dim by block_dim block in m1 will be multiplied
// by a block_dim by block_dim block in m2, and the result will be saved to a block_dim by block_dim block in result
void square_matrix_block_mult(int block_dim, Matrix *m1_ptr, int m1_start_row, int m1_start_col, 
						 	Matrix *m2_ptr, int m2_start_row, int m2_start_col,
						 	Matrix *result_ptr, int result_start_row, int result_start_col);
						 	
//this function performs scalar-matrix multiplication. A scalar is multiplied by every element in the matrix m1 and the
//result is saved in result_ptr.
void scalar_matrix_block_mult(Matrix_Unit scalar, Matrix *m1_ptr, int block_height, int block_width,
							  int m1_start_row, int m1_start_col, Matrix *result_ptr, 
							  int result_start_row, int result_start_col);
							  
//this function performs matrix multiplication on certain blocks in m1 and m2, and saves the result to a block in result.  
//the block in m1 must have the same inner dimensions as the block in m2, that is the width of the m1 block, must
//be the same as the height of the m2 block. The result matrix must be large enough to store the resulting matrix
//at the specified starting point. The resulting matrix will have a height equal to the m1 height, and
//a width equal to the m2 width. 
void matrix_block_mult(Matrix *m1_ptr, int m1_start_row, int m1_start_col, int m1_block_height, int m1_block_width,
					   Matrix *m2_ptr, int m2_start_row, int m2_start_col, int m2_block_height, int m2_block_width,
					   Matrix *result_ptr, int result_start_row, int result_start_col);
					   
//this function prints out the matrix using printf
void print_matrix_block(Matrix* m_ptr, int start_row, int start_col, int num_rows, int num_cols);

#include "matrix.c"

#endif
