311 lines
11 KiB
C#
311 lines
11 KiB
C#
using Microsoft.Win32;
|
||
using Lab9.Models;
|
||
using Lab9.Services;
|
||
using Lab9.Strategies;
|
||
using System.Globalization;
|
||
using System.Text;
|
||
using System.Windows;
|
||
using System.Windows.Documents;
|
||
using System.Windows.Media;
|
||
using System.IO;
|
||
using System.Text.RegularExpressions;
|
||
|
||
namespace Lab9
|
||
{
|
||
public partial class MainWindow : Window
|
||
{
|
||
private readonly SortingContext _context = new();
|
||
private readonly IReadOnlyList<ISortStrategy> _strategies;
|
||
private int[] _currentArray = Array.Empty<int>();
|
||
private SortResult? _lastResult;
|
||
|
||
public MainWindow()
|
||
{
|
||
InitializeComponent();
|
||
|
||
_strategies = new List<ISortStrategy>
|
||
{
|
||
new TimSortStrategy(),
|
||
new MsdRadixSortStrategy(),
|
||
new CommunistSortStrategy()
|
||
};
|
||
|
||
UpdatePreview("Массив ещё не сформирован.");
|
||
ResetMetrics();
|
||
}
|
||
|
||
private void ElementCountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||
{
|
||
if (!IsLoaded)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ElementCountTextBox.Text = ((int)ElementCountSlider.Value).ToString(CultureInfo.InvariantCulture);
|
||
}
|
||
|
||
private void ElementCountTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
||
{
|
||
if (!IsLoaded)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (int.TryParse(ElementCountTextBox.Text, out var value))
|
||
{
|
||
value = Math.Clamp(value, (int)ElementCountSlider.Minimum, (int)ElementCountSlider.Maximum);
|
||
ElementCountSlider.Value = value;
|
||
}
|
||
}
|
||
|
||
private void GenerateArray_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var count = (int)ElementCountSlider.Value;
|
||
var random = new Random();
|
||
_currentArray = Enumerable.Range(0, count)
|
||
.Select(_ => random.Next(-250, 1000))
|
||
.ToArray();
|
||
|
||
_lastResult = null;
|
||
ClearLog();
|
||
ResetMetrics();
|
||
UpdatePreview($"Исходный массив:{Environment.NewLine}{FormatArray(_currentArray)}");
|
||
}
|
||
|
||
private void LoadFromFile_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var dialog = new OpenFileDialog
|
||
{
|
||
Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*"
|
||
};
|
||
|
||
if (dialog.ShowDialog() != true)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_currentArray = ArrayIoService.ReadArrayFromFile(dialog.FileName);
|
||
_lastResult = null;
|
||
ClearLog();
|
||
ResetMetrics();
|
||
UpdatePreview($"Массив из файла:{Environment.NewLine}{FormatArray(_currentArray)}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(
|
||
$"Не удалось прочитать файл.\n{ex.Message}",
|
||
"Ошибка",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
private void StartSorting_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (_currentArray.Length == 0)
|
||
{
|
||
MessageBox.Show(
|
||
"Сначала сгенерируй массив или загрузи его из файла. Машина без данных сортирует только воздух.",
|
||
"Ошибка",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
|
||
var strategy = GetSelectedStrategy();
|
||
var ascending = DescendingCheckBox.IsChecked != true;
|
||
|
||
_context.ContextStrategy = strategy;
|
||
_lastResult = _context.Execute(_currentArray, ascending, enableLogging: true);
|
||
|
||
ComparisonsTextBlock.Text = _lastResult.Metrics.Comparisons.ToString(CultureInfo.InvariantCulture);
|
||
MovesTextBlock.Text = _lastResult.Metrics.Moves.ToString(CultureInfo.InvariantCulture);
|
||
ElapsedTextBlock.Text = $"{_lastResult.Elapsed.TotalMilliseconds:F6} мс";
|
||
|
||
RenderLog(_lastResult.Log);
|
||
UpdatePreview(
|
||
$"Исходный массив:{Environment.NewLine}{FormatArray(_currentArray)}{Environment.NewLine}{Environment.NewLine}" +
|
||
$"Результат ({_lastResult.AlgorithmName}):{Environment.NewLine}{FormatArray(_lastResult.SortedArray)}");
|
||
}
|
||
|
||
private void SaveToFile_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (_currentArray.Length == 0)
|
||
{
|
||
MessageBox.Show(
|
||
"Сохранять пока нечего. Даже файл не любит пустые жесты.",
|
||
"Ошибка",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
|
||
var dialog = new SaveFileDialog
|
||
{
|
||
Filter = "Текстовые файлы (*.txt)|*.txt",
|
||
FileName = "sorting_result.txt"
|
||
};
|
||
|
||
if (dialog.ShowDialog() != true)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
var content = BuildExportContent();
|
||
File.WriteAllText(dialog.FileName, content, Encoding.UTF8);
|
||
|
||
MessageBox.Show(
|
||
"Результаты сохранены в файл.",
|
||
"Готово",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show(
|
||
$"Не удалось сохранить файл.\n{ex.Message}",
|
||
"Ошибка",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
private void Clear_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
_currentArray = Array.Empty<int>();
|
||
_lastResult = null;
|
||
ClearLog();
|
||
ResetMetrics();
|
||
UpdatePreview("Массив очищен.");
|
||
}
|
||
|
||
private void OpenAnalysis_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var analysisWindow = new AnalysisWindow(_strategies, DescendingCheckBox.IsChecked == true)
|
||
{
|
||
Owner = this
|
||
};
|
||
|
||
analysisWindow.ShowDialog();
|
||
}
|
||
|
||
private ISortStrategy GetSelectedStrategy()
|
||
{
|
||
if (MsdRadioButton.IsChecked == true)
|
||
{
|
||
return _strategies.OfType<MsdRadixSortStrategy>().First();
|
||
}
|
||
|
||
if (CommunistRadioButton.IsChecked == true)
|
||
{
|
||
return _strategies.OfType<CommunistSortStrategy>().First();
|
||
}
|
||
|
||
return _strategies.OfType<TimSortStrategy>().First();
|
||
}
|
||
|
||
private void ResetMetrics()
|
||
{
|
||
ComparisonsTextBlock.Text = "0";
|
||
MovesTextBlock.Text = "0";
|
||
ElapsedTextBlock.Text = "0.000000 мс";
|
||
}
|
||
|
||
private void UpdatePreview(string text)
|
||
{
|
||
ArrayPreviewTextBlock.Text = text;
|
||
}
|
||
|
||
private void ClearLog()
|
||
{
|
||
LogRichTextBox.Document.Blocks.Clear();
|
||
}
|
||
|
||
private static string FormatArray(int[] array)
|
||
{
|
||
if (array.Length == 0)
|
||
{
|
||
return "Пустой массив.";
|
||
}
|
||
|
||
return string.Join(" ", array);
|
||
}
|
||
|
||
private void RenderLog(string log)
|
||
{
|
||
LogRichTextBox.Document.Blocks.Clear();
|
||
|
||
var regex = new Regex(@"\[-?\d+\]", RegexOptions.Compiled);
|
||
var lines = log.Replace("\r\n", "\n").Split('\n');
|
||
|
||
foreach (var line in lines)
|
||
{
|
||
var paragraph = new Paragraph
|
||
{
|
||
Margin = new Thickness(0)
|
||
};
|
||
|
||
if (string.IsNullOrWhiteSpace(line))
|
||
{
|
||
paragraph.Inlines.Add(new Run(" "));
|
||
LogRichTextBox.Document.Blocks.Add(paragraph);
|
||
continue;
|
||
}
|
||
|
||
var lastIndex = 0;
|
||
|
||
foreach (Match match in regex.Matches(line))
|
||
{
|
||
if (match.Index > lastIndex)
|
||
{
|
||
paragraph.Inlines.Add(new Run(line.Substring(lastIndex, match.Index - lastIndex)));
|
||
}
|
||
|
||
paragraph.Inlines.Add(new Run(match.Value)
|
||
{
|
||
Background = Brushes.LightSkyBlue,
|
||
FontWeight = FontWeights.Bold
|
||
});
|
||
|
||
lastIndex = match.Index + match.Length;
|
||
}
|
||
|
||
if (lastIndex < line.Length)
|
||
{
|
||
paragraph.Inlines.Add(new Run(line.Substring(lastIndex)));
|
||
}
|
||
|
||
LogRichTextBox.Document.Blocks.Add(paragraph);
|
||
}
|
||
}
|
||
|
||
private string BuildExportContent()
|
||
{
|
||
var builder = new StringBuilder();
|
||
builder.AppendLine("Лабораторная работа №9. Внутренняя сортировка данных");
|
||
builder.AppendLine($"Исходный массив: {string.Join(", ", _currentArray)}");
|
||
builder.AppendLine($"Режим сортировки: {(DescendingCheckBox.IsChecked == true ? "По убыванию" : "По возрастанию")}");
|
||
|
||
if (_lastResult is null)
|
||
{
|
||
builder.AppendLine("Сортировка ещё не запускалась.");
|
||
return builder.ToString();
|
||
}
|
||
|
||
builder.AppendLine($"Алгоритм: {_lastResult.AlgorithmName}");
|
||
builder.AppendLine($"Отсортированный массив: {string.Join(", ", _lastResult.SortedArray)}");
|
||
builder.AppendLine($"Количество сравнений: {_lastResult.Metrics.Comparisons}");
|
||
builder.AppendLine($"Количество пересылок: {_lastResult.Metrics.Moves}");
|
||
builder.AppendLine($"Время сортировки: {_lastResult.Elapsed.TotalMilliseconds:F6} мс");
|
||
builder.AppendLine();
|
||
builder.AppendLine("Пошаговый журнал:");
|
||
builder.AppendLine(_lastResult.Log);
|
||
|
||
return builder.ToString();
|
||
}
|
||
}
|
||
} |