text: /**
   * Implements a dictionary's functionality.
   *
   * Pset 5
   * CS50
   * Mohammed Attya
   *
   * @GitHub mohammedattya2
   *
   * MIT Licence
   *
   * OpenSource OpenMind
   *
   */
  
  #include <ctype.h>
  #include <stdio.h>
  #include <sys/resource.h>
  #include <sys/time.h>
  
  #include "dictionary.h"
  #undef calculate
  #undef getrusage
  
  // default dictionary
  #define DICTIONARY "dictionaries/large"
  
  // function prototype
  // DV note: rusage* is a pointer to b, a
  double calculate(const struct rusage* b, const struct rusage* a);
  
  int main(int argc, char* argv[])
  {
      // check for correct number of args
      if (argc != 2 && argc != 3)
      {
          printf("Usage: speller [dictionary] text\n");
          return 1;
      }
  
      // structs for timing data
      // DV note: before and after are instances of type struct rusage
      struct rusage before, after;
  
      // benchmarks
      double time_load = 0.0, time_check = 0.0, time_size = 0.0, time_unload = 0.0;
  
      // determine dictionary to use
      // DV note: conditional operator
      char* dictionary = (argc == 3) ? argv[1] : DICTIONARY;
  
      // load dictionary
      // DV note: RUSAGE_SELF tells getrusage to fill the instance with info about
      // resources used by the current process
      getrusage(RUSAGE_SELF, &before);
      bool loaded = load(dictionary);
      getrusage(RUSAGE_SELF, &after);
  
      // abort if dictionary not loaded
      if (!loaded)
      {
          printf("Could not load %s.\n", dictionary);
          return 1;
      }
  
      // calculate time to load dictionary
      time_load = calculate(&before, &after);
  
      // try to open text
      char* text = (argc == 3) ? argv[2] : argv[1];
      FILE* fp = fopen(text, "r");
      if (fp == NULL)
      {
          printf("Could not open %s.\n", text);
          unload();
          return 1;
      }
  
      // prepare to report misspellings
      printf("\nMISSPELLED WORDS\n\n");
  
      // prepare to spell-check
      int index = 0, misspellings = 0, words = 0;
      char word[LENGTH+1];
  
      // spell-check each word in text
      for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
      {
          // allow only alphabetical characters and apostrophes
          if (isalpha(c) || (c == '\'' && index > 0))
          {
              // append character to word
              word[index] = c;
              index++;
  
              // ignore alphabetical strings too long to be words
              if (index > LENGTH)
              {
                  // consume remainder of alphabetical string
                  while ((c = fgetc(fp)) != EOF && isalpha(c));
  
                  // prepare for new word
                  index = 0;
              }
          }
  
          // ignore words with numbers (like MS Word can)
          else if (isdigit(c))
          {
              // consume remainder of alphanumeric string
              while ((c = fgetc(fp)) != EOF && isalnum(c));
  
              // prepare for new word
              index = 0;
          }
  
          // we must have found a whole word
          // DV note: because we hit a space or punctuation which signifies EOW
          else if (index > 0)
          {
              // terminate current word
              word[index] = '\0';
  
              // update counter
              words++;
  
              // check word's spelling
              getrusage(RUSAGE_SELF, &before);
              bool misspelled = !check(word);
              getrusage(RUSAGE_SELF, &after);
  
              // update benchmark
              time_check += calculate(&before, &after);
  
              // print word if misspelled
              if (misspelled)
              {
                  printf("%s\n", word);
                  misspellings++;
              }
  
              // prepare for next word
              index = 0;
          }
      }
  
      // check whether there was an error
      if (ferror(fp))
      {
          fclose(fp);
          printf("Error reading %s.\n", text);
          unload();
          return 1;
      }
  
      // close text
      fclose(fp);
  
      // determine dictionary's size
      getrusage(RUSAGE_SELF, &before);
      unsigned int n = size();
      getrusage(RUSAGE_SELF, &after);
  
      // calculate time to determine dictionary's size
      time_size = calculate(&before, &after);
  
      // unload dictionary
      getrusage(RUSAGE_SELF, &before);
      bool unloaded = unload();
      getrusage(RUSAGE_SELF, &after);
  
      // abort if dictionary not unloaded
      if (!unloaded)
      {
          printf("Could not unload %s.\n", dictionary);
          return 1;
      }
  
      // calculate time to unload dictionary
      time_unload = calculate(&before, &after);
  
      // report benchmarks
      printf("\nWORDS MISSPELLED:     %d\n", misspellings);
      printf("WORDS IN DICTIONARY:  %d\n", n);
      printf("WORDS IN TEXT:        %d\n", words);
      printf("TIME IN load:         %.2f\n", time_load);
      printf("TIME IN check:        %.2f\n", time_check);
      printf("TIME IN size:         %.2f\n", time_size);
      printf("TIME IN unload:       %.2f\n", time_unload);
      printf("TIME IN TOTAL:        %.2f\n\n",
       time_load + time_check + time_size + time_unload);
  
      // that's all folks
      return 0;
  }
  
  // DV note: info loaded into before, process performed, info loaded into after
  // ...calculate is used for multiple different processes
  /**
   * Returns number of seconds between b and a.
   */
  double calculate(const struct rusage* b, const struct rusage* a)
  {
      if (b == NULL || a == NULL)
      {
          return 0.0;
      }
      else
      {
          return ((((a->ru_utime.tv_sec * 1000000 + a->ru_utime.tv_usec) -
                   (b->ru_utime.tv_sec * 1000000 + b->ru_utime.tv_usec)) +
                  ((a->ru_stime.tv_sec * 1000000 + a->ru_stime.tv_usec) -
                   (b->ru_stime.tv_sec * 1000000 + b->ru_stime.tv_usec)))
                  / 1000000.0);
      }
  }
  