/* temperature_data.c Performs various operations using Observation objects according to the specifications preceding the function definitions. Elvin Shoolbraid, V01029195 2023-12-2 */ #include #include "temperature_data.h" /* Do not copy the typedef statements for the structure types into this file (they should only be in the .h file) */ /* earlier_date(first observation, second observation) Given two observation objects, return 1 if they are in chronological order, return -1 if they are in reverse order, and return 0 if the dates are the same. */ int earlier_date(Observation obs_1, Observation obs_2){ if(obs_1.obs_date.year < obs_2.obs_date.year){ return 1; } if(obs_1.obs_date.year > obs_2.obs_date.year){ return -1; } if(obs_1.obs_date.month < obs_2.obs_date.month){ return 1; } if(obs_1.obs_date.month > obs_2.obs_date.month){ return -1; } if(obs_1.obs_date.day < obs_2.obs_date.day){ return 1; } if(obs_1.obs_date.day > obs_2.obs_date.day){ return -1; } return 0; } /* earlier_time(index 1, index 2, observation 1, observation 2) Given two observation objects and two indexes for those objects, return the the index of the earlier observation in the obs_array arrays. */ int earlier_time(int obs_1_index, int obs_2_index, Observation obs_1, Observation obs_2){ if(earlier_date(obs_1, obs_2) == 1){ return obs_1_index; } if(earlier_date(obs_1, obs_2) == -1){ return obs_2_index; } if(obs_1.hour < obs_2.hour){ return obs_1_index; } if(obs_1.hour > obs_2.hour){ return obs_2_index; } if(obs_1.minute < obs_2.minute){ return obs_1_index; } if(obs_1.minute > obs_2.minute){ return obs_2_index; } return obs_1_index; } /* next_earliest_date(observations_array, observation) Given an array of observations, along with an observation object, return the array index of the next earliest observation to the given one, or return -1 if there is no later observation. */ int next_earliest_date(int num_observations, Observation obs_array[num_observations], Observation earliest){ Observation current_earliest; current_earliest.obs_date.year = 3000; current_earliest.obs_date.month = 1; current_earliest.obs_date.day = 1; int output_index = -1; for(int i = 0; i < num_observations; i++){ if(earlier_date(obs_array[i], current_earliest) == 1 && earlier_date(obs_array[i], earliest) == -1){ output_index = i; current_earliest = obs_array[i]; } } return output_index; } /* average_temperature(observations_array, day_index) Given an array of observations, along with an index of an observation from the desired day, return the average temperature from all observations on that day. */ double average_temperature(int num_observations, Observation obs_array[num_observations], int date_index){ float sum = 0; int count = 0; for(int i = 0; i < num_observations; i++){ if(earlier_date(obs_array[i], obs_array[date_index]) == 0){ count++; sum += obs_array[i].temperature; } } return sum/count; } /* read_observation(input_file, obs) Given a file object input_file and a pointer obs to an Observation structure, try to read a single observation from the file into the structure pointed to by obs. If the complete observation is read successfully, return 1. Otherwise, return 0. Parameters: input_file (FILE pointer), obs (pointer to Observation object) Return value (integer): - If the observation was read successfully, return 1 - Otherwise, return 0 */ int read_observation(FILE* input_file, Observation* obs){ /* Your code here */ if (fscanf(input_file, "%d %d %d %d %d %d %lf", &obs->obs_date.year, &obs->obs_date.month, &obs->obs_date.day, &obs->hour, &obs->minute, &obs->station_id, &obs->temperature) == 7) { return 1; } return 0; } /* count_observations(filename) Given a filename, count and return the number of complete temperature observations in the file. If the file cannot be opened, return -1. Parameters: filename (string) Return value (integer): - If the file cannot be opened for reading, return -1 - Otherwise, return the number of temperature observations in the file. */ int count_observations(char filename[]){ int i; Observation temp; FILE* input_file = fopen(filename, "r"); if(input_file == NULL){ return -1; } for(i = 0; read_observation(input_file, &temp) == 1; i++){}; fclose(input_file); return i; } /* load_all_observations(filename, array_size, observation_array) Given a filename and an array of Observation objects, read as many observations from the file as possible (up to the size of the array) and store each observation (in order) into the array. If the file cannot be opened, return -1. Otherwise, return the number of observations read. If the file contains fewer observations then there are elements in the array, stop reading after the last observation in the file. Otherwise, stop reading once array_size observations are read. Parameters: filename (string), array_size (integer), observation_array (array of Observation) Return value (integer): - If the file could not be opened, return -1 - Otherwise, return the total number of observations read and stored into the array. */ int load_all_observations(char filename[], int array_size, Observation observation_array[array_size]){ FILE* input_file = fopen(filename, "r"); if(input_file == NULL){ return -1; } int i; for(i = 0; i < array_size && read_observation(input_file, &observation_array[i]) == 1; i++) {}; fclose(input_file); return i; } /* print_station_extremes(num_observations, obs_array) Given an array of Observation objects, compute and print the _extreme observations_ (lowest temperature observed and highest temperature observed) for each station that has at least one observation in the dataset. The output will contain one line for each station with at least one observation in the dataset, using a format equivalent to the following: Station 1: Minimum = -1.87 degrees (2023-11-21 06:10), Maximum = 10.6 degrees (2023-01-11 01:16) You should use the following printf format string to achieve the correct output format. "Station %d: Minimum = %.2f degrees (%04d-%02d-%02d %02d:%02d), Maximum = %.2f degrees (%04d-%02d-%02d %02d:%02d)\n" The output must be ordered by station number (with lower numbered station numbers appearing first). No more than one line of output should be generated for a particular station number. In cases where multiple observations achieve the extreme value (e.g. if the minimum temperature at a particular station is -1.87, but there are several observations with this temperature), print the date/time of the chronologically earliest observation with that extreme temperature. You may assume that all observations contain a station number between 1 and 250 (inclusive). This function must _not_ use any file I/O features whatsoever. Parameters: num_observations (integer), observation_array (array of Observation) Return value: None Side Effect: A printed representation of station extremes is output to the user. */ void print_station_extremes(int num_observations, Observation obs_array[num_observations]){ /* Create arrays of 250 elements to store the index of the minimum temperature reading for each station, and of the maximum temperature reading for each station. Initialize the indexes to -1, to tell if a station isn't in the original observations array. */ int max_indexes [251]; int min_indexes [251]; for(int i = 0; i < 251; i++){ max_indexes [i] = -1; min_indexes [i] = -1; } /* Iterate over each possible station id. Look through the array, and keep track of the index of the minimum and maximum temperature readings seen from the current station. If a new temperature reading is equal to the current minimum or maximum, compare the dates (and times, if necessary) of the two observations, and store the index of the chronologically earlier observation. */ for(int i = 1; i <= 250; i++){ double min_temp = 10000; double max_temp = -10000; for(int index = 0; index < num_observations; index++){ if(obs_array[index].station_id == i){ if(obs_array[index].temperature < min_temp){ min_temp = obs_array[index].temperature; min_indexes[i] = index; } if(obs_array[index].temperature > max_temp){ max_temp = obs_array[index].temperature; max_indexes[i] = index; } if(obs_array[index].temperature == min_temp){ min_indexes[i] = earlier_time(min_indexes[i], index, obs_array[min_indexes[i]], obs_array[index]); } if(obs_array[index].temperature == max_temp){ max_indexes[i] = earlier_time(max_indexes[i], index, obs_array[max_indexes[i]], obs_array[index]); } } } } /* Iterate over each possible station id. If the station is present in the dataset, print the minimum and maximum temperature readings from that station. */ for(int station = 1; station <= 250; station++){ if(max_indexes[station] != -1){ int j = min_indexes[station]; int k = max_indexes[station]; printf("Station %d: Minimum = %.2f degrees (%04d-%02d-%02d %02d:%02d), Maximum = %.2f degrees (%04d-%02d-%02d %02d:%02d)\n", station, obs_array[j].temperature, obs_array[j].obs_date.year, obs_array[j].obs_date.month, obs_array[j].obs_date.day, obs_array[j].hour, obs_array[j].minute, obs_array[k].temperature, obs_array[k].obs_date.year, obs_array[k].obs_date.month, obs_array[k].obs_date.day, obs_array[k].hour, obs_array[k].minute); } } } /* print_daily_averages(num_observations, obs_array) Given an array of observation objects, compute and print the average temperature for each day which has at least one observation in the dataset. The output must contain only dates which actually appear in at least one observation object in the array. The dates must be in ascending chronological order (so an earlier date must be printed before a later one) and each date may only appear once. Each line of output will have the form "year month day average", for example "2023 11 20 10.6" (which would be interpreted to mean that the average temperature on Nov. 21, 2023 was 10.6 degrees). Your code may assume that all dates are from years between 2015 and 2023 (inclusive) and that the month/day entries are all valid (that is, month will always be between 1 and 12 inclusive and day will always be between 1 and 31 inclusive). This function must _not_ use any file I/O features whatsoever. Parameters: num_observations (integer), observation_array (array of Observation) Return value: None Side Effect: A printed representation of the average daily temperature is output to the user. */ void print_daily_averages(int num_observations, Observation obs_array[num_observations]){ //Find the index of the earliest date in the array //Sum the temperatures of all the observations on that day, //and count the number of observations. Print the output for that day. //Find the next earliest date in the array, and repeat. //Once there are no dates in the array that are later than the current earliest date, //finish the loop. Observation startdate; startdate.obs_date.year = 2005; startdate.obs_date.month = 9; startdate.obs_date.day = 19; int i; for(i = next_earliest_date(num_observations, obs_array, startdate); next_earliest_date(num_observations, obs_array, obs_array[i]) != -1; i = next_earliest_date(num_observations, obs_array, obs_array[i])){ double temp = average_temperature(num_observations, obs_array, i); printf("%d %d %d %.1f\n", obs_array[i].obs_date.year, obs_array[i].obs_date.month, obs_array[i].obs_date.day, temp); } printf("%d %d %d %.1f\n", obs_array[i].obs_date.year, obs_array[i].obs_date.month, obs_array[i].obs_date.day, average_temperature(num_observations, obs_array, i)); }