Initial commit

This commit is contained in:
jslightham
2023-05-21 23:28:11 -04:00
commit 0360e7dfcc
31 changed files with 2068 additions and 0 deletions

62
P1/Calculator.cpp Normal file
View File

@@ -0,0 +1,62 @@
#include "Calculator.h"
#include "VariableNode.h"
using namespace std;
Calculator::Calculator(int cap)
{
this->data = new VariableLinkedList(cap);
}
Calculator::~Calculator()
{
delete this->data;
}
/// @brief Define a new variable
/// @param name The name of the new variable
/// @param value The initial value to store in the variable
/// @return True if the variable was added, false otherwise
bool Calculator::define(string name, double value)
{
return this->data->insert(name, value);
}
/// @brief Perform an operation on two variables, and store the result in a thrid
/// @param v1 The first variable to perform the operation on
/// @param v2 The second variable to perform the opetation on
/// @param v3 The variable to store the output result in
/// @param operation A lambda function to apply on the two variables
/// @return True if successful, false otherwise
bool Calculator::performOperation(string v1, string v2, string out, function<double(double, double)> operation)
{
VariableNode *vn1 = this->data->search(v1);
VariableNode *vn2 = this->data->search(v2);
VariableNode *output = this->data->search(out);
// If a single one of the variables is not found, we cannot continue.
if (vn1 == nullptr || vn2 == nullptr || output == nullptr)
return false;
output->setValue(operation(vn1->getValue(), vn2->getValue()));
return true;
}
/// @brief Remove the given variable from storage
/// @param name The name of the variable to remove
/// @return True if the variable was successfully removed, false otherwise
bool Calculator::remove(string name)
{
return this->data->remove(name);
}
/// @brief Print out the value of the given variable
/// @param name The name of the variable to print out
void Calculator::print(string name)
{
VariableNode *temp = this->data->search(name);
if (temp == nullptr)
cout << "variable " << name << " not found" << endl;
else
cout << temp->getValue() << endl;
}

22
P1/Calculator.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include <iostream>
#include <functional>
#include "VariableNode.h"
#include "VariableLinkedList.h"
class Calculator
{
public:
bool define(std::string name, double value);
bool performOperation(std::string v1, std::string v2, std::string output, std::function<double(double, double)> operation);
bool remove(std::string name);
void print(std::string name);
Calculator(int cap);
~Calculator();
private:
VariableLinkedList *data;
};
#endif

2
P1/Makefile Normal file
View File

@@ -0,0 +1,2 @@
all: test.cpp Calculator.cpp VariableNode.cpp VariableLinkedList.cpp
g++ -std=c++11 test.cpp Calculator.cpp VariableNode.cpp VariableLinkedList.cpp

88
P1/VariableLinkedList.cpp Normal file
View File

@@ -0,0 +1,88 @@
#include "VariableLinkedList.h"
using namespace std;
VariableLinkedList::VariableLinkedList(int cap)
{
this->maxCount = cap;
this->count = 0;
this->head = nullptr;
}
VariableLinkedList::~VariableLinkedList()
{
if (head != nullptr)
head->DestroyAll();
}
/// @brief Insert a variable into the head of the linked list
/// @param name The name of the variable to insert
/// @param value The double value to initialize the variable with
/// @return True if the variable was added, false otherwise
bool VariableLinkedList::insert(string name, double value)
{
// Ensure there is space
if (count == maxCount)
return false;
// Ensure the variable does not exist
VariableNode *s = this->search(name);
if (s != nullptr)
return false;
// Insert a new VariableNode into the head.
head = new VariableNode(name, value, head);
count++;
return true;
}
/// @brief Remove the variable with name from the linked list
/// @param name The name of the variable to remove
/// @return True if removed, false otherwise
bool VariableLinkedList::remove(string name)
{
// Root node must be handled in a seperate case
if (head != nullptr && head->getName() == name)
{
VariableNode *temp = head->getNext();
delete head;
head = temp;
count --;
return true;
}
// Search for the node before the one we want to remove.
VariableNode *currentIndex = this->head;
while (currentIndex != nullptr && currentIndex->getNext() != nullptr &&
currentIndex->getNext()->getName() != name)
currentIndex = currentIndex->getNext();
// currentIndex will be null if the index does not exist.
if (currentIndex == nullptr || currentIndex->getNext() == nullptr)
return false;
VariableNode *temp = currentIndex->getNext()->getNext();
delete currentIndex->getNext();
count--;
currentIndex->setNext(temp);
return true;
}
/// @brief Search for the variable with the given name
/// @param name The name of the variable to search for
/// @return A pointer to a valid VariableNode if the node exists, nullptr otherwise
VariableNode *VariableLinkedList::search(string name)
{
VariableNode *currentIndex = this->head;
while (currentIndex != nullptr)
{
if (currentIndex->getName() == name)
{
return currentIndex;
}
currentIndex = currentIndex->getNext();
}
return nullptr;
}

22
P1/VariableLinkedList.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef VARIABLELINKEDLIST_H
#define VARIABLELINKEDLIST_H
#include <iostream>
#include <functional>
#include "VariableNode.h"
class VariableLinkedList
{
public:
bool insert(std::string name, double value);
VariableNode *search(std::string name);
bool remove(std::string name);
VariableLinkedList(int cap);
~VariableLinkedList();
private:
VariableNode *head;
int count;
int maxCount;
};
#endif

44
P1/VariableNode.cpp Normal file
View File

@@ -0,0 +1,44 @@
#include "VariableNode.h"
using namespace std;
VariableNode::VariableNode(std::string name, double value, VariableNode *next)
{
this->name = name;
this->value = value;
this->next = next;
}
string VariableNode::getName()
{
return name;
}
double VariableNode::getValue()
{
return value;
}
void VariableNode::setValue(double value)
{
this->value = value;
}
void VariableNode::setNext(VariableNode *next)
{
this->next = next; // Be careful when calling setNext, or we could have a memory leak.
}
VariableNode *VariableNode::getNext()
{
return next;
}
/// @brief Delete all children nodes of this element
void VariableNode::DestroyAll()
{
if (this->next != nullptr)
this->next->DestroyAll();
delete this;
}

