public List <Component> read_components_from_xlsx(InputFile file, IEnumerable <Correction> correctionFactors)
        {
            raw_components_in_file.Clear();
            string absolute_path = file.directory + "\\" + file.filename + file.extension;

            using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(absolute_path, false))
            {
                // Get Data in Sheet1 of Excel file
                IEnumerable <Sheet> sheetcollection = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild <Sheets>().Elements <Sheet>();         // Get all sheets in spread sheet document
                WorksheetPart       worksheet_1     = (WorksheetPart)spreadsheetDocument.WorkbookPart.GetPartById(sheetcollection.First().Id.Value); // Get sheet1 Part of Spread Sheet Document
                SheetData           sheet_1         = worksheet_1.Worksheet.Elements <SheetData>().First();
                List <Row>          rowcollection   = worksheet_1.Worksheet.Descendants <Row>().ToList();

                Component new_component    = new Component();
                int       charge_row_index = 0;
                string    scan_range       = "";
                for (int i = 0; i < rowcollection.Count(); i++)
                {
                    if (i == 0)
                    {
                        continue;         //skip component header
                    }
                    IEnumerable <Cell> cells = rowcollection[i].Descendants <Cell>();

                    List <string> cellStrings = new List <string>();

                    for (int k = 0; k < rowcollection[i].Descendants <Cell>().Count(); k++)
                    {
                        cellStrings.Add(GetCellValue(spreadsheetDocument, rowcollection[i].Descendants <Cell>().ElementAt(k)));
                    }

                    if (cellStrings.Count > 4) //component row
                    {
                        if (i > 1)
                        {
                            add_component(new_component);                    // here we're adding the previously read component
                        }
                        new_component    = new Component(cellStrings, file); // starting fresh here with a newly created componet.
                        charge_row_index = 0;
                        scan_range       = cellStrings[8];
                    }
                    else if (cellStrings.Count == 4) //charge-state row
                    {
                        if (charge_row_index == 0)
                        {
                            charge_row_index += 1;
                            continue; //skip charge state headers
                        }
                        else
                        {
                            new_component.add_charge_state(cellStrings, Correction.GetCorrectionFactor(file.filename, scan_range, correctionFactors));
                        }
                    }
                }
                add_component(new_component); //add the final component
            }
            final_components = remove_monoisotopic_duplicates_harmonics_from_same_scan(raw_components_in_file);
            scan_ranges      = new HashSet <string>(final_components.Select(c => c.scan_range).ToList()).ToList();
            return(final_components);
        }
        public static List <Correction> CorrectionFactorInterpolation(IEnumerable <Correction> cFactors)
        {
            List <Correction> correction_factors    = cFactors.OrderBy(p => p.scan_number).ToList();
            bool              found_first_defined   = false;
            double            recent_correction     = 0;
            List <Correction> undefined_corrections = new List <Correction>();

            for (int i = 0; i < correction_factors.Count; i++)
            {
                Correction this_correction = correction_factors[i];
                if (Double.IsNaN(this_correction.correction))
                {
                    undefined_corrections.Add(this_correction); // Add all undefined corrections to a list for interpolating later
                }
                else if (!found_first_defined)                  //Found the first defined correction factor.
                {
                    //Copy the first defined correction factor to all undefined correction factors at the head of the list.
                    found_first_defined = true;
                    foreach (Correction c in undefined_corrections)
                    {
                        c.correction = this_correction.correction;
                    }
                    recent_correction = this_correction.correction;
                    undefined_corrections.Clear();
                }
                else
                {
                    //Average this defined correction value and the last one observed, and copy this to each recently seen undefined correction.
                    double correction_value = (recent_correction + this_correction.correction) / 2;
                    foreach (Correction c in undefined_corrections)
                    {
                        c.correction = correction_value;
                    }
                    recent_correction = this_correction.correction;
                    undefined_corrections.Clear();
                }
            }
            //We ran out of real estate. Let's do something with any remaining undefined corrections.
            foreach (Correction c in undefined_corrections)
            {
                c.correction = recent_correction;
            }
            return(correction_factors);
        }