CCCPaste
--- .\make-one.py
import os
from typing import List, Tuple
from dotenv import load_dotenv

load_dotenv()

delimiter = os.environ.get('DELIMITER')


def get_files(path=".", excludes=['.exe', '.txt', '.env']) -> List[str]:

# Items in the current folder
folder_items = os.listdir(path)

# Filter out the folders
folders = list(
filter(lambda x: '.' not in x, folder_items)
)
# Filter out the files
files = list(
map(
lambda file_name: os.path.join(path, file_name),
filter(lambda x: ('.' in x) and all(
[exclude not in x for exclude in excludes]), folder_items)
)
)

# It means we're at the deepest level
if len(folders) == 0:
return files

# Iterate over all the folders to the files
for folder in folders:
files += get_files(path=os.path.join(path, folder))

# Return the files we got
return files


def dump_into_one(files: list[str], output: str = "out.txt"):
"""
Dumps all the files into big text file,
delimiter=''
"""

with open(output, encoding='utf-8', mode='w+') as outFile:
# Read and write from every single file
content = ""

content_string = "{0} {1}\n{2}\n{0}\n"

for file in files:
with open(file, encoding='utf-8', mode='r') as inFile:
content += content_string.format(delimiter,
file, inFile.read())

# Write the read content
outFile.write(content)
return


def extract_from_dump(out_file: str = "out.txt") -> List[Tuple[str, str]]:

# Output filenames with their content
output: List[Tuple[str, str]] = []

# Read the file and split on the delimiter
content = ""

with open(out_file, encoding='utf-8', mode='r+') as readFile:
content = readFile.read().split(delimiter)

# Filters for the content
content = list(
filter(lambda x: x != '\n' and x != delimiter and x != '', content)
)

# Iterate over the content
for i, file_content in enumerate(content):
filename = file_content.split('\n')[0].strip()
content = file_content.replace(filename, '')
# Record this
output.append((filename, content))

return output


if __name__ == '__main__':

def bundle_files():
# To compress the files
files = get_files()

for file in files:
print(file)

dump_into_one(files)

def debundle_files():
# To debundle the files
files = extract_from_dump(out_file="out.txt")
# Make the directories exist
for (filename, file_content) in files:
# Split the path of the file
head, tail = os.path.split(filename)

try:
os.mkdir(head)
except FileExistsError:
pass

# Write the file
with open(filename, encoding='utf-8', mode='w+') as writeFile:
writeFile.write(file_content)
# end-with
# end-for

bundle_files()
# structure = extract_from_dump()

# for (filename, _) in structure:
# print(filename)

# debundle_files()

---
--- .\graphs\bipartite.cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

class Solution
{
// colors a component
private:
bool check(int start, int V, vector<int> adj[], int color[])
{
queue<int> q;
q.push(start);
color[start] = 0;
while (!q.empty())
{
int node = q.front();
q.pop();

for (auto it : adj[node])
{
// if the adjacent node is yet not colored
// you will give the opposite color of the node
if (color[it] == -1)
{

color[it] = !color[node];
q.push(it);
}
// is the adjacent guy having the same color
// someone did color it on some other path
else if (color[it] == color[node])
{
return false;
}
}
}
return true;
}

public:
bool isBipartite(int V, vector<int> adj[])
{
int *color = new int[V];
for (int i = 0; i < V; i++)
color[i] = -1;

for (int i = 0; i < V; i++)
{
// if not coloured
if (color[i] == -1)
{
if (check(i, V, adj, color) == false)
{
delete[] color;
return false;
}
}
}

delete[] color;
return true;
}
};

void addEdge(vector<int> adj[], int u, int v)
{
adj[u].push_back(v);
adj[v].push_back(u);
}

int main()
{

// V = 4, E = 4
vector<int> adj[4];

addEdge(adj, 0, 2);
addEdge(adj, 0, 3);
addEdge(adj, 2, 3);
addEdge(adj, 3, 1);

Solution obj;
bool ans = obj.isBipartite(4, adj);
if (ans)
cout << "1\n";
else
cout << "0\n";

return 0;
}
---
--- .\graphs\cycles.cpp
// A C++ Program to detect cycle in a graph
#include <iostream>
#include <list>
using namespace std;

