periodic-table-spell-checker/main.c

262 lines
4.5 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
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;
}