26
P1/VariableNode.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef VARIABLENODE_H
#define VARIABLENODE_H
#include <iostream>
class VariableNode
{
public:
std::string getName();
double getValue();
void setValue(double value);
VariableNode *getNext();
void setNext(VariableNode *next);
VariableNode(std::string name, double value, VariableNode *next);
void DestroyAll();
private:
std::string name;
double value;
VariableNode *next;
};
#endif

92
P1/test.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include <iostream>
#include "Calculator.h"
using namespace std;
int main ()
{
string cmd;
Calculator *calc = nullptr;
while (cin >> cmd)
{
if (cmd == "CRT")
{
int N;
cin >> N;
if (calc != nullptr)
delete calc;
calc = new Calculator(N);
cout << "success" << endl;
}
else if (cmd == "DEF")
{
string name;
double val;
cin >> name;
cin >> val;
if (calc->define(name, val))
{
cout << "success" << endl;
}
else
{
cout << "failure" << endl;
}
}
else if (cmd == "ADD" || cmd == "SUB")
{
string v1;
string v2;
string out;
cin >> v1;
cin >> v2;
cin >> out;
function<double(double, double)> operation;
if (cmd == "ADD")
operation = [](double d1, double d2) {return d1 + d2;};
else
operation = [](double d1, double d2) {return d1 - d2;};
if (calc->performOperation(v1, v2, out, operation))
{
cout << "success" << endl;
}
else
{
cout << "failure" << endl;
}
}
else if (cmd == "REM")
{
string name;
cin >> name;
if (calc->remove(name))
cout << "success" << endl;
else
cout << "failure" << endl;
}
else if (cmd == "PRT")
{
string name;
cin >> name;
calc->print(name);
}
else if (cmd == "END")
{
break;
}
}
delete calc;
}

156
P2/ChainingHashTable.cpp Normal file
View File

@@ -0,0 +1,156 @@
#include <iostream>
#include "ChainingHashTable.h"
#include "Process.h"
ChainingHashTable::ChainingHashTable(int size)
{
processList = new std::vector<Process *>[size];
maxCount = size;
count = 0;
}
ChainingHashTable::~ChainingHashTable()
{
for (int i = 0; i < maxCount; i++)
{
for (int j = 0; j < processList[i].size(); j++)
{
delete processList[i][j];
}
}
delete[] processList;
}
/// @brief Insert a process into the hash table
/// @param p the process to insert
/// @return true if insertion was successful, false otherwise
bool ChainingHashTable::Insert(Process *p)
{
if (count >= maxCount)
{
return false;
}
int hashedIndex = p->getPid() % maxCount;
int indexSize = processList[hashedIndex].size();
if (indexSize == 0)
{
processList[hashedIndex].emplace_back(p);
count++;
return true;
}
// attempt to insert in the middle of the vector
for (int i = 0; i < indexSize; i++)
{
if (processList[hashedIndex][i]->getPid() == p->getPid())
{
return false;
}
// store in descending order (9, 8, 7,...)
if (processList[hashedIndex][i]->getPid() < p->getPid())
{
processList[hashedIndex].insert(processList[hashedIndex].begin() + i, p);
count++;
return true;
}
}
// insert at the end of the vector if we have to
processList[hashedIndex].emplace_back(p);
return true;
}
/// @brief Search the hash table for the PID
/// @param PID the PID to search the hash table for
/// @return the index if in the hash table, or -1 otherwise
int ChainingHashTable::Search(unsigned int PID)
{
int hashedIndex = PID % maxCount;
int indexSize = processList[hashedIndex].size();
for (int i = 0; i < indexSize; i++)
{
if (processList[hashedIndex][i]->getPid() == PID)
{
return hashedIndex;
}
}
return -1;
}
/// @brief Get a pointer to the process with PID
/// @param PID the PID to look for
/// @return a pointer to the process, or nullptr if the process does not exist
Process *ChainingHashTable::Get(unsigned int PID)
{
int hashedIndex = PID % maxCount;
int indexSize = processList[hashedIndex].size();
for (int i = 0; i < indexSize; i++)
{
if (processList[hashedIndex][i]->getPid() == PID)
{
return processList[hashedIndex][i];
}
}
return nullptr;
}
/// @brief Remove a process from the hash table with a given PID
/// @param PID the PID of the process to remove
/// @return true if the process was removed, false otherwise
bool ChainingHashTable::Remove(unsigned int PID)
{
int hashedIndex = PID % maxCount;
int indexSize = processList[hashedIndex].size();
for (int i = 0; i < indexSize; i++)
{
if (processList[hashedIndex][i]->getPid() == PID)
{
delete processList[hashedIndex][i];
processList[hashedIndex].erase(processList[hashedIndex].begin() + i);
count--;
return true;
}
}
return false;
}
/// @brief Print out the value of the chain at index m
/// @param m the index to print the chain of
void ChainingHashTable::Print(unsigned int m)
{
if (m >= maxCount)
{
std::cout << "chain is empty" << std::endl;
return;
}
int indexSize = processList[m].size();
for (int i = 0; i < indexSize; i++)
{
if (i != 0)
std::cout << " ";
std::cout << processList[m][i]->getPid();
}
if (indexSize < 1)
{
std::cout << "chain is empty";
}
std::cout << std::endl;
}

23
P2/ChainingHashTable.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef CHAININGHASHTABLE_H
#define CHAININGHASHTABLE_H
#include <vector>
#include "Process.h"
#include "HashTable.h"
class ChainingHashTable : public HashTable
{
public:
bool Insert(Process *p);
int Search(unsigned int PID);
Process *Get(unsigned int PID);
bool Remove(unsigned int PID);
void Print(unsigned int m);
ChainingHashTable(int size);
~ChainingHashTable();
private:
std::vector<Process *> *processList;
};
#endif

177
P2/DoubleHashTable.cpp Normal file
View File

@@ -0,0 +1,177 @@
#include <iostream>
#include <cmath>
#include "DoubleHashTable.h"
#include "Process.h"
DoubleHashTable::DoubleHashTable(int size)
{
processList = new Process *[size];
maxCount = size;
count = 0;
for (int i = 0; i < size; i++)
{
processList[i] = nullptr;
}
}
DoubleHashTable::~DoubleHashTable()
{
for (int i = 0; i < maxCount; i++)
{
if (processList[i] != nullptr)
delete processList[i];
}
delete[] processList;
}
/// @brief Insert a process into the hash table
/// @param p pointer to the process to insert
/// @return true if successful, false otherwise
bool DoubleHashTable::Insert(Process *p)
{
if (count >= maxCount)
{
return false;
}
// Continually hash until we have reached a nullpointer, -1, or the end
int emptyIndex = -1;
for (int i = 0; i < maxCount; i++)
{
unsigned int hashedIndex = HashIndex(p->getPid(), i);
// If we have reached a nullptr, and have not seen any empty slots before
if (processList[hashedIndex] == nullptr && emptyIndex == -1)
{
// Replace the nullpointer with the process
processList[hashedIndex] = p;
return true;
}
// If we have reached a nullpointer and have seen an empty slot before
else if (processList[hashedIndex] == nullptr && emptyIndex != -1)
{
// Replace the -1 with the process
hashedIndex = HashIndex(p->getPid(), emptyIndex);
delete processList[hashedIndex];
processList[hashedIndex] = p;
return true;
}
// If we find a process with the same PID already, exit
else if (processList[hashedIndex]->getPid() == p->getPid())
{
return false;
}
// If we have found a slot of -1
else if (processList[hashedIndex]->getStartingIndex() == -1 && emptyIndex == -1)
{
// If we can insert in the empty slot (have reached the end)
emptyIndex = i;
if (!(i + 1 < maxCount))
{
delete processList[hashedIndex];
processList[hashedIndex] = p;
return true;
}
}
}
// Insert into the empty slot - theoretically we do not need the code in the if statement of the last else if clause, but it works...
if (emptyIndex != -1)
{
unsigned int hashedIndex = HashIndex(p->getPid(), emptyIndex);
delete processList[hashedIndex];
processList[hashedIndex] = p;
return true;
}
return false;
}
/// @brief Search the hash table for the given PID
/// @param PID the pid to search for
/// @return the index of the process, or -1 if the process does not exist
int DoubleHashTable::Search(unsigned int PID)
{
for (int i = 0; i < maxCount; i++)
{
int hashedIndex = HashIndex(PID, i);
if (hashedIndex >= maxCount || processList[hashedIndex] == nullptr)
{
return -1;
}
else if (processList[hashedIndex]->getPid() == PID)
{
return hashedIndex;
}
}
return -1;
}
/// @brief Search the hash table for the given PID, and return a pointer to the process object
/// @param PID the pid to search for
/// @return A valid process object if it exists, or a nullpointer otherwise
Process *DoubleHashTable::Get(unsigned int PID)
{
for (int i = 0; i < maxCount; i++)
{
int hashedIndex = HashIndex(PID, i);
if (hashedIndex >= maxCount || processList[hashedIndex] == nullptr)
{
return nullptr;
}
else if (processList[hashedIndex]->getPid() == PID)
{
return processList[hashedIndex];
}
}
return nullptr;
}
/// @brief Remove a process object from the hash table
/// @param PID the PID of the process to remove
/// @return true if the process was successfully removed, false otherwise
bool DoubleHashTable::Remove(unsigned int PID)
{
for (int i = 0; i < maxCount; i++)
{
int hashedIndex = HashIndex(PID, i);
if (hashedIndex >= maxCount || processList[hashedIndex] == nullptr)
{
return false;
}
else if (processList[hashedIndex]->getPid() == PID)
{
delete processList[hashedIndex];
processList[hashedIndex] = new Process(0, -1);
return true;
}
}
return false;
}
/// @brief Compute the hash of k and i.
/// @param k the k value to hash (PID)
/// @param i the i value to hash (index)
/// @return the hashed value
int DoubleHashTable::HashIndex(unsigned int k, int i)
{
unsigned int h1 = k % maxCount;
unsigned int h2 = (int)std::floor(k / maxCount) % maxCount;
if (h2 % 2 == 0)
h2++;
return (h1 + i * h2) % maxCount;
}
void DoubleHashTable::Print(unsigned int m)
{
// Not implemented
}

24
P2/DoubleHashTable.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef DOUBLEHASHTABLE_H
#define DOUBLEHASHTABLE_H
#include <vector>
#include "Process.h"
#include "HashTable.h"
class DoubleHashTable : public HashTable
{
public:
bool Insert(Process *p);
int Search(unsigned int PID);
Process *Get(unsigned int PID);
bool Remove(unsigned int PID);
void Print(unsigned int m);
DoubleHashTable(int size);
~DoubleHashTable();
private:
Process **processList;
int HashIndex(unsigned int k, int i);
};
#endif

20
P2/HashTable.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <vector>
#include "Process.h"
class HashTable
{
public:
virtual bool Insert(Process *p) = 0;
virtual int Search(unsigned int PID) = 0;
virtual Process *Get(unsigned int PID) = 0;
virtual bool Remove(unsigned int PID) = 0;
virtual void Print(unsigned int m) = 0;
protected:
int maxCount;
int count;
};
#endif

2
P2/Makefile Normal file
View File

@@ -0,0 +1,2 @@
all: test.cpp ChainingHashTable.cpp DoubleHashTable.cpp Process.cpp VirtualMemory.cpp
g++ -std=c++11 test.cpp ChainingHashTable.cpp DoubleHashTable.cpp Process.cpp VirtualMemory.cpp

17
P2/Process.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include "Process.h"
Process::Process(unsigned int PID, int startingIndex)
{
this->PID = PID;
this->startingIndex = startingIndex;
}
unsigned int Process::getPid()
{
return this->PID;
}
int Process::getStartingIndex()
{
return this->startingIndex;
}

16
P2/Process.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef PROCESS_H
#define PROCESS_H
class Process
{
public:
unsigned int getPid();
int getStartingIndex();
Process(unsigned int PID, int startingIndex);
private:
unsigned int PID;
int startingIndex;
};
#endif

136
P2/VirtualMemory.cpp Normal file
View File

@@ -0,0 +1,136 @@
#include <iostream>
#include "VirtualMemory.h"
#include "ChainingHashTable.h"
#include "DoubleHashTable.h"
VirtualMemory::VirtualMemory(int n, int p, bool chaining)
{
if (chaining)
{
ht = new ChainingHashTable(n / p);
}
else
{
ht = new DoubleHashTable(n / p);
}
this->chaining = chaining;
this->memorySize = n;
this->pageSize = p;
this->memory = new int[n];
// push the indices onto the free memory
for (int i = 0; i < n / p; i++)
{
freeMemory.push_back(i);
}
}
VirtualMemory::~VirtualMemory()
{
if (chaining)
{
delete (ChainingHashTable *)ht;
}
else
{
delete (DoubleHashTable *)ht;
}
delete[] memory;
}
/// @brief Allocate memory for process PID
/// @param PID the PID to allocate memory for
/// @return true if successful, false otherwise
bool VirtualMemory::Insert(unsigned int PID)
{
if (freeMemory.size() <= 0)
return false;
int memoryIndex = freeMemory.back();
freeMemory.pop_back();
Process *p = new Process(PID, memoryIndex);
if (!ht->Insert(p))
{
delete p;
freeMemory.push_back(memoryIndex);
return false;
}
return true;
}
/// @brief Search the hash table for the index of PID
/// @param PID the PID of the process to search for
/// @return the index of the process object, -1 if the object does not exist
int VirtualMemory::Search(unsigned int PID)
{
return ht->Search(PID);
}
/// @brief Write to a process's memory
/// @param PID The PID of the memory to write to
/// @param addr The virtual address of the memory to write to
/// @param x The value to write into memory
/// @return true if successful, false otherwise
bool VirtualMemory::Write(unsigned int PID, int addr, int x)
{
if (addr >= pageSize || addr < 0)
return false;
Process *p = ht->Get(PID);
// if we do not have any memory allocated to PID
if (p == nullptr)
return false;
memory[p->getStartingIndex() * pageSize + addr] = x;
return true;
}
/// @brief Read the memory
/// @param PID The PID of the process to read memory of
/// @param addr The virtual address of memory to read from
void VirtualMemory::Read(unsigned int PID, int addr)
{
if (addr >= pageSize)
{
std::cout << "failure" << std::endl;
return;
}
Process *p = ht->Get(PID);
// if we do not have any memory allocated to PID
if (p == nullptr)
{
std::cout << "failure" << std::endl;
return;
}
std::cout << addr << " " << memory[p->getStartingIndex() * pageSize + addr] << std::endl;
}
/// @brief Deallocate memory from process PID.
/// @param PID The PID of the process to dealocate memory from
/// @return true if successful, false otherwise
bool VirtualMemory::Delete(unsigned int PID)
{
Process *p = ht->Get(PID);
if (p == nullptr)
return false;
freeMemory.push_back(p->getStartingIndex());
return ht->Remove(PID);
}
/// @brief Print the chain of the hash table at index m
/// @param m the index of the chain to print
void VirtualMemory::Print(int m)
{
ht->Print(m);
}

30
P2/VirtualMemory.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef VIRTUALMEMORY_H
#define VIRTUALMEMORY_H
#include "Process.h"
#include "HashTable.h"
class VirtualMemory
{
public:
VirtualMemory(int n, int p, bool chaining);
~VirtualMemory();
bool Insert(unsigned int PID);
int Search(unsigned int PID);
bool Write(unsigned int PID, int addr, int x);
void Read(unsigned int PID, int addr);
bool Delete(unsigned int PID);
void Print(int m);
private:
HashTable *ht;
int memorySize;
int pageSize;
int *memory;
std::vector<int> freeMemory;
bool chaining;
};
#endif

103
P2/test.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include <iostream>
#include "VirtualMemory.h"
using namespace std;
int main()
{
string cmd;
VirtualMemory *vm = nullptr;
bool chaining = true;
while (cin >> cmd)
{
if (cmd == "OPEN")
{
chaining = false;
}
else if (cmd == "ORDERED")
{
chaining = true;
}
if (cmd == "M")
{
int N;
int P;
cin >> N;
cin >> P;
vm = new VirtualMemory(N, P, chaining);
cout << "success" << endl;
}
else if (cmd == "INSERT")
{
unsigned int PID;
cin >> PID;
if (vm->Insert(PID))
cout << "success" << endl;
else
cout << "failure" << endl;
}
else if (cmd == "SEARCH")
{
unsigned int PID;
cin >> PID;
int ret = vm->Search(PID);
if (ret == -1)
cout << "not found" << endl;
else
cout << "found " << PID << " in " << ret << endl;
}
else if (cmd == "WRITE")
{
unsigned int PID;
int ADDR;
int x;
cin >> PID;
cin >> ADDR;
cin >> x;
if (vm->Write(PID, ADDR, x))
cout << "success" << endl;
else
cout << "failure" << endl;
}
else if (cmd == "READ")
{
unsigned int PID;
int ADDR;
cin >> PID;
cin >> ADDR;
vm->Read(PID, ADDR);
}
else if (cmd == "DELETE")
{
unsigned int PID;
cin >> PID;
if (vm->Delete(PID))
cout << "success" << endl;
else
cout << "failure" << endl;
}
else if (cmd == "PRINT")
{
int m;
cin >> m;
vm->Print(m);
}
else if (cmd == "END")
{
break;
}
}
delete vm;
}

2
P3/Makefile Normal file
View File

@@ -0,0 +1,2 @@
all: trietest.cpp Trie.cpp
g++ -std=c++11 trietest.cpp Trie.cpp

218
P3/Trie.cpp Normal file
View File

@@ -0,0 +1,218 @@
#include "Trie.h"
#include "illegal_exception.h"
Trie::Trie()
{
children = new Trie *[MAX_SLOTS];
isEnd = false;
for (int i = 0; i < MAX_SLOTS; i++)
{
children[i] = nullptr;
}
count = 0;
}
Trie::~Trie()
{
delete[] children;
}
/// @brief Wrapper function to recursively insert string s into the trie.
/// @param s The string to insert into the trie.
/// @return True if the string was inserted, false otherwise.
bool Trie::Insert(std::string s)
{
// Wrapper function is needed to meet illegal_exception criteria without having O(n^2) runtime (recursive function).
for (int i = 0; i < s.length(); i++)
{
if (s[i] < ASCII_FACTOR || s[i] > ASCII_UPPER)
{
throw illegal_exception();
}
}
return PerformInsert(s);
}
/// @brief Recursive function to insert string s into the trie.
/// @param s The string to insert into the trie.
/// @return True if the string was inserted successfully, false otherwise.
bool Trie::PerformInsert(std::string s)
{
if (s.length() > 0) // Add until there are no more characters.
{
char c = s.at(0);
// Create the new trie if it does not exist. This condition will never be true if the word cannot be inserted.
if (children[c - ASCII_FACTOR] == nullptr)
children[c - ASCII_FACTOR] = new Trie();
// Recursively insert, checking if the previous insertion was successful (if so, increment count).
if (children[c - ASCII_FACTOR]->PerformInsert(s.substr(1)))
{
count++;
return true;
}
}
// If there are no characters and we have not reached a word end then the word is not already added.
else if (!this->isEnd)
{
this->isEnd = true;
count++;
return true;
}
return false; // If the word is already in the trie, return false.
}
/// @brief Wrapper function to remove an element from the trie.
/// @param s The string to remove from the trie.
/// @return True if the element was removed successfully, false otherwise.
bool Trie::Remove(std::string s)
{
// Achieve O(n) runtime using wrapper function.
for (int i = 0; i < s.length(); i++)
{
if (s[i] < ASCII_FACTOR || s[i] > ASCII_UPPER)
{
throw illegal_exception();
}
}
return PerformRemove(s);
}
/// @brief Recursively remove string s from the trie.
/// @param s The string to remove from the trie.
/// @return True if the string was removed, false otherwise.
bool Trie::PerformRemove(std::string s)
{
if (s.length() > 0) // Remove until there are no more characters to remove.
{
// If we have reached a trie that does not have the correct child, we cannot remove the word.
char c = s.at(0);
if (children[c - ASCII_FACTOR] == nullptr)
return false;
// Recursively remove the remaining bit of the word. Subtracting from the count if successful.
if (children[c - ASCII_FACTOR]->PerformRemove(s.substr(1)))
{
count--;
// Free any child tries that have a count of 0. These are guaranteed to have no children.
for (int i = 0; i < MAX_SLOTS; i++)
{
if (children[i] != nullptr && children[i]->IsEmpty())
{
delete children[i];
children[i] = nullptr;
}
}
return true; // If previous removes were successful, this remove is also successful.
}
}
// If we have reached the end of the word, we can succesfully remove it from the trie.
else if (this->isEnd)
{
this->isEnd = false;
count--;
return true;
}
return false; // When we are out of characters, but did not find a word end, cannot remove.
}
/// @brief Determine if the trie is empty.
/// @return True if the trie is empty, false otherwise.
bool Trie::IsEmpty()
{
return count == 0;
}
/// @brief Empty the trie.
void Trie::Clear()
{
// We do not need to worry about dangling pointers, since everything is being deleted in order.
for (int i = 0; i < MAX_SLOTS; i++)
{
if (children[i] != nullptr)
children[i]->Clear(); // Recursively delete any non-null pointer children.
}
delete this;
}
/// @brief Perform a depth first search on the trie for string s, outputting the result by performing in place modification of the vector out.
/// @param out
/// @param s
void Trie::DepthFirstSearch(std::vector<std::string> *out, std::string s)
{
if (isEnd)
out->push_back(s); // If we have reached the end, put the word into the output
// Recursively search children nodes, in order from A-Z.
for (int i = 0; i < MAX_SLOTS; i++)
{
if (children[i] != nullptr)
children[i]->DepthFirstSearch(out, s + (char)(i + ASCII_FACTOR));
}
}
/// @brief Get the number of words in the trie.
/// @return An integer of the number of words in the trie.
int Trie::Size()
{
return count;
}
/// @brief Wrapper function to count the number of suffixes for a given string s.
/// @param s
/// @return
int Trie::CountSuffixes(std::string s)
{
for (int i = 0; i < s.length(); i++)
{
if (s[i] < ASCII_FACTOR || s[i] > ASCII_UPPER)
{
throw illegal_exception();
}
}
return PerformCountSuffixes(s);
}
int Trie::PerformCountSuffixes(std::string s)
{
if (s.empty())
return count;
char c = s.at(0);
int index = c - ASCII_FACTOR;
if (children[index] != nullptr)
return children[index]->PerformCountSuffixes(s.substr(1));
else
return 0;
}
bool Trie::SpellCheck(std::vector<std::string> *out, std::string check, std::string s)
{
if (check.empty() && isEnd)
return true;
else if (check.empty())
return false;
char c = check.at(0);
int index = c - ASCII_FACTOR;
;
if (children[index] != nullptr)
{
if (children[index]->SpellCheck(out, check.substr(1), s + c))
{
return true;
}
else
{
children[index]->DepthFirstSearch(out, s + c);
return true;
}
}
return false;
}

33
P3/Trie.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef TRIE_H
#define TRIE_H
#include <iostream>
#include <vector>
class Trie
{
public:
Trie();
~Trie();
bool Insert(std::string s);
bool Remove(std::string s);
void DepthFirstSearch(std::vector<std::string> *out, std::string s);
bool IsEmpty();
void Clear();
int Size();
int CountSuffixes(std::string s);
bool SpellCheck(std::vector<std::string> *out, std::string check, std::string s);
private:
const int MAX_SLOTS = 26;
const int ASCII_FACTOR = 65;
const int ASCII_UPPER = 90;
int count;
bool isEnd;
Trie **children;
bool PerformInsert(std::string s);
bool PerformRemove(std::string s);
int PerformCountSuffixes(std::string s);
};
#endif

8
P3/illegal_exception.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef ILLEGAL_EXCEPTION_H
#define ILLEGAL_EXCEPTION_H
#include <exception>
class illegal_exception : std::exception {};
#endif

153
P3/trietest.cpp Normal file
View File