class Graph
{
// No. of vertices
int V;

// Pointer to an array containing adjacency lists
list<int> *adj;

// Used by isCyclic()
bool isCyclicUtil(int v, bool visited[], bool *rs);

public:
Graph(int V);
void addEdge(int v, int w);
bool isCyclic();
};

Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}

void Graph::addEdge(int v, int w)
{
// Add w to v’s list.
adj[v].push_back(w);
}

// DFS function to find if a cycle exists
bool Graph::isCyclicUtil(int v, bool visited[],
bool *recStack)
{
if (visited[v] == false)
{
// Mark the current node as visited
// and part of recursion stack
visited[v] = true;
recStack[v] = true;

// Recur for all the vertices adjacent to this
// vertex
list<int>::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); ++i)
{
if (!visited[*i] && isCyclicUtil(*i, visited, recStack))
return true;
else if (recStack[*i])
return true;
}
}

// Remove the vertex from recursion stack
recStack[v] = false;
return false;
}

// Returns true if the graph contains a cycle, else false
bool Graph::isCyclic()
{
// Mark all the vertices as not visited
// and not part of recursion stack
bool *visited = new bool[V];
bool *recStack = new bool[V];
for (int i = 0; i < V; i++)
{
visited[i] = false;
recStack[i] = false;
}

// Call the recursive helper function
// to detect cycle in different DFS trees
for (int i = 0; i < V; i++)
if (!visited[i] && isCyclicUtil(i, visited, recStack))
return true;

return false;
}

// Driver code
int main()
{
// Create a graph
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);

// Function call
if (g.isCyclic())
cout << "Graph contains cycle";
else
cout << "Graph doesn't contain cycle";
return 0;
}

---
--- .\graphs\main.cpp
#include <iostream>
#include "paths.hpp"
#include "prims.hpp"

int main()
{
/*
Graph *g = new Graph(13);

g->addNeighbors(0, {7, 9, 11});
g->addNeighbors(1, {8, 10});
g->addNeighbors(2, {3, 12});
g->addNeighbors(3, {2, 4, 7});
g->addNeighbors(4, {3});
g->addNeighbors(5, {6});
g->addNeighbors(6, {5});
g->addNeighbors(7, {3, 6, 11});
g->addNeighbors(8, {1, 12});
g->addNeighbors(9, {8, 10});
g->addNeighbors(10, {1});
g->addNeighbors(11, {0});
g->addNeighbors(12, {2, 8});

int src = 4, dest = 1;

std::cout << "Breadth First ";
int level = g->breadthFirstSearch(src, dest);
std::cout << std::endl;
if (level == -1)
{
std::cout << "Couldn't find a path from " << src << " to " << dest << "\n";
}

std::cout << "Depth First ";
int dfsLevel = g->depthFirstSearch(src, dest);
std::cout << std::endl;
if (dfsLevel == -1)
{
std::cout << "Couldn't find a path from " << src << " to " << dest << "\n";
}

// Free the taken memory
delete g;
*/

vector<vector<pair<int, int>>> adjList{
{{1, 1}, {2, 6}, {3, 5}}, // 0
{{0, 1}, {2, 6}}, // 1
{{0, 6}, {1, 6}, {4, 7}, {5, 3}}, // 2
{{0, 5}, {5, 2}, {6, 10}}, // 3
{{2, 7}, {7, 12}}, // 4
{{2, 3}, {3, 2}, {7, 8}}, // 5
{{3, 10}, {7, 7}, {8, 3}}, // 6
{{4, 12}, {5, 8}, {6, 7}, {8, 8}}, // 7
{{6, 3}, {7, 8}}, // 8
};

int start = 1;
int cost = primsMinimumCostSpanningTree(start, adjList);

std::cout << "The cost of the minimum spanning tree is: " << cost << "\n";

return 0;
}
---
--- .\graphs\paths.hpp
#ifndef PATHS
#define PATHS

#include <vector>
#include <queue>
#include <stack>

template <class T>
using vector = std::vector<T>;

template <class T>
using queue = std::queue<T>;

template <class T>
using stack = std::stack<T>;

template <class T1, class T2>
using pair = std::pair<T1, T2>;

