Skip to content

Instantly share code, notes, and snippets.

@SibTiger
Created November 13, 2017 04:17
Show Gist options
  • Save SibTiger/1c3829b2dea43e59b277384422e092c2 to your computer and use it in GitHub Desktop.
Save SibTiger/1c3829b2dea43e59b277384422e092c2 to your computer and use it in GitHub Desktop.
Operating Systems: Multiply two matrices [Dynamic]
// =====================================================
// Programmer: Nicholas Gautier
// Class: CS3513; Operating System
// Assignment #: 3
// Due Date: 31. Oktober. 2017
// Instructor: Dr. Zhao
// Description: This program is designed to take two
// matrix's and multiply them to capture
// the answer.
// NOTES:
// Credits: Dr. Agyros; teaching Matrix Algebra
// Return Codes:
// 0 = Successful Operation
// !0 = See Operating System documentation
// =====================================================
#pragma region Inclusions
#include <stdio.h> // Basic input and output
#include <stdbool.h> // Because I was spoiled in C[++ | #]; give me bool datatypes!
#include <ctype.h> // Used for tolower() function
#include <stddef.h> // NULLPTR
#include <stdlib.h> // Malloc
#include <pthread.h> // Used for threads; because I want to join threads (kill them)
#pragma endregion
#pragma region Function Prototypes
void DrawInstructions(); // Displays instructions to the end user
void DrawHeader(); // Displays the program header on the terminal buffer
char RunAgainQuestion(); // Provides the option for the user to solve another
// matrix or close the program.
int FetchMatrixSize(bool); // Allow the end-user to specify the matrix size (length or height)
int FetchNumber(); // This function will allow the end-user to provide numerical data.
// Preferably
void AllocateMemory(int***, int, int); // Allocate the memory space (pointers)
void DeallocateMemory(int***, int); // Thrash the memory space (pointers)
void DefineMatrixDimensions(int, int); // Allows the user to define the matrix size
void DrawMatrix(int**, int, int); // This function will display the matrix on the screen.
void FillMatrix(int**, int, int, bool); // Allow the user to populate the matrix
bool CheckMultiplicationTheorem(int, int); // This function will check if the two matrices follows the multiplication theorem
void MultiplyMatricesThreadingSetup(); // Setup the multiplication process.
void MultiplyMatricesThreadingProcess(void *); // Multiply the two matrices
#pragma endregion
#pragma region Global Definitions
#define _NAME_ "Matrix Product Solver" // Name of this program
#define _VERSION_ "1.0" // Version of this program
#define _THREAD_COUNT_MAX_ 10 // Maximum number of threads possible
#define _MATRIX_MAX_SIZE_ 100 // [DEPRECATED] This is the max size possible of the matrices within this program.
#pragma endregion
#pragma region Global Data
// Matrices
int **matrixA; // User Defined Matrix
int **matrixB; // User Defined Matrix
int **matrixC; // Answer Matrix
// Matrices Dimensions
int sizeColumnsMatrixA; // Used to determine the size of the 'A' matrix's height
int sizeRowsMatrixA; // Used to determine the size of the 'A' matrix's length
int sizeColumnsMatrixB; // Used to determine the size of the 'B' matrix's height
int sizeRowsMatrixB; // Used to determine the size of the 'B' matrix's length
#pragma endregion
#pragma region Structs
struct MatrixIndexes
{
// Indexing
int indexColumn;
int indexRow;
}; // Struct : Matrix
#pragma endregion
// Main program entry point
int main()
{
// Declaration and Initializations
// ------------------------
char userChoice = 'n'; // This will be used to determine if the user wants to
// calculate another matrix product.
// ------------------------
do
{
printf("\n\n\n\n"); // Provide some padding before displaying any information.
// Program Initial Run
DrawHeader(); // Display the program header
DrawInstructions(); // Display the instructions to the end-user
// Setup the two matrices
printf("Please provide the following information:\n");
printf("First Matrix\n-----------\n");
sizeColumnsMatrixA = FetchMatrixSize(false);
sizeRowsMatrixA = FetchMatrixSize(true);
printf("\n");
printf("Second Matrix\n-----------\n");
sizeColumnsMatrixB = FetchMatrixSize(false);
sizeRowsMatrixB = FetchMatrixSize(true);
printf("\n");
// Make sure that the matrices are valid; if not - do not proceed any further.
printf("Checking if Multiplication Theorem. . .\n");
if (!CheckMultiplicationTheorem(sizeColumnsMatrixA, sizeRowsMatrixB))
{ // Multiplication Theorem validation failed
printf ("\a"); // Sound bell for error
printf ("\n\nMULTIPLICATION THEOREM CHECK FAILED!\n");
printf ("The two matrices fails the 'Multiplication Theorem' rules\n");
printf ("Please make sure that Matrix A's column space matches with Matrix B's row space.\n");
printf ("----\n");
printf ("First Matrix Dimensions: %d x %d\n", sizeRowsMatrixA, sizeColumnsMatrixA);
printf ("Second Matrix Dimensions: %d x %d\n\n", sizeRowsMatrixB, sizeColumnsMatrixB);
} // IF: Bad Matrices - Multiplication Theorem
else
{ // Multiplication Theorem validation succeeded
printf("Allocating the required memory space. . .\n");
AllocateMemory(&matrixA, sizeRowsMatrixA, sizeColumnsMatrixA);
AllocateMemory(&matrixB, sizeRowsMatrixB, sizeColumnsMatrixB);
AllocateMemory(&matrixC, sizeColumnsMatrixA, sizeRowsMatrixB); // Answer Matrix
printf("Populating matrices. . .\n");
FillMatrix(matrixA, sizeRowsMatrixA, sizeColumnsMatrixA, true);
FillMatrix(matrixB, sizeRowsMatrixB, sizeColumnsMatrixB, true);
printf("Displaying matrices. . .\n");
printf("Matrix A:\n");
DrawMatrix(matrixA, sizeRowsMatrixA, sizeColumnsMatrixA);
printf("Matrix B:\n");
DrawMatrix(matrixB, sizeRowsMatrixB, sizeColumnsMatrixB);
// ----
// Initialize the Matrix C (the answer)
MultiplyMatricesThreadingSetup();
printf("Matrix C:\n");
DrawMatrix(matrixC, sizeRowsMatrixA, sizeColumnsMatrixB);
// ----
// Thrash the matrices
printf("Thrashing the matrices. . . .\n");
DeallocateMemory(&matrixA, sizeRowsMatrixA);
DeallocateMemory(&matrixB, sizeRowsMatrixB);
DeallocateMemory(&matrixC, sizeRowsMatrixB); // Answer Matrix
} // ELSE: Matrices follows Multiplication Theorem
// Does the user wants to solve another matrix?
userChoice = RunAgainQuestion();
// ----
} while(tolower(userChoice) == 'y'); // Solve another matrix product?
printf("Closing %s...\n", _NAME_);
return 0; // Close the program
} // main()
// Multiply Matrices [Threading]
// ====================================
// This function will prepare the multiplication process between the two matrices.
void MultiplyMatricesThreadingSetup()
{
// Initialization and Declarations
// ------------------------
struct MatrixIndexes *objMatrixData = (struct MatrixIndexes*) malloc(sizeof(struct MatrixIndexes)); // Create a new object of the matrix
// This will merely be held for preparation
int i; // Utilized for the 'for' loop; as we can not declare
// within the for-loop.
int j; // Utilized for the 'for' loop; as we can not declare
// within the for-loop.
// ------------------------
for(i = 0; i < sizeRowsMatrixA; i++)
for(j = 0; j < sizeColumnsMatrixB; j++)
{
// Assign the required data
objMatrixData->indexColumn = i;
objMatrixData->indexRow = j;
// Prepare thread data
pthread_t tid; // Thread ID
pthread_attr_t threadAttributes; // Declaration for thread attributes
pthread_attr_init(&threadAttributes); // Generate default attributes
// Generate thread
pthread_create(&tid, &threadAttributes, MultiplyMatricesThreadingProcess, objMatrixData);
// Wait for the thread to finish - then kill it
pthread_join(tid, NULL);
} // Inner-loop
} // MultiplyMatricesThreadingSetup()
// Multiply Matrices [[Threading _Process_]]
// ====================================
// This function will perform the actual multiplication process between the two matrices.
void MultiplyMatricesThreadingProcess(void* objMatrix)
{
// Initialization and Declarations
// ------------------------
struct MatrixIndexes *matrixData = objMatrix; // Utilizes another matrix object for processing
int i; // Used for the for-loop
int sum = 0; // Holds the answer
// ------------------------
// Multiplication Process
for (i = 0; i < sizeColumnsMatrixA; i++)
sum += matrixA[matrixData->indexColumn][i] * matrixB[i][matrixData->indexRow];
// Store the result in the actual matrix
matrixC[matrixData->indexColumn][matrixData->indexRow] = sum;
// Close the thread
pthread_exit(0);
} // MultiplyMatricesThreadingProcess()
// Check Multiplication Theorem
// ====================================
// This function will make sure that the two matrices are following the
// multiplication theorem. To do this, we assure that the first matrix
// columns matches with the number of rows with the second matrix.
// ----
// Parameters:
// matrixAColumns [int]
// Number of columns within the first matrix
// matrixBRows [int]
// Number of rows within the second matrix
// ----
// Output:
// [Bool]
// false
// When the two matrices fail the multiplication theorem, false will be returned.
// true
// When the two matrices pass the multiplication theorem, true will be returned.
bool CheckMultiplicationTheorem(int matrixAColumns, int matrixBRows)
{
if (matrixAColumns != matrixBRows)
return false;
else
return true;
} // CheckMultiplicationTheorem()
// Fill Matrix
// ====================================
// This function will allow the end-user to input data into
// the matrix's indexes (2-Dimensional array)
// ----
// Parameters:
// matrixArray [int***]
// The matrix in which we are going to populate data into.
// matrixRow [int]
// How many rows for the matrix.
// matrixColumn [int]
// How many columns for the matrix.
void FillMatrix(int** matrixArray, int matrixRow, int matrixColumn, bool enforceRandomValues)
{
// Initialization and Declarations
// ------------------------
int i; // Utilized for the 'for' loop; as we can not declare within the for-loop.
int j; // Utilized for the 'for' loop; as we can not declare within the for-loop.
// ------------------------
// Scan through the matrix's indexes to add data
for (i = 0; i < matrixRow; i++)
for (j = 0; j < matrixColumn; j++)
{
if (enforceRandomValues)
// Automatically generate data
matrixArray[i][j] = rand() % _MATRIX_MAX_SIZE_;
else
{
// User inputs data into the matrix
// *****************************************
// * I don't always test my code. *
// * But when I do, I do it in production. *
// *****************************************
printf("Enter data for the Matrix Index: row [ %d ] - column [ %d ]\n", i, j);
matrixArray[i][j] = FetchNumber();
} // Else: Manual Input
} // FOR: Matrix Column
} // FillMatrix()
// Draw Matrix
// ====================================
// This function will merely display the matrix provided.
// ----
// Parameters:
// matrixArray [int***]
// The matrix we are going to draw
// matrixRow [int]
// How many rows for the matrix.
// matrixColumn [int]
// How many columns for the matrix.
void DrawMatrix(int** matrixArray, int matrixRow, int matrixColumn)
{
// Initialization and Declarations
// ------------------------
int i; // Utilized for the 'for' loop; as we can not declare within the for-loop.
int j; // Utilized for the 'for' loop; as we can not declare within the for-loop.
// ------------------------
// Provide some information about the matrix
printf("Matrix contains [ %d ] rows and [ %d ] columns.\n", matrixRow, matrixColumn);
// Scan through the matrix's indexes
for (i = 0; i < matrixRow; i++)
{
for (j = 0; j < matrixColumn; j++)
printf("%d\t", matrixArray[i][j]);
printf("\n"); // We will use this to make our output nicer; just like an ordinary Matrix\cube
} // FOR: Outer-loop
printf("\n\n");
} // DrawMatrix()
// Allocate Memory (Pointers)
// ====================================
// This function will allocate the memory locations necessary for the requested matrix.
// ----
// Parameters:
// matrixArray [int***]
// The matrix in which is going to be allocated
// matrixRow [int]
// How many rows requested for the matrix.
// matrixColumn [int]
// How many columns requested for the matrix.
void AllocateMemory(int*** matrixArray, int matrixRow, int matrixColumn)
{
// Initialization and Declarations
// ------------------------
int i = 0; // Utilized for the 'for' loop; as we can not declare within the for-loop.
// ------------------------
// Setup the initial memory space
*matrixArray = (int**)malloc(matrixRow * sizeof(int*));
// Allocate the memory spaces within the matrix rows and columns.
for (i = 0; i < matrixRow; i++)
(*matrixArray)[i] = (int*)malloc(matrixColumn * sizeof(int));
} // AllocateMemory()
// Deallocate Memory (Pointers)
// ====================================
// This function is designed to thrash the memory locations
// previously initialized within the pointer array (Matrix).
// ----
// Parameters:
// matrixArray [int***]
// The matrix in which is going to be deallocated
// matrixRow [int]
// How many rows this matrix contains.
void DeallocateMemory(int*** matrixArray, int matrixRow)
{
// Initialization and Declarations
// ------------------------
int i = 0; // Utilized for the 'for' loop; as we can not declare within the for-loop.
// ------------------------
// Deallocate the memory spaces within the matrix's rows
for (i = 0; i < matrixRow; i++)
free((*matrixArray)[i]);
// Deallocate the initial memory space of the matrix.
free(*matrixArray);
matrixArray = NULL;
} // DeallocateMemory()
// Fetch Matrix Size
// ====================================
// This function will allow the end-user to provide input in regards
// to the matrix size - either length or height.
// ----
// Parameters:
// sizeType [Bool]
// true = Length [rows]
// false = Height [columns]
// ----
// Output:
// int:
// returns a number that specifies the matrix size
// NOTE: This value is CHECKED against the max
// size possible for the matrix.
int FetchMatrixSize(bool sizeType)
{
// Declaration and Initializations
// ------------------------
char sizeChoiceRaw[_MATRIX_MAX_SIZE_]; // This will be used to capture user input - unfiltered.
int sizeChoice = 0; // This variable will hold the user's size choice - filtered
bool badInput = true; // This will be used to determine if the input was valid.
// ------------------------
do
{
// Provide instructions to the end-user
// Output Formatting; this may seem weird
printf("Please specify the matrix ");
// Length [Row]
if (sizeType)
printf("length ");
// Height [column]
else
printf("height ");
printf("size:\n");
// ----
// Capture the input from the end-user
printf(">>>>>> ");
fgets (sizeChoiceRaw, sizeof(sizeChoiceRaw), stdin); // Fetch the input
sscanf(sizeChoiceRaw, "%d", &sizeChoice); // Filter the input
// If non-int values exists
// then this will return '0' or 'EOL'.
// If a number is given before non-int's
// then the number will be captured - anything
// after will be dropped.
// Example: 32d2
// Saved: 32
// Dropped: d2
// Check the input to make sure that it is valid
if ((sizeChoice > 1) && (sizeChoice < (_MATRIX_MAX_SIZE_ + 1)))
// Valid input
badInput = false;
else
// Invalid input
printf("The size of [ %d ] is not valid! Try again\n\n", sizeChoice);
} while (badInput);
return (int)sizeChoice; // Return the choice; casting for assurance sakes
} // int FetchMatrixSize()
// Fetch Number
// ====================================
// This function will retrieve a number from the end-user
// and send it back to the calling function.
// NOTE: This function uses scanf; if '0' is returned - then
// '0' will be used, even if it was accidental by user. For instance,
// instead of a number the user inputs a char, from there scanf()
// will use '0'.
// ----
// Output:
// int
// Provides any number to the calling function.
int FetchNumber()
{
// Declaration and Initializations
// ------------------------
char sizeChoiceRaw[_MATRIX_MAX_SIZE_]; // This will be used to capture user input - unfiltered.
int numData; // This variable will hold the user's size choice - filtered
// ------------------------
// Capture the input from the end-user
printf(">>>>>> ");
fgets (sizeChoiceRaw, sizeof(sizeChoiceRaw), stdin); // Fetch the input
sscanf(sizeChoiceRaw, "%d", &numData); // Filter the input
// If non-int values exists
// then this will return '0' or 'EOL'.
// If a number is given before non-int's
// then the number will be captured - anything
// after will be dropped.
// Example: 32d2
// Saved: 32
// Dropped: d2
return (int)numData; // Casting for assurance sakes
} // FetchNumber()
// Run Again Question
// ====================================
// This function is dedicated into asking the end-user if they want
// to terminate the program or run again.
// ----
// Output:
// char
// Returns only 'one' character of any value directly from the user.
char RunAgainQuestion()
{
// Declaration and Initializations
// ------------------------
char choice; // Used for capturing STDIN from the end-user [keyboard]
bool badInput = true; // Make sure that the input is valid
// ------------------------
do
{
printf("Solve another matrix?\n");
printf("\t[Y]es OR [N]o\n");
printf(">>>>>> ");
choice = getchar();
getchar(); // Get rid of the 'enter key ghosting'.
// Because the enter key is STILL in STDIN.
if ((tolower(choice) != 'y') && (tolower(choice) != 'n'))
// Bad input
printf("Input of [ %c ] was invalid!\n\n", choice);
else
// Valid input
badInput = false;
} while (badInput);
return choice;
} // RunAgainQuestion()
// Draw Header
// ====================================
// This function is designed to provide a header.
void DrawHeader()
{
printf("%s - Version: %s\n", _NAME_, _VERSION_);
printf("--------------------------------------------\n\n");
} // DrawHeader()
// Draw Instructions()
// ====================================
// This function will provide instructions to the end-user, along with how
// this program works.
void DrawInstructions()
{
printf("Instructions\n");
printf("----------------\n");
printf("This program will take two matrices and multiply them for you.\nHowever, this program will work with threads to accomplish this specific task.\n");
printf("You will be asked to provide the following information: Matrix size by length and height, and lastly - the values within the vertices's.\n");
printf("======================================\n");
printf("\n\n");
} // DrawInstructions()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment