Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions group/assignment5/Unknown Alphabet/UnknownAlphabet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "UnknownAlphabet.h"
#include <iostream>
using namespace std;

UnknownAlphabet::UnknownAlphabet(const vector<string> &dictionary) {
this->dictionary = &dictionary;
}

pair<char, char> UnknownAlphabet::mismatch(string s1, string s2) {
int it1 = 0, it2 = 0;
while(it1<s1.length() && it2<s2.length() && s1[it1] == s2[it2]) {
it1++;
it2++;
}
if(it1 == s1.length() || it2 == s2.length()) { // no mismatch chars are found
return make_pair('\0','\0');
}
return make_pair(s1[it1], s2[it2]);
}

void UnknownAlphabet::build_graph(unordered_map<char, unordered_set<char>>* graph, unordered_map<char, int>* indegree) {
for(int i = 0; i < (signed) (*dictionary).size()-1; i++) {
pair<char, char> mismatch_chars = mismatch((*dictionary)[i], (*dictionary)[i+1]);
if(mismatch_chars.first != '\0') { // there is a mismatch
(*graph)[mismatch_chars.first].insert(mismatch_chars.second);
(*indegree)[mismatch_chars.second]++; // increase indegree of the mismach char in second word by one
}
}
}

unordered_set<char> UnknownAlphabet::get_unordered_alphabet(){
unordered_set<char> unordered_alphabet;
for(const string word : (*dictionary)) {
for(const char letter : word) {
unordered_alphabet.insert(letter);
}
}
return unordered_alphabet;
}

// Unordered_map isn't passed as a const parameter here because the operator[] method is non-const as it might add a default value when the item looked up is not found
// the default value is zero which doens't contradict the purpose of this method
queue<char> UnknownAlphabet::get_zero_indegree_chars(const unordered_set<char> &unordered_alphabet, unordered_map<char, int> &indegree) {
queue<char> zero_indegree_chars;
for(const char letter: unordered_alphabet){
if(!indegree[letter]){
zero_indegree_chars.push(letter);
}
}
return zero_indegree_chars;
}

vector<char> UnknownAlphabet::get_ordered_alphabet() {
// build a letters directed graph from dictionary
unordered_map<char, unordered_set<char>> graph;
unordered_map<char, int> indegree;
build_graph(&graph, &indegree);
// get zero indegree chars i.e. letters that can be placed at the begining of the alphabet
unordered_set<char> unordered_alphabet = get_unordered_alphabet();
queue<char> zero_indegree_chars = get_zero_indegree_chars(unordered_alphabet, indegree);
// topological sort
vector<char> ordered_alphabet;
while(!zero_indegree_chars.empty()){
char current_letter = zero_indegree_chars.front();
// decrease all adjacent letters indegrees by one
for(char letter : graph[current_letter]){
indegree[letter]--;
if(!indegree[letter]){ // if letter's indegree is zero, push to zero_indegree_chars queue
zero_indegree_chars.push(letter);
}
}
ordered_alphabet.push_back(current_letter);
zero_indegree_chars.pop();
}
if(ordered_alphabet.size() != unordered_alphabet.size()){ //dictionary is inconsistent
return {};
}
return ordered_alphabet;
}
23 changes: 23 additions & 0 deletions group/assignment5/Unknown Alphabet/UnknownAlphabet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef UNKNOWNALPHAET_H_
#define UNKNOWNALPHAET_H_

#include <vector>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <utility>

class UnknownAlphabet{
const std::vector<std::string>* dictionary;
public:
explicit UnknownAlphabet(const std::vector<std::string> &dictionary); // constructor
std::vector<char> get_ordered_alphabet(); // returns one of the possible ordered alphabet if the dictionary is consistent, elsewise it returns an empty vector
private:
std::pair<char, char> mismatch(std::string s1, std::string s2); // returns the first 2 mismtached chars in 2 strings in the form of pair where the its first member is the mismatched char in the first string and its second member is the mismatched char in the second string
void build_graph(std::unordered_map<char, std::unordered_set<char>>* graph, std::unordered_map<char, int>* indegree); // build a directed chars graph from dictionary to use in topological sort
std::unordered_set<char> get_unordered_alphabet(); // returns a set of all chars in the alphabet unordered
std::queue<char> get_zero_indegree_chars(const std::unordered_set<char> &unordered_alphabet, std::unordered_map<char, int> &indegree); // returns zero indegree chars
};

#endif /* UNKNOWNALPHAET_H_ */
64 changes: 64 additions & 0 deletions group/assignment5/Unknown Alphabet/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "UnknownAlphabet.h"
#include "test.h"
using namespace std;

void normal_test() {
vector<string> dictionary {"ART", "RAT", "CAT", "CAR"};
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected {'T', 'A', 'R', 'C'};
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

void empty_dictionary_test() {
vector<string> dictionary;
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected;
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

// Upper/lower case characters are treated differently i.e. not considered the same for simplicity
void upper_lower_case_chars_dictionary_test() {
vector<string> dictionary {"A", "a", "b", "B"};
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected {'A', 'a', 'b', 'B'};
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

void non_english_chars_dictionary_test() {
vector<string> dictionary {"!@", "!#", "$%^"};
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected {'^', '%', '!', '@', '$', '#'};
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

void inconsistent_dictionary_test() {
vector<string> dictionary {"A", "M", "A"};
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected;
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

void one_word_dictionary_test() {
vector<string> dictionary {"cat"};
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected {'t', 'c', 'a'};
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

void duplicate_words_dictionary_test() {
vector<string> dictionary {"cat", "cat", "cat"};
UnknownAlphabet unknownAlphabet(dictionary);
vector<char> expected {'t', 'c', 'a'};
EXPECT_EQ(expected, unknownAlphabet.get_ordered_alphabet());
}

int main() {
normal_test();
empty_dictionary_test();
upper_lower_case_chars_dictionary_test();
non_english_chars_dictionary_test();
inconsistent_dictionary_test();
one_word_dictionary_test();
duplicate_words_dictionary_test();
return 0;
}
35 changes: 35 additions & 0 deletions group/assignment5/Unknown Alphabet/test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef TEST_H_
#define TEST_H_

#include <iostream>
#include <vector>

// Some convenient macros for simple testing.
// If expectation is fulfilled, prints nothing;
// If expectation is not fulfilled, prints what went wrong (expectation vs.
// actual value) with the filename and line number for convenience.

#define EXPECT_EQ(expected, actual) \
if ((expected) != (actual)) { \
std::cout << __FILE__ << ":" << __LINE__ << " Failed: " \
<< "Statement (" << #actual << ") is " << actual \
<< ", expected " << (expected) << " (" << #expected << ")." \
<< std::endl; \
}

// Print operator for vectors. Must be defined in order to use EXPECT_EQ macro
// with vectors (see example below).
template <class Type>
inline std::ostream& operator<<(std::ostream& os, const std::vector<Type>& v) {
os << "{";
for (int i = 0; i < v.size(); i++) {
if (i > 0) {
os << ", ";
}
os << v[i];
}
os << "}";
return os;
}

#endif /* TEST_H_ */