From 56ab2ab7c68ee29dd6d9aa6997368f033dcf7825 Mon Sep 17 00:00:00 2001 From: r4 Date: Thu, 24 Jun 2021 00:59:14 +0200 Subject: [PATCH] Initial commit --- Makefile | 13 +++ README.md | 6 +- main.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..123fd9f --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CC=cc +SRC=main.c +EXE=main + +all: $(EXE) + +$(EXE): $(SRC) + $(CC) $(SRC) -o $(EXE) + +.PHONY: clean + +clean: + rm -f $(EXE) diff --git a/README.md b/README.md index f8fe020..797a050 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # periodic-table-spell-checker -Gives you all the possible ways to spell a string using abbreviations from the periodic table. \ No newline at end of file +Gives you all the possible ways to spell a string using abbreviations from the periodic table. + +## Building/Usage + +Literally like any other c program, no libraries needed. You'll figure it out. diff --git a/main.c b/main.c new file mode 100644 index 0000000..b2e1558 --- /dev/null +++ b/main.c @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include + +const char* elements[] = { + "__ROOT__", /* Not actually an element; represents the root node */ + "H", + "He", + "Li", + "Be", + "B", + "C", + "N", + "O", + "F", + "Ne", + "Na", + "Mg", + "Al", + "Si", + "P", + "S", + "Cl", + "Ar", + "K", + "Ca", + "Sc", + "Ti", + "V", + "Cr", + "Mn", + "Fe", + "Co", + "Ni", + "Cu", + "Zn", + "Ga", + "Ge", + "As", + "Se", + "Br", + "Kr", + "Rb", + "Sr", + "Y", + "Zr", + "Nb", + "Mo", + "Tc", + "Ru", + "Rh", + "Pd", + "Ag", + "Cd", + "In", + "Sn", + "Sb", + "Te", + "I", + "Xe", + "Cs", + "Ba", + "La", + "Ce", + "Pr", + "Nd", + "Pm", + "Sm", + "Eu", + "Gd", + "Tb", + "Dy", + "Ho", + "Er", + "Tm", + "Yb", + "Lu", + "Hf", + "Ta", + "W", + "Re", + "Os", + "Ir", + "Pt", + "Au", + "Hg", + "Tl", + "Pb", + "Bi", + "Po", + "At", + "Rn", + "Fr", + "Ra", + "Ac", + "Th", + "Pa", + "U", + "Np", + "Pu", + "Am", + "Cm", + "Bk", + "Cf", + "Es", + "Fm", + "Md", + "No", + "Lr", + "Rf", + "Db", + "Sg", + "Bh", + "Hs", + "Mt", + "Ds", + "Rg", + "Cn", + "Nh", + "Fl", + "Mc", + "Lv", + "Ts", + "Og", + NULL +}; + +/* Holds the index of a chem. element in `elements` */ +typedef struct Node Node; +struct Node { + ssize_t data; + Node* children[2]; + Node* parent; +}; +void Node_init(Node* obj) { + obj->data = -1; + obj->children[0] = NULL; + obj->children[1] = NULL; + obj->parent = NULL; +} +void Node_recursive_term(Node* obj) { + size_t i; + for(i = 0; i < 2; i++) { + if(obj->children[i]) + Node_recursive_term(obj->children[i]); + } + free(obj); +} + +/* Returns true, if both strings up to `len0` or `len1` are equal, disregarding the '\0' character, as the length must be given */ +bool streq_len_ignore_case(const char* str0, size_t len0, const char* str1, size_t len1) { + if(len0 != len1) + return false; + size_t i; + for(i = 0; i < len0; i++) { + char c0 = tolower(str0[i]); + char c1 = tolower(str1[i]); + if(c0 == '\0') + break; + if(c0 != c1) + return false; + } + return true; +} + +/* See if the input element string exists (ignoring case) */ +ssize_t elms_contain(const char* str, size_t len) { + size_t i; + for(i = 0; elements[i] != NULL; i++) { + if(streq_len_ignore_case(str, len, elements[i], strlen(elements[i]))) + return i; + } + return -1; +} + +/* Prints all symbols in the path leading from the root node to the given leaf */ +void print_path(Node* leaf) { + size_t indices[512]; /* Maximum of 512 element symbols */ + size_t i; + Node* curr; + curr = leaf; + i = 0; + while(curr != NULL) { + indices[i++] = curr->data; + if(i >= 512) { + /* If the word has more than 512 element symbols, throw an error */ + fprintf(stderr, "Word too large\n"); + exit(EXIT_FAILURE); + } + curr = curr->parent; + } + /* Print in reverse order */ + i--; /* Skip root node */ + while(i--) + printf("%s ", elements[indices[i]]); + printf("\n"); +} + +/* Returns true if it has reached the end of the string */ +bool check_word_recursive(const char* in, Node* node) { + bool ret = false; /* Return value; holds whether this branch has a solution for writing the desired word with element symbols */ + size_t i; + /* 2 meaning to test for a max. element name length of 2 characters, + * where `i + 1` is the current element name length to test for */ + for(i = 0; i < 2; i++) { + /* Don't continue looping, if the end of the input was reached */ + if(in[i] != '\0') { + ssize_t el; /* (Chem.) element name index */ + el = elms_contain(in, i + 1); /* Test for an element name length of `i + 1` */ + /* Don't do anything, if the result was -1 meaning no matching element was found (with first `i + 1` chars of `in` as a symbol) */ + if(el != -1) { + Node* new; + new = malloc(sizeof(Node)); + Node_init(new); + new->data = el; + new->parent = node; + node->children[i] = new; + if(in[i + 1] == '\0') { + /* End of word was successfully reached using only element symbols */ + ret = true; + print_path(new); + } else { + if(!check_word_recursive(in + i + 1, new)) { + /* If the branch just created doesn't lead to the end of a word, destroy it */ + Node_recursive_term(new); + node->children[i] = NULL; + } else + /* One of the children/subchildren has reportedly reached the end */ + ret = true; + } + } + } else + break; + } + return ret; +} + +bool check_word(const char* input) { + bool res; + Node* root; + root = malloc(sizeof(Node)); + Node_init(root); + root->data = 0; /* 0 is root node idx(see `elements`) */ + + res = check_word_recursive(input, root); + + Node_recursive_term(root); + return res; +} + +int main(int argc, const char** argv) { + char buf[1024]; + bool res; + printf("Enter a word to check for element symbol combinations:\n"); + scanf("%s", buf); + res = check_word(buf); + if(res) return 0; + else return 1; +}