private void StartBulkSearch()
        {
            // special string signifying a test
            bool   isTest = comparisonData.Comparison == "\tes\t";
            string testId = isTest ? comparisonData.FB1 : string.Empty;

            if (!isTest)
            {
                Log.Info(strUserId, comparisonData.ToString(), "Comparison Start", strHp_Head + " / " + strHd_Head);
            }

            // frequencies cache
            DataTable dtFrequencies = new Database().GetFrequencyData();
            // create denominator cache
            Dictionary <string, Dictionary <string, double> > perRaceLocusDenominatorCache = new Dictionary <string, Dictionary <string, double> >();

            foreach (DataRow drEth in new Database().getAllEthnics().Rows)
            {
                perRaceLocusDenominatorCache.Add(drEth["EthnicName"].ToString(), new Dictionary <string, double>());
            }
            foreach (string key in perRaceLocusDenominatorCache.Keys)
            {
                foreach (DataRow drLoc in bi.GetLocusInOrder(comparisonData.LabKitID).Rows)
                {
                    perRaceLocusDenominatorCache[key].Add(drLoc["LocusName"].ToString().ToUpper(), double.NaN);
                }
            }
            // end create denominator cache
            // dropout cache
            Dictionary <int, Dictionary <string, Dictionary <string, float> > > perReplicateDropOutCache = new Dictionary <int, Dictionary <string, Dictionary <string, float> > >();

            for (int i = 0; i < 3; i++)
            {
                foreach (DataRow drLoc in bi.GetLocusInOrder(comparisonData.LabKitID).Rows)
                {
                    if (!perReplicateDropOutCache.ContainsKey(i))
                    {
                        perReplicateDropOutCache.Add(i, new Dictionary <string, Dictionary <string, float> >());
                    }

                    perReplicateDropOutCache[i].Add(drLoc["LocusName"].ToString().ToUpper(), new Dictionary <string, float>());
                }
            }
            // end dropout cache
            // start frequency cache
            Dictionary <string, Dictionary <string, Dictionary <string, float> > > perRaceLocusFrequencyCache = new Dictionary <string, Dictionary <string, Dictionary <string, float> > >();

            foreach (DataRow drEth in new Database().getAllEthnics().Rows)
            {
                perRaceLocusFrequencyCache.Add(drEth["EthnicName"].ToString(), new Dictionary <string, Dictionary <string, float> >());
            }
            foreach (string key in perRaceLocusDenominatorCache.Keys)
            {
                foreach (DataRow drLoc in bi.GetLocusInOrder(comparisonData.LabKitID).Rows)
                {
                    perRaceLocusFrequencyCache[key].Add(drLoc["LocusName"].ToString().ToUpper(), new Dictionary <string, float>());
                }
            }
            // end frequency cache
            // start bulk permutation caches
            // all we need to do is send these in as not null
            Dictionary <string, Dictionary <int, List <Comparison.AllelesPair> > > numeratorPermutationCache   = new Dictionary <string, Dictionary <int, List <Comparison.AllelesPair> > >();
            Dictionary <string, Dictionary <int, List <Comparison.AllelesPair> > > denominatorPermutationCache = new Dictionary <string, Dictionary <int, List <Comparison.AllelesPair> > >();
            // end bulk permutation cache

            Dictionary <string, Dictionary <string, float> > Results = new Dictionary <string, Dictionary <string, float> >();

            // gets "lab types," "population," or "from file" comparison list from the database
            DataTable dtKnownProfile = bi.GetKnown_Profile(strLab_Popultn_Type, comparisonData.FromFileGuid);

            for (int i = 0; i < dtKnownProfile.Rows.Count; i++)
            {
                try
                {
                    // reads datarow to a dictionary
                    comparisonData.ComparisonAlleles = ReadBulkComparison(dtKnownProfile, i);
                    FST.Common.Comparison comparison = new Comparison(comparisonData);

                    Dictionary <string, float> Result = comparison.DoCompare( // caches
                        dtFrequencies,
                        perRaceLocusDenominatorCache,
                        perRaceLocusFrequencyCache,
                        perReplicateDropOutCache,
                        numeratorPermutationCache,
                        denominatorPermutationCache
                        );

                    // if we're testing, write test results. otherwise, add results to the Results dictionary (gets passed to the bulk printer below)
                    if (isTest)
                    {
                        bi.WriteTestResults(testId, dtKnownProfile.Rows[i]["ID"].ToString(), Result["Asian"].ToString(), Result["Black"].ToString(), Result["Caucasian"].ToString(), Result["Hispanic"].ToString());
                    }
                    else
                    {
                        Results.Add(dtKnownProfile.Rows[i]["ID"].ToString(), Result);
                    }
                }
                catch
                {
                    continue;
                }
            }

            if (!isTest)
            {
                strReportFilePath = FSTService.Instance.BulkPrinter.Print(Results, dtKnownProfile, comparisonData);
            }

            if (!isTest)
            {
                Log.Info(strUserId, comparisonData.ToString(), "Comparison End", strHp_Head + " / " + strHd_Head);
            }
        }