diff --git a/.idea/.idea.InternalSortMethods/.idea/deploymentTargetSelector.xml b/.idea/.idea.InternalSortMethods/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..4751e02
--- /dev/null
+++ b/.idea/.idea.InternalSortMethods/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.InternalSortMethods/.idea/encodings.xml b/.idea/.idea.InternalSortMethods/.idea/encodings.xml
index df87cf9..21f951f 100644
--- a/.idea/.idea.InternalSortMethods/.idea/encodings.xml
+++ b/.idea/.idea.InternalSortMethods/.idea/encodings.xml
@@ -1,4 +1,6 @@
-
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.InternalSortMethods/.idea/libraries/androidx_annotation_annotation_jvm.xml b/.idea/.idea.InternalSortMethods/.idea/libraries/androidx_annotation_annotation_jvm.xml
new file mode 100644
index 0000000..d2763d2
--- /dev/null
+++ b/.idea/.idea.InternalSortMethods/.idea/libraries/androidx_annotation_annotation_jvm.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.qwen/settings.json b/.qwen/settings.json
new file mode 100644
index 0000000..846e455
--- /dev/null
+++ b/.qwen/settings.json
@@ -0,0 +1,13 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(mkdir *)",
+ "Bash(dotnet *)",
+ "Bash(powershell *)",
+ "Bash(del *)",
+ "Bash(rmdir *)",
+ "Bash(findstr *)"
+ ]
+ },
+ "$version": 3
+}
\ No newline at end of file
diff --git a/.qwen/settings.json.orig b/.qwen/settings.json.orig
new file mode 100644
index 0000000..9a9e4cc
--- /dev/null
+++ b/.qwen/settings.json.orig
@@ -0,0 +1,7 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(mkdir *)"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/InternalSortMethods/App.axaml b/InternalSortMethods/App.axaml
index 4fa6825..8eb4e25 100644
--- a/InternalSortMethods/App.axaml
+++ b/InternalSortMethods/App.axaml
@@ -2,8 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:InternalSortMethods"
x:Class="InternalSortMethods.App"
- RequestedThemeVariant="Default">
-
+ RequestedThemeVariant="Dark">
@@ -11,5 +10,52 @@
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/InternalSortMethods/Converters/StateToColorConverter.cs b/InternalSortMethods/Converters/StateToColorConverter.cs
new file mode 100644
index 0000000..b238d17
--- /dev/null
+++ b/InternalSortMethods/Converters/StateToColorConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+using InternalSortMethods.ViewModels;
+
+namespace InternalSortMethods.Converters;
+
+///
+/// Конвертирует SortItemState в соответствующий цвет столбца.
+/// Используется в DataTemplate для визуализации.
+///
+public class StateToColorConverter : IValueConverter
+{
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is not SortItemState state)
+ return Colors.Transparent;
+
+ return state switch
+ {
+ SortItemState.Default => Color.Parse("#818cf8"), // Индиго
+ SortItemState.Comparing => Color.Parse("#fbbf24"), // Жёлтый
+ SortItemState.Swapping => Color.Parse("#f87171"), // Красный
+ SortItemState.Writing => Color.Parse("#22d3ee"), // Голубой
+ SortItemState.Pivot => Color.Parse("#c084fc"), // Фиолетовый
+ SortItemState.Sorted => Color.Parse("#34d399"), // Зелёный
+ _ => Color.Parse("#818cf8")
+ };
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/InternalSortMethods/Models/BubbleSort.cs b/InternalSortMethods/Models/BubbleSort.cs
new file mode 100644
index 0000000..9e256ad
--- /dev/null
+++ b/InternalSortMethods/Models/BubbleSort.cs
@@ -0,0 +1,73 @@
+using InternalSortMethods.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Сортировка пузырьком (Bubble Sort).
+/// Последовательно сравнивает соседние элементы и меняет их, если они стоят в неверном порядке.
+/// Сложность: O(n²) по времени, O(1) по памяти.
+///
+public sealed class BubbleSort : SortAlgorithm
+{
+ public override string DisplayName => "Bubble Sort";
+
+ public override IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default)
+ {
+ var arr = (int[])array.Clone();
+ int n = arr.Length;
+
+ for (int i = 0; i < n - 1; i++)
+ {
+ bool swapped = false;
+
+ for (int j = 0; j < n - i - 1; j++)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Шаг сравнения
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [j, j + 1],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ if (arr[j] > arr[j + 1])
+ {
+ // Перестановка
+ (arr[j], arr[j + 1]) = (arr[j + 1], arr[j]);
+ swapped = true;
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Swap,
+ Indices = [j, j + 1],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+ }
+
+ // Элемент n-i-1 встал на своё место
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [n - i - 1],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ if (!swapped) break; // Массив отсортирован
+ }
+
+ // Помечаем все элементы как отсортированные
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = Enumerable.Range(0, n).ToArray(),
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+}
diff --git a/InternalSortMethods/Models/HeapSort.cs b/InternalSortMethods/Models/HeapSort.cs
new file mode 100644
index 0000000..972ae87
--- /dev/null
+++ b/InternalSortMethods/Models/HeapSort.cs
@@ -0,0 +1,121 @@
+using InternalSortMethods.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Пирамидальная сортировка (Heap Sort).
+/// Строит максимальную кучу, затем извлекает корень и восстанавливает кучу.
+/// Сложность: O(n log n) по времени, O(1) по памяти.
+///
+public sealed class HeapSort : SortAlgorithm
+{
+ public override string DisplayName => "Heap Sort";
+
+ public override IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default)
+ {
+ var arr = (int[])array.Clone();
+ int n = arr.Length;
+
+ // Построение кучи (heapify)
+ for (int i = n / 2 - 1; i >= 0; i--)
+ {
+ foreach (var step in Heapify(arr, n, i, cancellationToken))
+ yield return step;
+ }
+
+ // Извлечение элементов из кучи
+ for (int i = n - 1; i > 0; i--)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Перемещаем корень (максимум) в конец
+ (arr[0], arr[i]) = (arr[i], arr[0]);
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Swap,
+ Indices = [0, i],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [i],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ // Восстановление кучи
+ foreach (var step in Heapify(arr, i, 0, cancellationToken))
+ yield return step;
+ }
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [0],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+
+ ///
+ /// Восстанавливает свойство максимальной кучи для поддерева с корнем в index.
+ ///
+ private IEnumerable Heapify(int[] arr, int n, int index, CancellationToken ct)
+ {
+ ct.ThrowIfCancellationRequested();
+
+ int largest = index;
+ int left = 2 * index + 1;
+ int right = 2 * index + 2;
+
+ // Сравнение с левым потомком
+ if (left < n)
+ {
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [largest, left],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ if (arr[left] > arr[largest])
+ largest = left;
+ }
+
+ // Сравнение с правым потомком
+ if (right < n)
+ {
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [largest, right],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ if (arr[right] > arr[largest])
+ largest = right;
+ }
+
+ // Перестановка, если корень не максимум
+ if (largest != index)
+ {
+ (arr[index], arr[largest]) = (arr[largest], arr[index]);
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Swap,
+ Indices = [index, largest],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ // Рекурсивный heapify для затронутого поддерева
+ foreach (var step in Heapify(arr, n, largest, ct))
+ yield return step;
+ }
+ }
+}
diff --git a/InternalSortMethods/Models/InsertionSort.cs b/InternalSortMethods/Models/InsertionSort.cs
new file mode 100644
index 0000000..000e2ae
--- /dev/null
+++ b/InternalSortMethods/Models/InsertionSort.cs
@@ -0,0 +1,88 @@
+using InternalSortMethods.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Сортировка вставками (Insertion Sort).
+/// Вставляет каждый элемент в уже отсортированную часть массива.
+/// Сложность: O(n²) по времени, O(1) по памяти.
+///
+public sealed class InsertionSort : SortAlgorithm
+{
+ public override string DisplayName => "Insertion Sort";
+
+ public override IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default)
+ {
+ var arr = (int[])array.Clone();
+ int n = arr.Length;
+
+ // Первый элемент считается отсортированным
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [0],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ for (int i = 1; i < n; i++)
+ {
+ int key = arr[i];
+ int j = i - 1;
+
+ // Подсвечиваем вставляемый элемент
+ yield return new SortStep
+ {
+ Type = SortStepType.Pivot,
+ Indices = [i],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ while (j >= 0 && arr[j] > key)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Сравнение
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [j, i],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ // Сдвиг элемента вправо
+ arr[j + 1] = arr[j];
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Write,
+ Indices = [j + 1],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ j--;
+ }
+
+ // Вставка ключа на правильную позицию
+ arr[j + 1] = key;
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Write,
+ Indices = [j + 1],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ // Элементы до i включительно отсортированы
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = Enumerable.Range(0, i + 1).ToArray(),
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+ }
+}
diff --git a/InternalSortMethods/Models/MergeSort.cs b/InternalSortMethods/Models/MergeSort.cs
new file mode 100644
index 0000000..939049d
--- /dev/null
+++ b/InternalSortMethods/Models/MergeSort.cs
@@ -0,0 +1,140 @@
+using InternalSortMethods.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Сортировка слиянием (Merge Sort).
+/// Рекурсивно делит массив пополам, сортирует части и сливает их.
+/// Сложность: O(n log n) по времени, O(n) по памяти.
+///
+public sealed class MergeSort : SortAlgorithm
+{
+ public override string DisplayName => "Merge Sort";
+
+ public override IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default)
+ {
+ var arr = (int[])array.Clone();
+ var steps = MergeSortRecursive(arr, 0, arr.Length - 1, cancellationToken).ToList();
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = Enumerable.Range(0, arr.Length).ToArray(),
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+
+ private IEnumerable MergeSortRecursive(int[] arr, int left, int right, CancellationToken ct)
+ {
+ if (left >= right)
+ {
+ if (left == right)
+ {
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [left],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+ yield break;
+ }
+
+ ct.ThrowIfCancellationRequested();
+
+ int mid = left + (right - left) / 2;
+
+ foreach (var step in MergeSortRecursive(arr, left, mid, ct))
+ yield return step;
+
+ foreach (var step in MergeSortRecursive(arr, mid + 1, right, ct))
+ yield return step;
+
+ foreach (var step in Merge(arr, left, mid, right, ct))
+ yield return step;
+ }
+
+ private IEnumerable Merge(int[] arr, int left, int mid, int right, CancellationToken ct)
+ {
+ int n1 = mid - left + 1;
+ int n2 = right - mid;
+
+ var L = new int[n1];
+ var R = new int[n2];
+
+ Array.Copy(arr, left, L, 0, n1);
+ Array.Copy(arr, mid + 1, R, 0, n2);
+
+ int i = 0, j = 0, k = left;
+
+ while (i < n1 && j < n2)
+ {
+ ct.ThrowIfCancellationRequested();
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [left + i, mid + 1 + j],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ if (L[i] <= R[j])
+ {
+ arr[k] = L[i];
+ i++;
+ }
+ else
+ {
+ arr[k] = R[j];
+ j++;
+ }
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Write,
+ Indices = [k],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ k++;
+ }
+
+ while (i < n1)
+ {
+ ct.ThrowIfCancellationRequested();
+
+ arr[k] = L[i];
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Write,
+ Indices = [k],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ i++;
+ k++;
+ }
+
+ while (j < n2)
+ {
+ ct.ThrowIfCancellationRequested();
+
+ arr[k] = R[j];
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Write,
+ Indices = [k],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ j++;
+ k++;
+ }
+ }
+}
diff --git a/InternalSortMethods/Models/QuickSort.cs b/InternalSortMethods/Models/QuickSort.cs
new file mode 100644
index 0000000..8bdd2cb
--- /dev/null
+++ b/InternalSortMethods/Models/QuickSort.cs
@@ -0,0 +1,154 @@
+using InternalSortMethods.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Быстрая сортировка (Quick Sort).
+/// Разделяет массив по опорному элементу (pivot) и рекурсивно сортирует части.
+/// Сложность: O(n log n) в среднем, O(n²) в худшем случае.
+///
+public sealed class QuickSort : SortAlgorithm
+{
+ public override string DisplayName => "Quick Sort";
+
+ public override IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default)
+ {
+ var arr = (int[])array.Clone();
+ var steps = QuickSortRecursive(arr, 0, arr.Length - 1, cancellationToken).ToList();
+
+ // Помечаем все элементы как отсортированные в конце
+ steps.Add(new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = Enumerable.Range(0, arr.Length).ToArray(),
+ ArrayState = (int[])arr.Clone()
+ });
+
+ return steps;
+ }
+
+ private IEnumerable QuickSortRecursive(int[] arr, int low, int high, CancellationToken ct)
+ {
+ if (low < high)
+ {
+ ct.ThrowIfCancellationRequested();
+
+ // Разбиение и получение опорного индекса
+ foreach (var step in PartitionSteps(arr, low, high, ct))
+ {
+ yield return step;
+ }
+
+ // Определяем позицию pivot после разбиения
+ int pivotIdx = FindPivotIndex(arr, low, high);
+
+ // Рекурсивная сортировка левой и правой частей
+ foreach (var step in QuickSortRecursive(arr, low, pivotIdx - 1, ct))
+ yield return step;
+
+ foreach (var step in QuickSortRecursive(arr, pivotIdx + 1, high, ct))
+ yield return step;
+ }
+ else if (low == high)
+ {
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [low],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+ }
+
+ private int FindPivotIndex(int[] arr, int low, int high)
+ {
+ // После partition pivot стоит на правильном месте
+ // Ищем элемент, который уже на своём месте
+ for (int i = low; i <= high; i++)
+ {
+ bool isPivot = true;
+ for (int j = low; j < i; j++)
+ {
+ if (arr[j] > arr[i]) { isPivot = false; break; }
+ }
+ for (int j = i + 1; j <= high; j++)
+ {
+ if (arr[j] < arr[i]) { isPivot = false; break; }
+ }
+ if (isPivot) return i;
+ }
+ return low;
+ }
+
+ ///
+ /// Генерирует шаги для операции разбиения (partition) с визуализацией.
+ ///
+ private IEnumerable PartitionSteps(int[] arr, int low, int high, CancellationToken ct)
+ {
+ var temp = (int[])arr.Clone();
+ int pivot = arr[high];
+ int i = low - 1;
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Pivot,
+ Indices = [high],
+ ArrayState = (int[])temp.Clone()
+ };
+
+ for (int j = low; j < high; j++)
+ {
+ ct.ThrowIfCancellationRequested();
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [j, high],
+ ArrayState = (int[])temp.Clone()
+ };
+
+ if (temp[j] <= pivot)
+ {
+ i++;
+ if (i != j)
+ {
+ (temp[i], temp[j]) = (temp[j], temp[i]);
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Swap,
+ Indices = [i, j],
+ ArrayState = (int[])temp.Clone()
+ };
+ }
+ }
+ }
+
+ if (i + 1 != high)
+ {
+ (temp[i + 1], temp[high]) = (temp[high], temp[i + 1]);
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Swap,
+ Indices = [i + 1, high],
+ ArrayState = (int[])temp.Clone()
+ };
+ }
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [i + 1],
+ ArrayState = (int[])temp.Clone()
+ };
+
+ // Копируем результат в оригинальный массив
+ for (int k = low; k <= high; k++)
+ arr[k] = temp[k];
+ }
+}
diff --git a/InternalSortMethods/Models/SelectionSort.cs b/InternalSortMethods/Models/SelectionSort.cs
new file mode 100644
index 0000000..9e94766
--- /dev/null
+++ b/InternalSortMethods/Models/SelectionSort.cs
@@ -0,0 +1,83 @@
+using InternalSortMethods.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Сортировка выбором (Selection Sort).
+/// Находит минимальный элемент в неотсортированной части и ставит его на позицию.
+/// Сложность: O(n²) по времени, O(1) по памяти.
+///
+public sealed class SelectionSort : SortAlgorithm
+{
+ public override string DisplayName => "Selection Sort";
+
+ public override IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default)
+ {
+ var arr = (int[])array.Clone();
+ int n = arr.Length;
+
+ for (int i = 0; i < n - 1; i++)
+ {
+ int minIdx = i;
+
+ // Подсвечиваем текущий минимум
+ yield return new SortStep
+ {
+ Type = SortStepType.Pivot,
+ Indices = [minIdx],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ for (int j = i + 1; j < n; j++)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Сравнение с текущим минимумом
+ yield return new SortStep
+ {
+ Type = SortStepType.Compare,
+ Indices = [minIdx, j],
+ ArrayState = (int[])arr.Clone()
+ };
+
+ if (arr[j] < arr[minIdx])
+ {
+ minIdx = j;
+ }
+ }
+
+ // Перестановка минимума на позицию i
+ if (minIdx != i)
+ {
+ (arr[i], arr[minIdx]) = (arr[minIdx], arr[i]);
+
+ yield return new SortStep
+ {
+ Type = SortStepType.Swap,
+ Indices = [i, minIdx],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+
+ // Элемент i встал на своё место
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [i],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+
+ // Последний элемент автоматически отсортирован
+ yield return new SortStep
+ {
+ Type = SortStepType.Sorted,
+ Indices = [n - 1],
+ ArrayState = (int[])arr.Clone()
+ };
+ }
+}
diff --git a/InternalSortMethods/Models/SortAlgorithm.cs b/InternalSortMethods/Models/SortAlgorithm.cs
new file mode 100644
index 0000000..8243cd2
--- /dev/null
+++ b/InternalSortMethods/Models/SortAlgorithm.cs
@@ -0,0 +1,27 @@
+using InternalSortMethods.Models;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Базовый класс для всех алгоритмов сортировки.
+/// Генерирует последовательность шагов (SortStep), которые ViewModel выполняет
+/// с задержкой для анимации. Это позволяет реализовать паузу, шаг вперёд и отмену
+/// без блокировки UI-потока.
+///
+public abstract class SortAlgorithm
+{
+ /// Отображаемое название алгоритма.
+ public abstract string DisplayName { get; }
+
+ ///
+ /// Генерирует шаги сортировки для заданного массива.
+ /// Каждый шаг содержит копию состояния массива после операции,
+ /// что позволяет визуализировать процесс пошагово.
+ ///
+ /// Массив для сортировки.
+ /// Токен отмены для прерывания генерации шагов.
+ /// Последовательность шагов сортировки.
+ public abstract IEnumerable GenerateSteps(int[] array, CancellationToken cancellationToken = default);
+}
diff --git a/InternalSortMethods/Models/SortStep.cs b/InternalSortMethods/Models/SortStep.cs
new file mode 100644
index 0000000..268959b
--- /dev/null
+++ b/InternalSortMethods/Models/SortStep.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace InternalSortMethods.Models;
+
+///
+/// Представляет один шаг выполнения алгоритма сортировки.
+/// Используется для пошаговой визуализации с анимацией.
+///
+public sealed class SortStep
+{
+ /// Тип операции шага.
+ public SortStepType Type { get; init; }
+
+ /// Индексы элементов, участвующих в операции (сравнение или перестановка).
+ public int[] Indices { get; init; } = [];
+
+ ///
+ /// Состояние массива после выполнения данного шага.
+ /// Копия создаётся для того, чтобы UI мог отобразить актуальное состояние.
+ ///
+ public int[] ArrayState { get; init; } = [];
+}
+
+///
+/// Типы операций шага сортировки для визуальной подсветки.
+///
+public enum SortStepType
+{
+ /// Сравнение двух элементов (подсвечиваем жёлтым).
+ Compare,
+
+ /// Перестановка двух элементов (подсвечиваем красным).
+ Swap,
+
+ /// Запись/вставка элемента на позицию (подсвечиваем голубым).
+ Write,
+
+ /// Элемент определён как опорный (pivot) — для QuickSort.
+ Pivot,
+
+ /// Элемент окончательно стоит на своём месте.
+ Sorted
+}
diff --git a/InternalSortMethods/ViewModels/MainViewModel.cs b/InternalSortMethods/ViewModels/MainViewModel.cs
index a93e42b..e0c4ea7 100644
--- a/InternalSortMethods/ViewModels/MainViewModel.cs
+++ b/InternalSortMethods/ViewModels/MainViewModel.cs
@@ -1,8 +1,383 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using InternalSortMethods.Models;
namespace InternalSortMethods.ViewModels;
+///
+/// ViewModel .
+///
+/// MVVM:
+/// - ViewModel UI- (Button, ListBox ..)
+/// - (Items, IsRunning ..) INotifyPropertyChanged
+/// - RelayCommand
+/// - Model-
+///
+/// Thread.Sleep:
+/// - await Task.Delay(ms, _cancellationToken) UI-
+/// - CancellationToken
+/// - SemaphoreSlim /
+///
public partial class MainViewModel : ViewModelBase
{
- [ObservableProperty] private string _greeting = "Welcome to Avalonia!";
-}
\ No newline at end of file
+ // ===== =====
+ private const int MinArraySize = 5;
+ private const int MaxArraySize = 100;
+ private const int DefaultValue = 20;
+
+ // ===== =====
+ private static readonly SortAlgorithm[] Algorithms =
+ [
+ new BubbleSort(),
+ new SelectionSort(),
+ new InsertionSort(),
+ new QuickSort(),
+ new MergeSort(),
+ new HeapSort()
+ ];
+
+ // ===== =====
+ private readonly SemaphoreSlim _pauseSemaphore = new(1, 1);
+ private CancellationTokenSource? _cancellationTokenSource;
+ private int[] _currentArray = [];
+ private int _comparisons;
+ private int _swaps;
+
+ //
+ private System.Collections.Generic.IEnumerator? _stepEnumerator;
+ private System.Collections.Generic.List? _cachedSteps;
+ private int _currentStepIndex;
+
+ // ===== ObservableProperty (CommunityToolkit INPC) =====
+
+ [ObservableProperty] private ObservableCollection _items = [];
+ [ObservableProperty] private int _arraySize = DefaultValue;
+ [ObservableProperty] private int _selectedAlgorithmIndex;
+ [ObservableProperty] private int _animationSpeed = 100;
+ [ObservableProperty] private bool _isRunning;
+ [ObservableProperty] private bool _isCompleted;
+ [ObservableProperty] private string _statusText = "";
+ [ObservableProperty] private int _currentStep;
+ [ObservableProperty] private int _totalSteps;
+ [ObservableProperty] private int _comparisonsCount;
+ [ObservableProperty] private int _swapsCount;
+ [ObservableProperty] private bool _isPaused;
+
+ // ===== =====
+
+ /// ComboBox.
+ public string[] AlgorithmNames => Algorithms.Select(a => a.DisplayName).ToArray();
+
+ /// .
+ public bool CanStart => !IsRunning && Items.Count > 0;
+
+ /// /.
+ public bool CanPause => IsRunning && !IsCompleted;
+
+ /// ( ).
+ public bool CanStepForward => IsRunning && IsPaused && !IsCompleted;
+
+ /// .
+ public bool CanReset => IsRunning || IsCompleted || Items.Count > 0;
+
+ // ===== =====
+
+ ///
+ /// .
+ ///
+ [RelayCommand]
+ private void GenerateArray()
+ {
+ if (IsRunning) return;
+
+ ArraySize = Math.Clamp(ArraySize, MinArraySize, MaxArraySize);
+ ResetState();
+
+ var random = new Random();
+ _currentArray = Enumerable.Range(0, ArraySize)
+ .Select(_ => random.Next(1, 100))
+ .ToArray();
+
+ UpdateItems(_currentArray);
+ StatusText = $" {ArraySize} ";
+ }
+
+ ///
+ /// .
+ ///
+ /// Thread.Sleep:
+ /// - Task.Delay UI- ( continuation)
+ /// - CancellationToken
+ ///
+ [RelayCommand]
+ private async Task StartSortAsync()
+ {
+ if (IsRunning || Items.Count == 0) return;
+
+ var algorithm = Algorithms[SelectedAlgorithmIndex];
+ ResetState();
+ IsRunning = true;
+
+ _cancellationTokenSource = new CancellationTokenSource();
+ var token = _cancellationTokenSource.Token;
+
+ try
+ {
+ //
+ _cachedSteps = algorithm.GenerateSteps(_currentArray, token).ToList();
+ TotalSteps = _cachedSteps.Count;
+ _stepEnumerator = _cachedSteps.GetEnumerator();
+ _currentStepIndex = 0;
+
+ StatusText = $": {algorithm.DisplayName}...";
+
+ //
+ foreach (var step in _cachedSteps)
+ {
+ token.ThrowIfCancellationRequested();
+
+ // ()
+ await _pauseSemaphore.WaitAsync(token);
+ _pauseSemaphore.Release();
+
+ //
+ if (IsPaused)
+ {
+ await WaitUntilResumed(token);
+ }
+
+ token.ThrowIfCancellationRequested();
+
+ ApplyStep(step);
+ _currentStepIndex++;
+ CurrentStep = _currentStepIndex;
+
+ // UI-!
+ // Task.Delay , Thread.Sleep
+ await Task.Delay(AnimationSpeed, token);
+ }
+
+ IsCompleted = true;
+ StatusText = $" ! : {ComparisonsCount}, : {SwapsCount}";
+ }
+ catch (OperationCanceledException)
+ {
+ StatusText = " ";
+ }
+ catch (Exception ex)
+ {
+ StatusText = $": {ex.Message}";
+ }
+ finally
+ {
+ IsRunning = false;
+ }
+ }
+
+ ///
+ /// .
+ ///
+ /// :
+ /// - SemaphoreSlim .
+ /// - permit (Wait), WaitAsync.
+ /// - permit (Release), .
+ /// - IsPaused WaitUntilResumed.
+ ///
+ [RelayCommand]
+ private void PauseResume()
+ {
+ if (!IsRunning || IsCompleted) return;
+
+ IsPaused = !IsPaused;
+
+ if (IsPaused)
+ {
+ // permit WaitAsync
+ _pauseSemaphore.Wait();
+ StatusText = "";
+ }
+ else
+ {
+ // permit WaitAsync
+ _pauseSemaphore.Release();
+ StatusText = "...";
+ }
+ }
+
+ ///
+ /// .
+ /// .
+ ///
+ [RelayCommand]
+ private async Task StepForwardAsync()
+ {
+ if (!IsRunning || !IsPaused || IsCompleted) return;
+
+ if (_currentStepIndex < TotalSteps)
+ {
+ //
+ IsPaused = false;
+ _pauseSemaphore.Release();
+
+ //
+ var step = _cachedSteps![_currentStepIndex];
+ ApplyStep(step);
+ _currentStepIndex++;
+ CurrentStep = _currentStepIndex;
+
+ await Task.Delay(50); // UI
+
+ //
+ IsPaused = true;
+ _pauseSemaphore.Wait();
+ }
+ }
+
+ ///
+ /// .
+ ///
+ [RelayCommand]
+ private void Reset()
+ {
+ CancelRunningOperation();
+ ResetState();
+ UpdateItems(_currentArray);
+ StatusText = "";
+ }
+
+ // ===== =====
+
+ ///
+ /// .
+ /// 50, .
+ /// UI- await Task.Delay.
+ ///
+ private async Task WaitUntilResumed(CancellationToken token)
+ {
+ while (IsPaused)
+ {
+ token.ThrowIfCancellationRequested();
+ await Task.Delay(50, token);
+ }
+ }
+
+ ///
+ /// Items .
+ ///
+ private void ApplyStep(SortStep step)
+ {
+ //
+ foreach (var item in Items)
+ {
+ item.State = SortItemState.Default;
+ }
+
+ //
+ foreach (var idx in step.Indices)
+ {
+ if (idx >= 0 && idx < Items.Count)
+ {
+ Items[idx].State = step.Type switch
+ {
+ SortStepType.Compare => SortItemState.Comparing,
+ SortStepType.Swap => SortItemState.Swapping,
+ SortStepType.Write => SortItemState.Writing,
+ SortStepType.Pivot => SortItemState.Pivot,
+ SortStepType.Sorted => SortItemState.Sorted,
+ _ => SortItemState.Default
+ };
+ }
+ }
+
+ // ( )
+ for (int i = 0; i < Math.Min(step.ArrayState.Length, Items.Count); i++)
+ {
+ Items[i].Value = step.ArrayState[i];
+ }
+
+ //
+ if (step.Type == SortStepType.Compare)
+ {
+ ComparisonsCount++;
+ _comparisons++;
+ }
+
+ if (step.Type == SortStepType.Swap)
+ {
+ SwapsCount++;
+ _swaps++;
+ }
+
+ //
+ _currentArray = (int[])step.ArrayState.Clone();
+ }
+
+ private void UpdateItems(int[] array)
+ {
+ Items.Clear();
+
+ foreach (var value in array)
+ {
+ Items.Add(new SortItemViewModel(value));
+ }
+ }
+
+ private void ResetState()
+ {
+ IsRunning = false;
+ IsCompleted = false;
+ IsPaused = false;
+ CurrentStep = 0;
+ TotalSteps = 0;
+ ComparisonsCount = 0;
+ SwapsCount = 0;
+ _comparisons = 0;
+ _swaps = 0;
+ _stepEnumerator = null;
+ _cachedSteps = null;
+ _currentStepIndex = 0;
+
+ //
+ foreach (var item in Items)
+ {
+ item.State = SortItemState.Default;
+ }
+
+ //
+ if (_pauseSemaphore.CurrentCount == 0)
+ {
+ _pauseSemaphore.Release();
+ }
+ }
+
+ private void CancelRunningOperation()
+ {
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource?.Dispose();
+ _cancellationTokenSource = null;
+
+ // ,
+ if (IsPaused)
+ {
+ IsPaused = false;
+ if (_pauseSemaphore.CurrentCount == 0)
+ {
+ _pauseSemaphore.Release();
+ }
+ }
+ }
+
+ // ===== ObservableProperty =====
+ partial void OnArraySizeChanged(int value)
+ {
+ if (!IsRunning)
+ {
+ GenerateArray();
+ }
+ }
+}
diff --git a/InternalSortMethods/ViewModels/SortItemViewModel.cs b/InternalSortMethods/ViewModels/SortItemViewModel.cs
new file mode 100644
index 0000000..a878b95
--- /dev/null
+++ b/InternalSortMethods/ViewModels/SortItemViewModel.cs
@@ -0,0 +1,62 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace InternalSortMethods.ViewModels;
+
+///
+/// ViewModel для одного элемента массива в визуализации.
+/// Содержит значение и текущее состояние подсветки (цвет).
+///
+public partial class SortItemViewModel : ObservableObject
+{
+ private int _value;
+ private SortItemState _state = SortItemState.Default;
+
+ public SortItemViewModel(int value)
+ {
+ _value = value;
+ }
+
+ /// Значение элемента (высота столбца).
+ public int Value
+ {
+ get => _value;
+ set => SetProperty(ref _value, value);
+ }
+
+ ///
+ /// Текущее состояние элемента для визуальной подсветки.
+ /// Изменяется при каждом шаге сортировки.
+ ///
+ public SortItemState State
+ {
+ get => _state;
+ set => SetProperty(ref _state, value);
+ }
+
+ /// Относительная высота для привязки к UI (0.0 – 1.0).
+ public double NormalizedHeight { get; set; }
+}
+
+///
+/// Состояния элемента для цветовой подсветки.
+///
+public enum SortItemState
+{
+ /// Обычное состояние (нейтральный цвет).
+ Default,
+
+ /// Элемент сравнивается с другим (жёлтый/оранжевый).
+ Comparing,
+
+ /// Элемент участвует в перестановке (красный).
+ Swapping,
+
+ /// Элемент записан/вставлен (синий/голубой).
+ Writing,
+
+ /// Опорный элемент — pivot для QuickSort (фиолетовый).
+ Pivot,
+
+ /// Элемент окончательно отсортирован (зелёный).
+ Sorted
+}
diff --git a/InternalSortMethods/Views/MainView.axaml b/InternalSortMethods/Views/MainView.axaml
index 9358c78..b548a5a 100644
--- a/InternalSortMethods/Views/MainView.axaml
+++ b/InternalSortMethods/Views/MainView.axaml
@@ -3,14 +3,238 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:InternalSortMethods.ViewModels"
- mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ xmlns:converters="using:InternalSortMethods.Converters"
x:Class="InternalSortMethods.Views.MainView"
- x:DataType="vm:MainViewModel">
+ x:DataType="vm:MainViewModel"
+ mc:Ignorable="d"
+ d:DesignWidth="800"
+ d:DesignHeight="700">
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+