/**
* Class to represent graphs
*/
class Graph
{
private:
vector<vector<int>> adjList;
int vertexCount;

/**
* Prints the shortest path found in the graph
* @param parent The list containing parent for each vertex
* @param src The starting vertex of the journey
* @param dest The ending vertex of the journey
*/
int printShortestPath(vector<int> const &parent, const int src, const int dest)
{
static int level = 0;

// If we've reached the root of the shortest path tree
if (parent[src] == -1)
{
std::cout << "Shortest path between " << src << " and " << dest << " is " << src << " ";
return level;
}
// Recursively find the path
printShortestPath(parent, parent[src], dest);
level++;

if (src < vertexCount)
{
std::cout << src << " ";
}
return level;
}

public:
/**
* The vertex numbers start from '0'
*/
Graph(int vertices)
{
// Add empty list for the adjacency
for (int i = 0; i < vertices; i++)
adjList.push_back({});
// Store the number of the vertices
vertexCount = vertices;
}

/**
* Adds an edge to the graph from u -> v
* @param u The first vertex of the dge
* @param v The second vertex of the edge
*/
void addEdge(int u, int v)
{
// Check if the vertex is within the bounds
if (0 > u || vertexCount <= u)
return;

if (0 > v || vertexCount <= v)
return;

// Add the vertex to the list
adjList[u].push_back(v);
}

/**
* Adds a whole list for the adjacency list
* @param vertex The vertex to add neighbors to
* @param neighbors The neighbor list for the vertex
*/
void addNeighbors(const int &vertex, const vector<int> &neighbors)
{
adjList[vertex] = neighbors;
}

/**
* To perform a breadth first search on the graph
* @param start The starting vertex of the graph
* @param end The destination for the search
*/
int breadthFirstSearch(int const start, int const end)
{
// Create a queue frontier for the search
queue<int> q;
// Add the start to the queue
q.push(start);

// Make a list to keep track of parents
vector<int> parent(vertexCount, -1);

// Make a visited list
vector<bool> visited(vertexCount, false);
// Mark the current node as visited
visited[start] = true;

while (!q.empty())
{
// Get the current element to explore, pop it from the queue
int current = q.front();
q.pop();

// Check if we've reached our goal
if (current == end)
{
return printShortestPath(parent, current, end);
}

// Iterate over all the neighbors to the find the
for (int const &neighbor : adjList[current])
{
if (!visited[neighbor])
{
q.push(neighbor);
visited[neighbor] = true;
parent[neighbor] = current;
}
}
}

// Return -1, if we don't find any path
return -1;
}

/**
* To perform a depth-first search on the graph
* @param start The starting vertex of the graph
* @param end The destination for the search
*/
int depthFirstSearch(int const start, int const end)
{
// Create a stack frontier
stack<int> stack;
// Keep track of the visited nodes
vector<bool> visited(vertexCount, false);

// Make a list of parents to create a path later
vector<int> parent(vertexCount, -1);

// Add the current node to frontier, and mark it visited
stack.push(start);
visited[start] = true;

// Repeat till the stack is empty
while (!stack.empty())
{

// Get the element at top of the stack
int current = stack.top();
stack.pop();

// Check if we've reached our destination
if (current == end)
{
return printShortestPath(parent, current, end);
}

// Mark this node as visited
visited[current] = true;

// Explore all the neighbors
for (int const &neighbor : adjList[current])
{
if (!visited[neighbor])
{
// Add this node to the frontier to explore later
stack.push(neighbor);
// Mark the current element as parent of the neighbor (in dfs tree)
parent[neighbor] = current;
// Mark this node as visited
visited[neighbor] = true;
}
}
}

// Return -1, if we didn't find any path
return -1;
}
};

