Пример #1
0
        /// <summary>
        /// Converts TimeSpan to human-readable string
        /// </summary>
        /// <param name="time">time span</param>
        /// <returns>human-readable string</returns>
        public static string ToTextString(this TimeSpan time)
        {
            if (time.Days > 0)
            {
                if (time.Days > 365)
                {
                    int years = (time.Days / 365);
                    int days  = (time.Days % 365);

                    if (years == 1)
                    {
                        return(Tx.T("time.yr days", Tx.N(days)));
                    }
                    else if (years < 5)
                    {
                        return(Tx.T("time.few yrs days", Tx.N(years), Tx.N(days)));
                    }
                    else
                    {
                        return(Tx.T("time.yrs days", Tx.N(years), Tx.N(days)));
                    }
                }
                else
                {
                    return(Tx.T("time.days hrs", Tx.N(time.Days), Tx.N(time.Hours)));
                }
            }
            else if (time.Hours > 0)
            {
                return(Tx.T("time.hrs mins", Tx.N(time.Hours), Tx.N(time.Minutes)));
            }
            else if (time.Minutes > 0)
            {
                return(Tx.T("time.mins secs", Tx.N(time.Minutes), Tx.N(time.Seconds)));
            }
            else
            {
                return(Tx.T("time.secs", Tx.N(time.Seconds)));
            }
        }
