/// <summary> /// Linearly goes through all records looking for the highest and lowest record for given target property /// </summary> /// <typeparam name="T">Generic type for compiler to infer for the delegate</typeparam> /// <param name="region">Region's records to search through</param> /// <param name="targetProperty">for a property to find min max versions</param> public static void FindMinMax <T>(ref RecordCollection region, Func <SeismicRecord, T> targetProperty) where T : IComparable { //seed values to compare against T min = targetProperty(region.Records[0]); T max = targetProperty(region.Records[0]); //Indexes to retrieve the max and min records when the search is done int minIndex = 0; int maxIndex = 0; for (int i = 0; i < region.Records.currentCapacity; i++) { //when the current min is bigger than the next value in the array if (min.CompareTo(targetProperty(region.Records[i])) > 0) { //set the current min to that lower value min = targetProperty(region.Records[i]); maxIndex = i; //record the most up-to-date min record's position } //when the current max is lower than the next value in the array else if (max.CompareTo(targetProperty(region.Records[i])) < 0) { //set the current max to higher value max = targetProperty(region.Records[i]); minIndex = i;//record the most up-to-date max record's position } } //informs user for which parameter the min max request was and tells which column to look for. string paramName = targetProperty.Method.GetParameters()[0].Name; string maxOutput = RecordCollection.formatPrinter(paramName, region.Records[minIndex], targetProperty); string minOutput = RecordCollection.formatPrinter(paramName, region.Records[maxIndex], targetProperty); Console.WriteLine($"Minimum Value for {paramName.ToUpper()}:\n{minOutput}\n" + $"Maximum Value for {paramName.ToUpper()}:\n{maxOutput}"); }
/// <summary> /// Decide upon which type of input to ask user based on the type of property being passed as arg /// </summary> /// <typeparam name="T">Generic type for the delegate's 'out TResult'</typeparam> /// <param name="region">Region record data</param> /// <param name="propertyField">record's property the search operation to be performed</param> static public void TypeSafeSearch <T>(RecordCollection region, Func <SeismicRecord, T> propertyField) where T : IComparable { //paramName; a name of currently passed propertField parameter i.e. month => month.Month is month string paramName = propertyField.Method.GetParameters()[0].Name; //retrieves the type from a selected record property Type propertyType = propertyField(region.Records[0]).GetType(); //if the current property integer type, then the user is searching by either year, month or day etc. if (propertyType == typeof(int)) { int key; //months can be searched by string name or integer equivalent, so it makes sense to see if the current parameter is of name month if (paramName == "month") { //then notify user of possible search inputs Console.WriteLine("You're searching for month; you can use numbers (1-12) or words like june"); //a variable to test what type of input the user provided string tempInput = Console.ReadLine(); //if it's true that you can't parse the user input as an integer, it's a string if (!int.TryParse(tempInput, out key)) { //take the string equivalent for the month input and parse it to months integer for the key key = DateTime.ParseExact(tempInput, "MMMM", CultureInfo.InvariantCulture).Month; } } else { //if input is parasable as an int, then parse it as ant integer and assign it to the key Console.WriteLine($"You're searching for {paramName}; expected input is integer"); key = int.Parse(Console.ReadLine()); } //after acquiring the key from the condition statement, it's passed to the search algorithm. SearchAlgorithms.BinarySearchAll(key, 0, region.Records.currentCapacity - 1, ref region, propertyField); } //when the property is of string type else if (propertyType == typeof(string)) { //ask user for string input for the key Console.WriteLine($"You're searching for {paramName}; expected input is string (lower or upper-case)"); string key = Console.ReadLine().ToUpper(); SearchAlgorithms.BinarySearchAll(key, 0, region.Records.currentCapacity - 1, ref region, propertyField); } //when the property is of TimeSpan, it's clear the user wants to search by the Time property else if (propertyType == typeof(TimeSpan)) { //Parse the the input as a TimeSpan type Console.WriteLine($"You're searching for {paramName}; expected input is HH:MM:SS"); TimeSpan key = TimeSpan.Parse(Console.ReadLine()); SearchAlgorithms.BinarySearchAll(key, 0, region.Records.currentCapacity - 1, ref region, propertyField); } //when property of type double (magnituded, latitude etc) else if (propertyType == typeof(double)) { //Parse the input as a double Console.WriteLine($"You're searching for {paramName}; expected input is double (DDD.DDD or -DDD.DDDD)"); double key = double.Parse(Console.ReadLine()); SearchAlgorithms.BinarySearchAll(key, 0, region.Records.currentCapacity - 1, ref region, propertyField); } }
/// <summary> /// To join the current instance of Record collection to another instance /// </summary> /// <param name="target">Target array you wish to combine with the source</param> public void JoinArrays(RecordCollection target) { //loops through each record in the target and adds it to the current records. for (int i = 0; i < target.Records.currentCapacity; i++) { Records.AddRecord(target.Records[i]); } SortAlgorithms.HeapSort(this, c => c.Timestamp); }
/// <summary> /// Once the region is initialized, then you want to choose region specific fields to operate upon /// </summary> /// <param name="regionData">The initialized instance of the selected region</param> static private void FieldMenu(RecordCollection regionData) { int userInput; do { Console.WriteLine("Which field do you want to perform an operation on?" + "\n1)Years" + "\n2)Months" + "\n3)Days" + "\n4)Times" + "\n5)Magnitude" + "\n6)Latitude" + "\n7)Longitude" + "\n8)Depth" + "\n9)Region" + "\n10)IRIS_ID" + "\n11)Timestamp" + "\n12)List current records" + "\n13)Go back"); userInput = short.Parse(Console.ReadLine()); switch (userInput) { //selected field should refer to OperationMenu with lambda expression to satisfy the delegate condition and the region's data case 1: OperationMenu(regionData, year => year.Year); break; case 2: OperationMenu(regionData, month => int.Parse(month.Month[0])); break; //month integer equivalents (for sorting and searching) case 3: OperationMenu(regionData, day => day.Day); break; case 4: OperationMenu(regionData, time => time.Time); break; case 5: OperationMenu(regionData, magnitude => magnitude.Magnitude); break; case 6: OperationMenu(regionData, latitude => latitude.Latitude); break; case 7: OperationMenu(regionData, longitude => longitude.Longitude); break; case 8: OperationMenu(regionData, depth => depth.Depth); break; case 9: OperationMenu(regionData, region => region.Region); break; case 10: OperationMenu(regionData, iris_id => iris_id.Iris_id); break; case 11: OperationMenu(regionData, timestamp => timestamp.Timestamp); break; case 12: regionData.Records.ListRecords(); break; //if the user wants to view the current state of the records before performing any operation case 13: break; default: Console.WriteLine("Such field option doesn't exist!"); break; } } while (userInput != 13); //loops until the input is a correct case or intention to quit }
/// <summary> /// The root menu is gateway to other sub-menu's depending on the path chosen by the user in the root menu. /// </summary> static public void RootMenu() { int userInput; do { //clearing the console when making back to root Console.Clear(); Console.WriteLine("Seismic Data Manager - Antanas Skiudulas" + "\nInstructions: The application is navigated using numbers i.e. to select first option, type 1."); Console.WriteLine("Please select which region you want to analyse" + "\n1)Region 1" + "\n2)Region 2" + "\n3)Region 1 & 2" + "\n4)Quit"); userInput = short.Parse(Console.ReadLine()); //since the choice here are regions, it makes sense to instantiate and initialize in the cases selected RecordCollection tempReg = new RecordCollection(); RecordCollection tempReg2 = new RecordCollection(); //Initializes certain region(s) depending on the input switch (userInput) { case 1: tempReg.InitializeCollections(1); FieldMenu(tempReg); break; case 2: tempReg2.InitializeCollections(2); FieldMenu(tempReg2); break; case 3: //intializing all these objects. tempReg.InitializeCollections(1); tempReg2.InitializeCollections(2); //joining the regions into one array. tempReg.JoinArrays(tempReg2); FieldMenu(tempReg); break; case 4: Environment.Exit(0); break; default: Console.WriteLine("The region you've entered doesn't exist!"); break; } } while (userInput != 4); //loops until the input is a correct case or intention to quit }
/// <summary> /// sorts the heap into parent nodes higher than children /// </summary> /// <typeparam name="T">Generic type</typeparam> /// <param name="region">region to sort</param> /// <param name="HeapSize">the size of the array</param> /// <param name="Index">where to point</param> /// <param name="properyField">by which property to sort</param> private static void Max_Heapify <T>(RecordCollection region, int HeapSize, int Index, Func <SeismicRecord, T> properyField) where T : IComparable { int Left = (Index + 1) * 2 - 1; int Right = (Index + 1) * 2; int largest = 0; //sorting ascendingly if (region.SortOrder) { //when the left node from the middle is more than indexed node if (Left < HeapSize && properyField(region.Records[Left]).CompareTo(properyField(region.Records[Index])) > 0) { largest = Left; //set largest index } else { largest = Index; } if (Right < HeapSize && properyField(region.Records[Right]).CompareTo(properyField(region.Records[largest])) > 0) { largest = Right; } } //sorting descendingly else { if (Left < HeapSize && properyField(region.Records[Left]).CompareTo(properyField(region.Records[Index])) < 0) { largest = Left; } else { largest = Index; } if (Right < HeapSize && properyField(region.Records[Right]).CompareTo(properyField(region.Records[largest])) < 0) { largest = Right; } } if (largest != Index) { SeismicRecord temp = region.Records[Index]; region.Records[Index] = region.Records[largest]; region.Records[largest] = temp; Max_Heapify(region, HeapSize, largest, properyField); } }
/// <summary> /// BinarySearch looks for matching records in the sorted array and prints all matching records /// </summary> /// <typeparam name="T">Generic type for the TResult in targetProperty</typeparam> /// <typeparam name="F">Generic type for the key that will be searched (Type T == Type F in this program)</typeparam> /// <param name="key">The reference to generic key type</param> /// <param name="region">Which array the operation to be performed on</param> /// <param name="foundVals">Reference to array outside the method to store matched keys</param> /// <param name="targetProperty">Property that the search will be conducted on (month, day etc)</param> /// <param name="left">Lowest index boundry in the array</param> /// <param name="right">Highest index boundry in the array</param> private static void BinarySearch <T, F>(F key, int left, int right, ref RecordCollection region, ref RecordCollection foundVals, Func <SeismicRecord, T> targetProperty) where T : IComparable where F : IComparable { //during recursion, if left boundry exceeds right, return void if (left > right) { return; } int mid = (left + right) / 2; //calibrating current middle position in the array if (region.SortOrder == true) //checking the order in which the array is sorted in ascended way { if (key.CompareTo(targetProperty(region.Records[mid])) < 0) // when key is < mid record's key { BinarySearch(key, left, mid - 1, ref region, ref foundVals, targetProperty); //call with updated right boundry } else if (key.CompareTo(targetProperty(region.Records[mid])) > 0) // when key is > mid record's key { BinarySearch(key, mid + 1, right, ref region, ref foundVals, targetProperty); //call with updated left boundry } } else//when the array is sorted in descending order { //same operation as above, but changed boundries to account for descending order if (key.CompareTo(targetProperty(region.Records[mid])) < 0) { BinarySearch(key, mid + 1, right, ref region, ref foundVals, targetProperty); } else if (key.CompareTo(targetProperty(region.Records[mid])) > 0) { BinarySearch(key, left, mid - 1, ref region, ref foundVals, targetProperty); } } //when the key is matched if (key.Equals(targetProperty(region.Records[mid]))) { //add record to the array of matched keys foundVals.Records.AddRecord(region.Records[mid]); //branch out and search for other values in both halves of current mid record (in case they're between the current matched record) BinarySearch(key, left, mid - 1, ref region, ref foundVals, targetProperty); BinarySearch(key, mid + 1, right, ref region, ref foundVals, targetProperty); } }
/// <summary> /// Heap algorithm transform the records into a heap and sorts it logarithmically /// </summary> /// <typeparam name="T">Generic type</typeparam> /// <param name="region">The data to be sorted</param> /// <param name="properyField">Property to sort</param> public static void HeapSort <T>(RecordCollection region, Func <SeismicRecord, T> properyField) where T : IComparable { int HeapSize = region.Records.currentCapacity; int i; //starting from the middle for (i = (HeapSize) / 2; i >= 0; i--) { //create a max heap (sorted) Max_Heapify(region, HeapSize, i, properyField); } for (i = region.Records.currentCapacity - 1; i > 0; i--) { SeismicRecord temp = region.Records[i]; region.Records[i] = region.Records[0]; region.Records[0] = temp; HeapSize--; Max_Heapify(region, HeapSize, 0, properyField); } }
/// <summary> /// Helper method to print out all the found results in the BinarySearch /// </summary> /// <typeparam name="T">Generic type for the TResult in targetProperty</typeparam> /// <typeparam name="F">Generic type for the key that will be searched (Type T == Type F in this program)</typeparam> /// <param name="key">The reference to generic key type</param> /// <param name="region">Which array the operation to be performed on</param> /// <param name="targetProperty">Property that the search will be conducted on (month, day etc)</param> /// <param name="left">Lowest index boundry in the array</param> /// <param name="right">Highest index boundry in the array</param> public static void BinarySearchAll <T, F>(F key, int left, int right, ref RecordCollection region, Func <SeismicRecord, T> targetProperty) where T : IComparable where F : IComparable { //array to capture the matched keys from the binary search RecordCollection foundVals = new RecordCollection(); BinarySearch(key, left, right, ref region, ref foundVals, targetProperty); //if the array of found records is not empty if (foundVals.Records.currentCapacity != 0) { //if the record should be displayed with the corresponding fields if (region.CorrespondingFields == true) { foundVals.Records.ListRecords(); } else//if only the value for the record should be displaye { //capturing the name of the lambda parameter in the delegate to determine which record field's format to return the key in string paramName = targetProperty.Method.GetParameters()[0].Name; //setting the string to the formatted key string formatOutput = RecordCollection.formatPrinter(paramName, foundVals.Records[0], targetProperty); //for each record present in foundVals for (int i = 0; i < foundVals.Records.currentCapacity; i++) { Console.WriteLine(formatOutput);//output the record } } } else //if the array is emptry, it means the BinarySearch yielded no results { Console.WriteLine($"'{key}' was not found among the records!"); } //search yield information for the key Console.WriteLine($"Found records for '{key}' : {foundVals.Records.currentCapacity} records"); }
/// <summary> /// The operation menu contains all the operations that you can perform on a given field /// </summary> /// <typeparam name="T">To reduce redundancy, I've specified a generic type method to ensure properties of the SesmicRecord /// such as int, double, TimeSpan are inferred by the compiler</typeparam> /// <param name="region">region data to operate on</param> /// <param name="propertyField">A selected property (column) to perform the required operations on</param> static private void OperationMenu <T>(RecordCollection region, Func <SeismicRecord, T> propertyField) where T : IComparable { int userInput; do { Console.WriteLine("Which of the following you want to perform?" + "\n1)Heap Sort and display corresponding values" + "\n2)Binary Search by a field" + "\n3)Find Max and minimum value" + "\n4)Go back!"); userInput = short.Parse(Console.ReadLine()); //paramName; a name of currently passed propertField parameter i.e. month => month.Month is month string paramName = propertyField.Method.GetParameters()[0].Name; switch (userInput) { case 1: //sort in the chosen order Console.WriteLine("1)Ascending" + "\n2)Descending"); int sortInput = short.Parse(Console.ReadLine()); switch (sortInput) { //sets the region's desired sort order before sorting case 1: region.SortOrder = true; break; case 2: region.SortOrder = false; break; default: Console.WriteLine("No such sorting option exists!"); break; } //when order is set, sort the region records by the property SortAlgorithms.HeapSort(region, propertyField); region.Records.ListRecords(); //list the records when it's sorted Console.WriteLine("There are currently: {0} records\n", region.Records.currentCapacity); break; case 2: //how the found records to be displayed Console.WriteLine("1)Display corresponding values" + "\n2)Display only selected field"); int searchInput = short.Parse(Console.ReadLine()); switch (searchInput) { //sets the decision on how to display the records case 1: region.CorrespondingFields = true; break; case 2: region.CorrespondingFields = false; break; default: Console.WriteLine("No such sorting option exists!"); break; } Console.WriteLine($"Enter your search value for {paramName}:"); region.SortOrder = true; SortAlgorithms.HeapSort(region, propertyField); //calls a generic method for the users input TypeSafeSearch(region, propertyField); break; case 3: //the static method for finding minimum maximum for current field MinMax.FindMinMax(ref region, propertyField); break; case 4: break; default: Console.WriteLine("Such operation doesn't exist!"); break; } } while (userInput != 4); //loops until the input is a correct case or intention to quit }