#endif
---
--- .\graphs\prims.cpp
#include <stdio.h>
#include <limits.h>
#define vertices 5 /*Define the number of vertices in the graph*/
/* create minimum_key() method for finding the vertex that has minimum key-value and that is not added in MST yet */
int minimum_key(int k[], int mst[])
{
int minimum = INT_MAX, min, i;

/*iterate over all vertices to find the vertex with minimum key-value*/
for (i = 0; i < vertices; i++)
if (mst[i] == 0 && k[i] < minimum)
minimum = k[i], min = i;
return min;
}
/* create prim() method for constructing and printing the MST.
The g[vertices][vertices] is an adjacency matrix that defines the graph for MST.*/
void prim(int g[vertices][vertices])
{
/* create array of size equal to total number of vertices for storing the MST*/
int parent[vertices];
/* create k[vertices] array for selecting an edge having minimum weight*/
int k[vertices];
int mst[vertices];
int i, count, edge, v; /*Here 'v' is the vertex*/
for (i = 0; i < vertices; i++)
{
k[i] = INT_MAX;
mst[i] = 0;
}
k[0] = 0; /*It select as first vertex*/
parent[0] = -1; /* set first value of parent[] array to -1 to make it root of MST*/
for (count = 0; count < vertices - 1; count++)
{
/*select the vertex having minimum key and that is not added in the MST yet from the set of vertices*/
edge = minimum_key(k, mst);
mst[edge] = 1;
for (v = 0; v < vertices; v++)
{
if (g[edge][v] && mst[v] == 0 && g[edge][v] < k[v])
{
parent[v] = edge, k[v] = g[edge][v];
}
}
}
/*Print the constructed Minimum spanning tree*/
printf("\n Edge \t Weight\n");
for (i = 1; i < vertices; i++)
printf(" %d <-> %d %d \n", parent[i], i, g[i][parent[i]]);
}
int main()
{
int g[vertices][vertices] = {
{0, 0, 3, 0, 0},
{0, 0, 10, 4, 0},
{3, 10, 0, 2, 6},
{0, 4, 2, 0, 1},
{0, 0, 6, 1, 0},
};
prim(g);
return 0;
}
---
--- .\graphs\strongly.cpp
// Program to check if the given directed graph
// is strongly connected or not.
#include <iostream>
#include <list>
#include <queue>
using namespace std;

class Graph
{
int vertices; // To store no. of vertices
list<int> *adjList;

// Function to print DFS starting from v
void BFSUtil(int v, bool visited[]);

public:
Graph(int V)
{
this->vertices = V;
adjList = new list<int>[V];
}
~Graph() { delete[] adjList; }

// Function to add an edge
void addEdge(int v, int w);

// Function that returns if the
// graph is strongly connected or not.
bool isStronglyConnected();

// Function that returns reverse (or transpose)
// of this graph
Graph getTranspose();
};

// A recursive function to print DFS starting from v
void Graph::BFSUtil(int v, bool visited[])
{
// Creating a queue for BFS traversal
list<int> queue;
visited[v] = true;
queue.push_back(v);

list<int>::iterator i;

while (!queue.empty())
{
// Dequeue a vertex from queue
v = queue.front();
queue.pop_front();
for (i = adjList[v].begin(); i != adjList[v].end(); ++i)
{
if (!visited[*i])
{
visited[*i] = true;
queue.push_back(*i);
}
}
}
}

Graph Graph::getTranspose()
{
Graph g(vertices);
for (int v = 0; v < vertices; v++)
{
list<int>::iterator i;
for (i = adjList[v].begin(); i != adjList[v].end(); ++i)
g.adjList[*i].push_back(v);
}
return g;
}

void Graph::addEdge(int v, int w)
{
adjList[v].push_back(w);
}

// Function that returns true if graph
// is strongly connected
bool Graph::isStronglyConnected()
{
// Step 1: Mark all the vertices as not
// visited (For first BFS)
bool *visited = new bool[vertices];
for (int i = 0; i < vertices; i++)
visited[i] = false;

// Do BFS traversal starting from the first vertex.
BFSUtil(0, visited);

// If BFS traversal doesn’t visit all vertices
// return false.
for (int i = 0; i < vertices; i++)
if (visited[i] == false)
{
delete[] visited;
return false;
}

// Create a reversed graph
Graph gr = getTranspose();

// Step 4: Mark all the vertices as not
// visited (For second BFS)
for (int i = 0; i < vertices; i++)
visited[i] = false;

gr.BFSUtil(0, visited);

// If all vertices are not visited in the second DFS
// return false
for (int i = 0; i < vertices; i++)
if (visited[i] == false)
{
delete[] visited;
return false;
}

delete[] visited;
return true;
}