Пример #2
0
        /// <summary>
        /// Renders text overlay
        /// </summary>
        /// <param name="e">PaintEventArgs</param>
        /// <param name="clientSize">Client size</param>
        /// <param name="axisX">Axis X</param>
        /// <param name="axisY">Axis Y</param>
        private void OnPaintTextOverlay(PaintEventArgs e, Size clientSize, long axisX, long axisY)
        {
            const int TextTop        = 5;
            const int LineHeight     = 13;
            const int LineSeparation = 6;

            double mouseX, mouseY;

            if (renderMode == RenderModeType.ExtremePrecision)
            {
                mouseX = mousePosition.X.SubtractDivideLossless(axisX, scaleFactor);
                mouseY = -mousePosition.Y.SubtractDivideLossless(axisY, scaleFactor);
            }
            else
            {
                mouseX = ((mousePosition.X - axisX) / scaleFactor);
                mouseY = -((mousePosition.Y - axisY) / scaleFactor);
            }

            switch (renderMode)
            {
            case RenderModeType.LowPrecision:
                TextRenderer.DrawText(e.Graphics, "X:", monospacedFont, new Point(5, TextTop), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics, mouseX.ToString("+0.00000;-0.00000; 0", CultureInfo.InvariantCulture),
                                      monospacedFont, new Point(22, TextTop), ForeColor, TextFormatFlags.Default);

                TextRenderer.DrawText(e.Graphics, "Y:", monospacedFont, new Point(5, TextTop + LineHeight), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics, mouseY.ToString("+0.00000;-0.00000; 0", CultureInfo.InvariantCulture),
                                      monospacedFont, new Point(22, TextTop + LineHeight), ForeColor, TextFormatFlags.Default);
                break;

            case RenderModeType.MediumPrecision:
                TextRenderer.DrawText(e.Graphics, "X:", monospacedFont, new Point(5, TextTop), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics,
                                      mouseX.ToString("+0.000000######;-0.000000######; 0", CultureInfo.InvariantCulture), monospacedFont,
                                      new Point(22, TextTop), ForeColor, TextFormatFlags.Default);

                TextRenderer.DrawText(e.Graphics, "Y:", monospacedFont, new Point(5, TextTop + LineHeight), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics,
                                      mouseY.ToString("+0.000000######;-0.000000######; 0", CultureInfo.InvariantCulture), monospacedFont,
                                      new Point(22, TextTop + LineHeight), ForeColor, TextFormatFlags.Default);
                break;

            case RenderModeType.HighPrecision:
                TextRenderer.DrawText(e.Graphics, "X:", monospacedFont, new Point(5, TextTop), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics,
                                      mouseX.ToString("+0.0000000000##################;-0.0000000000##################; 0",
                                                      CultureInfo.InvariantCulture), monospacedFont, new Point(22, TextTop), ForeColor,
                                      TextFormatFlags.Default);

                TextRenderer.DrawText(e.Graphics, "Y:", monospacedFont, new Point(5, TextTop + LineHeight), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics,
                                      mouseY.ToString("+0.0000000000##################;-0.0000000000##################; 0",
                                                      CultureInfo.InvariantCulture), monospacedFont, new Point(22, TextTop + LineHeight), ForeColor,
                                      TextFormatFlags.Default);
                break;

            case RenderModeType.ExtremePrecision:
                TextRenderer.DrawText(e.Graphics, "X:", monospacedFont, new Point(5, TextTop), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics, mouseX.ToBits(), monospacedFont, new Point(22, TextTop), ForeColor,
                                      TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics, mouseX.ToExactString(), monospacedFont,
                                      new Point(22, TextTop + LineHeight), ForeColor, TextFormatFlags.Default);

                TextRenderer.DrawText(e.Graphics, "Y:", monospacedFont,
                                      new Point(5, TextTop + LineHeight * 2 + LineSeparation), ForeColor, TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics, mouseY.ToBits(), monospacedFont,
                                      new Point(22, TextTop + LineHeight * 2 + LineSeparation), ForeColor, TextFormatFlags.Default);
                TextRenderer.DrawText(e.Graphics, mouseY.ToExactString(), monospacedFont,
                                      new Point(22, TextTop + LineHeight * 3 + LineSeparation), ForeColor, TextFormatFlags.Default);

                if (dataSource != null)
                {
                    int y = TextTop + LineHeight * 4 + LineSeparation * 3;

                    if (highlightDifferences == HighlightDifferencesMode.None)
                    {
                        foreach (ListView.Item item in dataSource)
                        {
                            if (item.NumericValueSource == null || item.CheckState != CheckState.Checked)
                            {
                                continue;
                            }

                            double resultY = item.NumericValueSource.Evaluate(mouseX);
                            TextRenderer.DrawText(e.Graphics, "■:", monospacedFont, new Point(5, y), item.Color,
                                                  TextFormatFlags.Default);
                            TextRenderer.DrawText(e.Graphics, resultY.ToBits(), monospacedFont, new Point(22, y), item.Color,
                                                  TextFormatFlags.Default);
                            TextRenderer.DrawText(e.Graphics, resultY.ToExactString(), monospacedFont,
                                                  new Point(22, y + LineHeight), item.Color, TextFormatFlags.Default);

                            y += LineHeight * 2 + LineSeparation;
                        }
                    }
                    else
                    {
                        List <string> bitsList = new List <string>();

                        foreach (ListView.Item item in dataSource)
                        {
                            if (item.NumericValueSource == null || item.CheckState != CheckState.Checked)
                            {
                                continue;
                            }

                            double resultY = item.NumericValueSource.Evaluate(mouseX);
                            string bits    = resultY.ToBits();

                            TextRenderer.DrawText(e.Graphics, "■:", monospacedFont, new Point(5, y), item.Color,
                                                  TextFormatFlags.Default);
                            TextRenderer.DrawText(e.Graphics, bits, monospacedFont, new Point(22, y), item.Color,
                                                  TextFormatFlags.Default);
                            TextRenderer.DrawText(e.Graphics, resultY.ToExactString(), monospacedFont,
                                                  new Point(22, y + LineHeight), item.Color, TextFormatFlags.Default);

                            y += LineHeight * 2 + LineSeparation;

                            bitsList.Add(bits);
                        }

                        // Find differences in bits
                        const int       BitCount        = 8 * sizeof(double) + 2;
                        StringBuilder[] bitsDifferences = new StringBuilder[bitsList.Count];

                        for (int i = 0; i < bitsDifferences.Length; i++)
                        {
                            bitsDifferences[i] = new StringBuilder(BitCount);
                        }

                        for (int j = 0; j < BitCount; j++)
                        {
                            bool diff = false;
                            for (int i = 1; i < bitsDifferences.Length; i++)
                            {
                                if (bitsList[i - 1][j] != bitsList[i][j])
                                {
                                    diff = true;
                                    break;
                                }
                            }

                            for (int i = 0; i < bitsDifferences.Length; i++)
                            {
                                // Append only digits that differ
                                bitsDifferences[i].Append(diff ? bitsList[i][j] : ' ');
                            }
                        }

                        // Highlight them
                        y = TextTop + LineHeight * 4 + LineSeparation * 3;
                        for (int i = 0; i < bitsDifferences.Length; i++)
                        {
                            TextRenderer.DrawText(e.Graphics, bitsDifferences[i].ToString(), monospacedFont,
                                                  new Point(22, y), Color.FromArgb(unchecked ((int)0xffe00000)), TextFormatFlags.Default);
                            y += LineHeight * 2 + LineSeparation;
                        }
                    }
                }
                break;
            }

            string info;

            if (highlightDifferences != HighlightDifferencesMode.None)
            {
                info = string.Format(Tx.T("main.cached diffs"), Tx.N(cachedDifferences.Count), Tx.N(CachedDifferencesThreshold));
            }
            else
            {
                info = "";
            }
            TextRenderer.DrawText(e.Graphics, info, monospacedFont, new Point(5, clientSize.Height - LineHeight - LineSeparation), ForeColor, TextFormatFlags.Default);
        }
