/* Author: Alexander Wong */ #include #include #include #include #include #include #include #include #define READ_END 0 #define WRITE_END 1 #define BUFFER_SIZE sizeof(long long int) int main(int argc, char* argv[]) { // Create pipe file descriptors with two ends. int fdIn[2]; // Pipe for sending numbers. pid_t pid; // Create new PID for holding the forks PID value if (pipe(fdIn) < 0) { // Initialize and check if pipes were properly created. fprintf(stderr, "There was an issue creating the pipes.\n"); return 1; // Return 1, there was an issue. } if (argc != 2) { // Check if there are an unexpected number of arguments. // Value is set to 2 because the first argument is always the program binary being executed. fprintf(stderr, "Invalid number of arguments, expected one. Got %u.\n", argc - 1); return 1; // Return 1, there was an issue. } for (int i = 0; i < strlen(argv[1]); i++) { // Iterates over each value to check and see if it is a digit in the input string. // This will handle negatives automatically since the - character is not considered // as a negative in the isdigit() function. if (!isdigit((argv[1][i]))) { fprintf(stderr, "Input must be a valid positive integer.\n"); return 1; // Return 1, there was an issue. } } long long int input = atoll(argv[1]); // Convert the first argument to an int. if (input <= 0) { // Checks to make sure the value is non-zero and not negative. // A negative number shouldn't make it here however. fprintf(stderr, "Integer cannot be 0 or negative.\n"); return 1; // Return 1, there was an issue. } pid = fork(); // Fork the process if (pid > 0) { /* If the PID is greater than 0, this is the parent process. The parent process is responsible for informing the child process what number to generate the collatz conjecture sequence for. This input is expected to be an integer value that is non-negative or zero. Once the child has finished producing the collatz conjecture sequence, the parent process will then read the pipe that the child users to write to the parent and output the collatz conjecture sequence. It will also record the maximum value found in the sequence and the number of steps taken while generating the collatz conjecture sequence. These are output as (s, m) where s being the number of steps and m being the max. */ close(fdIn[WRITE_END]); // Close the write end of the pipe since we're not writing. int status; wait(&status); // Wait for the child process to exit. int returnCode = WEXITSTATUS(status); // Retrieve the status code. if (returnCode == 1) { // Report there was an error if the error code is 1. fprintf(stderr, "There was an error while generating the collatz conjecture sequence.\n" "Your input of %lld was likely too large, we exceeded the max length,\n" "or something else happened.\n", input); return 1; } int remainingBytes; // Used to determine if we still have bytes to read in the pipe. long long int collatzNumber; // Represents the current number in the collatz conjecture sequence beign read. int counter = 0; // Initialize a count of 0, used to determine the number of steps. long long int peak = 0; // The max value found in the collatz conjecture sequence. bool inputWasOne = false; if (input == 1) // If the input was 1, set true. inputWasOne = true; while ((remainingBytes = read(fdIn[READ_END], &collatzNumber, BUFFER_SIZE)) > 0) { // This loop checks to make sure there are bytes to read and if so will increment // the counter and record the peak of the collatz conjecture sequence. It will then // output the number to produce the sequence in stdout. if (collatzNumber > peak) peak = collatzNumber; // Set the new max value if (collatzNumber == 1 && !inputWasOne) // If the value is one, drop the comma as it's the last in the sequence. fprintf(stdout, "%lld ", collatzNumber); else fprintf(stdout, "%lld, ", collatzNumber); if (inputWasOne) inputWasOne = false; // Clear statement to enable writing 1. counter++; // Increment the counter } counter--; // Decrement counter by 1 since the input does not count. fprintf(stdout, "(%d, %lld)\n", counter, peak); // Output the, s, number of steps and, m, max. (s, n) close(fdIn[READ_END]); // Close the pipe. } // Child else if (pid == 0) { /* If the PID is equal to 0, this is the child process. The child process is responsible for producing the collatz conjecture sequence for the number provided by the parent. This sequence is then written back to the parent over the pipe. */ long long int collatzNumber = input; // Used to hold the number used in the collatz conjecture sequence. close(fdIn[READ_END]); // Close the read end of the pipe, no more inputs expected. write(fdIn[WRITE_END], &collatzNumber, BUFFER_SIZE); // Write input to pipe. do { // This loop performs the collatz conjecture algorithm to produce // the collatz conjecture sequence. A do loop is required because // the collatz conjecture is still performed om 1 and standard while // loop will not treat it as such and assume it is done. if (collatzNumber % 2 == 0) { // If the number is even divide by two. collatzNumber = collatzNumber / 2; } else { // If the number is odd, multiply it by 3 and add 1. collatzNumber = 3 * collatzNumber + 1; } write(fdIn[WRITE_END], &collatzNumber, BUFFER_SIZE); // Write the number to the pipe. } while (collatzNumber > 1); if (collatzNumber <= 0) exit(EXIT_FAILURE); // If the number is negative or 0, an error or overflow occurred. close(fdIn[WRITE_END]); // Close the write end of the pipe. return 0; // Return 0, no issues. } return 0; // Return 0, no issues. }