// Driver program to test the above functions
int main()
{
Graph g1(7);
g1.addEdge(0, 1);
g1.addEdge(1, 2);
g1.addEdge(2, 3);
g1.addEdge(3, 0);
g1.addEdge(2, 4);
g1.addEdge(4, 2);
g1.addEdge(4, 5);
g1.addEdge(5, 6);
g1.addEdge(6, 4);

g1.isStronglyConnected() ? cout << "Yes" : cout << "No";
return 0;
}
---
--- .\graphs\topologicalSorting.cpp
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

// Function to perform DFS and topological sorting
void topologicalSortUtil(int v, vector<vector<int>> &adj,
vector<bool> &visited,
stack<int> &Stack)
{
// Mark the current node as visited
visited[v] = true;

// Recur for all adjacent vertices
for (int i : adj[v])
{
if (!visited[i])
topologicalSortUtil(i, adj, visited, Stack);
}

// Push current vertex to stack which stores the result
Stack.push(v);
}

// Function to perform Topological Sort
void topologicalSort(vector<vector<int>> &adj, int V)
{
stack<int> Stack; // Stack to store the result
vector<bool> visited(V, false);

// Call the recursive helper function to store
// Topological Sort starting from all vertices one by
// one
for (int i = 0; i < V; i++)
{
if (!visited[i])
topologicalSortUtil(i, adj, visited, Stack);
}

// Print contents of stack
while (!Stack.empty())
{
cout << Stack.top() << " ";
Stack.pop();
}
}

int main()
{

// Number of nodes
int V = 4;

// Edges
vector<vector<int>> edges = {{0, 1}, {1, 2}, {3, 1}, {3, 2}};

// Graph represented as an adjacency list
vector<vector<int>> adj(V);

for (auto i : edges)
{
adj[i[0]].push_back(i[1]);
}

cout << "Topological sorting of the graph: ";
topologicalSort(adj, V);

return 0;
}
---
--- .\knapsack\knapsack.cpp
#include <iostream>
#include <vector>
using namespace std;

/**
* @param itemCount The number of items to pick from
* @param sackWeight The weight of the knapsack
* @param weights The weights to pick from
* @param profits The profits relating to the weights
* @param pickSeq The picking sequence of the items (0 or 1)
*/
int knapsackForItem(int itemCount, int sackWeight, vector<int> const &weights, vector<int> const &profits, vector<bool> &pickSeq, vector<vector<int>> memTable = {})
{

// If the item is non-existent or the sack weight
// is non-existent, then return
if (itemCount <= 0 || sackWeight <= 0)
return 0;

// Check if we have memoized answer
if (memTable.size() != 0 && memTable[itemCount - 1][sackWeight - 1] != -1)
{
return memTable[itemCount - 1][sackWeight - 1];
}

// If the weight of the current item is more than
// the sack weight, then return
if (weights[itemCount - 1] > sackWeight)
{
// We cannot fit this item in the sack
pickSeq[itemCount - 1] = false;

// Get the result from the table if possible
if (memTable.size() != 0 && memTable[itemCount - 1][sackWeight - 1] != -1)
return memTable[itemCount - 1][sackWeight - 1];

// Memoize the result
int result = knapsackForItem(itemCount - 1, sackWeight, weights, profits, pickSeq);

if (memTable.size() != 0)
{
memTable[itemCount - 1][sackWeight - 1] = result;
return memTable[itemCount - 1][sackWeight - 1];
}

// We're not given a memTable
return result;
}

/* Else, we need to get the result where the profit is maximum */

// Not picking the item
int notPicking = knapsackForItem(itemCount - 1, sackWeight, weights, profits, pickSeq);
// Picking the item
int picking = knapsackForItem(itemCount - 1, sackWeight - weights[itemCount - 1], weights, profits, pickSeq) + profits[itemCount - 1];

// If we're given a memTable
if (memTable.size() != 0)
{
memTable[itemCount - 1][sackWeight - 1] = max(notPicking, picking);
}

if (notPicking > picking)
{
pickSeq[itemCount - 1] = false;
std::cout << "Not picking the item with weight: " << weights[itemCount - 1] << "\n";
return notPicking;
}
else
{
pickSeq[itemCount - 1] = true;
std::cout << "Picking the item with weight: " << weights[itemCount - 1] << "\n";
return picking;
}
}