@@ -0,0 +1,153 @@
#include <iostream>
#include <fstream>
#include "Trie.h"
#include <vector>
#include "illegal_exception.h"
using namespace std;
const string FILE_NAME = "corpus.txt";
void PrintVector(vector<string> *out)
{
for (int i = 0; i < out->size(); i++)
{
cout << (*out)[i];
if (i < out->size() - 1)
cout << " ";
}
}
int main()
{
string cmd;
string addWord;
ifstream fin(FILE_NAME);
Trie *trie = new Trie();
while (cin >> cmd)
{
if (cmd == "load")
{
if (!fin.is_open())
fin.open(FILE_NAME);
while (fin >> addWord)
{
trie->Insert(addWord);
}
fin.close();
cout << "success" << endl;
}
else if (cmd == "i")
{
string word;
cin >> word;
try
{
if (trie->Insert(word))
cout << "success" << endl;
else
cout << "failure" << endl;
}
catch (illegal_exception e)
{
cout << "illegal argument" << endl;
}
}
else if (cmd == "e")
{
string word;
cin >> word;
try
{
if (trie->Remove(word))
cout << "success" << endl;
else
cout << "failure" << endl;
}
catch (illegal_exception e)
{
cout << "illegal argument" << endl;
}
}
else if (cmd == "clear")
{
trie->Clear();
trie = new Trie();
cout << "success" << endl;
}
else if (cmd == "size")
{
cout << "number of words is " << trie->Size() << endl;
}
else if (cmd == "empty")
{
if (trie->IsEmpty())
cout << "empty 1" << endl;
else
cout << "empty 0" << endl;
}
else if (cmd == "spellcheck")
{
string word;
cin >> word;
vector<string> out;
bool result = trie->SpellCheck(&out, word, "");
if (out.empty() && result)
{
cout << "correct" << endl;
}
else if (!result)
{
cout << endl;
}
else
{
PrintVector(&out);
cout << endl;
}
}
else if (cmd == "p")
{
vector<string> out;
trie->DepthFirstSearch(&out, "");
PrintVector(&out);
if (out.size() > 0)
cout << endl;
}
else if (cmd == "c")
{
string prefix;
cin >> prefix;
try
{
int count = trie->CountSuffixes(prefix);
if (count != -1 && count != 0)
cout << "count is " << count << endl;
else
cout << "not found" << endl;
}
catch (illegal_exception e)
{
cout << "illegal argument" << endl;
}
}
else if (cmd == "exit")
{
break;
}
}
trie->Clear();
}

189
P4/Graph.cpp Normal file
View File

@@ -0,0 +1,189 @@
#include "Graph.h"
#include <limits.h>
#include "PriorityQueue.h"
#include "illegal_exception.h"
/// @brief Create a new graph
/// @param maxCount The maximum number of elements the graph is required to store
/// @param mst A flag to enable MST (enabling makes the runtime of the graph worse)
Graph::Graph(int maxCount, bool mst)
{
vertexList.reserve(maxCount);
if (!mst)
pq = new PriorityQueue(maxCount);
this->maxCount = maxCount;
this->mstEnabled = mst;
}
Graph::~Graph()
{
delete pq;
}
/// @brief Insert an edge from vertex a to b, with weight w. When MST flag is true, it is invalid to pass in edges that already exist in the graph.
/// @param a Vertex a
/// @param b Vertex b
/// @param weight The weight of the edge to insert
/// @return True if successful, false otherwise
/// @throws illegal_exception if given invalid input
bool Graph::Insert(int a, int b, int weight)
{
// Check input validity
if (a > 50000 || a < 1 || b > 50000 || b < 1 || weight < 1)
{
throw illegal_exception();
}
// Check if the node already exists. Skip this check when the MST is disabled - allows efficient inserts for building tree.
int count = adjacencyList[a].size();
if (!mstEnabled)
{
for (int i = 0; i < count; i++)
{
int currentData = std::get<0>(adjacencyList[a][i]);
if (currentData == b)
return false;
}
}
// Add the b vertex to the adjacency list for a
adjacencyList[a].push_back(std::make_tuple(b, weight));
// Add the vertex a to the vertexList, if a has never been added to the graph
if (count == 0)
vertexList.insert(vertexList.begin() + vertexList.size(), std::make_tuple(a, INT_MAX, -1));
// Add a vertex to the adjacency list for b, and insert b into vertexList if required
count = adjacencyList[b].size();
adjacencyList[b].push_back(std::make_tuple(a, weight));
if (count == 0)
vertexList.insert(vertexList.begin() + vertexList.size(), std::make_tuple(b, INT_MAX, -1));
return true;
}
/// @brief Delete vertex a from the graph.
/// @param a The vertex to delete from the graph.
/// @return True if successful, false otherwise.
/// @throws illegal_exception if given invalid input.
bool Graph::Delete(int a)
{
// Check input conditions
if (a > 50000 || a < 1)
{
throw illegal_exception();
}
// Ensure the vertex is in the graph
int count = adjacencyList[a].size();
if (count == 0)
return false;
// Remove all references to the vertex in the adjacency lists of other vertices, and in the vertexList.
while (count != 0)
{
std::tuple<int, int> lastData = adjacencyList[a][count - 1];
adjacencyList[a].pop_back();
// Remove vertex from all adjacency lists
for (int j = 0; j < adjacencyList[std::get<0>(lastData)].size(); j++)
{
if (std::get<0>(adjacencyList[std::get<0>(lastData)][j]) == a)
{
adjacencyList[std::get<0>(lastData)].erase(adjacencyList[std::get<0>(lastData)].begin() + j);
}
}
// If removing vertex a causes any adjacency lists to become empty, remove that vertex from the vertexList.
if (adjacencyList[std::get<0>(lastData)].size() < 1)
{
for (int j = 0; j < vertexList.size(); j++)
{
if (std::get<0>(vertexList[j]) == std::get<0>(lastData))
vertexList.erase(vertexList.begin() + j);
}
}
count--;
}
// Remove a from the vertex list.
for (int i = 0; i < vertexList.size(); i++)
{
if (std::get<0>(vertexList[i]) == a)
vertexList.erase(vertexList.begin() + i);
}
return true;
}
/// @brief Get a vector containing the adjacent vertices to a
/// @param a The vertex to check
/// @return A vector of all vertices
/// @throws illegal_exception if given invalid input.
std::vector<std::tuple<int, int>> *Graph::GetAdjacent(int a)
{
if (a > 50000 || a < 1)
{
throw illegal_exception();
}
return &adjacencyList[a];
}
/// @brief Compute the MST of the graph
/// @param out A vector to output the resulting MST edges in
/// @return The cost of the MST
int Graph::MST(std::vector<std::tuple<int, int, int>> *out)
{
// If there are no nodes, or somehow only one node.
if (vertexList.size() < 2)
return 0;
// When there is only one edge, that edge is the MST
if (vertexList.size() < 3)
{
out->push_back(std::make_tuple(std::get<0>(vertexList[0]), std::get<0>(vertexList[1]), std::get<1>(adjacencyList[std::get<0>(vertexList[0])][0])));
return std::get<1>(adjacencyList[std::get<0>(vertexList[0])][0]);
}
// Attribution: Some of the Pseudocode used to write this function is from the CLRS textbook.
int cost = 0;
pq->Init(&vertexList);
// Apply Prim's Algorithm using a PriorityQueue.
// Extract elements from the PQ until it is empty
while (!pq->IsEmpty())
{
// Extract the element with the lowest weight
std::tuple<int, int, int> u = pq->HeapExtractMin();
// Prevent insertion of first node (or any infinite weights).
if (std::get<1>(u) != INT_MAX)
{
out->push_back(std::make_tuple(std::get<2>(u), std::get<0>(u), std::get<1>(u)));
cost += std::get<1>(u);
}
// Iterate through all of the adjacent vertices
for (int i = 0; i < adjacencyList[std::get<0>(u)].size(); i++)
{
std::tuple<int, int> v = adjacencyList[std::get<0>(u)][i]; // The vertex being examined
// If the current path is better than the stored path, update it.
if (pq->Contains(std::get<0>(v)) && std::get<1>(v) < pq->GetKey(std::get<0>(v)))
{
pq->Modify(std::get<0>(v), std::get<1>(v), std::get<0>(u));
}
}
}
return cost;
}
/// @brief Get the number of vertices in the graph
/// @return The number of vertices in the graph
int Graph::GetVertexCount()
{
return vertexList.size();
}

