/// <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))); } }
/// <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); }
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 *)ξ 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); } }
/// <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 *)ξ 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); }
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 *)ξ 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); }