/**
* Returns the answer using memoized
*/
int optimalKnapsack(int itemCount, int sackWeight, vector<int> const &weights, vector<int> const &profits, vector<bool> &pickSeq)
{
// Make a memoized table
vector<vector<int>> memTable(itemCount, vector<int>(sackWeight, -1));

// Return the memoized version
return knapsackForItem(itemCount, sackWeight, weights, profits, pickSeq, memTable);
}

int main()
{

/*
Consider the problem where
weights = {3, 4, 6, 5}
profits = {2, 3, 1, 4}

The max weight is: 8 Kgs
*/

// The weights for the item to pick
vector<int> weights{3, 4, 6, 5};

// The profits corresponding to those items
vector<int> profits{2, 3, 1, 4};

// Number of items to pick from
const int itemCount = 4;

// The maximum carry weight for the sack
const int maxWeight = 8;

// The picking sequence
vector<bool> pickSeq(itemCount, false);

int maxProfit = optimalKnapsack(itemCount, maxWeight, weights, profits, pickSeq);

std::cout << "Max profit from the knapsack is: " << maxProfit << std::endl;

// Picking sequence
std::cout << "Answer to 0-1 Knapsack: [ ";
for (bool const &didPick : pickSeq)
{
std::cout << didPick << " ";
}
std::cout << "]" << std::endl;

return 0;
}
---
--- .\multilpication\strassen.cpp
#include <iostream>
#include <vector>

using namespace std;

// Function to add two matrices
vector<vector<int>> matrixAdd(const vector<vector<int>> &A, const vector<vector<int>> &B)
{
int n = A.size();
vector<vector<int>> C(n, vector<int>(n));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
C[i][j] = A[i][j] + B[i][j];
}
}
return C;
}

// Function to subtract two matrices
vector<vector<int>> matrixSubtract(const vector<vector<int>> &A, const vector<vector<int>> &B)
{
int n = A.size();
vector<vector<int>> C(n, vector<int>(n));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
C[i][j] = A[i][j] - B[i][j];
}
}
return C;
}

// Function to multiply two matrices using Strassen's algorithm
vector<vector<int>> strassenMultiply(const vector<vector<int>> &A, const vector<vector<int>> &B)
{
int n = A.size();

// Base case: If the matrix size is 1x1
if (n == 1)
{
vector<vector<int>> C(1, vector<int>(1));
C[0][0] = A[0][0] * B[0][0];
return C;
}

// Splitting matrices into quadrants
int mid = n / 2;
vector<vector<int>> A11(mid, vector<int>(mid)), A12(mid, vector<int>(mid)), A21(mid, vector<int>(mid)), A22(mid, vector<int>(mid));
vector<vector<int>> B11(mid, vector<int>(mid)), B12(mid, vector<int>(mid)), B21(mid, vector<int>(mid)), B22(mid, vector<int>(mid));

for (int i = 0; i < mid; i++)
{
for (int j = 0; j < mid; j++)
{
A11[i][j] = A[i][j];
A12[i][j] = A[i][j + mid];
A21[i][j] = A[i + mid][j];
A22[i][j] = A[i + mid][j + mid];

B11[i][j] = B[i][j];
B12[i][j] = B[i][j + mid];
B21[i][j] = B[i + mid][j];
B22[i][j] = B[i + mid][j + mid];
}
}

// Calculating intermediate matrices
vector<vector<int>> M1 = strassenMultiply(matrixAdd(A11, A22), matrixAdd(B11, B22));
vector<vector<int>> M2 = strassenMultiply(matrixAdd(A21, A22), B11);
vector<vector<int>> M3 = strassenMultiply(A11, matrixSubtract(B12, B22));
vector<vector<int>> M4 = strassenMultiply(A22, matrixSubtract(B21, B11));
vector<vector<int>> M5 = strassenMultiply(matrixAdd(A11, A12), B22);
vector<vector<int>> M6 = strassenMultiply(matrixSubtract(A21, A11), matrixAdd(B11, B12));
vector<vector<int>> M7 = strassenMultiply(matrixSubtract(A12, A22), matrixAdd(B21, B22));

// Calculating result matrix
vector<vector<int>> C(n, vector<int>(n));
vector<vector<int>> C11 = matrixAdd(matrixSubtract(matrixAdd(M1, M4), M5), M7);
vector<vector<int>> C12 = matrixAdd(M3, M5);
vector<vector<int>> C21 = matrixAdd(M2, M4);
vector<vector<int>> C22 = matrixAdd(matrixSubtract(matrixAdd(M1, M3), M2), M6);

// Combining result quadrants into the result matrix
for (int i = 0; i < mid; i++)
{
for (int j = 0; j < mid; j++)
{
C[i][j] = C11[i][j];
C[i][j + mid] = C12[i][j];
C[i + mid][j] = C21[i][j];
C[i + mid][j + mid] = C22[i][j];
}
}

return C;
}