27
P4/Graph.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef TRIE_H
#define TRIE_H
#include <iostream>
#include <vector>
#include <tuple>
#include "PriorityQueue.h"
class Graph
{
public:
Graph(int maxCount, bool mst);
~Graph();
bool Insert(int a, int b, int weight);
bool Delete(int a);
std::vector<std::tuple<int, int>> *GetAdjacent(int a);
int GetVertexCount();
int MST(std::vector<std::tuple<int, int, int>> *outList);
private:
PriorityQueue *pq;
std::vector<std::tuple<int, int>> adjacencyList[50001]; // An array of vectors of tuples <node2, weight>
std::vector<std::tuple<int, int, int>> vertexList; // List of all vertices in the graph stored as <node1, inf, -1>
int maxCount;
bool mstEnabled;
};
#endif

2
P4/Makefile Normal file
View File

@@ -0,0 +1,2 @@
all: Test.cpp Graph.cpp PriorityQueue.cpp
g++ -std=c++11 Test.cpp Graph.cpp PriorityQueue.cpp

153
P4/PriorityQueue.cpp Normal file
View File

@@ -0,0 +1,153 @@
#include "PriorityQueue.h"
#include <limits>
// Attribution: Some of the Pseudocode used to write these functions is from the CLRS textbook.
PriorityQueue::PriorityQueue(int maxCount)
{
count = 0;
locations.reserve(maxCount);
arr.reserve(maxCount);
for (int i = 0; i < maxCount; i++)
{
locations[i] = -1;
}
}
/// @brief Initialize the priority queue with the given vector of tuples. The tuples should be in the format: <vertex, weight, parent>
/// @param vertexList The vector of tuples to insert into the priority queue.
void PriorityQueue::Init(std::vector<std::tuple<int, int, int>> *vertexList)
{
count = vertexList->size();
for (int i = 0; i < count; i++)
{
arr[i] = vertexList->at(i);
locations[std::get<0>(vertexList->at(i))] = i;
}
}
/// @brief Modify the weight and/or the parent of vertex v
/// @param v The vertex to modify (not the index in the priority queue)
/// @param w The weight to set
/// @param parent The parent to set
/// @return True if successful, false otherwise
bool PriorityQueue::Modify(int v, int w, int parent)
{
if (count < 1)
return false;
int i = locations[v];
if (i < 0)
return false;
std::get<1>(arr[i]) = w;
std::get<2>(arr[i]) = parent;
while (i > 0 && std::get<1>(arr[Parent(i)]) > std::get<1>(arr[i]))
{
Exchange(i, Parent(i));
i = Parent(i);
}
return true;
}
/// @brief Extract the smallest value from the priority queue (the topmost element, since this is a min pq).
/// @return The element in the priority queue if not empty, or a tuple of all ints <-1, -1, -1> if empty
std::tuple<int, int, int> PriorityQueue::HeapExtractMin()
{
// Signal no minimum element if there are no elements.
if (IsEmpty())
return std::make_tuple<int, int, int>(-1, -1, -1);
std::tuple<int, int, int> min = arr[0];
locations[std::get<0>(arr[count - 1])] = 0;
locations[std::get<0>(min)] = -1;
arr[0] = arr[count - 1];
count--;
MinHeapify(0);
return min;
}
/// @brief Run the MinHeapify algorithm on the pq.
/// @param i The index (not the vertex).
void PriorityQueue::MinHeapify(int i)
{
int l = Left(i);
int r = Right(i);
int smallest = i;
if (l < count && std::get<1>(arr[l]) < std::get<1>(arr[i]))
smallest = l;
if (r < count && std::get<1>(arr[r]) < std::get<1>(arr[smallest]))
smallest = r;
if (smallest != i)
{
Exchange(i, smallest);
MinHeapify(smallest);
}
}
/// @brief Get a boolean value to determine if the priority queue is empty
/// @return True if the priority queue is empty, false otherwise.
bool PriorityQueue::IsEmpty()
{
return count < 1;
}
/// @brief Check if the priority queue contains a vertex v.
/// @param v The vertex (not index) to check.
/// @return True if the vertex (not index) is contained in the priority queue, false otherwise.
bool PriorityQueue::Contains(int v)
{
return locations[v] != -1;
}
/// @brief Get the key (weight) of a vertex (not index) in the priority queue
/// @param v The vertex (not index) to get the weight for
/// @return The key (weight) of the vertex
int PriorityQueue::GetKey(int v)
{
return std::get<1>(arr[locations[v]]);
}
/// @brief Swap the elements of index a and index b (not the vertex number)
/// @param a The index (not vertex) of the element to swap
/// @param b The index (not vertex) of the element to swap
void PriorityQueue::Exchange(int a, int b)
{
locations[std::get<0>(arr[a])] = b;
locations[std::get<0>(arr[b])] = a;
std::tuple<int, int, int> temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
/// @brief Given an index (not vertex), get the parent of that index (not vertex)
/// @param i The index (not vertex) to get the parent of
/// @return The index (not vertex) of the parent
int PriorityQueue::Parent(int i)
{
return (i - 1) / 2;
}
/// @brief Given an index (not vertex), get the left child of that index (not vertex)
/// @param i The index (not vertex) to get the left child of
/// @return The index (not vertex) of the left child
int PriorityQueue::Left(int i)
{
return 2 * i + 1;
}
/// @brief Given an index (not vertex), get the right child of that index (not vertex)
/// @param i The index (not vertex) to get the right child of
/// @return The index (not vertex) of the right child
int PriorityQueue::Right(int i)
{
return 2 * i + 2;
}

31
P4/PriorityQueue.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef PRIORITYQUEUE_H
#define PRIORITYQUEUE_H
#include <iostream>
#include <vector>
#include <tuple>
class PriorityQueue
{
public:
PriorityQueue(int maxCount);
void Init(std::vector<std::tuple<int, int, int>> *vertexList);
std::tuple<int, int, int> HeapExtractMin();
bool Modify(int i, int w, int parent);
bool Contains(int v);
bool IsEmpty();
int GetKey(int v);
private:
int count;
void Exchange(int a, int b);
void MinHeapify(int i);
int Parent(int i);
int Left(int i);
int Right(int i);
std::vector<std::tuple<int, int, int>> arr; // The actual priority queue.
std::vector<int> locations; // References for vertex -> PQ index. Allows indexing PQ in O(1) time.
const int MAX_UNIQUE_KEYS = 50001;
};
#endif

180
P4/Test.cpp Normal file
View File

@@ -0,0 +1,180 @@
#include <iostream>
#include <fstream>
#include "Graph.h"
#include "illegal_exception.h"
using namespace std;
const int MAX_NODE_COUNT = 50001;
int main()
{
string cmd;
Graph *graph = new Graph(MAX_NODE_COUNT, false);
bool mstModified = true;
int prevCost = 0;
std::vector<std::tuple<int, int, int>> prevOutput;
while (cin >> cmd)
{
if (cmd == "LOAD")
{
mstModified = true;
string filename;
cin >> filename;
ifstream fin(filename.c_str());
int count;
fin >> count;
int n1;
while (fin >> n1)
{
int n2;
fin >> n2;
int w;
fin >> w;
graph->Insert(n1, n2, w);
}
fin.close();
cout << "success" << endl;
}
else if (cmd == "INSERT")
{
int a;
int b;
int w;
cin >> a;
cin >> b;
cin >> w;
try
{
if (graph->Insert(a, b, w))
{
mstModified = true;
std::cout << "success" << std::endl;
}
else
{
std::cout << "failure" << std::endl;
}
}
catch (illegal_exception)
{
std::cout << "illegal argument" << std::endl;
}
}
else if (cmd == "DELETE")
{
int a;
cin >> a;
try
{
if (graph->Delete(a))
{
mstModified = true;
std::cout << "success" << std::endl;
}
else
{
std::cout << "failure" << std::endl;
}
}
catch (illegal_exception)
{
std::cout << "illegal argument" << std::endl;
}
}
else if (cmd == "PRINT")
{
int a;
cin >> a;
try
{
std::vector<std::tuple<int, int>> *out = graph->GetAdjacent(a);
for (int i = 0; i < out->size(); i++)
{
std::cout << get<0>(out->at(i)) << " ";
}
if (out->size() < 1)
{
std::cout << "failure";
}
std::cout << endl;
}
catch (illegal_exception)
{
std::cout << "illegal argument" << std::endl;
}
}
else if (cmd == "MST")
{
// Prevent useless regeneration of MST.
if (!mstModified)
{
if (prevOutput.size() < 1)
std::cout << "failure";
for (int i = 0; i < prevOutput.size(); i++)
{
std::cout << std::get<0>(prevOutput[i]) << " " << std::get<1>(prevOutput[i]) << " " << std::get<2>(prevOutput[i]) << " ";
}
std::cout << std::endl;
}
else
{
std::vector<std::tuple<int, int, int>> out;
out.reserve(graph->GetVertexCount());
prevCost = graph->MST(&out);
prevOutput = out;
for (int i = 0; i < out.size(); i++)
{
std::cout << std::get<0>(out[i]) << " " << std::get<1>(out[i]) << " " << std::get<2>(out[i]) << " ";
}
if (out.size() < 1)
std::cout << "failure";
std::cout << std::endl;
mstModified = false;
}
}
else if (cmd == "COST")
{
// Prevent useless regeneration of MST.
if (!mstModified)
{
std::cout << "cost is " << prevCost << std::endl;
}
else
{
std::vector<std::tuple<int, int, int>> out;
out.reserve(graph->GetVertexCount());
int cost = graph->MST(&out);
prevOutput = out;
prevCost = cost;
std::cout << "cost is " << cost << std::endl;
mstModified = false;
}
}
else if (cmd == "END")
{
break;
}
}
delete graph;
}

10
P4/illegal_exception.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef ILLEGAL_EXCEPTION_H
#define ILLEGAL_EXCEPTION_H
#include <exception>
class illegal_exception : std::exception
{
};
#endif