Пример #3
0
        private unsafe void OnExportSelectedButtonClick(object sender, EventArgs e)
        {
            ListView.Item selected = listView.FocusedItem;
            if (selected == null)
            {
                // Nothing to export...
                return;
            }

            ArithmeticExpression ae = selected.NumericValueSource as ArithmeticExpression;

            if (ae == null)
            {
                MessageBox.Show(this, Tx.T("dataset.errors.item not supported"), Tx.T("main.error"), MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            long xFi = 0, xLi = 0, distance = 0;

            if (ae.VariableName != null)
            {
                double xF_ = graph.MinVisibleX;
                double xL_ = graph.MaxVisibleX;

                if (xF_ >= xL_)
                {
                    MessageBox.Show(this, Tx.T("dataset.errors.cannot determine interval"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                if (Math.Sign(xF_) != Math.Sign(xL_))
                {
                    MessageBox.Show(this, Tx.T("dataset.errors.interval with zero"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                // Swap first and last for negative "x" values
                xFi = *(long *)&xF_;
                xLi = *(long *)&xL_;
                if (xFi > xLi)
                {
                    long swap = xLi;
                    xLi = xFi;
                    xFi = swap;
                }
                distance = (xLi - xFi);

                if (distance < 0)
                {
                    MessageBox.Show(this, Tx.T("dataset.errors.interval too big"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                if (distance > 10 * 1000 * 1000)
                {
                    long   sizeInMB = (distance * 8 * 2 / 1024 / 1024);
                    string sizeString;
                    if (sizeInMB > 100L * 1024L)
                    {
                        sizeString = Tx.N(sizeInMB / 1024) + " GB";
                    }
                    else
                    {
                        sizeString = Tx.N(sizeInMB) + " MB";
                    }

                    if (MessageBox.Show(this, Tx.T("dataset.errors.file too big", Tx.N(distance), sizeString), Tx.T("main.warning"),
                                        MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
                    {
                        return;
                    }
                }
            }

            using (SaveFileDialog dialog = new SaveFileDialog()) {
                const string ext = ".fvis-values";

                dialog.Filter           = Tx.T("dataset.filetype") + " (*" + ext + ")|*" + ext;
                dialog.FilterIndex      = 0;
                dialog.RestoreDirectory = true;
                dialog.Title            = Tx.T("dataset.save");

                string filename     = selected.Text;
                char[] invalidChars = Path.GetInvalidFileNameChars();
                for (int i = 0; i < invalidChars.Length; i++)
                {
                    filename = filename.Replace(invalidChars[i], '_');
                }

                dialog.FileName = filename + ext;

                if (dialog.ShowDialog(this) != DialogResult.OK)
                {
                    return;
                }

                filename = dialog.FileName;

                ProgressDialog progressDialog = new ProgressDialog();
                progressDialog.Text            = Tx.T("dataset.saving.title");
                progressDialog.MainInstruction = Tx.T("dataset.saving.description");
                progressDialog.Line1           = Tx.T("dataset.saving.progress", "0", Tx.N(xLi - xFi));
                progressDialog.Show(this);

                ThreadPool.UnsafeQueueUserWorkItem(delegate {
                    using (FileStream s = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
                        using (BinaryWriter bw = new BinaryWriter(s)) {
                            bw.Write(new[] { (byte)'f', (byte)'V', (byte)'i', (byte)'s' });

                            const int flags = 0;
                            bw.Write(flags);

                            string description = selected.Text;
                            bw.Write(description);

                            // Save graph viewport settings
                            long axisX         = graph.AxisX;
                            long axisY         = graph.AxisY;
                            double scaleFactor = graph.ScaleFactor;

                            bw.Write(axisX);
                            bw.Write(axisY);
                            bw.Write(scaleFactor);

                            if (ae.VariableName == null)
                            {
                                // Constant function, save only one value
                                double y = ae.Evaluate(double.NaN);

                                const long count = 2;
                                bw.Write(count);

                                bw.Write(double.MinValue);
                                bw.Write(y);
                                bw.Write(double.MaxValue);
                                bw.Write(y);
                            }
                            else
                            {
                                bw.Write(distance + 1);

                                Stopwatch sw       = Stopwatch.StartNew();
                                long lastProcessed = xFi;

                                for (long xi = xFi; xi <= xLi; xi++)
                                {
                                    double x = *(double *)&xi;
                                    double y = ae.Evaluate(x);

                                    bw.Write(x);
                                    bw.Write(y);

                                    if (sw.ElapsedMilliseconds > 4000)
                                    {
                                        if (progressDialog.IsCancelled)
                                        {
                                            break;
                                        }

                                        sw.Stop();

                                        long delta         = (xi - lastProcessed);
                                        long rate          = delta * 1000 / (long)sw.Elapsed.TotalMilliseconds;
                                        TimeSpan remaining = TimeSpan.FromSeconds((xLi - xi) / rate);

                                        lastProcessed = xi;

                                        BeginInvoke((MethodInvoker) delegate {
                                            progressDialog.Line1    = Tx.T("dataset.saving.progress", Tx.N(lastProcessed - xFi), Tx.N(xLi - xFi));
                                            progressDialog.Line2    = Tx.T("main.remaining time", remaining.ToTextString());
                                            progressDialog.Progress = (int)((lastProcessed - xFi) * 100 / (xLi - xFi));
                                        });

                                        sw.Restart();
                                    }
                                }

                                sw.Stop();
                            }
                        }

                    if (progressDialog.IsCancelled)
                    {
                        try {
                            File.Delete(filename);
                        } catch {
                            // Nothing to do...
                        }
                    }

                    BeginInvoke((MethodInvoker) delegate {
                        progressDialog.TaskCompleted();
                    });
                }, null);
            }
        }
Пример #4
0
        /// <summary>
        /// Run time estamination process
        /// </summary>
        /// <param name="silent">Suppress warning messages</param>
        /// <returns>Returns true if input data are correct; false, otherwise</returns>
        private unsafe bool EstimateTime(bool silent)
        {
            refreshNeeded = false;

            int progressRefreshRate = 0;

            int count = 0;

            foreach (ListView.Item item in listView.Items)
            {
                if (item.NumericValueSource == null || item.CheckState != CheckState.Checked)
                {
                    continue;
                }

                count++;

                ArithmeticExpression ae = item.NumericValueSource as ArithmeticExpression;
                if (ae?.Callbacks is NativeOperatorRemotingCallbacks)
                {
                    // Compute fewer samples with x86 Remoting
                    progressRefreshRate += 3000;
                }
                else
                {
                    progressRefreshRate += 1;
                }
            }

            if (count < 2)
            {
                infoLabel.Text = "";
                if (!silent)
                {
                    MessageBox.Show(this, Tx.T("find diffs.errors.invalid selection"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return(false);
            }

            double xF_, xL_;

            if (!double.TryParse(startTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out xF_))
            {
                infoLabel.Text = "";
                if (!silent)
                {
                    MessageBox.Show(this, Tx.T("find diffs.errors.invalid start"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return(false);
            }

            if (!double.TryParse(endTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out xL_))
            {
                infoLabel.Text = "";
                if (!silent)
                {
                    MessageBox.Show(this, Tx.T("find diffs.errors.invalid end"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return(false);
            }

            if (xF_ >= xL_)
            {
                infoLabel.Text = "";
                if (!silent)
                {
                    MessageBox.Show(this, Tx.T("find diffs.errors.invalid interval"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return(false);
            }

            if (Math.Sign(xF_) != Math.Sign(xL_))
            {
                infoLabel.Text = "";
                if (!silent)
                {
                    MessageBox.Show(this, Tx.T("find diffs.errors.interval with zero"), Tx.T("main.error"),
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                return(false);
            }

            // Swap first and last for negative "x" values
            long xFi = *(long *)&xF_;
            long xLi = *(long *)&xL_;

            if (xFi > xLi)
            {
                long swap = xLi;
                xLi = xFi;
                xFi = swap;
            }
            distance = (xLi - xFi);

            int thresholdPerItem = (Threshold / progressRefreshRate);

            if (distance < thresholdPerItem)
            {
                estimatedTime  = TimeSpan.MinValue;
                infoLabel.Text = Tx.T("find diffs.status", Tx.T("time.immediately"), Tx.N(distance));
                return(true);
            }

            Stopwatch sw = Stopwatch.StartNew();

            for (int i = 0; i < thresholdPerItem; i++)
            {
                long   xi = *(long *)&xF_ + i;
                double x  = *(double *)&xi;

                bool   isFirst      = true;
                double computedMinY = double.MaxValue;
                double computedMaxY = double.MinValue;

                foreach (ListView.Item item in listView.Items)
                {
                    if (item.NumericValueSource == null || item.CheckState != CheckState.Checked)
                    {
                        continue;
                    }

                    double y = item.NumericValueSource.Evaluate(x);

                    if (isFirst)
                    {
                        isFirst      = false;
                        computedMinY = y;
                        computedMaxY = y;
                    }
                    else
                    {
                        if (y > computedMaxY)
                        {
                            computedMaxY = y;
                        }
                        else if (y < computedMinY)
                        {
                            computedMinY = y;
                        }
                    }
                }
            }

            sw.Stop();

            const int MultithreadPenalty = 4;

            int   threadCount = Math.Max(Environment.ProcessorCount, 1);
            ulong secs        = ((ulong)(((sw.ElapsedMilliseconds * distance / 1000) / thresholdPerItem) / threadCount) * MultithreadPenalty);

            if (secs > 1000L * 365L * 24L * 60L * 60L)   // 1000 years in seconds
            {
                estimatedTime  = TimeSpan.MaxValue;
                infoLabel.Text = Tx.T("find diffs.status", Tx.T("time.infinitely"), Tx.N(distance));
            }
            else
            {
                estimatedTime  = TimeSpan.FromSeconds(secs);
                infoLabel.Text = Tx.T("find diffs.status", estimatedTime.ToTextString(), Tx.N(distance));
            }

            return(true);
        }
Пример #5
0
        private unsafe void OnRunButtonClick(object sender, EventArgs e)
        {
            if (!EstimateTime(false))
            {
                return;
            }

            double start, end;

            double.TryParse(startTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out start);
            double.TryParse(endTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out end);

            if (estimatedTime.TotalMinutes > 0.5)
            {
                string timeString;
                if (estimatedTime == TimeSpan.MaxValue)
                {
                    timeString = Tx.T("time.infinitely");
                }
                else
                {
                    timeString = estimatedTime.ToTextString();
                }

                if (MessageBox.Show(this, Tx.T("find diffs.errors.interval too big", Tx.N(distance), timeString), Tx.T("main.warning"),
                                    MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
                {
                    return;
                }
            }

            long starti = *(long *)&start;
            long endi   = *(long *)&end;

            if (starti > endi)
            {
                long swap = starti;
                starti = endi;
                endi   = swap;
            }
            distance = (endi - starti);

            long currentProcessed = 0, lastProcessed = 0;
            long differencesFound = 0;

            SpinLock syncLock = new SpinLock();

            ProgressDialog progressDialog = new ProgressDialog {
                Text            = Tx.T("find diffs.finding.title"),
                MainInstruction = Tx.T("find diffs.finding.description"),
                Line1           = Tx.T("find diffs.finding.progress", "0", Tx.N(distance)),
                ShowInTaskbar   = false,
                MinimizeBox     = false
            };

            ThreadPool.UnsafeQueueUserWorkItem(delegate {
                BeginInvoke((MethodInvoker) delegate {
                    progressDialog.ShowDialog(this);
                });

                Stopwatch sw = Stopwatch.StartNew();

                Parallel.For(starti, endi, (xi, state) => {
                    double x = *(double *)&xi;

                    bool isFirst        = true;
                    double computedMinY = double.MaxValue, computedMaxY = double.MinValue;

                    foreach (ListView.Item item in listView.Items)
                    {
                        if (item.NumericValueSource == null || item.CheckState != CheckState.Checked)
                        {
                            continue;
                        }

                        double y = item.NumericValueSource.Evaluate(x);

                        if (isFirst)
                        {
                            isFirst      = false;
                            computedMinY = y;
                            computedMaxY = y;
                        }
                        else
                        {
                            if (y > computedMaxY)
                            {
                                computedMaxY = y;
                            }
                            else if (y < computedMinY)
                            {
                                computedMinY = y;
                            }
                        }
                    }

                    bool lockTaken = false;
                    syncLock.Enter(ref lockTaken);

                    if (!isFirst && computedMinY != computedMaxY)
                    {
                        graph.AddDifferenceUnsafe(x, computedMinY, computedMaxY);
                        differencesFound++;
                    }

                    if (sw.ElapsedMilliseconds > 4000)
                    {
                        if (progressDialog.IsCancelled)
                        {
                            if (lockTaken)
                            {
                                syncLock.Exit();
                            }
                            state.Stop();
                            return;
                        }

                        sw.Stop();

                        long delta         = (currentProcessed - lastProcessed);
                        long rate          = delta * 1000 / (long)sw.Elapsed.TotalMilliseconds;
                        TimeSpan remaining = TimeSpan.FromSeconds((distance - currentProcessed) / rate);

                        lastProcessed = currentProcessed;

                        BeginInvoke((MethodInvoker) delegate {
                            progressDialog.Line1    = Tx.T("find diffs.finding.progress", Tx.N(lastProcessed), Tx.N(distance));
                            progressDialog.Line2    = Tx.T("main.remaining time", remaining.ToTextString());
                            progressDialog.Progress = (int)(lastProcessed * 100 / distance);
                        });

                        sw.Restart();
                    }

                    currentProcessed++;

                    if (lockTaken)
                    {
                        syncLock.Exit();
                    }
                });

                sw.Stop();

                BeginInvoke((MethodInvoker) delegate {
                    progressDialog.TaskCompleted();

                    if (!progressDialog.IsCancelled)
                    {
                        MessageBox.Show(this,
                                        Tx.T("find diffs.complete.description",
                                             Tx.N(distance),
                                             Tx.N(differencesFound),
                                             Tx.N(differencesFound * 100 / distance)),
                                        Tx.T("find diffs.complete.title"),
                                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                });
            }, null);
        }