// Function to print a matrix
void printMatrix(const vector<vector<int>> &matrix)
{
int n = matrix.size();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout << matrix[i][j] << " ";
}
cout << endl;
}
}

int main()
{
vector<vector<int>> A = {{1, 2}, {3, 4}};
vector<vector<int>> B = {{5, 6}, {7, 8}};

cout << "Matrix A:" << endl;
printMatrix(A);
cout << endl;

cout << "Matrix B:" << endl;
printMatrix(B);
cout << endl;

cout << "Result of matrix multiplication (Strassen's algorithm):" << endl;
vector<vector<int>> C = strassenMultiply(A, B);
printMatrix(C);

return 0;
}
---
--- .\sorting\countSort.hpp
#ifndef COUNT_SORT
#define COUNT_SORT

/**
* Sorts the array using count-sort
* @param array The array to sort
* @param size The length of the array to sort
*/
void countSort(int array[], int size)
{
int maxElement = array[0];
for (int i = 1; i < size; i++)
{
if (array[i] > maxElement)
maxElement = array[i];
}

// Initialize count array with all elements as 0
int *count = new int[maxElement + 1];
// Initialize it with zeros
for (size_t j = 0; j < maxElement + 1; j++)
count[j] = 0;

// Count the occurrences of each element
for (int i = 0; i < size; i++)
{
count[array[i]]++;
}

// Fill the original array with sorted elements
int index = 0;
for (int i = 0; i <= maxElement; i++)
{
while (count[i] > 0)
{
array[index++] = i;
count[i]--;
}
}
delete[] count;
}

#endif
---
--- .\sorting\heapSort.hpp
#ifndef HEAP_SORT
#define HEAP_SORT

/**
* Swaps two values
* @param a The first value to swap
* @param b The second value to swap with
*/
template <class T>
void swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}

/**
* Builds a max heap from the given array
* @param array The array to perform max-heapify on
* @param size The length of the array
* @param i The index to start heapify with '0' for root
*/
void maxHeapify(int array[], int size, int i)
{
int largest = i; // Initialize largest as the root
int left = 2 * i + 1; // Left child of the root
int right = 2 * i + 2; // Right child of the root

// Find the new largest element
if (left < size && array[left] > array[largest])
largest = left;
if (right < size && array[right] > array[largest])
largest = right;

// If the largest is not the root, then swap
if (largest != i)
{
swap(array[i], array[largest]);
// Recursively heapify the affected sub-tree
maxHeapify(array, size, largest);
}
}

/**
* Sorts the array using heap sort
* @param array The array to sort
* @param size The length of the array
*/
void heapSort(int array[], int size)
{
// Build a max-heap (rearrange array)
for (int i = (size / 2) - 1; i >= 0; i--)
{
maxHeapify(array, size, i);
}

// One-by-one extract an element from the heap
for (int i = size - 1; i >= 0; i--)
{
// Move the current root to the end
swap(array[0], array[i]);

// Call the max heapify on the reduced heap
maxHeapify(array, i, 0);
}
}

#endif
---
--- .\sorting\insertionSort.hpp

#ifndef INSERTION_SORT
#define INSERTION_SORT

