complex numbers


C: Reading Complex Numbers

A complex number is a number that can be expressed in the form a + bi, where a and b are real numbers and i is the imaginary unit, satisfying the equation i*i=−1. In this expression, a is the real part and b is the imaginary part of the complex number.

From wikipedia

The following code, will open a file where each line contains one complex number, read the number and store it into the appropriate variables.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

  const char *filename = argv[1];
  FILE *fin = fopen(filename, "r");

  // Making sure we managed to open the file
  if (fin != NULL) {

    //Initially this pointer will hold the whole line, eventually the data for the second Component will be removed
    char *first_value = NULL;
    size_t length_of_buffer = 0;
    ssize_t bytes_read;

    // Read Line by line until end of file:
    // If *line is set to NULL and *length_of_buffer is set 0 before the call, then getline() will allocate a buffer for storing the line.
    // This buffer should be freed by the user program even if getline() failed.
    while ((bytes_read = getline(&first_value, &length_of_buffer, fin)) != -1) {
      printf("Data = '%s' : Length = %zu\n", first_value, bytes_read);

      // Create char string to store the second Component
      char *second_value;
      // We are a bit wasteful here but it makes the code simpler
      second_value = (char *) malloc(bytes_read * sizeof(char));

      // Create Imaginary flag to distinguish real values from imaginary values - One for each Component
      char imaginary_flag[2] = {0, 0};

      // Initialize Iterators
      // i: for pointing to characters of current line
      // j: for pointing to characters in first Component
      // k: for pointing to characters in second Component
      int i, j = 0, k = 0;
      // Flag to indicate that first character if the first Component was found - This helps distinguish between first and second Component
      char met_a_number = 0;
      // Flag to indicate first Component ends and we should process the second Component
      char copy_to_second_value = 0;
      // To store the previous character value, we use it to check that when a sign is found, it does not belong to an exponential
      char previous_character = '\0';

      for (i = 0; i < bytes_read; i++) {
        // Initialize current character being processed/evaluated from the line
        const char current_character = first_value[i];

        // Find the 'j' character that shows that this number is imaginary, then drop the character
        if (current_character == 'j') {
          if (copy_to_second_value == 0) {
            // First Component is the imaginary part
            imaginary_flag[0] = 1;
          } else {
            // Second Component is the imaginary part
            imaginary_flag[1] = 1;
          }
          continue;
        }

        //We check that the current character is useful to us
        if (!(current_character == ' ' || current_character == '(' || current_character == ')')) {
          // If current character still part of first Component [based on flag]
          if (copy_to_second_value == 0) {
            // Check when second Component begins
            // When we find one of the sign characters and it is not part of an exponential, then we switch to the second Component
            // Exclude if previous character was "e" because after "e" a sign is followed
            if (current_character == '-' || current_character == '+') {
              if (met_a_number == 1 && previous_character != 'e') {
                // Update Flag to indicate that second Component/Value begins
                copy_to_second_value = 1;
                // Terminate first Component/Value of current Line
                first_value[i] = '\0';
                second_value[k++] = current_character;
              } else {
                // Update First Component with the current character value
                first_value[j++] = current_character;
              }
            }
              // If current character still part of the first Component
            else {
              // Update First Component with the current character value
              first_value[j++] = current_character;
              // A valid character of a number was found, so we are processing a number.
              // If this is the first time it happens, we are processing the first Component
              met_a_number = 1;
            }
          }
            // If current character part of second Component [based on flag]
          else {
            second_value[k++] = current_character;
          }
        }
          // If current character belongs in one of the garbage characters
        else {
          // If current character is garbage number and we already met a number
          // then we start copying to the second Component
          if (met_a_number == 1) {
            copy_to_second_value = 1;
            first_value[i] = '\0';
          }
        }

        // Store current_character before reading the next
        previous_character = current_character;
      }

      // Properly terminate first Component
      if (first_value[j - 1] == '\n') {
        first_value[j - 1] = '\0';
      } else {
        first_value[j] = '\0';
      }

      // Properly terminate second Component
      second_value[k - 1] = '\0';

      // To store the first and second Components in float format
      double numbers[2];

      // Convert to float and Print first Component
      sscanf(first_value, "%lf", &(numbers[0]));
      printf("%.f = '%s' is imaginary = %s\n", numbers[0], first_value, imaginary_flag[0] == 0 ? "FALSE" : "TRUE");

      // Convert to float and Print second Component if it exists
      if (copy_to_second_value == 1) {
        sscanf(second_value, "%lf", &(numbers[1]));
        printf("%.f = '%s' is imaginary = %s\n", numbers[1], second_value, imaginary_flag[1] == 0 ? "FALSE" : "TRUE");
      }

      free(second_value);

      double real = 0, imaginary = 0;
      if (copy_to_second_value == 1) {
        if (imaginary_flag[0] == imaginary_flag[1]) {
          fprintf(stderr, "Invalid input line.\n");
          if (imaginary_flag[0] == 0) {
            fprintf(stderr, "None of the components is imaginary\n");
          } else {
            fprintf(stderr, "Both components are imaginary.\n");
          }
          continue;
        }
        if (imaginary_flag[0] == 0) {
          real = numbers[0];
          imaginary = numbers[1];
        } else {
          real = numbers[1];
          imaginary = numbers[0];
        }

      } else {
        if (imaginary_flag[0] == 0) {
          real = numbers[0];
        } else {
          imaginary = numbers[0];
        }
      }
      printf("Real part: '%f'\tImaginary part: '%f'\n", real, imaginary);
      printf("\n");
    }
    free(first_value);
    fclose(fin);
  } else {
    fprintf(stderr, "Failed to open file '%s'\n", filename);
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}

This application will only perform some basic checks on the input, a lot of invalid data can pass as valid..
What this application expects though is the following:

  • On each line, there can be either one or two numbers.
  • The numbers can be encapsulated in parenthesis ()
  • The first number can have a sign or not
  • There might be space between the characters or not
  • There might be space between the sign characters and the numbers
  • The imaginary part of the number will have the character j either before the number or after, attached to the number
Advertisements