private void entropyRunCore_implant_singleThread(bool previewMode, int numberOfCases)
    {
        const int numberOfResultsFields = 1;
        // Let's sample this
        const int sampleRate = 100;

        setSampler_implant(numberOfCases, previewMode);
        sampler_implant.sample(false);
        sw.Start();

        implantResultPackage = new SimResultPackage(ref varianceContext.implantPreviewLock, numberOfCases, numberOfResultsFields);
        sw.Reset();
        for (int i = 0; i < numberOfCases; i++)
        {
            sw.Start();
            currentProgress = i + 1;
            try
            {
                // Get the results from the implant calc engine.
                ChaosSettings_implant cs = sampler_implant.getSample(i);
                implantResultPackage.Add(entropyEval_implant(cs));

                if (numberOfCases == 1 || currentProgress % sampleRate == 0)
                {
                    // Update the preview configuration.
                    if (implantResultPackage.getImplantResult(i).isValid())
                    {
                        if (numberOfCases > 1)
                        {
                            try
                            {
                                resultString = implantResultPackage.getMeanAndStdDev();
                            }
                            catch (Exception)
                            {
                                // Non-critical if an exception is raised. Ignore and carry on.
                            }
                        }
                        else
                        {
                            // Need to workaround some oddness here. The .ToString() calls below seemed to turn "0.0" into blanks.
                            string tmp = Convert.ToDouble(implantResultPackage.getImplantResult(i).getResult()).ToString("0.##");
                            resultString += tmp + ",";

                            tmp           = Convert.ToDouble(implantResultPackage.getImplantResult(i).getMin()).ToString("0.##");
                            resultString += tmp + ",";

                            tmp           = Convert.ToDouble(implantResultPackage.getImplantResult(i).getMax()).ToString("0.##");
                            resultString += tmp;
                        }
                        updateImplantSimUIFunc?.Invoke(false, implantResultPackage, resultString);
                    }
                    // Force redraw. We could use the progress bar to repaint, though.
                    // Note that this is an ugly hack to also ensure we collect stop button presses.
                    forceImplantRepaintFunc?.Invoke();
                }
            }
            catch (Exception)
            {
                // Reject the case - something happened.
            }
            // Nudge progress bar.
            if (numberOfCases > 1)
            {
                timeOfFlight_implant(sw.Elapsed.TotalSeconds);
                stepProgress?.Invoke();
            }

            // Check if user cancelled.
            if (abortRunFunc == null)
            {
                continue;
            }

            abortRunFunc();
            if (!commonVars.runAbort)
            {
                continue;
            }

            sw.Stop();
            implantResultPackage.setState(false);
            commonVars.runAbort = false; // reset state to allow user to abort save of results.
            break;
        }

        implantResultPackage.setRunTime(sw.Elapsed.TotalSeconds);
        sw.Stop();
    }
    private bool saveResults_implant()
    {
        string csvFileName = baseFileName;

        csvFileName += ".csv";

        bool returnValue = false;

        int doneCases = implantResultPackage.getListOfResults_implant().Count;

        int rows = doneCases + 1;

        string[] stringList = new string[rows];

        configProgress?.Invoke(0, doneCases);
        string headerString = "Run,Implant Shadowing";

        headerString       += ",";
        headerString       += "CDUVar" + ",";
        headerString       += "HeightVar" + ",";
        headerString       += "CRRVar" + ",";
        headerString       += "tiltAngleVar" + ",";
        headerString       += "twistAngleVar";
        stringList[0]       = headerString;
        commonVars.runAbort = false;

        try
        {
            summaryFile_implant(csvFileName);
        }
        catch (Exception)
        {
            // MessageBox.Show(ex.Message);
        }

        string statusLine = "No CSV or external files written per job settings.All done.";

        if (commonVars.getImplantSettings_nonSim().getInt(EntropySettings_nonSim.properties_i.csv) == 1)
        {
            // Set our parallel task options based on user settings.
            // Attempt at parallelism.
            CancellationTokenSource cancelSource      = new();
            CancellationToken       cancellationToken = cancelSource.Token;

            string statusString = "Compiling CSV file for results";
            updateStatus?.Invoke(statusString);
            int counter = 0;
            Parallel.For(0, doneCases, (resultEntry, loopState) =>
            {
                try
                {
                    if (implantResultPackage.getImplantResult(resultEntry).isValid())
                    {
                        if (commonVars.getImplantSettings_nonSim().getInt(EntropySettings_nonSim.properties_i.csv) == 1)
                        {
                            // result can be a CSV string for multiple values - header is aligned above
                            string csvString            = resultEntry + "," + implantResultPackage.getImplantResult(resultEntry).getResult();
                            csvString                  += ",";
                            csvString                  += implantResultPackage.getImplantResult(resultEntry).resistWidthVar + ",";
                            csvString                  += implantResultPackage.getImplantResult(resultEntry).resistHeightVar + ",";
                            csvString                  += implantResultPackage.getImplantResult(resultEntry).resistCRRVar + ",";
                            csvString                  += implantResultPackage.getImplantResult(resultEntry).tiltVar + ",";
                            csvString                  += implantResultPackage.getImplantResult(resultEntry).twistVar.ToString(CultureInfo.InvariantCulture);
                            stringList[resultEntry + 1] = csvString;
                        }
                    }

                    Interlocked.Increment(ref counter);
                    abortCSVFunc?.Invoke(cancelSource, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    commonVars.runAbort   = false;
                    commonVars.cancelling = false;
                    loopState.Stop();
                }
                catch (Exception)
                {
                }
            });

            File.WriteAllLines(csvFileName, stringList);
            statusLine = counter + " results saved to CSV file";
        }

        if (commonVars.getImplantSettings_nonSim().getInt(EntropySettings_nonSim.properties_i.external) == 1)
        {
            if (commonVars.getImplantSettings_nonSim().getInt(EntropySettings_nonSim.properties_i.csv) == 1)
            {
                statusLine += " and";
            }
            statusLine += " shapes saved to ";

            statusLine += commonVars.getExternalTypes()[commonVars.getImplantSettings_nonSim().getInt(EntropySettings_nonSim.properties_i.externalType)] + " files";
        }

        updateStatus?.Invoke(statusLine);
        forceRepaintFunc?.Invoke();
        return(returnValue);
    }