/**
* Insertion Sort (in-place)
* @param array The array to sort
* @param size The size of the array
*/
void insertionSort(int array[], int size)
{
for (int i = 1; i < size; i++)
{
int key = array[i];
int j = i - 1;

while (j >= 0 && array[j] > key)
{
// Shift the elements to the right
array[j + 1] = array[j];
j = j - 1;
}
array[j + 1] = key;
}
}

#endif
---
--- .\sorting\main.cpp
#include <iostream>

// Internal Dependencies
#include "insertionSort.hpp"
#include "mergeSort.hpp"
// #include "heapSort.hpp"
#include "quickSort.hpp"

/**
* @param array The array to print
* @param size The size of the array
* @param endline The character at the end of the line
*/
template <class T>
void printArray(T array[], int size, char endline = '\0')
{
std::cout << "[ ";
for (int i = 0; i < size; i++)
std::cout << array[i] << " ";
std::cout << "]" << endline;
}

#include "countSort.hpp"

/**
* The driver function of the code
*/
int main()
{

int a[] = {1, 4, 7, 4, 2, 8, 1, 9, 0, 3};

// Get the size of the array
int size = sizeof(a) / sizeof(a[0]);

// Before sorting
printArray(a, size, '\n');

// quickSort(a, 0, size - 1);
countSort(a, size);

// After sorting
printArray(a, size, '\n');

return 0;
}

---
--- .\sorting\mergeSort.hpp
#ifndef MERGE_SORT
#define MERGE_SORT

/**
* Merges the sub-arrays
*/
void merge(int array[], int const left, int const mid, int const right)
{
int const subArrayLeft = mid - left + 1;
int const subArrayRight = right - mid;

// Create new temporary arrays
int *leftArray = new int[subArrayLeft];
int *rightArray = new int[subArrayRight];

// Copy the data of sub-arrays to this new array
for (size_t i = 0; i < subArrayLeft; i++)
{
leftArray[i] = array[left + i];
}
for (size_t i = 0; i < subArrayRight; i++)
{
rightArray[i] = array[mid + 1 + i];
}

int i = 0; // Iterator for the left sub-array
int j = 0; // Iterator for the right sub-array
int k = left; // Iterator for the merged array

while (i < subArrayLeft && j < subArrayRight)
{
if (leftArray[i] <= rightArray[j])
{
// Choose the element from the left sub-array
array[k] = leftArray[i];
i++;
}
else
{
// Choose the element from the right sub-array
array[k] = rightArray[j];
j++;
}
// Increment the sub-array iterator
k++;
}

// Add the remaining left array elements
while (i < subArrayLeft)
{
array[k] = leftArray[i];
i++;
k++;
}

// Add the remaining right array elements
while (j < subArrayRight)
{
array[k] = rightArray[j];
j++;
k++;
}

delete[] leftArray;
delete[] rightArray;
}

/**
* Sorts the array using merge sort, (in-place)
* @param array The array to sort
* @param begin The beginning of the index in the array
* @param end The end index of the array (should be 'length-1')
*/
void mergeSort(int array[], int const begin, int const end)
{
if (begin >= end)
return;
// Get the middle of the array and divide it there
int mid = (begin + end) / 2;
// Sort the left half of the array
mergeSort(array, begin, mid);
// Sort the right half of the array
mergeSort(array, mid + 1, end);
// Merge the two halves
merge(array, begin, mid, end);
}

#endif
---
--- .\sorting\quickSort.hpp
#ifndef QUICK_SORT
#define QUICK_SORT

/**
* Swaps two values
* @param a The first value to swap
* @param b The second value to swap with
*/
template <class T>
void swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}

/**
* Function to partition the array and return the pivot index
*/
int partition(int array[], int low, int high)
{
// Choosing the last element as pivot
int pivot = array[high];
// Index of the smaller element
int i = low - 1;

for (int j = low; j <= high - 1; j++)
{
// If the current element is smaller than or equal to pivot
if (array[j] <= pivot)
{
i++; // Increment the index of smaller element
swap(array[i], array[j]);
}
}
swap(array[i + 1], array[high]);
return (i + 1);
}

void quickSort(int array[], int low, int high)
{
if (low >= high)
return;

// Partitioning index
int pi = partition(array, low, high);
// Separately sort elements before and after partition
quickSort(array, low, pi - 1);
quickSort(array, pi + 1, high);
}

#endif
---