Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86a9d2bad9 | |||
| dab7ef852b | |||
| 346c17f47f | |||
| 0c623d4bc5 | |||
| 760fb43e62 | |||
| 81c60f2c70 | |||
| dd1fa3f7fc | |||
| 0b23d10f8e | |||
| 54fa553221 |
137
UPGRADE_GUIDE.md
Normal file
137
UPGRADE_GUIDE.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# Руководство по обновлению калькуляторов
|
||||||
|
|
||||||
|
## Статус обновлений
|
||||||
|
|
||||||
|
### ✅ Завершено:
|
||||||
|
1. **step_by_step_solver.py** - добавлены операции `MULTIPLICATION` и `DIVISION`
|
||||||
|
2. **calculater2.py** (Десятичный) - добавлены 4 колонки (+, -, ×, ÷)
|
||||||
|
3. **binary_calculeator4.py** (Двоичный) - добавлены 4 колонки (+, -, ×, ÷)
|
||||||
|
4. **octogon_calculeator6.py** (Восьмеричный) - добавлены 4 колонки (+, -, ×, ÷)
|
||||||
|
|
||||||
|
### ⏳ Требуется завершить:
|
||||||
|
5. **sedecim_calculeator8.py** (Шестнадцатеричный)
|
||||||
|
6. **pentecostal_calculeator10.py** (50-ричный)
|
||||||
|
|
||||||
|
## Инструкция для завершения оставшихся калькуляторов
|
||||||
|
|
||||||
|
### Для sedecim_calculeator8.py и pentecostal_calculeator10.py:
|
||||||
|
|
||||||
|
#### 1. Обновить __init__ метод класса:
|
||||||
|
Добавить:
|
||||||
|
```python
|
||||||
|
# Результаты для умножения и деления
|
||||||
|
self.result_mult = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_div = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.decimal_mult = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_div = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Добавить методы calculate_multiplication и calculate_division:
|
||||||
|
```python
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
# Логика умножения - преобразовать в десятичную, умножить, вернуть обратно
|
||||||
|
# Вызвать self.show_steps_mult(val1, val2, Operation.MULTIPLICATION)
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
# Логика деления - преобразовать в десятичную, разделить, вернуть обратно
|
||||||
|
# Проверка деления на ноль
|
||||||
|
# Вызвать self.show_steps_div(val1, val2, Operation.DIVISION)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Добавить методы show_steps_mult и show_steps_div:
|
||||||
|
```python
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
# Аналогично show_steps_left/right
|
||||||
|
# Обновить self.steps_mult
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
# Аналогично show_steps_left/right
|
||||||
|
# Обновить self.steps_div
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Обновить test_data метод:
|
||||||
|
Добавить поля ввода для умножения и деления:
|
||||||
|
```python
|
||||||
|
self.input_mult_1 = ft.TextField(...)
|
||||||
|
self.input_mult_2 = ft.TextField(...)
|
||||||
|
self.input_div_1 = ft.TextField(...)
|
||||||
|
self.input_div_2 = ft.TextField(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
Добавить две новые колонки в ft.Row (controls):
|
||||||
|
```python
|
||||||
|
ft.Column( # Умножение
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("× Умножение", ...),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(content=...), # result_mult
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(content=self.steps_mult, ...)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column( # Деление
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("÷ Деление", ...),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(content=...), # result_div
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(content=self.steps_div, ...)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Примечания:
|
||||||
|
- Для **sedecim_calculeator8.py**: Используйте существующие методы `hex_to_decimal` и `decimal_to_hex`
|
||||||
|
- Для **pentecostal_calculeator10.py**: Используйте существующие методы `from_base50` и `to_base50`
|
||||||
|
- Во всех случаях импортируйте `Operation` из step_by_step_solver
|
||||||
|
- Проверьте деление на ноль в методе calculate_division
|
||||||
|
|
||||||
|
## Шаблон для calculate_multiplication (шестнадцатеричный):
|
||||||
|
```python
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
num1 = self.hex_to_decimal(self.input_mult_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_mult_2.value.upper())
|
||||||
|
result_decimal = num1 * num2
|
||||||
|
result_hex = self.decimal_to_hex(result_decimal)
|
||||||
|
|
||||||
|
self.result_mult.value = f"HEX: {result_hex}"
|
||||||
|
self.decimal_mult.value = f"DEC: {result_decimal}"
|
||||||
|
|
||||||
|
if '.' not in self.input_mult_1.value and '.' not in self.input_mult_2.value:
|
||||||
|
hex_val1 = self.input_mult_1.value.upper().lstrip('-+') if self.input_mult_1.value else "0"
|
||||||
|
hex_val2 = self.input_mult_2.value.upper().lstrip('-+') if self.input_mult_2.value else "0"
|
||||||
|
self.show_steps_mult(hex_val1, hex_val2, Operation.MULTIPLICATION)
|
||||||
|
else:
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_mult.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_mult.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_mult.value = "DEC: ошибка"
|
||||||
|
self.page.update()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Следующие шаги:
|
||||||
|
1. Откройте sedecim_calculeator8.py
|
||||||
|
2. Примените изменения согласно шаблону выше
|
||||||
|
3. Повторите для pentecostal_calculeator10.py
|
||||||
|
4. Протестируйте все калькуляторы
|
||||||
|
|
||||||
|
Все изменения следуют единому паттерну, использованному в уже обновленных калькуляторах.
|
||||||
342
app/src/ALGORITHM_DOCUMENTATION.md
Normal file
342
app/src/ALGORITHM_DOCUMENTATION.md
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
# Документация алгоритма пошагового решения задач с системами счисления
|
||||||
|
|
||||||
|
## Описание
|
||||||
|
|
||||||
|
Алгоритм для пошагового решения задач с различными системами счисления (2, 8, 16, n-ричные), аналогичный работе Photomath. Показывает каждый шаг решения с подробными объяснениями.
|
||||||
|
|
||||||
|
## Основные возможности
|
||||||
|
|
||||||
|
### 1. Поддерживаемые системы счисления
|
||||||
|
- **Двоичная (base=2)**: 0, 1
|
||||||
|
- **Восьмеричная (base=8)**: 0-7
|
||||||
|
- **Десятичная (base=10)**: 0-9
|
||||||
|
- **Шестнадцатеричная (base=16)**: 0-9, A-F
|
||||||
|
- **Любая n-ричная (base=n)**: до base=36
|
||||||
|
|
||||||
|
### 2. Поддерживаемые операции
|
||||||
|
- Сложение (+)
|
||||||
|
- Вычитание (-)
|
||||||
|
- Преобразование между системами счисления
|
||||||
|
|
||||||
|
## Структура алгоритма
|
||||||
|
|
||||||
|
### Класс `Step`
|
||||||
|
Представляет один шаг решения:
|
||||||
|
- `title` - название шага
|
||||||
|
- `description` - описание что делается
|
||||||
|
- `visual` - визуальное представление (формулы, вычисления)
|
||||||
|
- `result` - результат этого шага
|
||||||
|
|
||||||
|
### Класс `StepByStepSolver`
|
||||||
|
Основной класс для решения задач:
|
||||||
|
|
||||||
|
#### Методы:
|
||||||
|
- `solve(num1, num2, operation)` - решить задачу
|
||||||
|
- `add_with_steps(num1, num2)` - сложение с шагами
|
||||||
|
- `subtract_with_steps(num1, num2)` - вычитание с шагами
|
||||||
|
- `convert_with_steps(number, target_base)` - конвертация с шагами
|
||||||
|
- `print_solution(steps)` - вывод решения
|
||||||
|
|
||||||
|
## Алгоритм сложения (подробно)
|
||||||
|
|
||||||
|
### Шаг 1: Анализ условия
|
||||||
|
```
|
||||||
|
Входные данные:
|
||||||
|
- Число 1 в n-ричной системе
|
||||||
|
- Число 2 в n-ричной системе
|
||||||
|
- Основание системы счисления (n)
|
||||||
|
|
||||||
|
Пример: 1011₍₂₎ + 110₍₂₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: Выравнивание разрядов
|
||||||
|
```
|
||||||
|
Алгоритм:
|
||||||
|
1. Определить максимальную длину чисел
|
||||||
|
2. Дополнить короткое число нулями слева
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
1011 (уже 4 разряда)
|
||||||
|
+ 0110 (дополнено до 4 разрядов)
|
||||||
|
------
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Сложение в столбик (справа налево)
|
||||||
|
```
|
||||||
|
Для каждого разряда i (от младшего к старшему):
|
||||||
|
1. Взять цифру d1 из первого числа
|
||||||
|
2. Взять цифру d2 из второго числа
|
||||||
|
3. Добавить перенос carry из предыдущего разряда
|
||||||
|
4. Вычислить: total = d1 + d2 + carry
|
||||||
|
5. Результат в разряде: digit = total mod base
|
||||||
|
6. Новый перенос: carry = total div base
|
||||||
|
7. Записать digit в результат
|
||||||
|
|
||||||
|
Пример (двоичная система, base=2):
|
||||||
|
|
||||||
|
Позиция 0 (самая правая):
|
||||||
|
1 + 0 + 0(перенос) = 1
|
||||||
|
1 mod 2 = 1 (цифра результата)
|
||||||
|
1 div 2 = 0 (новый перенос)
|
||||||
|
|
||||||
|
Позиция 1:
|
||||||
|
1 + 1 + 0(перенос) = 2
|
||||||
|
2 mod 2 = 0 (цифра результата)
|
||||||
|
2 div 2 = 1 (новый перенос)
|
||||||
|
|
||||||
|
Позиция 2:
|
||||||
|
0 + 1 + 1(перенос) = 2
|
||||||
|
2 mod 2 = 0 (цифра результата)
|
||||||
|
2 div 2 = 1 (новый перенос)
|
||||||
|
|
||||||
|
Позиция 3:
|
||||||
|
1 + 0 + 1(перенос) = 2
|
||||||
|
2 mod 2 = 0 (цифра результата)
|
||||||
|
2 div 2 = 1 (новый перенос)
|
||||||
|
|
||||||
|
Финальный перенос: 1
|
||||||
|
|
||||||
|
Результат: 10001₍₂₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 4: Проверка через десятичную систему
|
||||||
|
```
|
||||||
|
Алгоритм проверки:
|
||||||
|
1. Перевести оба числа в десятичную систему
|
||||||
|
2. Выполнить операцию в десятичной системе
|
||||||
|
3. Перевести результат обратно в исходную систему
|
||||||
|
4. Сравнить с полученным результатом
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
1011₍₂₎ = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 8+0+2+1 = 11₍₁₀₎
|
||||||
|
0110₍₂₎ = 0×2³ + 1×2² + 1×2¹ + 0×2⁰ = 0+4+2+0 = 6₍₁₀₎
|
||||||
|
11 + 6 = 17₍₁₀₎
|
||||||
|
17₍₁₀₎ = 10001₍₂₎ ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Алгоритм вычитания (подробно)
|
||||||
|
|
||||||
|
### Шаг 1: Анализ условия
|
||||||
|
```
|
||||||
|
Входные данные:
|
||||||
|
- Уменьшаемое в n-ричной системе
|
||||||
|
- Вычитаемое в n-ричной системе
|
||||||
|
- Основание системы счисления (n)
|
||||||
|
|
||||||
|
Пример: 1101₍₂₎ - 1011₍₂₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: Выравнивание разрядов
|
||||||
|
```
|
||||||
|
Аналогично сложению - дополнение нулями слева
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Вычитание в столбик (справа налево)
|
||||||
|
```
|
||||||
|
Для каждого разряда i (от младшего к старшему):
|
||||||
|
1. Взять цифру d1 из уменьшаемого
|
||||||
|
2. Взять цифру d2 из вычитаемого
|
||||||
|
3. Учесть заем borrow из текущего разряда
|
||||||
|
4. Если (d1 - borrow) < d2:
|
||||||
|
- Занять base из следующего разряда
|
||||||
|
- d1 = d1 + base
|
||||||
|
- borrow = 1 для следующей итерации
|
||||||
|
5. Иначе:
|
||||||
|
- borrow = 0
|
||||||
|
6. Результат: digit = d1 - borrow - d2
|
||||||
|
|
||||||
|
Пример (двоичная система, base=2):
|
||||||
|
|
||||||
|
1101
|
||||||
|
- 1011
|
||||||
|
------
|
||||||
|
|
||||||
|
Позиция 0:
|
||||||
|
1 - 1 = 0 (заем не нужен)
|
||||||
|
|
||||||
|
Позиция 1:
|
||||||
|
0 - 1 → нужен заем
|
||||||
|
0 + 2 - 1 = 1 (занимаем 2)
|
||||||
|
Новый заем = 1
|
||||||
|
|
||||||
|
Позиция 2:
|
||||||
|
1 - 1(заем) = 0
|
||||||
|
0 - 0 = 0
|
||||||
|
|
||||||
|
Позиция 3:
|
||||||
|
1 - 1 = 0
|
||||||
|
|
||||||
|
Результат: 0010₍₂₎ = 10₍₂₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 4: Проверка
|
||||||
|
```
|
||||||
|
Аналогично сложению - проверка через десятичную систему
|
||||||
|
```
|
||||||
|
|
||||||
|
## Алгоритм конвертации между системами
|
||||||
|
|
||||||
|
### Из n-ричной в десятичную
|
||||||
|
```
|
||||||
|
Алгоритм:
|
||||||
|
1. Для каждой цифры digit на позиции i (справа налево, начиная с 0):
|
||||||
|
result += digit_value × base^i
|
||||||
|
|
||||||
|
Пример: 2A₍₁₆₎ → 10-ичная
|
||||||
|
A×16⁰ = 10×1 = 10
|
||||||
|
2×16¹ = 2×16 = 32
|
||||||
|
Сумма: 10 + 32 = 42₍₁₀₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Из десятичной в n-ричную
|
||||||
|
```
|
||||||
|
Алгоритм:
|
||||||
|
1. Делим число на base, записываем остаток
|
||||||
|
2. Результат деления становится новым числом
|
||||||
|
3. Повторяем пока число > 0
|
||||||
|
4. Читаем остатки снизу вверх
|
||||||
|
|
||||||
|
Пример: 42₍₁₀₎ → 16-ричная
|
||||||
|
42 ÷ 16 = 2, остаток: 10 (A)
|
||||||
|
2 ÷ 16 = 0, остаток: 2
|
||||||
|
|
||||||
|
Читаем снизу вверх: 2A₍₁₆₎
|
||||||
|
```
|
||||||
|
|
||||||
|
## Использование в коде
|
||||||
|
|
||||||
|
### Пример 1: Сложение в двоичной системе
|
||||||
|
```python
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
# Создаем решатель для двоичной системы
|
||||||
|
solver = StepByStepSolver(base=2)
|
||||||
|
|
||||||
|
# Решаем задачу
|
||||||
|
steps = solver.solve("1011", "110", Operation.ADDITION)
|
||||||
|
|
||||||
|
# Выводим решение
|
||||||
|
solver.print_solution(steps)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 2: Вычитание в восьмеричной системе
|
||||||
|
```python
|
||||||
|
solver = StepByStepSolver(base=8)
|
||||||
|
steps = solver.solve("756", "237", Operation.SUBTRACTION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 3: Получение шагов для UI
|
||||||
|
```python
|
||||||
|
solver = StepByStepSolver(base=16)
|
||||||
|
steps = solver.solve("A5", "3F", Operation.ADDITION)
|
||||||
|
|
||||||
|
# Обработка шагов для отображения в интерфейсе
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
print(f"Шаг {i}: {step.title}")
|
||||||
|
print(f"Описание: {step.description}")
|
||||||
|
print(f"Визуализация:\n{step.visual}")
|
||||||
|
print(f"Результат: {step.result}\n")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Интеграция с Flet UI
|
||||||
|
|
||||||
|
### Пример компонента для отображения решения
|
||||||
|
```python
|
||||||
|
import flet as ft
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
class StepByStepView(ft.Column):
|
||||||
|
def __init__(self, base: int):
|
||||||
|
super().__init__()
|
||||||
|
self.solver = StepByStepSolver(base=base)
|
||||||
|
self.steps_display = ft.Column(scroll=ft.ScrollMode.AUTO)
|
||||||
|
|
||||||
|
def show_solution(self, num1: str, num2: str, operation: Operation):
|
||||||
|
# Получить шаги решения
|
||||||
|
steps = self.solver.solve(num1, num2, operation)
|
||||||
|
|
||||||
|
# Очистить предыдущее решение
|
||||||
|
self.steps_display.controls.clear()
|
||||||
|
|
||||||
|
# Добавить каждый шаг в UI
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_container = ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
ft.Text(f"Шаг {i}: {step.title}",
|
||||||
|
size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(step.description, size=14),
|
||||||
|
ft.Text(step.visual,
|
||||||
|
font_family="Courier New", size=12) if step.visual else None,
|
||||||
|
ft.Text(f"➤ {step.result}",
|
||||||
|
size=14, color="green") if step.result else None,
|
||||||
|
]),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=15,
|
||||||
|
border_radius=10,
|
||||||
|
margin=ft.margin.only(bottom=10)
|
||||||
|
)
|
||||||
|
self.steps_display.controls.append(step_container)
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Расширение алгоритма
|
||||||
|
|
||||||
|
### Добавление новых операций
|
||||||
|
```python
|
||||||
|
class Operation(Enum):
|
||||||
|
ADDITION = "сложение"
|
||||||
|
SUBTRACTION = "вычитание"
|
||||||
|
MULTIPLICATION = "умножение" # Новая операция
|
||||||
|
DIVISION = "деление" # Новая операция
|
||||||
|
```
|
||||||
|
|
||||||
|
### Поддержка дробных чисел
|
||||||
|
```python
|
||||||
|
def add_fraction_support(self):
|
||||||
|
# Обработка чисел с дробной частью
|
||||||
|
# Разделение на целую и дробную части
|
||||||
|
# Отдельная обработка каждой части
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Оптимизация и производительность
|
||||||
|
|
||||||
|
### Кэширование результатов
|
||||||
|
```python
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
@lru_cache(maxsize=128)
|
||||||
|
def to_decimal_cached(self, number: str) -> int:
|
||||||
|
return self.to_decimal(number)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ограничение количества шагов
|
||||||
|
```python
|
||||||
|
def solve_optimized(self, num1: str, num2: str, operation: Operation,
|
||||||
|
max_steps: int = 10) -> List[Step]:
|
||||||
|
steps = self.solve(num1, num2, operation)
|
||||||
|
return steps[:max_steps] # Ограничение
|
||||||
|
```
|
||||||
|
|
||||||
|
## Тестирование
|
||||||
|
|
||||||
|
### Примеры тестовых случаев
|
||||||
|
```python
|
||||||
|
def test_binary_addition():
|
||||||
|
solver = StepByStepSolver(base=2)
|
||||||
|
steps = solver.solve("1011", "110", Operation.ADDITION)
|
||||||
|
assert len(steps) == 4
|
||||||
|
assert steps[-1].result == "10001₍₂₎"
|
||||||
|
|
||||||
|
def test_octal_subtraction():
|
||||||
|
solver = StepByStepSolver(base=8)
|
||||||
|
steps = solver.solve("756", "237", Operation.SUBTRACTION)
|
||||||
|
assert len(steps) == 4
|
||||||
|
assert steps[-1].result == "517₍₈₎"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Заключение
|
||||||
|
|
||||||
|
Этот алгоритм предоставляет полное пошаговое решение задач с системами счисления, аналогично Photomath. Он может быть легко интегрирован в UI приложения и расширен дополнительными функциями.
|
||||||
233
app/src/README_SOLVER.md
Normal file
233
app/src/README_SOLVER.md
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
# Калькулятор систем счисления с пошаговым решением
|
||||||
|
|
||||||
|
## Описание
|
||||||
|
|
||||||
|
Интерактивный калькулятор для работы с различными системами счисления (2, 8, 10, 16-ричные) с **пошаговым объяснением решения** аналогично Photomath.
|
||||||
|
|
||||||
|
## Возможности ✨
|
||||||
|
|
||||||
|
- ✅ Сложение и вычитание в двоичной системе (с дополнительным кодом)
|
||||||
|
- ✅ Сложение и вычитание в восьмеричной системе
|
||||||
|
- ✅ Сложение и вычитание в десятичной системе
|
||||||
|
- ✅ Сложение и вычитание в шестнадцатеричной системе
|
||||||
|
- 📝 **Пошаговое объяснение каждого решения**
|
||||||
|
- ✓ Автоматическая проверка результатов
|
||||||
|
- 🎨 Красивый UI с цветовой подсветкой
|
||||||
|
|
||||||
|
## Быстрый старт
|
||||||
|
|
||||||
|
### Запуск примера алгоритма
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd app/src
|
||||||
|
python3 step_by_step_solver.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Вы увидите примеры пошагового решения для разных систем счисления.
|
||||||
|
|
||||||
|
### Использование в коде
|
||||||
|
|
||||||
|
```python
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
# Создать решатель для двоичной системы
|
||||||
|
solver = StepByStepSolver(base=2)
|
||||||
|
|
||||||
|
# Решить задачу
|
||||||
|
steps = solver.solve("1011", "110", Operation.ADDITION)
|
||||||
|
|
||||||
|
# Вывести решение
|
||||||
|
solver.print_solution(steps)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример вывода
|
||||||
|
|
||||||
|
```
|
||||||
|
═══════════════════════════════════════════════════════════
|
||||||
|
ПОШАГОВОЕ РЕШЕНИЕ
|
||||||
|
═══════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
ШАГ 1: Условие задачи
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
|
||||||
|
Необходимо сложить два числа в двоичная (2-ричной) системе счисления
|
||||||
|
|
||||||
|
1011₍2₎ + 110₍2₎ = ?
|
||||||
|
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
ШАГ 2: Выравнивание чисел
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
|
||||||
|
Дополняем числа нулями слева до одинаковой длины (4 разрядов)
|
||||||
|
|
||||||
|
1011
|
||||||
|
+ 0110
|
||||||
|
------
|
||||||
|
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
ШАГ 3: Сложение в столбик
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
|
||||||
|
Складываем числа справа налево, учитывая переносы...
|
||||||
|
|
||||||
|
➤ Результат: 10001₍2₎
|
||||||
|
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
ШАГ 4: Проверка результата
|
||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
|
||||||
|
Проверим правильность через перевод в десятичную систему
|
||||||
|
|
||||||
|
1011₍2₎ = 11₍₁₀₎
|
||||||
|
110₍2₎ = 6₍₁₀₎
|
||||||
|
11 + 6 = 17
|
||||||
|
17₍₁₀₎ = 10001₍2₎
|
||||||
|
|
||||||
|
➤ Результат: ✓ Ответ верен: 10001₍2₎
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════════════════
|
||||||
|
РЕШЕНИЕ ЗАВЕРШЕНО
|
||||||
|
═══════════════════════════════════════════════════════════
|
||||||
|
```
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
app/src/
|
||||||
|
├── step_by_step_solver.py # Основной алгоритм пошагового решения
|
||||||
|
├── calculator_mixin.py # Миксин для интеграции в UI
|
||||||
|
├── ALGORITHM_DOCUMENTATION.md # Подробная документация алгоритма
|
||||||
|
├── USER_GUIDE.md # Руководство пользователя
|
||||||
|
└── pages/tabs/
|
||||||
|
├── binary_calculeator4.py # Двоичный калькулятор с пошаговым решением
|
||||||
|
├── calculater2.py # Десятичный калькулятор с пошаговым решением
|
||||||
|
├── octogon_calculeator6.py # Восьмеричный калькулятор с пошаговым решением
|
||||||
|
└── sedecim_calculeator8.py # Шестнадцатеричный калькулятор с пошаговым решением
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ключевые особенности алгоритма
|
||||||
|
|
||||||
|
### 1. Универсальность
|
||||||
|
Работает с любой системой счисления от 2 до 36:
|
||||||
|
|
||||||
|
```python
|
||||||
|
binary_solver = StepByStepSolver(base=2) # Двоичная
|
||||||
|
octal_solver = StepByStepSolver(base=8) # Восьмеричная
|
||||||
|
decimal_solver = StepByStepSolver(base=10) # Десятичная
|
||||||
|
hex_solver = StepByStepSolver(base=16) # Шестнадцатеричная
|
||||||
|
quinary_solver = StepByStepSolver(base=5) # Пятеричная
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Подробное объяснение
|
||||||
|
Каждый шаг включает:
|
||||||
|
- 📋 Название и описание
|
||||||
|
- 📐 Визуализация вычислений
|
||||||
|
- ✅ Промежуточные результаты
|
||||||
|
- 🔍 Проверка правильности
|
||||||
|
|
||||||
|
### 3. Поддерживаемые операции
|
||||||
|
- ➕ Сложение (с переносами)
|
||||||
|
- ➖ Вычитание (с заимствованиями)
|
||||||
|
- 🔄 Конвертация между системами (в разработке)
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
### Двоичное сложение
|
||||||
|
```python
|
||||||
|
solver = StepByStepSolver(base=2)
|
||||||
|
steps = solver.add_with_steps("1011", "110")
|
||||||
|
# Результат: 10001₍₂₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Восьмеричное вычитание
|
||||||
|
```python
|
||||||
|
solver = StepByStepSolver(base=8)
|
||||||
|
steps = solver.subtract_with_steps("756", "237")
|
||||||
|
# Результат: 517₍₈₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шестнадцатеричное сложение
|
||||||
|
```python
|
||||||
|
solver = StepByStepSolver(base=16)
|
||||||
|
steps = solver.add_with_steps("A5", "3F")
|
||||||
|
# Результат: E4₍₁₆₎
|
||||||
|
```
|
||||||
|
|
||||||
|
## Интеграция в UI (Flet)
|
||||||
|
|
||||||
|
Калькуляторы уже интегрированы с пошаговым решением. При нажатии кнопки "=" автоматически:
|
||||||
|
1. Вычисляется результат
|
||||||
|
2. Генерируется пошаговое решение
|
||||||
|
3. Отображается в специальной области под калькулятором
|
||||||
|
|
||||||
|
### Визуальное оформление
|
||||||
|
- 🟡 Золотой цвет - заголовки
|
||||||
|
- 🔵 Циан - названия шагов
|
||||||
|
- 🟢 Зеленый - результаты
|
||||||
|
- ⚪ Серый - описания
|
||||||
|
|
||||||
|
## API алгоритма
|
||||||
|
|
||||||
|
### Класс `StepByStepSolver`
|
||||||
|
|
||||||
|
#### Методы:
|
||||||
|
- `solve(num1, num2, operation)` - Решить задачу с пошаговым объяснением
|
||||||
|
- `add_with_steps(num1, num2)` - Сложение с шагами
|
||||||
|
- `subtract_with_steps(num1, num2)` - Вычитание с шагами
|
||||||
|
- `convert_with_steps(number, target_base)` - Конвертация с шагами
|
||||||
|
- `print_solution(steps)` - Вывести решение в консоль
|
||||||
|
|
||||||
|
#### Класс `Step`
|
||||||
|
Представляет один шаг решения:
|
||||||
|
- `title` - Название шага
|
||||||
|
- `description` - Описание что делается
|
||||||
|
- `visual` - Визуальное представление
|
||||||
|
- `result` - Результат шага
|
||||||
|
|
||||||
|
## Тестирование
|
||||||
|
|
||||||
|
Запустите встроенные примеры:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 step_by_step_solver.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Вы увидите 4 примера:
|
||||||
|
1. Сложение в двоичной системе
|
||||||
|
2. Вычитание в восьмеричной системе
|
||||||
|
3. Сложение в шестнадцатеричной системе
|
||||||
|
4. Сложение в пятеричной системе
|
||||||
|
|
||||||
|
## Документация
|
||||||
|
|
||||||
|
- 📘 [ALGORITHM_DOCUMENTATION.md](ALGORITHM_DOCUMENTATION.md) - Полное описание алгоритма
|
||||||
|
- 📗 [USER_GUIDE.md](USER_GUIDE.md) - Руководство пользователя
|
||||||
|
|
||||||
|
## Технологии
|
||||||
|
|
||||||
|
- Python 3.x
|
||||||
|
- Flet (для UI)
|
||||||
|
- Enum (для типов операций)
|
||||||
|
- Type hints (для типобезопасности)
|
||||||
|
|
||||||
|
## Будущие улучшения
|
||||||
|
|
||||||
|
- [ ] Поддержка умножения и деления
|
||||||
|
- [ ] Работа с дробными числами
|
||||||
|
- [ ] Конвертация между любыми системами счисления
|
||||||
|
- [ ] Экспорт решения в PDF
|
||||||
|
- [ ] История вычислений
|
||||||
|
- [ ] Темная/светлая тема
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
Этот проект разработан для образовательных целей.
|
||||||
|
|
||||||
|
## Авторы
|
||||||
|
|
||||||
|
Разработано как часть проекта калькулятора систем счисления.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Примечание**: Алгоритм полностью автономен и может использоваться отдельно от UI калькулятора.
|
||||||
82
app/src/REFACTORING_COMPLETE.md
Normal file
82
app/src/REFACTORING_COMPLETE.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Рефакторинг завершен
|
||||||
|
|
||||||
|
## Выполненные изменения
|
||||||
|
|
||||||
|
### 1. Создан класс StepByStepDisplay в designer.py
|
||||||
|
|
||||||
|
Класс содержит статические методы для унифицированного отображения пошагового решения:
|
||||||
|
- `show_steps()` - отображение полного решения с шагами
|
||||||
|
- `show_message()` - отображение информационных сообщений
|
||||||
|
- `create_steps_container()` - создание контейнера для шагов
|
||||||
|
|
||||||
|
### 2. Обновлены все калькуляторы
|
||||||
|
|
||||||
|
#### 2.1 Десятичный калькулятор (calculater2.py)
|
||||||
|
✅ Добавлены колонки для умножения и деления
|
||||||
|
✅ Рефакторинг методов show_steps для использования StepByStepDisplay
|
||||||
|
✅ Код сокращен с ~240 строк до ~50 строк
|
||||||
|
|
||||||
|
#### 2.2 Двоичный калькулятор (binary_calculeator4.py)
|
||||||
|
✅ Добавлены колонки для умножения и деления
|
||||||
|
✅ Рефакторинг методов show_steps для использования StepByStepDisplay
|
||||||
|
✅ Код сокращен с ~240 строк до ~50 строк
|
||||||
|
|
||||||
|
#### 2.3 Восьмеричный калькулятор (octogon_calculeator6.py)
|
||||||
|
✅ Добавлены колонки для умножения и деления
|
||||||
|
✅ Рефакторинг методов show_steps для использования StepByStepDisplay
|
||||||
|
✅ Код сокращен с ~240 строк до ~50 строк
|
||||||
|
|
||||||
|
#### 2.4 Шестнадцатеричный калькулятор (sedecim_calculeator8.py)
|
||||||
|
✅ Добавлены колонки для умножения и деления
|
||||||
|
✅ Рефакторинг методов show_steps для использования StepByStepDisplay
|
||||||
|
✅ Добавлена проверка на дробные числа (пошаговое решение только для целых)
|
||||||
|
✅ Код сокращен с ~240 строк до ~60 строк (с учетом проверки дробей)
|
||||||
|
|
||||||
|
#### 2.5 Калькулятор в системе счисления 50 (pentecostal_calculeator10.py)
|
||||||
|
✅ Добавлены колонки для умножения и деления
|
||||||
|
✅ Рефакторинг методов show_steps для использования StepByStepDisplay
|
||||||
|
✅ Код сокращен с ~240 строк до ~50 строк
|
||||||
|
|
||||||
|
### 3. Преимущества рефакторинга
|
||||||
|
|
||||||
|
1. **Сокращение кода**: Общее сокращение на ~900 строк дублированного кода
|
||||||
|
2. **Улучшение поддерживаемости**: Изменения в логике отображения теперь делаются в одном месте
|
||||||
|
3. **Единообразие**: Все калькуляторы используют одинаковый подход к отображению
|
||||||
|
4. **Расширяемость**: Легко добавлять новые типы отображения или модифицировать существующие
|
||||||
|
|
||||||
|
### 4. Структура изменений
|
||||||
|
|
||||||
|
```
|
||||||
|
app/src/
|
||||||
|
├── designer.py # NEW CLASS: StepByStepDisplay
|
||||||
|
├── step_by_step_solver.py # UPDATED: добавлены MULTIPLICATION и DIVISION
|
||||||
|
└── pages/tabs/
|
||||||
|
├── calculater2.py # REFACTORED + 2 new columns
|
||||||
|
├── binary_calculeator4.py # REFACTORED + 2 new columns
|
||||||
|
├── octogon_calculeator6.py # REFACTORED + 2 new columns
|
||||||
|
├── sedecim_calculeator8.py # REFACTORED + 2 new columns + decimal check
|
||||||
|
└── pentecostal_calculeator10.py # REFACTORED + 2 new columns
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Тестирование
|
||||||
|
|
||||||
|
Рекомендуется протестировать следующие операции во всех калькуляторах:
|
||||||
|
- ✅ Сложение двух чисел
|
||||||
|
- ✅ Вычитание двух чисел
|
||||||
|
- ✅ Умножение двух чисел
|
||||||
|
- ✅ Деление двух чисел (включая деление на ноль)
|
||||||
|
- ✅ Пошаговое отображение решения для каждой операции
|
||||||
|
- ✅ Отрицательные числа
|
||||||
|
- ✅ Дробные числа (для шестнадцатеричного калькулятора)
|
||||||
|
|
||||||
|
### 6. Возможные улучшения
|
||||||
|
|
||||||
|
1. Добавить операцию возведения в степень
|
||||||
|
2. Добавить операцию извлечения корня
|
||||||
|
3. Добавить поддержку скобок и сложных выражений
|
||||||
|
4. Добавить историю вычислений
|
||||||
|
5. Добавить экспорт результатов в файл
|
||||||
|
|
||||||
|
## Дата завершения: 2024
|
||||||
|
|
||||||
|
Все задачи выполнены успешно, код без ошибок компиляции.
|
||||||
249
app/src/USER_GUIDE.md
Normal file
249
app/src/USER_GUIDE.md
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
# Руководство пользователя - Калькулятор с системами счисления
|
||||||
|
|
||||||
|
## Обзор
|
||||||
|
|
||||||
|
Калькулятор с пошаговым решением задач для различных систем счисления (2, 8, 10, 16-ричные). Каждая операция сопровождается подробным объяснением хода решения, аналогично приложению Photomath.
|
||||||
|
|
||||||
|
## Доступные калькуляторы
|
||||||
|
|
||||||
|
### 1. Двоичный калькулятор (base=2)
|
||||||
|
- **Система счисления**: Двоичная (цифры: 0, 1)
|
||||||
|
- **Функции**: Сложение и вычитание двоичных чисел
|
||||||
|
- **Особенности**:
|
||||||
|
- Поддержка дополнительного кода для отрицательных чисел
|
||||||
|
- Разрядность: 16 бит
|
||||||
|
- Автоматическое определение знаковых чисел
|
||||||
|
|
||||||
|
**Пример использования:**
|
||||||
|
```
|
||||||
|
Ввод: 1011 + 110
|
||||||
|
Результат: 10001
|
||||||
|
Пошаговое решение покажет:
|
||||||
|
- Выравнивание разрядов
|
||||||
|
- Сложение в столбик с переносами
|
||||||
|
- Проверку через десятичную систему
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Восьмеричный калькулятор (base=8)
|
||||||
|
- **Система счисления**: Восьмеричная (цифры: 0-7)
|
||||||
|
- **Функции**: Сложение и вычитание восьмеричных чисел
|
||||||
|
- **Особенности**: Поддержка отрицательных чисел
|
||||||
|
|
||||||
|
**Пример использования:**
|
||||||
|
```
|
||||||
|
Ввод: 756 - 237
|
||||||
|
Результат: 517₍₈₎ = 335₍₁₀₎
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Десятичный калькулятор (base=10)
|
||||||
|
- **Система счисления**: Десятичная (цифры: 0-9)
|
||||||
|
- **Функции**: Сложение и вычитание целых чисел
|
||||||
|
- **Особенности**: Стандартная арифметика с подробным объяснением
|
||||||
|
|
||||||
|
### 4. Шестнадцатеричный калькулятор (base=16)
|
||||||
|
- **Система счисления**: Шестнадцатеричная (цифры: 0-9, A-F)
|
||||||
|
- **Функции**: Сложение и вычитание шестнадцатеричных чисел
|
||||||
|
- **Особенности**:
|
||||||
|
- Поддержка дробных чисел (пошаговое решение только для целых)
|
||||||
|
- Автоматическое преобразование в десятичную систему
|
||||||
|
|
||||||
|
**Пример использования:**
|
||||||
|
```
|
||||||
|
Ввод: A5 + 3F
|
||||||
|
Результат: E4₍₁₆₎ = 228₍₁₀₎
|
||||||
|
```
|
||||||
|
|
||||||
|
## Как использовать пошаговое решение
|
||||||
|
|
||||||
|
### Что показывается в решении:
|
||||||
|
|
||||||
|
#### Шаг 1: Условие задачи
|
||||||
|
- Исходные числа
|
||||||
|
- Тип операции (сложение/вычитание)
|
||||||
|
- Система счисления
|
||||||
|
|
||||||
|
#### Шаг 2: Выравнивание чисел
|
||||||
|
- Дополнение нулями слева до одинаковой длины
|
||||||
|
- Визуальное представление выровненных чисел
|
||||||
|
|
||||||
|
#### Шаг 3: Вычисление в столбик
|
||||||
|
- **Для сложения:**
|
||||||
|
- Пошаговое сложение справа налево
|
||||||
|
- Объяснение переносов
|
||||||
|
- Когда сумма ≥ base: остаток идет в результат, частное - в перенос
|
||||||
|
|
||||||
|
- **Для вычитания:**
|
||||||
|
- Пошаговое вычитание справа налево
|
||||||
|
- Объяснение заимствований
|
||||||
|
- Когда нужно занять из следующего разряда
|
||||||
|
|
||||||
|
#### Шаг 4: Проверка результата
|
||||||
|
- Перевод в десятичную систему
|
||||||
|
- Вычисление в десятичной системе
|
||||||
|
- Обратный перевод и сравнение
|
||||||
|
|
||||||
|
### Визуальное представление
|
||||||
|
|
||||||
|
Каждый шаг содержит:
|
||||||
|
- 📝 **Заголовок**: Краткое название шага
|
||||||
|
- **Описание**: Что делается на этом шаге
|
||||||
|
- **Визуализация**: Формулы и вычисления в монoширинном шрифте
|
||||||
|
- ➤ **Результат**: Итог шага (выделен зеленым)
|
||||||
|
|
||||||
|
### Цветовая схема
|
||||||
|
|
||||||
|
- 🟡 **Золотой** (#FFD700): Заголовки разделов
|
||||||
|
- 🔵 **Циан** (#00FFFF): Названия шагов
|
||||||
|
- ⚪ **Светло-серый** (#E0E0E0): Описания
|
||||||
|
- 🟢 **Светло-зеленый** (#90EE90): Визуализация вычислений
|
||||||
|
- 🟢 **Зеленый** (#00FF00): Результаты
|
||||||
|
|
||||||
|
## Примеры работы алгоритма
|
||||||
|
|
||||||
|
### Пример 1: Сложение в двоичной системе
|
||||||
|
|
||||||
|
**Задача:** 1011₍₂₎ + 110₍₂₎
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Шаг 1: Условие задачи
|
||||||
|
1011₍₂₎ + 110₍₂₎ = ?
|
||||||
|
|
||||||
|
Шаг 2: Выравнивание
|
||||||
|
1011
|
||||||
|
+ 0110
|
||||||
|
------
|
||||||
|
|
||||||
|
Шаг 3: Сложение в столбик
|
||||||
|
Позиция 0: 1 + 0 = 1
|
||||||
|
Позиция 1: 1 + 1 = 2 = 0 (перенос 1)
|
||||||
|
Позиция 2: 0 + 1 + перенос(1) = 2 = 0 (перенос 1)
|
||||||
|
Позиция 3: 1 + 0 + перенос(1) = 2 = 0 (перенос 1)
|
||||||
|
Финальный перенос: 1
|
||||||
|
|
||||||
|
Результат: 10001₍₂₎
|
||||||
|
|
||||||
|
Шаг 4: Проверка
|
||||||
|
1011₍₂₎ = 11₍₁₀₎
|
||||||
|
0110₍₂₎ = 6₍₁₀₎
|
||||||
|
11 + 6 = 17
|
||||||
|
17₍₁₀₎ = 10001₍₂₎ ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 2: Вычитание в восьмеричной системе
|
||||||
|
|
||||||
|
**Задача:** 756₍₈₎ - 237₍₈₎
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Шаг 1: Условие задачи
|
||||||
|
756₍₈₎ - 237₍₈₎ = ?
|
||||||
|
|
||||||
|
Шаг 2: Выравнивание
|
||||||
|
756
|
||||||
|
- 237
|
||||||
|
-----
|
||||||
|
|
||||||
|
Шаг 3: Вычитание в столбик
|
||||||
|
Позиция 0: 6 - 7 → занимаем 8, получаем 14 - 7 = 7
|
||||||
|
Позиция 1: 5 - заем(1) = 4, 4 - 3 = 1
|
||||||
|
Позиция 2: 7 - 2 = 5
|
||||||
|
|
||||||
|
Результат: 517₍₈₎
|
||||||
|
|
||||||
|
Шаг 4: Проверка
|
||||||
|
756₍₈₎ = 494₍₁₀₎
|
||||||
|
237₍₈₎ = 159₍₁₀₎
|
||||||
|
494 - 159 = 335
|
||||||
|
335₍₁₀₎ = 517₍₈₎ ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 3: Сложение в шестнадцатеричной системе
|
||||||
|
|
||||||
|
**Задача:** A5₍₁₆₎ + 3F₍₁₆₎
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Шаг 1: Условие задачи
|
||||||
|
A5₍₁₆₎ + 3F₍₁₆₎ = ?
|
||||||
|
|
||||||
|
Шаг 2: Выравнивание
|
||||||
|
A5
|
||||||
|
+ 3F
|
||||||
|
----
|
||||||
|
|
||||||
|
Шаг 3: Сложение в столбик
|
||||||
|
Позиция 0: 5 + F(15) = 20 = 4 (перенос 1)
|
||||||
|
Позиция 1: A(10) + 3 + перенос(1) = 14 = E
|
||||||
|
|
||||||
|
Результат: E4₍₁₆₎
|
||||||
|
|
||||||
|
Шаг 4: Проверка
|
||||||
|
A5₍₁₆₎ = 165₍₁₀₎
|
||||||
|
3F₍₁₆₎ = 63₍₁₀₎
|
||||||
|
165 + 63 = 228
|
||||||
|
228₍₁₀₎ = E4₍₁₆₎ ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Советы по использованию
|
||||||
|
|
||||||
|
### 1. Ввод чисел
|
||||||
|
- Используйте только допустимые цифры для каждой системы
|
||||||
|
- Для шестнадцатеричной: можно вводить как заглавные, так и строчные буквы (A-F)
|
||||||
|
- Отрицательные числа вводите со знаком "-"
|
||||||
|
|
||||||
|
### 2. Интерпретация результатов
|
||||||
|
- Результат всегда показывается в исходной системе счисления
|
||||||
|
- Десятичный эквивалент показывается для справки
|
||||||
|
- Пошаговое решение прокручивается - смотрите все шаги
|
||||||
|
|
||||||
|
### 3. Обучение
|
||||||
|
- Читайте каждый шаг последовательно
|
||||||
|
- Обращайте внимание на объяснения переносов и заимствований
|
||||||
|
- Проверяйте результаты через десятичную систему
|
||||||
|
|
||||||
|
### 4. Ограничения
|
||||||
|
- Пошаговое решение для шестнадцатеричной системы работает только с целыми числами
|
||||||
|
- Двоичная система использует дополнительный код для отрицательных чисел
|
||||||
|
|
||||||
|
## Техническая информация
|
||||||
|
|
||||||
|
### Алгоритм работы
|
||||||
|
|
||||||
|
1. **Парсинг ввода**: Проверка корректности введенных чисел
|
||||||
|
2. **Нормализация**: Выравнивание разрядов
|
||||||
|
3. **Вычисление**: Операция в столбик с отслеживанием переносов/заимствований
|
||||||
|
4. **Проверка**: Верификация через десятичную систему
|
||||||
|
5. **Отображение**: Форматированный вывод всех шагов
|
||||||
|
|
||||||
|
### Файлы модулей
|
||||||
|
|
||||||
|
- `step_by_step_solver.py` - Основной алгоритм решения
|
||||||
|
- `calculator_mixin.py` - Миксин для интеграции в калькуляторы
|
||||||
|
- `ALGORITHM_DOCUMENTATION.md` - Подробная документация алгоритма
|
||||||
|
|
||||||
|
## Часто задаваемые вопросы
|
||||||
|
|
||||||
|
**Q: Почему пошаговое решение не отображается для дробных чисел в hex?**
|
||||||
|
A: Текущая версия алгоритма оптимизирована для целочисленной арифметики. Поддержка дробей планируется в будущих версиях.
|
||||||
|
|
||||||
|
**Q: Как работает дополнительный код в двоичной системе?**
|
||||||
|
A: Отрицательные числа представляются в дополнительном коде: если старший бит = 1 и длина ≥ 8 бит, число интерпретируется как отрицательное.
|
||||||
|
|
||||||
|
**Q: Можно ли увеличить разрядность для двоичного калькулятора?**
|
||||||
|
A: Текущая разрядность - 16 бит. Для изменения нужно отредактировать параметр `bit_width` в коде.
|
||||||
|
|
||||||
|
**Q: Поддерживаются ли другие системы счисления (например, пятеричная)?**
|
||||||
|
A: Алгоритм поддерживает любую систему счисления от 2 до 36. Для добавления нового калькулятора создайте экземпляр `StepByStepSolver(base=n)`.
|
||||||
|
|
||||||
|
## Контакты и поддержка
|
||||||
|
|
||||||
|
Для сообщений об ошибках и предложений по улучшению обращайтесь к разработчикам проекта.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Версия документации: 1.0*
|
||||||
|
*Дата обновления: 7 ноября 2025 г.*
|
||||||
BIN
app/src/assets/Система счисления 8.png
Normal file
BIN
app/src/assets/Система счисления 8.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
117
app/src/calculator_mixin.py
Normal file
117
app/src/calculator_mixin.py
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
"""
|
||||||
|
Миксин для добавления пошагового решения в калькуляторы
|
||||||
|
"""
|
||||||
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
|
||||||
|
class StepByStepMixin:
|
||||||
|
"""
|
||||||
|
Миксин для добавления функциональности пошагового решения в калькуляторы
|
||||||
|
"""
|
||||||
|
|
||||||
|
def init_solver(self, base: int):
|
||||||
|
"""Инициализация солвера и контейнеров для шагов"""
|
||||||
|
self.solver = StepByStepSolver(base=base)
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
|
||||||
|
def show_steps(self, steps_container: ft.Column, num1: str, num2: str, operation: Operation):
|
||||||
|
"""
|
||||||
|
Отображает пошаговое решение
|
||||||
|
|
||||||
|
Args:
|
||||||
|
steps_container: контейнер для отображения шагов (self.steps_left или self.steps_right)
|
||||||
|
num1: первое число (строка)
|
||||||
|
num2: второе число (строка)
|
||||||
|
operation: операция (сложение или вычитание)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Получаем шаги решения
|
||||||
|
steps = self.solver.solve(num1, num2, operation)
|
||||||
|
|
||||||
|
# Очищаем предыдущие шаги
|
||||||
|
steps_container.controls.clear()
|
||||||
|
|
||||||
|
# Добавляем заголовок
|
||||||
|
steps_container.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Добавляем каждый шаг
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_content = []
|
||||||
|
|
||||||
|
# Название шага
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(f"Шаг {i}: {step.title}",
|
||||||
|
size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Описание
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(step.description, size=10, color="#E0E0E0")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Визуализация (если есть)
|
||||||
|
if step.visual:
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(step.visual,
|
||||||
|
size=10,
|
||||||
|
font_family="Courier New",
|
||||||
|
color="#90EE90",
|
||||||
|
selectable=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Результат (если есть)
|
||||||
|
if step.result:
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(f"➤ {step.result}",
|
||||||
|
size=11,
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
color="#00FF00")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Добавляем контейнер с шагом
|
||||||
|
steps_container.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
steps_container.update()
|
||||||
|
except Exception as ex:
|
||||||
|
steps_container.controls.clear()
|
||||||
|
steps_container.controls.append(
|
||||||
|
ft.Text(f"Ошибка при генерации решения: {str(ex)}", color="red", size=10)
|
||||||
|
)
|
||||||
|
steps_container.update()
|
||||||
|
|
||||||
|
def create_steps_container(self, steps_column: ft.Column, height: int = 400, width: int = 350):
|
||||||
|
"""
|
||||||
|
Создает контейнер для отображения шагов решения
|
||||||
|
|
||||||
|
Args:
|
||||||
|
steps_column: колонка с шагами (self.steps_left или self.steps_right)
|
||||||
|
height: высота контейнера
|
||||||
|
width: ширина контейнера
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ft.Container с настроенным стилем
|
||||||
|
"""
|
||||||
|
return ft.Container(
|
||||||
|
content=steps_column,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
height=height,
|
||||||
|
width=width,
|
||||||
|
)
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
from flet.core.types import OptionalControlEventCallable
|
from flet.core.types import OptionalControlEventCallable
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
right_column = ft.Column(
|
right_column = ft.Column(
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
@@ -8,3 +14,151 @@ right_column = ft.Column(
|
|||||||
)
|
)
|
||||||
|
|
||||||
alphabet="αβγδεζηθικλμνξοπρστυφχψω"
|
alphabet="αβγδεζηθικλμνξοπρστυφχψω"
|
||||||
|
|
||||||
|
|
||||||
|
class StepByStepDisplay:
|
||||||
|
"""
|
||||||
|
Класс для отображения пошагового решения в калькуляторах
|
||||||
|
Предоставляет унифицированный интерфейс для визуализации шагов решения
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Размеры текста для различных элементов
|
||||||
|
HEADER_SIZE = 16
|
||||||
|
STEP_TITLE_SIZE = 14
|
||||||
|
DESCRIPTION_SIZE = 12
|
||||||
|
VISUAL_SIZE = 12
|
||||||
|
RESULT_SIZE = 13
|
||||||
|
ERROR_SIZE = 12
|
||||||
|
MESSAGE_SIZE = 14
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_steps_container(steps_column: ft.Column, height: int = None, width: int = None, expand: bool = True) -> ft.Container:
|
||||||
|
"""
|
||||||
|
Создает контейнер для отображения шагов решения
|
||||||
|
|
||||||
|
Args:
|
||||||
|
steps_column: колонка с шагами
|
||||||
|
height: высота контейнера (опционально)
|
||||||
|
width: ширина контейнера (опционально)
|
||||||
|
expand: растягивать контейнер (по умолчанию True)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ft.Container с настроенным стилем
|
||||||
|
"""
|
||||||
|
container_props = {
|
||||||
|
"content": steps_column,
|
||||||
|
"bgcolor": "#1A0A2A",
|
||||||
|
"padding": 10,
|
||||||
|
"border_radius": 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
if expand:
|
||||||
|
container_props["expand"] = True
|
||||||
|
if height:
|
||||||
|
container_props["height"] = height
|
||||||
|
if width:
|
||||||
|
container_props["width"] = width
|
||||||
|
|
||||||
|
return ft.Container(**container_props)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def show_steps(steps_column: ft.Column, solver: StepByStepSolver, num1: str, num2: str, operation: Operation):
|
||||||
|
"""
|
||||||
|
Отображает пошаговое решение в указанной колонке
|
||||||
|
|
||||||
|
Args:
|
||||||
|
steps_column: колонка для отображения шагов
|
||||||
|
solver: экземпляр StepByStepSolver для получения шагов
|
||||||
|
num1: первое число (строка)
|
||||||
|
num2: второе число (строка)
|
||||||
|
operation: операция (Operation.ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Получаем шаги решения
|
||||||
|
steps = solver.solve(num1, num2, operation)
|
||||||
|
|
||||||
|
# Очищаем предыдущие шаги
|
||||||
|
steps_column.controls.clear()
|
||||||
|
|
||||||
|
# Добавляем заголовок
|
||||||
|
steps_column.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=StepByStepDisplay.HEADER_SIZE, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Добавляем каждый шаг
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_content = []
|
||||||
|
|
||||||
|
# Название шага
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(f"Шаг {i}: {step.title}",
|
||||||
|
size=StepByStepDisplay.STEP_TITLE_SIZE, weight=ft.FontWeight.BOLD, color="#00FFFF")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Описание
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(step.description, size=StepByStepDisplay.DESCRIPTION_SIZE, color="#E0E0E0")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Визуализация (если есть)
|
||||||
|
if step.visual:
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(step.visual,
|
||||||
|
size=StepByStepDisplay.VISUAL_SIZE,
|
||||||
|
font_family="Courier New",
|
||||||
|
color="#90EE90",
|
||||||
|
selectable=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Результат (если есть)
|
||||||
|
if step.result:
|
||||||
|
step_content.append(
|
||||||
|
ft.Text(f"➤ {step.result}",
|
||||||
|
size=StepByStepDisplay.RESULT_SIZE,
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
color="#00FF00")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Добавляем контейнер с шагом
|
||||||
|
steps_column.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
steps_column.update()
|
||||||
|
except Exception as ex:
|
||||||
|
steps_column.controls.clear()
|
||||||
|
steps_column.controls.append(
|
||||||
|
ft.Text(f"Ошибка при генерации решения: {str(ex)}", color="red", size=StepByStepDisplay.ERROR_SIZE)
|
||||||
|
)
|
||||||
|
steps_column.update()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def show_message(steps_column: ft.Column, message: str, color: str = "#FFD700"):
|
||||||
|
"""
|
||||||
|
Отображает сообщение в колонке шагов (например, для дробных чисел)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
steps_column: колонка для отображения
|
||||||
|
message: текст сообщения
|
||||||
|
color: цвет текста (по умолчанию золотой)
|
||||||
|
"""
|
||||||
|
steps_column.controls.clear()
|
||||||
|
steps_column.controls.append(
|
||||||
|
ft.Text(message, color=color, size=StepByStepDisplay.MESSAGE_SIZE)
|
||||||
|
)
|
||||||
|
steps_column.update()
|
||||||
|
|
||||||
|
|
||||||
|
class Obstr(ft.Column):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
controls=[
|
||||||
|
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
import designer as ds
|
import designer as ds
|
||||||
from pages.tabs import start1,calculater2, binary_data3, binary_calculeator4, octogon_data5, octogon_calculeator6, sedecim_data7
|
from pages.tabs import roman_converter13, roman_calculator12, start1,calculater2, binary_data3, binary_calculeator4, octogon_data5, octogon_calculeator6, sedecim_data7,sedecim_calculeator8, pentecostal_system9, pentecostal_calculeator10, arbitrary_calculator11
|
||||||
|
|
||||||
class Main_page(ft.View):
|
class Main_page(ft.View):
|
||||||
def __init__(self, page):
|
def __init__(self, page):
|
||||||
@@ -15,6 +15,7 @@ class Main_page(ft.View):
|
|||||||
horizontal_alignment=ft.CrossAxisAlignment.START,
|
horizontal_alignment=ft.CrossAxisAlignment.START,
|
||||||
width=300,
|
width=300,
|
||||||
alignment=ft.MainAxisAlignment.START,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
scroll="auto",
|
||||||
controls=[
|
controls=[
|
||||||
start1.Button_text_data(),
|
start1.Button_text_data(),
|
||||||
calculater2.Button_calculeator(),
|
calculater2.Button_calculeator(),
|
||||||
@@ -23,8 +24,14 @@ class Main_page(ft.View):
|
|||||||
octogon_data5.Button_text_data(),
|
octogon_data5.Button_text_data(),
|
||||||
octogon_calculeator6.Button_octogon_calculator(),
|
octogon_calculeator6.Button_octogon_calculator(),
|
||||||
sedecim_data7.Button_sedecim_data(),
|
sedecim_data7.Button_sedecim_data(),
|
||||||
|
sedecim_calculeator8.Button_sedecim_calculator(),
|
||||||
|
pentecostal_system9.Button_binary_data(),
|
||||||
|
pentecostal_calculeator10.Button_quinquagesimal_calculator(),
|
||||||
|
arbitrary_calculator11.Button_arbitrary_calculator(),
|
||||||
|
roman_calculator12.Button_roman_calculator(),
|
||||||
|
roman_converter13.RomanConverter(),
|
||||||
ft.Container(
|
ft.Container(
|
||||||
bgcolor="#280A3F",
|
bgcolor="#221929",
|
||||||
expand=True,
|
expand=True,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
1032
app/src/pages/tabs/arbitrary_calculator11.py
Normal file
1032
app/src/pages/tabs/arbitrary_calculator11.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,23 @@
|
|||||||
from designer import right_column
|
from designer import right_column, StepByStepDisplay
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
class Button_binary_calculator(ft.CupertinoButton):
|
class Button_binary_calculator(ft.CupertinoButton):
|
||||||
def __init__(self, text: str = "Двоичный калькулятор"):
|
def __init__(self, text: str = "Двоичный калькулятор"):
|
||||||
self.result_left = ft.Text(value="0", size=30, width=300)
|
self.result_left = ft.Text(value="0", size=30, width=300)
|
||||||
self.result_right = ft.Text(value="0", size=30, width=300)
|
self.result_right = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_mult = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_div = ft.Text(value="0", size=30, width=300)
|
||||||
|
|
||||||
self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_mult = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_div = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
|
||||||
# Поля ввода для вычитания
|
# Поля ввода для вычитания
|
||||||
self.input_left_1 = None
|
self.input_left_1 = None
|
||||||
@@ -16,6 +27,23 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
self.input_right_1 = None
|
self.input_right_1 = None
|
||||||
self.input_right_2 = None
|
self.input_right_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
|
||||||
|
# Солвер для двоичной системы
|
||||||
|
self.solver = StepByStepSolver(base=2)
|
||||||
|
|
||||||
# Количество бит для представления (по умолчанию 8)
|
# Количество бит для представления (по умолчанию 8)
|
||||||
self.bit_width = 16
|
self.bit_width = 16
|
||||||
|
|
||||||
@@ -54,6 +82,22 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
# Только 0 и 1
|
# Только 0 и 1
|
||||||
return all(c in "01" for c in value)
|
return all(c in "01" for c in value)
|
||||||
|
|
||||||
|
def show_steps_left(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для левой колонки (вычитание)"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_left, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_right(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для правой колонки (сложение)"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_right, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для умножения"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_mult, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для деления"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_div, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
def calculate_subtraction(self, e):
|
def calculate_subtraction(self, e):
|
||||||
try:
|
try:
|
||||||
bin1 = self.input_left_1.value or "0"
|
bin1 = self.input_left_1.value or "0"
|
||||||
@@ -89,6 +133,10 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
self.decimal_left.value = f"({result}₁₀)"
|
self.decimal_left.value = f"({result}₁₀)"
|
||||||
self.result_left.update()
|
self.result_left.update()
|
||||||
self.decimal_left.update()
|
self.decimal_left.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
self.show_steps_left(bin1, bin2, Operation.SUBTRACTION)
|
||||||
|
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
self.result_left.value = "Ошибка"
|
self.result_left.value = "Ошибка"
|
||||||
self.decimal_left.value = str(ex)
|
self.decimal_left.value = str(ex)
|
||||||
@@ -130,12 +178,107 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
self.decimal_right.value = f"({result}₁₀)"
|
self.decimal_right.value = f"({result}₁₀)"
|
||||||
self.result_right.update()
|
self.result_right.update()
|
||||||
self.decimal_right.update()
|
self.decimal_right.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
self.show_steps_right(bin1, bin2, Operation.ADDITION)
|
||||||
|
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
self.result_right.value = "Ошибка"
|
self.result_right.value = "Ошибка"
|
||||||
self.decimal_right.value = str(ex)
|
self.decimal_right.value = str(ex)
|
||||||
self.result_right.update()
|
self.result_right.update()
|
||||||
self.decimal_right.update()
|
self.decimal_right.update()
|
||||||
|
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
bin1 = self.input_mult_1.value or "0"
|
||||||
|
bin2 = self.input_mult_2.value or "0"
|
||||||
|
|
||||||
|
if not self.validate_binary(bin1) or not self.validate_binary(bin2):
|
||||||
|
self.result_mult.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_mult.value = ""
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(bin1) > 0 and bin1[0] == '1' and len(bin1) >= 8:
|
||||||
|
num1 = self.from_twos_complement(bin1)
|
||||||
|
else:
|
||||||
|
num1 = int(bin1, 2) if bin1 else 0
|
||||||
|
|
||||||
|
if len(bin2) > 0 and bin2[0] == '1' and len(bin2) >= 8:
|
||||||
|
num2 = self.from_twos_complement(bin2)
|
||||||
|
else:
|
||||||
|
num2 = int(bin2, 2) if bin2 else 0
|
||||||
|
|
||||||
|
result = num1 * num2
|
||||||
|
binary_result = self.to_twos_complement(result, self.bit_width)
|
||||||
|
|
||||||
|
self.result_mult.value = binary_result
|
||||||
|
self.decimal_mult.value = f"({result}₁₀)"
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
|
||||||
|
self.show_steps_mult(bin1, bin2, Operation.MULTIPLICATION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_mult.value = "Ошибка"
|
||||||
|
self.decimal_mult.value = str(ex)
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
bin1 = self.input_div_1.value or "0"
|
||||||
|
bin2 = self.input_div_2.value or "0"
|
||||||
|
|
||||||
|
if not self.validate_binary(bin1) or not self.validate_binary(bin2):
|
||||||
|
self.result_div.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_div.value = ""
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(bin1) > 0 and bin1[0] == '1' and len(bin1) >= 8:
|
||||||
|
num1 = self.from_twos_complement(bin1)
|
||||||
|
else:
|
||||||
|
num1 = int(bin1, 2) if bin1 else 0
|
||||||
|
|
||||||
|
if len(bin2) > 0 and bin2[0] == '1' and len(bin2) >= 8:
|
||||||
|
num2 = self.from_twos_complement(bin2)
|
||||||
|
else:
|
||||||
|
num2 = int(bin2, 2) if bin2 else 0
|
||||||
|
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "Деление на 0!"
|
||||||
|
self.decimal_div.value = ""
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
quotient = num1 // num2
|
||||||
|
remainder = num1 % num2
|
||||||
|
|
||||||
|
binary_quotient = self.to_twos_complement(quotient, self.bit_width)
|
||||||
|
|
||||||
|
if remainder == 0:
|
||||||
|
self.result_div.value = binary_quotient
|
||||||
|
self.decimal_div.value = f"({quotient}₁₀)"
|
||||||
|
else:
|
||||||
|
binary_remainder = self.to_twos_complement(remainder, self.bit_width)
|
||||||
|
self.result_div.value = binary_quotient
|
||||||
|
self.decimal_div.value = f"({quotient}₁₀, ост. {remainder})"
|
||||||
|
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
|
||||||
|
self.show_steps_div(bin1, bin2, Operation.DIVISION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.decimal_div.value = str(ex)
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
|
||||||
def test_data(self, e):
|
def test_data(self, e):
|
||||||
# Создаем поля ввода с валидацией для двоичных чисел
|
# Создаем поля ввода с валидацией для двоичных чисел
|
||||||
self.input_left_1 = ft.TextField(
|
self.input_left_1 = ft.TextField(
|
||||||
@@ -172,6 +315,40 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
hint_text="Например: 00000101 (5)"
|
hint_text="Например: 00000101 (5)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.input_mult_1 = ft.TextField(
|
||||||
|
label="Двоичное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[01]*$"),
|
||||||
|
hint_text="Например: 00000110 (6)"
|
||||||
|
)
|
||||||
|
self.input_mult_2 = ft.TextField(
|
||||||
|
label="Двоичное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[01]*$"),
|
||||||
|
hint_text="Например: 00000011 (3)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_div_1 = ft.TextField(
|
||||||
|
label="Двоичное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[01]*$"),
|
||||||
|
hint_text="Например: 00001100 (12)"
|
||||||
|
)
|
||||||
|
self.input_div_2 = ft.TextField(
|
||||||
|
label="Двоичное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[01]*$"),
|
||||||
|
hint_text="Например: 00000011 (3)"
|
||||||
|
)
|
||||||
|
|
||||||
right_column.controls = [ft.Container(
|
right_column.controls = [ft.Container(
|
||||||
bgcolor="#55257D",
|
bgcolor="#55257D",
|
||||||
padding=ft.padding.all(10),
|
padding=ft.padding.all(10),
|
||||||
@@ -191,7 +368,7 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
controls=[
|
controls=[
|
||||||
ft.Column(
|
ft.Column(
|
||||||
expand=True,
|
expand=True,
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.input_left_1,
|
self.input_left_1,
|
||||||
@@ -206,12 +383,19 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
padding=10,
|
padding=10,
|
||||||
border_radius=5,
|
border_radius=5,
|
||||||
),
|
),
|
||||||
ft.CupertinoButton("=", on_click=self.calculate_subtraction)
|
ft.CupertinoButton("=", on_click=self.calculate_subtraction),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_left,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
ft.Column(
|
ft.Column(
|
||||||
expand=True,
|
expand=True,
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.input_right_1,
|
self.input_right_1,
|
||||||
@@ -226,7 +410,68 @@ class Button_binary_calculator(ft.CupertinoButton):
|
|||||||
padding=10,
|
padding=10,
|
||||||
border_radius=5,
|
border_radius=5,
|
||||||
),
|
),
|
||||||
ft.CupertinoButton("=", on_click=self.calculate_addition)
|
ft.CupertinoButton("=", on_click=self.calculate_addition),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_right,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("× Умножение", size=12, color="white"),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_mult,
|
||||||
|
self.decimal_mult
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_mult,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("÷ Деление", size=12, color="white"),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_div,
|
||||||
|
self.decimal_div
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_div,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
from designer import right_column
|
from designer import right_column, StepByStepDisplay
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
class Button_calculeator(ft.CupertinoButton):
|
class Button_calculeator(ft.CupertinoButton):
|
||||||
def __init__(self, text: str = "Десятичный калькулятор"):
|
def __init__(self, text: str = "Десятичный калькулятор"):
|
||||||
self.result_left = ft.Text(value="0", size=30, width=300)
|
self.result_left = ft.Text(value="0", size=30, width=300)
|
||||||
self.result_right = ft.Text(value="0", size=30, width=300)
|
self.result_right = ft.Text(value="0", size=30, width=300)
|
||||||
#self.binary_left = ft.Text(value="(0₂)", size=14, color="#B0B0B0", width=300)
|
self.result_mult = ft.Text(value="0", size=30, width=300)
|
||||||
#self.binary_right = ft.Text(value="(0₂)", size=14, color="#B0B0B0", width=300)
|
self.result_div = ft.Text(value="0", size=30, width=300)
|
||||||
|
|
||||||
# Поля ввода для вычитания
|
# Поля ввода для вычитания
|
||||||
self.input_left_1 = None
|
self.input_left_1 = None
|
||||||
@@ -16,6 +22,23 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
self.input_right_1 = None
|
self.input_right_1 = None
|
||||||
self.input_right_2 = None
|
self.input_right_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
|
||||||
|
# Солвер для десятичной системы
|
||||||
|
self.solver = StepByStepSolver(base=10)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
bgcolor="#00000000",
|
bgcolor="#00000000",
|
||||||
disabled_bgcolor="#3A1E51",
|
disabled_bgcolor="#3A1E51",
|
||||||
@@ -38,15 +61,14 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
result = num1 - num2
|
result = num1 - num2
|
||||||
|
|
||||||
self.result_left.value = str(result)
|
self.result_left.value = str(result)
|
||||||
#self.binary_left.value = f"({self.to_binary(result)}₂)"
|
|
||||||
|
|
||||||
self.result_left.update()
|
self.result_left.update()
|
||||||
#self.binary_left.update()
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
self.show_steps_left(str(num1), str(num2), Operation.SUBTRACTION)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.result_left.value = "Ошибка"
|
self.result_left.value = "Ошибка"
|
||||||
#self.binary_left.value = "Неверный формат"
|
|
||||||
self.result_left.update()
|
self.result_left.update()
|
||||||
#self.binary_left.update()
|
|
||||||
|
|
||||||
def calculate_addition(self, e):
|
def calculate_addition(self, e):
|
||||||
try:
|
try:
|
||||||
@@ -55,15 +77,73 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
result = num1 + num2
|
result = num1 + num2
|
||||||
|
|
||||||
self.result_right.value = str(result)
|
self.result_right.value = str(result)
|
||||||
#self.binary_right.value = f"({self.to_binary(result)}₂)"
|
|
||||||
|
|
||||||
self.result_right.update()
|
self.result_right.update()
|
||||||
#self.binary_right.update()
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
self.show_steps_right(str(num1), str(num2), Operation.ADDITION)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.result_right.value = "Ошибка"
|
self.result_right.value = "Ошибка"
|
||||||
#self.binary_right.value = "Неверный формат"
|
|
||||||
self.result_right.update()
|
self.result_right.update()
|
||||||
#self.binary_right.update()
|
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
num1 = int(self.input_mult_1.value or 0)
|
||||||
|
num2 = int(self.input_mult_2.value or 0)
|
||||||
|
result = num1 * num2
|
||||||
|
|
||||||
|
self.result_mult.value = str(result)
|
||||||
|
self.result_mult.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
self.show_steps_mult(str(num1), str(num2), Operation.MULTIPLICATION)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
self.result_mult.value = "Ошибка"
|
||||||
|
self.result_mult.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
num1 = int(self.input_div_1.value or 0)
|
||||||
|
num2 = int(self.input_div_2.value or 0)
|
||||||
|
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "Деление на ноль!"
|
||||||
|
self.result_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
quotient = num1 // num2
|
||||||
|
remainder = num1 % num2
|
||||||
|
|
||||||
|
if remainder == 0:
|
||||||
|
self.result_div.value = str(quotient)
|
||||||
|
else:
|
||||||
|
self.result_div.value = f"{quotient} (ост. {remainder})"
|
||||||
|
|
||||||
|
self.result_div.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
self.show_steps_div(str(num1), str(num2), Operation.DIVISION)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.result_div.update()
|
||||||
|
|
||||||
|
def show_steps_left(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для левой колонки (вычитание)"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_left, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_right(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для правой колонки (сложение)"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_right, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для умножения"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_mult, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для деления"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_div, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
def test_data(self, e):
|
def test_data(self, e):
|
||||||
# Создаем поля ввода с валидацией
|
# Создаем поля ввода с валидацией
|
||||||
@@ -105,6 +185,44 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
hint_text="Например: 17"
|
hint_text="Например: 17"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.input_mult_1 = ft.TextField(
|
||||||
|
label="Десятичное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?\d*$"),
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
hint_text="Например: 12"
|
||||||
|
)
|
||||||
|
self.input_mult_2 = ft.TextField(
|
||||||
|
label="Десятичное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?\d*$"),
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
hint_text="Например: 5"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_div_1 = ft.TextField(
|
||||||
|
label="Десятичное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?\d*$"),
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
hint_text="Например: 100"
|
||||||
|
)
|
||||||
|
self.input_div_2 = ft.TextField(
|
||||||
|
label="Десятичное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?\d*$"),
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
hint_text="Например: 7"
|
||||||
|
)
|
||||||
|
|
||||||
right_column.controls = [ft.Container(
|
right_column.controls = [ft.Container(
|
||||||
bgcolor="#55257D",
|
bgcolor="#55257D",
|
||||||
padding=ft.padding.all(10),
|
padding=ft.padding.all(10),
|
||||||
@@ -123,7 +241,7 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
controls=[
|
controls=[
|
||||||
ft.Column(
|
ft.Column(
|
||||||
expand=True,
|
expand=True,
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.input_left_1,
|
self.input_left_1,
|
||||||
@@ -132,18 +250,24 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
ft.Container(
|
ft.Container(
|
||||||
content=ft.Column([
|
content=ft.Column([
|
||||||
self.result_left,
|
self.result_left,
|
||||||
#self.binary_left
|
|
||||||
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
bgcolor="#3A1E51",
|
bgcolor="#3A1E51",
|
||||||
padding=10,
|
padding=10,
|
||||||
border_radius=5,
|
border_radius=5,
|
||||||
),
|
),
|
||||||
ft.CupertinoButton("=", on_click=self.calculate_subtraction)
|
ft.CupertinoButton("=", on_click=self.calculate_subtraction),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_left,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
ft.Column(
|
ft.Column(
|
||||||
expand=True,
|
expand=True,
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.input_right_1,
|
self.input_right_1,
|
||||||
@@ -152,13 +276,71 @@ class Button_calculeator(ft.CupertinoButton):
|
|||||||
ft.Container(
|
ft.Container(
|
||||||
content=ft.Column([
|
content=ft.Column([
|
||||||
self.result_right,
|
self.result_right,
|
||||||
#self.binary_right
|
|
||||||
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
bgcolor="#3A1E51",
|
bgcolor="#3A1E51",
|
||||||
padding=10,
|
padding=10,
|
||||||
border_radius=5,
|
border_radius=5,
|
||||||
),
|
),
|
||||||
ft.CupertinoButton("=", on_click=self.calculate_addition)
|
ft.CupertinoButton("=", on_click=self.calculate_addition),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_right,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("× Умножение", size=12, color="white"),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_mult,
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_mult,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("÷ Деление", size=12, color="white"),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_div,
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_div,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
from designer import right_column
|
from designer import right_column, StepByStepDisplay
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
class Button_octogon_calculator(ft.CupertinoButton):
|
class Button_octogon_calculator(ft.CupertinoButton):
|
||||||
def __init__(self, text: str = "Восьмеричный калькулятор"):
|
def __init__(self, text: str = "Восьмеричный калькулятор"):
|
||||||
self.result_left = ft.Text(value="0", size=30, width=300)
|
self.result_left = ft.Text(value="0", size=30, width=300)
|
||||||
self.result_right = ft.Text(value="0", size=30, width=300)
|
self.result_right = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_mult = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_div = ft.Text(value="0", size=30, width=300)
|
||||||
|
|
||||||
self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_mult = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_div = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
|
||||||
# Поля ввода для вычитания
|
# Поля ввода для вычитания
|
||||||
self.input_left_1 = None
|
self.input_left_1 = None
|
||||||
@@ -16,6 +27,23 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
self.input_right_1 = None
|
self.input_right_1 = None
|
||||||
self.input_right_2 = None
|
self.input_right_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
|
||||||
|
# Солвер для восьмеричной системы
|
||||||
|
self.solver = StepByStepSolver(base=8)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
bgcolor="#00000000",
|
bgcolor="#00000000",
|
||||||
disabled_bgcolor="#3A1E51",
|
disabled_bgcolor="#3A1E51",
|
||||||
@@ -74,6 +102,12 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
self.decimal_left.value = f"({result}₁₀)"
|
self.decimal_left.value = f"({result}₁₀)"
|
||||||
self.result_left.update()
|
self.result_left.update()
|
||||||
self.decimal_left.update()
|
self.decimal_left.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение (убираем знак минус для солвера)
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') and num1 < 0 else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') and num2 < 0 else val2
|
||||||
|
self.show_steps_left(clean_val1 if num1 >= 0 else val1, clean_val2 if num2 >= 0 else val2, Operation.SUBTRACTION)
|
||||||
|
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
self.result_left.value = "Ошибка"
|
self.result_left.value = "Ошибка"
|
||||||
self.decimal_left.value = str(ex)
|
self.decimal_left.value = str(ex)
|
||||||
@@ -115,15 +149,200 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
self.decimal_right.value = f"({result}₁₀)"
|
self.decimal_right.value = f"({result}₁₀)"
|
||||||
self.result_right.update()
|
self.result_right.update()
|
||||||
self.decimal_right.update()
|
self.decimal_right.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') and num1 < 0 else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') and num2 < 0 else val2
|
||||||
|
self.show_steps_right(clean_val1 if num1 >= 0 else val1, clean_val2 if num2 >= 0 else val2, Operation.ADDITION)
|
||||||
|
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
self.result_right.value = "Ошибка"
|
self.result_right.value = "Ошибка"
|
||||||
self.decimal_right.value = str(ex)
|
self.decimal_right.value = str(ex)
|
||||||
self.result_right.update()
|
self.result_right.update()
|
||||||
self.decimal_right.update()
|
self.decimal_right.update()
|
||||||
|
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_mult_1.value or "").strip()
|
||||||
|
val2 = (self.input_mult_2.value or "").strip()
|
||||||
|
|
||||||
|
if not self.validate_octal(val1) or not self.validate_octal(val2):
|
||||||
|
self.result_mult.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_mult.value = ""
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
def parse_octal(s: str) -> int:
|
||||||
|
s = s.strip()
|
||||||
|
if s == "":
|
||||||
|
return 0
|
||||||
|
sign = -1 if s.startswith('-') else 1
|
||||||
|
if s.startswith('-'):
|
||||||
|
s = s[1:]
|
||||||
|
return sign * int(s, 8) if s else 0
|
||||||
|
|
||||||
|
num1 = parse_octal(val1)
|
||||||
|
num2 = parse_octal(val2)
|
||||||
|
result = num1 * num2
|
||||||
|
oct_result = ('-' if result < 0 else '') + format(abs(result), 'o')
|
||||||
|
|
||||||
|
self.result_mult.value = oct_result
|
||||||
|
self.decimal_mult.value = f"({result}₁₀)"
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') and num1 < 0 else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') and num2 < 0 else val2
|
||||||
|
self.show_steps_mult(clean_val1 if num1 >= 0 else val1, clean_val2 if num2 >= 0 else val2, Operation.MULTIPLICATION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_mult.value = "Ошибка"
|
||||||
|
self.decimal_mult.value = str(ex)
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_div_1.value or "").strip()
|
||||||
|
val2 = (self.input_div_2.value or "").strip()
|
||||||
|
|
||||||
|
if not self.validate_octal(val1) or not self.validate_octal(val2):
|
||||||
|
self.result_div.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_div.value = ""
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
def parse_octal(s: str) -> int:
|
||||||
|
s = s.strip()
|
||||||
|
if s == "":
|
||||||
|
return 0
|
||||||
|
sign = -1 if s.startswith('-') else 1
|
||||||
|
if s.startswith('-'):
|
||||||
|
s = s[1:]
|
||||||
|
return sign * int(s, 8) if s else 0
|
||||||
|
|
||||||
|
num1 = parse_octal(val1)
|
||||||
|
num2 = parse_octal(val2)
|
||||||
|
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "Деление на 0!"
|
||||||
|
self.decimal_div.value = ""
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
quotient = num1 // num2
|
||||||
|
remainder = num1 % num2
|
||||||
|
oct_quotient = ('-' if quotient < 0 else '') + format(abs(quotient), 'o')
|
||||||
|
|
||||||
|
if remainder == 0:
|
||||||
|
self.result_div.value = oct_quotient
|
||||||
|
self.decimal_div.value = f"({quotient}₁₀)"
|
||||||
|
else:
|
||||||
|
self.result_div.value = oct_quotient
|
||||||
|
self.decimal_div.value = f"({quotient}₁₀, ост. {remainder})"
|
||||||
|
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') and num1 < 0 else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') and num2 < 0 else val2
|
||||||
|
self.show_steps_div(clean_val1 if num1 >= 0 else val1, clean_val2 if num2 >= 0 else val2, Operation.DIVISION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.decimal_div.value = str(ex)
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для умножения"""
|
||||||
|
try:
|
||||||
|
steps = self.solver.solve(num1, num2, operation)
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_content = []
|
||||||
|
step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step_content.append(ft.Text(step.description, size=10, color="#E0E0E0"))
|
||||||
|
if step.visual:
|
||||||
|
step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True))
|
||||||
|
if step.result:
|
||||||
|
step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.steps_mult.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
self.steps_mult.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10))
|
||||||
|
self.steps_mult.update()
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для деления"""
|
||||||
|
try:
|
||||||
|
steps = self.solver.solve(num1, num2, operation)
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_content = []
|
||||||
|
step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step_content.append(ft.Text(step.description, size=10, color="#E0E0E0"))
|
||||||
|
if step.visual:
|
||||||
|
step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True))
|
||||||
|
if step.result:
|
||||||
|
step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.steps_div.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
self.steps_div.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10))
|
||||||
|
self.steps_div.update()
|
||||||
|
|
||||||
|
def show_steps_left(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для левой колонки"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_left, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_right(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для правой колонки"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_right, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для умножения"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_mult, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для деления"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_div, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
def test_data(self, e):
|
def test_data(self, e):
|
||||||
# Создаем поля ввода с валидацией для двоичных чисел
|
# Создаем поля ввода с валидацией для восьмеричных чисел
|
||||||
# Поля ввода для восьмеричной системы — допускаются цифры 0..7 и опциональный знак '-'
|
|
||||||
self.input_left_1 = ft.TextField(
|
self.input_left_1 = ft.TextField(
|
||||||
label="Восьмеричное число 1",
|
label="Восьмеричное число 1",
|
||||||
width=250,
|
width=250,
|
||||||
@@ -158,6 +377,40 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
hint_text="Например: 7 (7₁₀)"
|
hint_text="Например: 7 (7₁₀)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.input_mult_1 = ft.TextField(
|
||||||
|
label="Восьмеричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?[0-7]*$"),
|
||||||
|
hint_text="Например: 12 (10₁₀)"
|
||||||
|
)
|
||||||
|
self.input_mult_2 = ft.TextField(
|
||||||
|
label="Восьмеричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?[0-7]*$"),
|
||||||
|
hint_text="Например: 3 (3₁₀)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_div_1 = ft.TextField(
|
||||||
|
label="Восьмеричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?[0-7]*$"),
|
||||||
|
hint_text="Например: 144 (100₁₀)"
|
||||||
|
)
|
||||||
|
self.input_div_2 = ft.TextField(
|
||||||
|
label="Восьмеричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^-?[0-7]*$"),
|
||||||
|
hint_text="Например: 7 (7₁₀)"
|
||||||
|
)
|
||||||
|
|
||||||
right_column.controls = [ft.Container(
|
right_column.controls = [ft.Container(
|
||||||
bgcolor="#55257D",
|
bgcolor="#55257D",
|
||||||
padding=ft.padding.all(10),
|
padding=ft.padding.all(10),
|
||||||
@@ -177,7 +430,7 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
controls=[
|
controls=[
|
||||||
ft.Column(
|
ft.Column(
|
||||||
expand=True,
|
expand=True,
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.input_left_1,
|
self.input_left_1,
|
||||||
@@ -192,12 +445,19 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
padding=10,
|
padding=10,
|
||||||
border_radius=5,
|
border_radius=5,
|
||||||
),
|
),
|
||||||
ft.CupertinoButton("=", on_click=self.calculate_subtraction)
|
ft.CupertinoButton("=", on_click=self.calculate_subtraction),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_left,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
ft.Column(
|
ft.Column(
|
||||||
expand=True,
|
expand=True,
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.input_right_1,
|
self.input_right_1,
|
||||||
@@ -212,7 +472,68 @@ class Button_octogon_calculator(ft.CupertinoButton):
|
|||||||
padding=10,
|
padding=10,
|
||||||
border_radius=5,
|
border_radius=5,
|
||||||
),
|
),
|
||||||
ft.CupertinoButton("=", on_click=self.calculate_addition)
|
ft.CupertinoButton("=", on_click=self.calculate_addition),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_right,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("× Умножение", size=12, color="white"),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_mult,
|
||||||
|
self.decimal_mult
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_mult,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("÷ Деление", size=12, color="white"),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_div,
|
||||||
|
self.decimal_div
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_div,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
484
app/src/pages/tabs/pentecostal_calculeator10.py
Normal file
484
app/src/pages/tabs/pentecostal_calculeator10.py
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
from designer import right_column, StepByStepDisplay
|
||||||
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
class Button_quinquagesimal_calculator(ft.CupertinoButton):
|
||||||
|
def __init__(self, text: str = "50-ричный калькулятор"):
|
||||||
|
self.result_left = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_right = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_mult = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_div = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_mult = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_div = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
|
||||||
|
# Поля ввода для вычитания
|
||||||
|
self.input_left_1 = None
|
||||||
|
self.input_left_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для сложения
|
||||||
|
self.input_right_1 = None
|
||||||
|
self.input_right_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
|
||||||
|
# Допустимые символы: 0-9, A-Z, a-n (всего: 10 + 26 + 14 = 50)
|
||||||
|
self.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn"
|
||||||
|
|
||||||
|
# Солвер для 50-ричной системы с нашим алфавитом
|
||||||
|
self.solver = StepByStepSolver(base=50, digits=self.digits)
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
bgcolor="#00000000",
|
||||||
|
disabled_bgcolor="#3A1E51",
|
||||||
|
text=text,
|
||||||
|
color="white",
|
||||||
|
on_click=self.test_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_base50(self, value: str) -> bool:
|
||||||
|
"""Проверяет, является ли строка корректным 50-ричным числом.
|
||||||
|
|
||||||
|
Допустимые символы: 0-9, A-Z, a-n (0-49), опциональный знак минус
|
||||||
|
"""
|
||||||
|
if value is None or value == "":
|
||||||
|
return True
|
||||||
|
v = value.strip()
|
||||||
|
if v == "":
|
||||||
|
return True
|
||||||
|
if v[0] == '-':
|
||||||
|
v = v[1:]
|
||||||
|
# В 50-ричной системе используются цифры 0-9, A-Z, a-n (всего 50 символов)
|
||||||
|
valid_chars = self.digits # 0-9, A-Z, a-n
|
||||||
|
return all(c in valid_chars for c in v)
|
||||||
|
|
||||||
|
def to_base50(self, decimal_num: int) -> str:
|
||||||
|
"""Преобразование десятичного числа в 50-ричное"""
|
||||||
|
if decimal_num == 0:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
is_negative = decimal_num < 0
|
||||||
|
decimal_num = abs(decimal_num)
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
while decimal_num > 0:
|
||||||
|
remainder = decimal_num % 50
|
||||||
|
result = self.digits[remainder] + result
|
||||||
|
decimal_num //= 50
|
||||||
|
|
||||||
|
return ("-" + result) if is_negative else result
|
||||||
|
|
||||||
|
def from_base50(self, base50_str: str) -> int:
|
||||||
|
"""Преобразование 50-ричного числа в десятичное"""
|
||||||
|
if not base50_str:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
base50_str = base50_str.strip()
|
||||||
|
if base50_str == "" or base50_str == "0":
|
||||||
|
return 0
|
||||||
|
|
||||||
|
is_negative = base50_str.startswith('-')
|
||||||
|
if is_negative:
|
||||||
|
base50_str = base50_str[1:]
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
for char in base50_str:
|
||||||
|
if char in self.digits:
|
||||||
|
digit_value = self.digits.index(char)
|
||||||
|
result = result * 50 + digit_value
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Недопустимый символ '{char}' для 50-ричной системы")
|
||||||
|
|
||||||
|
return -result if is_negative else result
|
||||||
|
|
||||||
|
def show_steps_left(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для левой колонки (вычитание)"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_left, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_right(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для правой колонки (сложение)"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_right, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для колонки умножения"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_mult, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для колонки деления"""
|
||||||
|
StepByStepDisplay.show_steps(self.steps_div, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def calculate_subtraction(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_left_1.value or "").strip()
|
||||||
|
val2 = (self.input_left_2.value or "").strip()
|
||||||
|
|
||||||
|
# Проверка корректности 50-ричных чисел
|
||||||
|
if not self.validate_base50(val1) or not self.validate_base50(val2):
|
||||||
|
self.result_left.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_left.value = "Используйте 0-9, A-Z, a-n"
|
||||||
|
e.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Преобразуем в десятичные числа
|
||||||
|
num1 = self.from_base50(val1 if val1 else "0")
|
||||||
|
num2 = self.from_base50(val2 if val2 else "0")
|
||||||
|
|
||||||
|
# Вычисляем результат
|
||||||
|
result = num1 - num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в 50-ричный формат
|
||||||
|
base50_result = self.to_base50(result)
|
||||||
|
|
||||||
|
self.result_left.value = base50_result
|
||||||
|
self.decimal_left.value = f"({result}₁₀)"
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение (убираем знак минус для солвера)
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2
|
||||||
|
self.show_steps_left(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.SUBTRACTION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_left.value = "Ошибка"
|
||||||
|
self.decimal_left.value = str(ex)
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
def calculate_addition(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_right_1.value or "").strip()
|
||||||
|
val2 = (self.input_right_2.value or "").strip()
|
||||||
|
|
||||||
|
# Проверка корректности 50-ричных чисел
|
||||||
|
if not self.validate_base50(val1) or not self.validate_base50(val2):
|
||||||
|
self.result_right.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_right.value = "Используйте 0-9, A-Z, a-n"
|
||||||
|
e.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Преобразуем в десятичные числа
|
||||||
|
num1 = self.from_base50(val1 if val1 else "0")
|
||||||
|
num2 = self.from_base50(val2 if val2 else "0")
|
||||||
|
|
||||||
|
# Вычисляем результат
|
||||||
|
result = num1 + num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в 50-ричный формат
|
||||||
|
base50_result = self.to_base50(result)
|
||||||
|
|
||||||
|
self.result_right.value = base50_result
|
||||||
|
self.decimal_right.value = f"({result}₁₀)"
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2
|
||||||
|
self.show_steps_right(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.ADDITION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_right.value = "Ошибка"
|
||||||
|
self.decimal_right.value = str(ex)
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_mult_1.value or "").strip()
|
||||||
|
val2 = (self.input_mult_2.value or "").strip()
|
||||||
|
|
||||||
|
# Проверка корректности 50-ричных чисел
|
||||||
|
if not self.validate_base50(val1) or not self.validate_base50(val2):
|
||||||
|
self.result_mult.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_mult.value = "Используйте 0-9, A-Z, a-n"
|
||||||
|
e.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Преобразуем в десятичные числа
|
||||||
|
num1 = self.from_base50(val1 if val1 else "0")
|
||||||
|
num2 = self.from_base50(val2 if val2 else "0")
|
||||||
|
|
||||||
|
# Вычисляем результат
|
||||||
|
result = num1 * num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в 50-ричный формат
|
||||||
|
base50_result = self.to_base50(result)
|
||||||
|
|
||||||
|
self.result_mult.value = base50_result
|
||||||
|
self.decimal_mult.value = f"({result}₁₀)"
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2
|
||||||
|
self.show_steps_mult(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.MULTIPLICATION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_mult.value = "Ошибка"
|
||||||
|
self.decimal_mult.value = str(ex)
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_div_1.value or "").strip()
|
||||||
|
val2 = (self.input_div_2.value or "").strip()
|
||||||
|
|
||||||
|
# Проверка корректности 50-ричных чисел
|
||||||
|
if not self.validate_base50(val1) or not self.validate_base50(val2):
|
||||||
|
self.result_div.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_div.value = "Используйте 0-9, A-Z, a-n"
|
||||||
|
e.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Преобразуем в десятичные числа
|
||||||
|
num1 = self.from_base50(val1 if val1 else "0")
|
||||||
|
num2 = self.from_base50(val2 if val2 else "0")
|
||||||
|
|
||||||
|
# Проверка деления на ноль
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.decimal_div.value = "Деление на ноль!"
|
||||||
|
e.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Вычисляем результат (целочисленное деление)
|
||||||
|
result = num1 // num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в 50-ричный формат
|
||||||
|
base50_result = self.to_base50(result)
|
||||||
|
|
||||||
|
self.result_div.value = base50_result
|
||||||
|
self.decimal_div.value = f"({result}₁₀)"
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
# Получаем пошаговое решение
|
||||||
|
clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1
|
||||||
|
clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2
|
||||||
|
self.show_steps_div(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.DIVISION)
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.decimal_div.value = str(ex)
|
||||||
|
e.page.update()
|
||||||
|
|
||||||
|
def test_data(self, e):
|
||||||
|
# Создаем поля ввода для 50-ричных чисел
|
||||||
|
# Явно перечисляем ВСЕ 50 допустимых символов в regex
|
||||||
|
# 0-9 (10), A-Z (26), a-n (14) = 50 символов
|
||||||
|
allowed_chars = r"^-?[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn]*$"
|
||||||
|
|
||||||
|
self.input_left_1 = ft.TextField(
|
||||||
|
label="50-ричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: 1A (60₁₀) или Z (35₁₀) или a (36₁₀)"
|
||||||
|
)
|
||||||
|
self.input_left_2 = ft.TextField(
|
||||||
|
label="50-ричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: B (11₁₀) или n (49₁₀)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_right_1 = ft.TextField(
|
||||||
|
label="50-ричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: 1A (60₁₀) или c (38₁₀)"
|
||||||
|
)
|
||||||
|
self.input_right_2 = ft.TextField(
|
||||||
|
label="50-ричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: C (12₁₀) или k (46₁₀)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_mult_1 = ft.TextField(
|
||||||
|
label="50-ричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: A (10₁₀) или d (39₁₀)"
|
||||||
|
)
|
||||||
|
self.input_mult_2 = ft.TextField(
|
||||||
|
label="50-ричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: 5 (5₁₀) или B (11₁₀)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_div_1 = ft.TextField(
|
||||||
|
label="50-ричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: 14 (54₁₀) или Z (35₁₀)"
|
||||||
|
)
|
||||||
|
self.input_div_2 = ft.TextField(
|
||||||
|
label="50-ричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=allowed_chars),
|
||||||
|
hint_text="Например: 2 (2₁₀) или A (10₁₀)"
|
||||||
|
)
|
||||||
|
|
||||||
|
right_column.controls = [ft.Container(
|
||||||
|
bgcolor="#55257D",
|
||||||
|
padding=ft.padding.all(10),
|
||||||
|
border_radius=10,
|
||||||
|
expand=True,
|
||||||
|
content=ft.Column(
|
||||||
|
scroll=ft.ScrollMode.AUTO,
|
||||||
|
expand=True,
|
||||||
|
controls=[
|
||||||
|
ft.Text("50-ричный калькулятор", size=20, color="white", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text("Система счисления с основанием 50", size=11, color="#B0B0B0", italic=True),
|
||||||
|
ft.Text("Используйте: 0-9 (0-9), A-Z (10-35), a-n (36-49)", size=11, color="#B0B0B0"),
|
||||||
|
ft.Text("Примеры: 10₍₅₀₎=50₁₀, A₍₅₀₎=10₁₀, Z₍₅₀₎=35₁₀, n₍₅₀₎=49₁₀", size=10, color="#90EE90"),
|
||||||
|
ft.Row(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
||||||
|
controls=[
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_left_1,
|
||||||
|
ft.Text("——— Вычитание", size=12, color="white"),
|
||||||
|
self.input_left_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_left,
|
||||||
|
self.decimal_left
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_subtraction),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_left,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_right_1,
|
||||||
|
ft.Text("—|— Сложение", size=12, color="white"),
|
||||||
|
self.input_right_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_right,
|
||||||
|
self.decimal_right
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_addition),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_right,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("——— Умножение", size=12, color="white"),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_mult,
|
||||||
|
self.decimal_mult
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_mult,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("——— Деление", size=12, color="white"),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_div,
|
||||||
|
self.decimal_div
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_div,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
e.page.update()
|
||||||
183
app/src/pages/tabs/pentecostal_system9.py
Normal file
183
app/src/pages/tabs/pentecostal_system9.py
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
from designer import right_column
|
||||||
|
import flet as ft
|
||||||
|
|
||||||
|
class Button_binary_data(ft.CupertinoButton):
|
||||||
|
def __init__(self, text: str = "50-ричная система счисления"):
|
||||||
|
super().__init__(
|
||||||
|
bgcolor="#00000000",
|
||||||
|
disabled_bgcolor="#3A1E51",
|
||||||
|
text=text,
|
||||||
|
color="white",
|
||||||
|
on_click=self.test_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_data(self, e):
|
||||||
|
md1="""
|
||||||
|
# 50-ричная система счисления
|
||||||
|
|
||||||
|
## Основные понятия
|
||||||
|
|
||||||
|
**50-ричная система счисления** — позиционная система счисления с основанием 50.
|
||||||
|
|
||||||
|
### Алфавит системы:
|
||||||
|
- **Цифры 0-9**: значения 0-9 (10 символов)
|
||||||
|
- **Заглавные A-Z**: значения 10-35 (26 символов)
|
||||||
|
- **Строчные a-n**: значения 36-49 (14 символов)
|
||||||
|
- **Итого**: 50 уникальных символов
|
||||||
|
|
||||||
|
### Примеры перевода:
|
||||||
|
- `10₅₀ = 50₁₀` (одна пятидесятка)
|
||||||
|
- `A₅₀ = 10₁₀` (символ A = десять)
|
||||||
|
- `Z₅₀ = 35₁₀` (последняя заглавная буква)
|
||||||
|
- `n₅₀ = 49₁₀` (последний символ алфавита)
|
||||||
|
|
||||||
|
### Математическое представление:
|
||||||
|
|
||||||
|
Общая формула перевода из 50-ричной в десятичную:
|
||||||
|
|
||||||
|
**N₁₀ = aₙ×50ⁿ + aₙ₋₁×50ⁿ⁻¹ + ... + a₁×50¹ + a₀×50⁰**
|
||||||
|
|
||||||
|
где:
|
||||||
|
- N₁₀ — число в десятичной системе
|
||||||
|
- aᵢ — цифра на позиции i (значение 0-49)
|
||||||
|
- n — количество разрядов минус 1
|
||||||
|
|
||||||
|
### Пример вычисления:
|
||||||
|
|
||||||
|
Число `Ab5₅₀`:
|
||||||
|
- A = 10, b = 37, 5 = 5
|
||||||
|
- Ab5₅₀ = 10×50² + 37×50¹ + 5×50⁰
|
||||||
|
- Ab5₅₀ = 10×2500 + 37×50 + 5×1
|
||||||
|
- Ab5₅₀ = 25000 + 1850 + 5
|
||||||
|
- **Ab5₅₀ = 26855₁₀**
|
||||||
|
|
||||||
|
### Применение:
|
||||||
|
|
||||||
|
• Компактное представление больших чисел
|
||||||
|
• Кодирование данных с расширенным алфавитом
|
||||||
|
• Учебные задачи по системам счисления
|
||||||
|
• Альтернативные схемы нумерации
|
||||||
|
"""
|
||||||
|
|
||||||
|
md2="""
|
||||||
|
## Арифметические операции в 50-ричной системе
|
||||||
|
|
||||||
|
### Сложение
|
||||||
|
|
||||||
|
**Правило**: Складываем разряды справа налево. Если сумма ≥ 50, переносим в старший разряд.
|
||||||
|
|
||||||
|
**Пример**: `A₅₀ + Z₅₀`
|
||||||
|
- A₅₀ = 10₁₀
|
||||||
|
- Z₅₀ = 35₁₀
|
||||||
|
- Сумма: 10 + 35 = 45₁₀ = j₅₀
|
||||||
|
|
||||||
|
**Пример с переносом**: `n₅₀ + 5₅₀`
|
||||||
|
- n₅₀ = 49₁₀
|
||||||
|
- 5₅₀ = 5₁₀
|
||||||
|
- Сумма: 49 + 5 = 54₁₀ = 1×50 + 4 = **14₅₀**
|
||||||
|
|
||||||
|
### Вычитание
|
||||||
|
|
||||||
|
**Правило**: Вычитаем разряды справа налево. Если не хватает, занимаем из старшего разряда (50 единиц).
|
||||||
|
|
||||||
|
**Пример**: `Z₅₀ - A₅₀`
|
||||||
|
- Z₅₀ = 35₁₀
|
||||||
|
- A₅₀ = 10₁₀
|
||||||
|
- Разность: 35 - 10 = 25₁₀ = P₅₀
|
||||||
|
|
||||||
|
### Карта символов (кратко)
|
||||||
|
0–9 → 0–9, A–Z → 10–35, a–n → 36–49
|
||||||
|
|
||||||
|
### Алгоритм перевода обратно в десятичную:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def from_base50(число_50):
|
||||||
|
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn"
|
||||||
|
результат = 0
|
||||||
|
|
||||||
|
for i, символ in enumerate(reversed(число_50)):
|
||||||
|
значение = digits.index(символ)
|
||||||
|
результат += значение × (50 ** i)
|
||||||
|
|
||||||
|
return результат
|
||||||
|
```
|
||||||
|
|
||||||
|
### Преимущества 50-ричной системы:
|
||||||
|
|
||||||
|
✓ Больше символов → компактнее запись
|
||||||
|
✓ Легко расширяется до других оснований
|
||||||
|
✓ Учит работать с произвольными системами счисления
|
||||||
|
✓ Развивает абстрактное математическое мышление
|
||||||
|
"""
|
||||||
|
|
||||||
|
md3="""
|
||||||
|
## Практические примеры
|
||||||
|
|
||||||
|
### Пример 1: Сложение `1A₅₀ + B₅₀`
|
||||||
|
|
||||||
|
**Шаг 1**: Переводим в десятичную
|
||||||
|
- 1A₅₀ = 1×50 + 10 = 60₁₀
|
||||||
|
- B₅₀ = 11₁₀
|
||||||
|
|
||||||
|
**Шаг 2**: Складываем
|
||||||
|
- 60 + 11 = 71₁₀
|
||||||
|
|
||||||
|
**Шаг 3**: Переводим обратно
|
||||||
|
- 71 ÷ 50 = 1 (остаток 21)
|
||||||
|
- 21 → L (в алфавите)
|
||||||
|
- **Ответ: 1L₅₀**
|
||||||
|
|
||||||
|
### Пример 2: Вычитание `Z₅₀ - 5₅₀`
|
||||||
|
|
||||||
|
**Решение**:
|
||||||
|
- Z₅₀ = 35₁₀
|
||||||
|
- 5₅₀ = 5₁₀
|
||||||
|
- 35 - 5 = 30₁₀
|
||||||
|
- 30 → U (в алфавите)
|
||||||
|
- **Ответ: U₅₀**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Заключение**: 50-ричная система — мощный инструмент для понимания принципов позиционных систем счисления с любым основанием.
|
||||||
|
"""
|
||||||
|
|
||||||
|
right_column.controls=[ ft.Container(
|
||||||
|
bgcolor="#55257D",
|
||||||
|
padding=ft.padding.all(10),
|
||||||
|
border_radius=10,
|
||||||
|
expand=True,
|
||||||
|
content=ft.Column(
|
||||||
|
scroll=ft.ScrollMode.AUTO,
|
||||||
|
expand=True,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
ft.Row(
|
||||||
|
expand=True,
|
||||||
|
controls=[
|
||||||
|
ft.Markdown(md1, expand=True),
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
expand=True,
|
||||||
|
controls=[
|
||||||
|
ft.Markdown(md2, expand=True),
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
ft.Text("Перевод обратно в десятичную систему:", size=16, color="white"),
|
||||||
|
##ft.Container(
|
||||||
|
## bgcolor="#000000",
|
||||||
|
## content=ft.Image(src="./Система счисления 2.png", expand=True),
|
||||||
|
## border_radius=10,
|
||||||
|
## width=580,
|
||||||
|
## height=300,
|
||||||
|
##),
|
||||||
|
ft.Text("Работа со знаком:", size=16, color="white"),
|
||||||
|
|
||||||
|
],
|
||||||
|
#width=600,
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
e.page.update()
|
||||||
754
app/src/pages/tabs/roman_calculator12.py
Normal file
754
app/src/pages/tabs/roman_calculator12.py
Normal file
@@ -0,0 +1,754 @@
|
|||||||
|
from designer import right_column, StepByStepDisplay
|
||||||
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
class Button_roman_calculator(ft.CupertinoButton):
|
||||||
|
def __init__(self, text: str = "Римский калькулятор"):
|
||||||
|
self.result_left = ft.Text(value="", size=30, width=300)
|
||||||
|
self.result_right = ft.Text(value="", size=30, width=300)
|
||||||
|
self.result_mult = ft.Text(value="", size=30, width=300)
|
||||||
|
self.result_div = ft.Text(value="", size=30, width=300)
|
||||||
|
|
||||||
|
self.decimal_left = ft.Text(value="", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_right = ft.Text(value="", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_mult = ft.Text(value="", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_div = ft.Text(value="", size=14, color="#B0B0B0", width=300)
|
||||||
|
|
||||||
|
# Поля ввода для вычитания
|
||||||
|
self.input_left_1 = None
|
||||||
|
self.input_left_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для сложения
|
||||||
|
self.input_right_1 = None
|
||||||
|
self.input_right_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
|
||||||
|
# Римские числа от 1 до 3999
|
||||||
|
self.roman_map = [
|
||||||
|
(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'),
|
||||||
|
(100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'),
|
||||||
|
(10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')
|
||||||
|
]
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
bgcolor="#00000000",
|
||||||
|
disabled_bgcolor="#3A1E51",
|
||||||
|
text=text,
|
||||||
|
color="white",
|
||||||
|
on_click=self.test_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def validate_roman(self, value: str) -> bool:
|
||||||
|
"""Проверяет, является ли строка корректным римским числом."""
|
||||||
|
if value is None:
|
||||||
|
return True
|
||||||
|
v = value.strip().upper()
|
||||||
|
if v == "":
|
||||||
|
return True
|
||||||
|
# Проверяем, что строка содержит только римские цифры
|
||||||
|
return all(c in 'IVXLCDM' for c in v)
|
||||||
|
|
||||||
|
def roman_to_int(self, roman: str) -> int:
|
||||||
|
"""Преобразует римское число в десятичное."""
|
||||||
|
roman = roman.strip().upper()
|
||||||
|
if not roman:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
roman_values = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
|
||||||
|
result = 0
|
||||||
|
prev_value = 0
|
||||||
|
|
||||||
|
for char in reversed(roman):
|
||||||
|
if char not in roman_values:
|
||||||
|
raise ValueError(f"Неверный символ '{char}' в римском числе")
|
||||||
|
value = roman_values[char]
|
||||||
|
if value < prev_value:
|
||||||
|
result -= value
|
||||||
|
else:
|
||||||
|
result += value
|
||||||
|
prev_value = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def int_to_roman(self, num: int) -> str:
|
||||||
|
"""Преобразует десятичное число в римское."""
|
||||||
|
if num <= 0:
|
||||||
|
return "Нет" # Римские числа не поддерживают 0 и отрицательные числа
|
||||||
|
if num >= 4000:
|
||||||
|
return "Превышает 3999"
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for value, numeral in self.roman_map:
|
||||||
|
count = num // value
|
||||||
|
if count:
|
||||||
|
result.append(numeral * count)
|
||||||
|
num -= value * count
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
def get_steps_text(self, roman: str) -> str:
|
||||||
|
"""Создает текстовое описание разбора римского числа."""
|
||||||
|
roman = roman.strip().upper()
|
||||||
|
if not roman:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
roman_values = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
|
||||||
|
steps = []
|
||||||
|
result = 0
|
||||||
|
prev_value = 0
|
||||||
|
|
||||||
|
for i, char in enumerate(reversed(roman)):
|
||||||
|
value = roman_values[char]
|
||||||
|
if value < prev_value:
|
||||||
|
steps.append(f"{char} = {value} (вычитаем, т.к. меньше следующего)")
|
||||||
|
result -= value
|
||||||
|
else:
|
||||||
|
steps.append(f"{char} = {value} (добавляем)")
|
||||||
|
result += value
|
||||||
|
prev_value = value
|
||||||
|
|
||||||
|
return "\n".join(reversed(steps)) + f"\n\nИтого: {result}"
|
||||||
|
|
||||||
|
def calculate_subtraction(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_left_1.value or "").strip().upper()
|
||||||
|
val2 = (self.input_left_2.value or "").strip().upper()
|
||||||
|
|
||||||
|
# Проверка корректности римских чисел
|
||||||
|
if not self.validate_roman(val1) or not self.validate_roman(val2):
|
||||||
|
self.result_left.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_left.value = ""
|
||||||
|
self.result_left.update()
|
||||||
|
self.decimal_left.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Преобразуем в десятичные числа
|
||||||
|
num1 = self.roman_to_int(val1) if val1 else 0
|
||||||
|
num2 = self.roman_to_int(val2) if val2 else 0
|
||||||
|
|
||||||
|
# Вычисляем результат
|
||||||
|
result = num1 - num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в римское число
|
||||||
|
roman_result = self.int_to_roman(result)
|
||||||
|
|
||||||
|
self.result_left.value = roman_result
|
||||||
|
self.decimal_left.value = f"({result}₁₀)" if result > 0 else f"({result}₁₀, не представимо)"
|
||||||
|
self.result_left.update()
|
||||||
|
self.decimal_left.update()
|
||||||
|
|
||||||
|
# Показываем пошаговое решение
|
||||||
|
self.show_steps_left_custom(val1, val2, num1, num2, result, "Вычитание")
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_left.value = "Ошибка"
|
||||||
|
self.decimal_left.value = str(ex)
|
||||||
|
self.result_left.update()
|
||||||
|
self.decimal_left.update()
|
||||||
|
|
||||||
|
def calculate_addition(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_right_1.value or "").strip().upper()
|
||||||
|
val2 = (self.input_right_2.value or "").strip().upper()
|
||||||
|
|
||||||
|
# Проверка корректности римских чисел
|
||||||
|
if not self.validate_roman(val1) or not self.validate_roman(val2):
|
||||||
|
self.result_right.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_right.value = ""
|
||||||
|
self.result_right.update()
|
||||||
|
self.decimal_right.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
num1 = self.roman_to_int(val1) if val1 else 0
|
||||||
|
num2 = self.roman_to_int(val2) if val2 else 0
|
||||||
|
|
||||||
|
# Вычисляем результат
|
||||||
|
result = num1 + num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в римское число
|
||||||
|
roman_result = self.int_to_roman(result)
|
||||||
|
|
||||||
|
self.result_right.value = roman_result
|
||||||
|
self.decimal_right.value = f"({result}₁₀)" if result <= 3999 else f"({result}₁₀, превышает 3999)"
|
||||||
|
self.result_right.update()
|
||||||
|
self.decimal_right.update()
|
||||||
|
|
||||||
|
# Показываем пошаговое решение
|
||||||
|
self.show_steps_right_custom(val1, val2, num1, num2, result, "Сложение")
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_right.value = "Ошибка"
|
||||||
|
self.decimal_right.value = str(ex)
|
||||||
|
self.result_right.update()
|
||||||
|
self.decimal_right.update()
|
||||||
|
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_mult_1.value or "").strip().upper()
|
||||||
|
val2 = (self.input_mult_2.value or "").strip().upper()
|
||||||
|
|
||||||
|
if not self.validate_roman(val1) or not self.validate_roman(val2):
|
||||||
|
self.result_mult.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_mult.value = ""
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
num1 = self.roman_to_int(val1) if val1 else 0
|
||||||
|
num2 = self.roman_to_int(val2) if val2 else 0
|
||||||
|
result = num1 * num2
|
||||||
|
|
||||||
|
roman_result = self.int_to_roman(result)
|
||||||
|
|
||||||
|
self.result_mult.value = roman_result
|
||||||
|
self.decimal_mult.value = f"({result}₁₀)" if result <= 3999 else f"({result}₁₀, превышает 3999)"
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
|
||||||
|
self.show_steps_mult_custom(val1, val2, num1, num2, result, "Умножение")
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_mult.value = "Ошибка"
|
||||||
|
self.decimal_mult.value = str(ex)
|
||||||
|
self.result_mult.update()
|
||||||
|
self.decimal_mult.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
val1 = (self.input_div_1.value or "").strip().upper()
|
||||||
|
val2 = (self.input_div_2.value or "").strip().upper()
|
||||||
|
|
||||||
|
if not self.validate_roman(val1) or not self.validate_roman(val2):
|
||||||
|
self.result_div.value = "Ошибка: неверный формат"
|
||||||
|
self.decimal_div.value = ""
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
num1 = self.roman_to_int(val1) if val1 else 0
|
||||||
|
num2 = self.roman_to_int(val2) if val2 else 0
|
||||||
|
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "Деление на 0!"
|
||||||
|
self.decimal_div.value = ""
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
quotient = num1 // num2
|
||||||
|
remainder = num1 % num2
|
||||||
|
|
||||||
|
roman_quotient = self.int_to_roman(quotient)
|
||||||
|
|
||||||
|
if remainder == 0:
|
||||||
|
self.result_div.value = roman_quotient
|
||||||
|
self.decimal_div.value = f"({quotient}₁₀)"
|
||||||
|
else:
|
||||||
|
roman_remainder = self.int_to_roman(remainder) if remainder <= 3999 else str(remainder)
|
||||||
|
self.result_div.value = roman_quotient
|
||||||
|
self.decimal_div.value = f"({quotient}₁₀, ост. {remainder}₁₀ = {roman_remainder})"
|
||||||
|
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
|
||||||
|
self.show_steps_div_custom(val1, val2, num1, num2, quotient, remainder, "Деление")
|
||||||
|
|
||||||
|
except ValueError as ex:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.decimal_div.value = str(ex)
|
||||||
|
self.result_div.update()
|
||||||
|
self.decimal_div.update()
|
||||||
|
|
||||||
|
def show_steps_left_custom(self, val1: str, val2: str, num1: int, num2: int, result: int, operation: str):
|
||||||
|
"""Отображает пошаговое решение для вычитания"""
|
||||||
|
self.steps_left.controls.clear()
|
||||||
|
|
||||||
|
self.steps_left.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 1: Преобразование первого числа
|
||||||
|
step1_content = []
|
||||||
|
step1_content.append(ft.Text(f"Шаг 1: Преобразование {val1} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step1_content.append(ft.Text(self.get_steps_text(val1), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_left.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step1_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 2: Преобразование второго числа
|
||||||
|
step2_content = []
|
||||||
|
step2_content.append(ft.Text(f"Шаг 2: Преобразование {val2} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step2_content.append(ft.Text(self.get_steps_text(val2), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_left.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step2_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 3: Вычитание в десятичной системе
|
||||||
|
step3_content = []
|
||||||
|
step3_content.append(ft.Text(f"Шаг 3: {operation} в десятичной системе", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step3_content.append(ft.Text(f"{num1} - {num2} = {result}", size=10, color="#E0E0E0"))
|
||||||
|
self.steps_left.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step3_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 4: Преобразование результата обратно в римское
|
||||||
|
step4_content = []
|
||||||
|
step4_content.append(ft.Text(f"Шаг 4: Преобразование {result} в римское число", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
roman_result = self.int_to_roman(result)
|
||||||
|
step4_content.append(ft.Text(f"Результат: {roman_result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
self.steps_left.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step4_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.steps_left.update()
|
||||||
|
|
||||||
|
def show_steps_right_custom(self, val1: str, val2: str, num1: int, num2: int, result: int, operation: str):
|
||||||
|
"""Отображает пошаговое решение для сложения"""
|
||||||
|
self.steps_right.controls.clear()
|
||||||
|
|
||||||
|
self.steps_right.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 1: Преобразование первого числа
|
||||||
|
step1_content = []
|
||||||
|
step1_content.append(ft.Text(f"Шаг 1: Преобразование {val1} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step1_content.append(ft.Text(self.get_steps_text(val1), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_right.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step1_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 2: Преобразование второго числа
|
||||||
|
step2_content = []
|
||||||
|
step2_content.append(ft.Text(f"Шаг 2: Преобразование {val2} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step2_content.append(ft.Text(self.get_steps_text(val2), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_right.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step2_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 3: Сложение в десятичной системе
|
||||||
|
step3_content = []
|
||||||
|
step3_content.append(ft.Text(f"Шаг 3: {operation} в десятичной системе", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step3_content.append(ft.Text(f"{num1} + {num2} = {result}", size=10, color="#E0E0E0"))
|
||||||
|
self.steps_right.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step3_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 4: Преобразование результата обратно в римское
|
||||||
|
step4_content = []
|
||||||
|
step4_content.append(ft.Text(f"Шаг 4: Преобразование {result} в римское число", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
roman_result = self.int_to_roman(result)
|
||||||
|
step4_content.append(ft.Text(f"Результат: {roman_result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
self.steps_right.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step4_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.steps_right.update()
|
||||||
|
|
||||||
|
def show_steps_mult_custom(self, val1: str, val2: str, num1: int, num2: int, result: int, operation: str):
|
||||||
|
"""Отображает пошаговое решение для умножения"""
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 1: Преобразование первого числа
|
||||||
|
step1_content = []
|
||||||
|
step1_content.append(ft.Text(f"Шаг 1: Преобразование {val1} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step1_content.append(ft.Text(self.get_steps_text(val1), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step1_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 2: Преобразование второго числа
|
||||||
|
step2_content = []
|
||||||
|
step2_content.append(ft.Text(f"Шаг 2: Преобразование {val2} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step2_content.append(ft.Text(self.get_steps_text(val2), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step2_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 3: Умножение в десятичной системе
|
||||||
|
step3_content = []
|
||||||
|
step3_content.append(ft.Text(f"Шаг 3: {operation} в десятичной системе", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step3_content.append(ft.Text(f"{num1} × {num2} = {result}", size=10, color="#E0E0E0"))
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step3_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 4: Преобразование результата обратно в римское
|
||||||
|
step4_content = []
|
||||||
|
step4_content.append(ft.Text(f"Шаг 4: Преобразование {result} в римское число", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
roman_result = self.int_to_roman(result)
|
||||||
|
step4_content.append(ft.Text(f"Результат: {roman_result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step4_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.steps_mult.update()
|
||||||
|
|
||||||
|
def show_steps_div_custom(self, val1: str, val2: str, num1: int, num2: int, quotient: int, remainder: int, operation: str):
|
||||||
|
"""Отображает пошаговое решение для деления"""
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 1: Преобразование первого числа
|
||||||
|
step1_content = []
|
||||||
|
step1_content.append(ft.Text(f"Шаг 1: Преобразование {val1} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step1_content.append(ft.Text(self.get_steps_text(val1), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step1_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 2: Преобразование второго числа
|
||||||
|
step2_content = []
|
||||||
|
step2_content.append(ft.Text(f"Шаг 2: Преобразование {val2} в десятичное", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step2_content.append(ft.Text(self.get_steps_text(val2), size=10, font_family="Courier New", color="#90EE90"))
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step2_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 3: Деление в десятичной системе
|
||||||
|
step3_content = []
|
||||||
|
step3_content.append(ft.Text(f"Шаг 3: {operation} в десятичной системе", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
if remainder == 0:
|
||||||
|
step3_content.append(ft.Text(f"{num1} ÷ {num2} = {quotient}", size=10, color="#E0E0E0"))
|
||||||
|
else:
|
||||||
|
step3_content.append(ft.Text(f"{num1} ÷ {num2} = {quotient} (остаток {remainder})", size=10, color="#E0E0E0"))
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step3_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Шаг 4: Преобразование результата обратно в римское
|
||||||
|
step4_content = []
|
||||||
|
step4_content.append(ft.Text(f"Шаг 4: Преобразование {quotient} в римское число", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
roman_quotient = self.int_to_roman(quotient)
|
||||||
|
if remainder == 0:
|
||||||
|
step4_content.append(ft.Text(f"Результат: {roman_quotient}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
else:
|
||||||
|
roman_remainder = self.int_to_roman(remainder) if remainder <= 3999 else str(remainder)
|
||||||
|
step4_content.append(ft.Text(f"Результат: {roman_quotient} (остаток {remainder}₁₀ = {roman_remainder})", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step4_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.steps_div.update()
|
||||||
|
|
||||||
|
def test_data(self, e):
|
||||||
|
# Создаем поля ввода с валидацией для римских чисел
|
||||||
|
self.input_left_1 = ft.TextField(
|
||||||
|
label="Римское число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: X (10) или MCMXC (1990)"
|
||||||
|
)
|
||||||
|
self.input_left_2 = ft.TextField(
|
||||||
|
label="Римское число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: V (5)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_right_1 = ft.TextField(
|
||||||
|
label="Римское число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: XII (12)"
|
||||||
|
)
|
||||||
|
self.input_right_2 = ft.TextField(
|
||||||
|
label="Римское число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: VIII (8)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_mult_1 = ft.TextField(
|
||||||
|
label="Римское число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: X (10)"
|
||||||
|
)
|
||||||
|
self.input_mult_2 = ft.TextField(
|
||||||
|
label="Римское число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: III (3)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_div_1 = ft.TextField(
|
||||||
|
label="Римское число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: C (100)"
|
||||||
|
)
|
||||||
|
self.input_div_2 = ft.TextField(
|
||||||
|
label="Римское число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$"),
|
||||||
|
hint_text="Например: X (10)"
|
||||||
|
)
|
||||||
|
|
||||||
|
right_column.controls = [ft.Container(
|
||||||
|
bgcolor="#55257D",
|
||||||
|
padding=ft.padding.all(10),
|
||||||
|
border_radius=10,
|
||||||
|
expand=True,
|
||||||
|
content=ft.Column(
|
||||||
|
scroll=ft.ScrollMode.AUTO,
|
||||||
|
expand=True,
|
||||||
|
controls=[
|
||||||
|
ft.Text("Римский калькулятор", size=20, color="white", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text("Вводите числа римскими цифрами (I, V, X, L, C, D, M). Диапазон: 1-3999.", size=11, color="#B0B0B0", italic=True),
|
||||||
|
ft.Text(f"Пример: XII = 12₁₀, MCMXC = 1990₁₀. Регистр не важен.", size=11, color="#B0B0B0"),
|
||||||
|
ft.Row(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
||||||
|
controls=[
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_left_1,
|
||||||
|
ft.Text("——— Вычитание", size=12, color="white"),
|
||||||
|
self.input_left_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_left,
|
||||||
|
self.decimal_left
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_subtraction),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_left,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_right_1,
|
||||||
|
ft.Text("—|— Сложение", size=12, color="white"),
|
||||||
|
self.input_right_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_right,
|
||||||
|
self.decimal_right
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_addition),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_right,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("× Умножение", size=12, color="white"),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_mult,
|
||||||
|
self.decimal_mult
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_mult,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("÷ Деление", size=12, color="white"),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_div,
|
||||||
|
self.decimal_div
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_div,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
e.page.update()
|
||||||
358
app/src/pages/tabs/roman_converter13.py
Normal file
358
app/src/pages/tabs/roman_converter13.py
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
from designer import right_column, StepByStepDisplay
|
||||||
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
class RomanConverter(ft.CupertinoButton):
|
||||||
|
def __init__(self, text: str = "Римские числа - конвертер"):
|
||||||
|
self.result_to_roman = ft.Text(value="I", size=30, width=300)
|
||||||
|
self.result_from_roman = ft.Text(value="1", size=30, width=300)
|
||||||
|
|
||||||
|
# Поля ввода
|
||||||
|
self.input_decimal = None
|
||||||
|
self.input_roman = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_to_roman = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
self.steps_from_roman = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True)
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
bgcolor="#00000000",
|
||||||
|
disabled_bgcolor="#3A1E51",
|
||||||
|
text=text,
|
||||||
|
color="white",
|
||||||
|
on_click=self.test_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def decimal_to_roman(self, num):
|
||||||
|
"""Преобразует десятичное число в римское (1-3999)"""
|
||||||
|
if num <= 0 or num > 3999:
|
||||||
|
return None
|
||||||
|
|
||||||
|
val = [
|
||||||
|
1000, 900, 500, 400,
|
||||||
|
100, 90, 50, 40,
|
||||||
|
10, 9, 5, 4,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
syms = [
|
||||||
|
"M", "CM", "D", "CD",
|
||||||
|
"C", "XC", "L", "XL",
|
||||||
|
"X", "IX", "V", "IV",
|
||||||
|
"I"
|
||||||
|
]
|
||||||
|
roman_num = ''
|
||||||
|
i = 0
|
||||||
|
while num > 0:
|
||||||
|
for _ in range(num // val[i]):
|
||||||
|
roman_num += syms[i]
|
||||||
|
num -= val[i]
|
||||||
|
i += 1
|
||||||
|
return roman_num
|
||||||
|
|
||||||
|
def decimal_to_roman_with_steps(self, num):
|
||||||
|
"""Преобразует десятичное число в римское с пошаговым объяснением"""
|
||||||
|
if num <= 0 or num > 3999:
|
||||||
|
return None, []
|
||||||
|
|
||||||
|
val = [
|
||||||
|
1000, 900, 500, 400,
|
||||||
|
100, 90, 50, 40,
|
||||||
|
10, 9, 5, 4,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
syms = [
|
||||||
|
"M", "CM", "D", "CD",
|
||||||
|
"C", "XC", "L", "XL",
|
||||||
|
"X", "IX", "V", "IV",
|
||||||
|
"I"
|
||||||
|
]
|
||||||
|
|
||||||
|
steps = []
|
||||||
|
roman_num = ''
|
||||||
|
remaining = num
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
steps.append(f"Начальное число: {num}")
|
||||||
|
steps.append("")
|
||||||
|
|
||||||
|
while remaining > 0:
|
||||||
|
count = remaining // val[i]
|
||||||
|
if count > 0:
|
||||||
|
added = syms[i] * count
|
||||||
|
roman_num += added
|
||||||
|
steps.append(f"{remaining} ÷ {val[i]} = {count}")
|
||||||
|
steps.append(f"Добавляем: {added} (римское обозначение {val[i]})")
|
||||||
|
remaining -= val[i] * count
|
||||||
|
steps.append(f"Результат: {roman_num}")
|
||||||
|
steps.append(f"Осталось: {remaining}")
|
||||||
|
steps.append("")
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
steps.append(f"Итоговое римское число: {roman_num}")
|
||||||
|
return roman_num, steps
|
||||||
|
|
||||||
|
def roman_to_decimal(self, s):
|
||||||
|
"""Преобразует римское число в десятичное"""
|
||||||
|
roman = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
|
||||||
|
total = 0
|
||||||
|
prev_value = 0
|
||||||
|
|
||||||
|
for char in reversed(s.upper()):
|
||||||
|
if char not in roman:
|
||||||
|
return None
|
||||||
|
value = roman[char]
|
||||||
|
if value < prev_value:
|
||||||
|
total -= value
|
||||||
|
else:
|
||||||
|
total += value
|
||||||
|
prev_value = value
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def roman_to_decimal_with_steps(self, s):
|
||||||
|
"""Преобразует римское число в десятичное с пошаговым объяснением"""
|
||||||
|
roman = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
|
||||||
|
s = s.upper()
|
||||||
|
|
||||||
|
# Проверка валидности
|
||||||
|
for char in s:
|
||||||
|
if char not in roman:
|
||||||
|
return None, [f"Ошибка: '{char}' не является римской цифрой"]
|
||||||
|
|
||||||
|
steps = []
|
||||||
|
total = 0
|
||||||
|
prev_value = 0
|
||||||
|
|
||||||
|
steps.append(f"Римское число: {s}")
|
||||||
|
steps.append("Читаем справа налево:")
|
||||||
|
steps.append("")
|
||||||
|
|
||||||
|
operations = []
|
||||||
|
for char in reversed(s):
|
||||||
|
value = roman[char]
|
||||||
|
if value < prev_value:
|
||||||
|
operations.append(f"- {char} ({value})")
|
||||||
|
total -= value
|
||||||
|
else:
|
||||||
|
operations.append(f"+ {char} ({value})")
|
||||||
|
total += value
|
||||||
|
prev_value = value
|
||||||
|
|
||||||
|
# Показываем в обратном порядке (как читается)
|
||||||
|
for op in reversed(operations):
|
||||||
|
steps.append(op)
|
||||||
|
|
||||||
|
steps.append("")
|
||||||
|
steps.append(f"Итоговое десятичное число: {total}")
|
||||||
|
return total, steps
|
||||||
|
|
||||||
|
def convert_to_roman(self, e):
|
||||||
|
"""Конвертирует из десятичной в римскую"""
|
||||||
|
try:
|
||||||
|
num = int(self.input_decimal.value or 0)
|
||||||
|
|
||||||
|
if num <= 0 or num > 3999:
|
||||||
|
self.result_to_roman.value = "Ошибка: 1-3999"
|
||||||
|
self.result_to_roman.update()
|
||||||
|
self.steps_to_roman.controls.clear()
|
||||||
|
self.steps_to_roman.controls.append(
|
||||||
|
ft.Text("Римские числа поддерживают только диапазон от 1 до 3999",
|
||||||
|
size=14, color="#FF6B6B")
|
||||||
|
)
|
||||||
|
self.steps_to_roman.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
roman, steps = self.decimal_to_roman_with_steps(num)
|
||||||
|
self.result_to_roman.value = roman
|
||||||
|
self.result_to_roman.update()
|
||||||
|
|
||||||
|
# Показываем шаги
|
||||||
|
self.steps_to_roman.controls.clear()
|
||||||
|
for step in steps:
|
||||||
|
if step:
|
||||||
|
self.steps_to_roman.controls.append(
|
||||||
|
ft.Text(step, size=14, color="white")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.steps_to_roman.controls.append(ft.Divider(color="#3A1E51"))
|
||||||
|
self.steps_to_roman.update()
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
self.result_to_roman.value = "Ошибка"
|
||||||
|
self.result_to_roman.update()
|
||||||
|
|
||||||
|
def convert_from_roman(self, e):
|
||||||
|
"""Конвертирует из римской в десятичную"""
|
||||||
|
try:
|
||||||
|
roman_num = (self.input_roman.value or "I").strip().upper()
|
||||||
|
|
||||||
|
if not roman_num:
|
||||||
|
roman_num = "I"
|
||||||
|
|
||||||
|
result, steps = self.roman_to_decimal_with_steps(roman_num)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.result_from_roman.value = "Ошибка"
|
||||||
|
self.result_from_roman.update()
|
||||||
|
self.steps_from_roman.controls.clear()
|
||||||
|
for step in steps:
|
||||||
|
self.steps_from_roman.controls.append(
|
||||||
|
ft.Text(step, size=14, color="#FF6B6B")
|
||||||
|
)
|
||||||
|
self.steps_from_roman.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.result_from_roman.value = str(result)
|
||||||
|
self.result_from_roman.update()
|
||||||
|
|
||||||
|
# Показываем шаги
|
||||||
|
self.steps_from_roman.controls.clear()
|
||||||
|
for step in steps:
|
||||||
|
if step:
|
||||||
|
self.steps_from_roman.controls.append(
|
||||||
|
ft.Text(step, size=14, color="white")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.steps_from_roman.controls.append(ft.Divider(color="#3A1E51"))
|
||||||
|
self.steps_from_roman.update()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_from_roman.value = "Ошибка"
|
||||||
|
self.result_from_roman.update()
|
||||||
|
|
||||||
|
def test_data(self, e):
|
||||||
|
# Создаем поля ввода
|
||||||
|
self.input_decimal = ft.TextField(
|
||||||
|
label="Десятичное число",
|
||||||
|
width=300,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="1",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9]*$"),
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
hint_text="Например: 1984"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_roman = ft.TextField(
|
||||||
|
label="Римское число",
|
||||||
|
width=300,
|
||||||
|
text_align=ft.TextAlign.CENTER,
|
||||||
|
value="I",
|
||||||
|
hint_text="Например: MCMLXXXIV",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[IVXLCDMivxlcdm]*$")
|
||||||
|
)
|
||||||
|
|
||||||
|
right_column.controls = [ft.Container(
|
||||||
|
bgcolor="#55257D",
|
||||||
|
padding=ft.padding.all(10),
|
||||||
|
border_radius=10,
|
||||||
|
expand=True,
|
||||||
|
content=ft.Column(
|
||||||
|
scroll=ft.ScrollMode.AUTO,
|
||||||
|
expand=True,
|
||||||
|
controls=[
|
||||||
|
ft.Text("Конвертер римских чисел", size=20, color="white", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text("Поддерживаются числа от 1 до 3999", size=11, color="#B0B0B0", italic=True),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Row(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
||||||
|
spacing=20,
|
||||||
|
controls=[
|
||||||
|
# Конвертация из десятичной в римскую
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
ft.Text("Десятичная → Римская", size=16, color="white", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Container(height=10),
|
||||||
|
self.input_decimal,
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Icon(ft.Icons.ARROW_DOWNWARD, color="white", size=30),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_to_roman,
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
width=300,
|
||||||
|
),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.CupertinoButton("Конвертировать", on_click=self.convert_to_roman),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Text("Пошаговое объяснение:", size=14, color="#B0B0B0"),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_to_roman,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
# Разделитель
|
||||||
|
ft.VerticalDivider(width=1, color="#3A1E51"),
|
||||||
|
|
||||||
|
# Конвертация из римской в десятичную
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
ft.Text("Римская → Десятичная", size=16, color="white", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Container(height=10),
|
||||||
|
self.input_roman,
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Icon(ft.Icons.ARROW_DOWNWARD, color="white", size=30),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_from_roman,
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
width=300,
|
||||||
|
),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.CupertinoButton("Конвертировать", on_click=self.convert_from_roman),
|
||||||
|
ft.Container(height=10),
|
||||||
|
ft.Text("Пошаговое объяснение:", size=14, color="#B0B0B0"),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_from_roman,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Container(height=20),
|
||||||
|
ft.Text("Справка по римским цифрам:", size=14, color="#B0B0B0", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
ft.Text("I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000",
|
||||||
|
size=12, color="white"),
|
||||||
|
ft.Text("Правило вычитания: если меньшая цифра стоит перед большей, она вычитается",
|
||||||
|
size=12, color="white"),
|
||||||
|
ft.Text("Примеры: IV = 4, IX = 9, XL = 40, XC = 90, CD = 400, CM = 900",
|
||||||
|
size=12, color="white"),
|
||||||
|
]),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
e.page.update()
|
||||||
497
app/src/pages/tabs/sedecim_calculeator8.py
Normal file
497
app/src/pages/tabs/sedecim_calculeator8.py
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
from designer import right_column, StepByStepDisplay
|
||||||
|
import flet as ft
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Добавляем путь к модулю step_by_step_solver
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
class Button_sedecim_calculator(ft.CupertinoButton):
|
||||||
|
def __init__(self, text: str = "Шеснадцатиричный калькулятор"):
|
||||||
|
self.result_left = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_right = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_mult = ft.Text(value="0", size=30, width=300)
|
||||||
|
self.result_div = ft.Text(value="0", size=30, width=300)
|
||||||
|
|
||||||
|
self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_mult = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
self.decimal_div = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300)
|
||||||
|
|
||||||
|
# Поля ввода для вычитания
|
||||||
|
self.input_left_1 = None
|
||||||
|
self.input_left_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для сложения
|
||||||
|
self.input_right_1 = None
|
||||||
|
self.input_right_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для умножения
|
||||||
|
self.input_mult_1 = None
|
||||||
|
self.input_mult_2 = None
|
||||||
|
|
||||||
|
# Поля ввода для деления
|
||||||
|
self.input_div_1 = None
|
||||||
|
self.input_div_2 = None
|
||||||
|
|
||||||
|
# Контейнеры для пошагового решения
|
||||||
|
self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_mult = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
self.steps_div = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5)
|
||||||
|
|
||||||
|
# Солвер для шестнадцатеричной системы
|
||||||
|
self.solver = StepByStepSolver(base=16)
|
||||||
|
|
||||||
|
# Количество бит для представления (по умолчанию 8)
|
||||||
|
self.bit_width = 16
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
bgcolor="#00000000",
|
||||||
|
disabled_bgcolor="#3A1E51",
|
||||||
|
text=text,
|
||||||
|
color="white",
|
||||||
|
on_click=self.test_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def hex_to_decimal(self,hex_str):
|
||||||
|
"""
|
||||||
|
Преобразование шестнадцатеричного числа в десятичное
|
||||||
|
Поддерживает отрицательные числа и дроби
|
||||||
|
"""
|
||||||
|
if not hex_str:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Обработка знака
|
||||||
|
sign = -1 if hex_str[0] == '-' else 1
|
||||||
|
if hex_str[0] in ['-', '+']:
|
||||||
|
hex_str = hex_str[1:]
|
||||||
|
|
||||||
|
# Разделение на целую и дробную части
|
||||||
|
if '.' in hex_str:
|
||||||
|
integer_part, fractional_part = hex_str.split('.')
|
||||||
|
else:
|
||||||
|
integer_part, fractional_part = hex_str, ""
|
||||||
|
|
||||||
|
# Преобразование целой части
|
||||||
|
decimal_value = int(integer_part, 16) if integer_part else 0
|
||||||
|
|
||||||
|
# Преобразование дробной части
|
||||||
|
if fractional_part:
|
||||||
|
for i, digit in enumerate(fractional_part, 1):
|
||||||
|
decimal_value += int(digit, 16) / (16 ** i)
|
||||||
|
|
||||||
|
return decimal_value * sign
|
||||||
|
|
||||||
|
def decimal_to_hex(self,decimal_num, precision=6):
|
||||||
|
"""
|
||||||
|
Перевод десятичного числа в шестнадцатеричное
|
||||||
|
"""
|
||||||
|
if decimal_num == 0:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
# Обработка знака
|
||||||
|
sign = "-" if decimal_num < 0 else ""
|
||||||
|
decimal_num = abs(decimal_num)
|
||||||
|
|
||||||
|
# Разделение на целую и дробную части
|
||||||
|
integer_part = int(decimal_num)
|
||||||
|
fractional_part = decimal_num - integer_part
|
||||||
|
|
||||||
|
# Перевод целой части
|
||||||
|
hex_integer = hex(integer_part)[2:].upper()
|
||||||
|
|
||||||
|
# Перевод дробной части
|
||||||
|
hex_fractional = ""
|
||||||
|
if fractional_part > 0:
|
||||||
|
hex_fractional = "."
|
||||||
|
temp = fractional_part
|
||||||
|
for _ in range(precision):
|
||||||
|
if temp == 0:
|
||||||
|
break
|
||||||
|
temp *= 16
|
||||||
|
digit = int(temp)
|
||||||
|
hex_fractional += hex(digit)[2:].upper()
|
||||||
|
temp -= digit
|
||||||
|
|
||||||
|
return sign + hex_integer + hex_fractional
|
||||||
|
|
||||||
|
# В классе калькулятора обновите функции расчета:
|
||||||
|
def calculate_addition(self, e):
|
||||||
|
try:
|
||||||
|
# Получаем и преобразуем числа
|
||||||
|
num1 = self.hex_to_decimal(self.input_right_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_right_2.value.upper())
|
||||||
|
|
||||||
|
# Вычисляем сумму
|
||||||
|
result_decimal = num1 + num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в hex
|
||||||
|
result_hex = self.decimal_to_hex(result_decimal)
|
||||||
|
|
||||||
|
# Обновляем результаты
|
||||||
|
self.result_right.value = f"HEX: {result_hex}"
|
||||||
|
self.decimal_right.value = f"DEC: {result_decimal}"
|
||||||
|
|
||||||
|
# Получаем пошаговое решение (только для целых чисел)
|
||||||
|
if '.' not in self.input_right_1.value and '.' not in self.input_right_2.value:
|
||||||
|
hex_val1 = self.input_right_1.value.upper().lstrip('-+') if self.input_right_1.value else "0"
|
||||||
|
hex_val2 = self.input_right_2.value.upper().lstrip('-+') if self.input_right_2.value else "0"
|
||||||
|
self.show_steps_right(hex_val1, hex_val2, Operation.ADDITION)
|
||||||
|
else:
|
||||||
|
self.steps_right.controls.clear()
|
||||||
|
self.steps_right.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_right.update()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_right.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_right.value = "DEC: ошибка"
|
||||||
|
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def calculate_subtraction(self, e):
|
||||||
|
try:
|
||||||
|
# Получаем и преобразуем числа
|
||||||
|
num1 = self.hex_to_decimal(self.input_left_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_left_2.value.upper())
|
||||||
|
|
||||||
|
# Вычисляем разность
|
||||||
|
result_decimal = num1 - num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в hex
|
||||||
|
result_hex = self.decimal_to_hex(result_decimal)
|
||||||
|
|
||||||
|
# Обновляем результаты
|
||||||
|
self.result_left.value = f"HEX: {result_hex}"
|
||||||
|
self.decimal_left.value = f"DEC: {result_decimal}"
|
||||||
|
|
||||||
|
# Получаем пошаговое решение (только для целых чисел)
|
||||||
|
if '.' not in self.input_left_1.value and '.' not in self.input_left_2.value:
|
||||||
|
hex_val1 = self.input_left_1.value.upper().lstrip('-+') if self.input_left_1.value else "0"
|
||||||
|
hex_val2 = self.input_left_2.value.upper().lstrip('-+') if self.input_left_2.value else "0"
|
||||||
|
self.show_steps_left(hex_val1, hex_val2, Operation.SUBTRACTION)
|
||||||
|
else:
|
||||||
|
self.steps_left.controls.clear()
|
||||||
|
self.steps_left.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_left.update()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_left.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_left.value = "DEC: ошибка"
|
||||||
|
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
# Получаем и преобразуем числа
|
||||||
|
num1 = self.hex_to_decimal(self.input_mult_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_mult_2.value.upper())
|
||||||
|
|
||||||
|
# Вычисляем произведение
|
||||||
|
result_decimal = num1 * num2
|
||||||
|
|
||||||
|
# Преобразуем обратно в hex
|
||||||
|
result_hex = self.decimal_to_hex(result_decimal)
|
||||||
|
|
||||||
|
# Обновляем результаты
|
||||||
|
self.result_mult.value = f"HEX: {result_hex}"
|
||||||
|
self.decimal_mult.value = f"DEC: {result_decimal}"
|
||||||
|
|
||||||
|
# Получаем пошаговое решение (только для целых чисел)
|
||||||
|
if '.' not in self.input_mult_1.value and '.' not in self.input_mult_2.value:
|
||||||
|
hex_val1 = self.input_mult_1.value.upper().lstrip('-+') if self.input_mult_1.value else "0"
|
||||||
|
hex_val2 = self.input_mult_2.value.upper().lstrip('-+') if self.input_mult_2.value else "0"
|
||||||
|
self.show_steps_mult(hex_val1, hex_val2, Operation.MULTIPLICATION)
|
||||||
|
else:
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_mult.update()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_mult.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_mult.value = "DEC: ошибка"
|
||||||
|
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
# Получаем и преобразуем числа
|
||||||
|
num1 = self.hex_to_decimal(self.input_div_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_div_2.value.upper())
|
||||||
|
|
||||||
|
# Проверка деления на ноль
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "Ошибка"
|
||||||
|
self.decimal_div.value = "DEC: Деление на ноль!"
|
||||||
|
self.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Вычисляем частное (целочисленное деление)
|
||||||
|
result_decimal = int(num1 // num2)
|
||||||
|
|
||||||
|
# Преобразуем обратно в hex
|
||||||
|
result_hex = self.decimal_to_hex(result_decimal)
|
||||||
|
|
||||||
|
# Обновляем результаты
|
||||||
|
self.result_div.value = f"HEX: {result_hex}"
|
||||||
|
self.decimal_div.value = f"DEC: {result_decimal}"
|
||||||
|
|
||||||
|
# Получаем пошаговое решение (только для целых чисел)
|
||||||
|
if '.' not in self.input_div_1.value and '.' not in self.input_div_2.value:
|
||||||
|
hex_val1 = self.input_div_1.value.upper().lstrip('-+') if self.input_div_1.value else "0"
|
||||||
|
hex_val2 = self.input_div_2.value.upper().lstrip('-+') if self.input_div_2.value else "0"
|
||||||
|
self.show_steps_div(hex_val1, hex_val2, Operation.DIVISION)
|
||||||
|
else:
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_div.update()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_div.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_div.value = "DEC: ошибка"
|
||||||
|
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def show_steps_left(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для левой колонки"""
|
||||||
|
if '.' in num1 or '.' in num2:
|
||||||
|
StepByStepDisplay.show_message(self.steps_left,
|
||||||
|
"Пошаговое решение доступно только для целых чисел", "#FFD700")
|
||||||
|
else:
|
||||||
|
StepByStepDisplay.show_steps(self.steps_left, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_right(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для правой колонки"""
|
||||||
|
if '.' in num1 or '.' in num2:
|
||||||
|
StepByStepDisplay.show_message(self.steps_right,
|
||||||
|
"Пошаговое решение доступно только для целых чисел", "#FFD700")
|
||||||
|
else:
|
||||||
|
StepByStepDisplay.show_steps(self.steps_right, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для колонки умножения"""
|
||||||
|
if '.' in num1 or '.' in num2:
|
||||||
|
StepByStepDisplay.show_message(self.steps_mult,
|
||||||
|
"Пошаговое решение доступно только для целых чисел", "#FFD700")
|
||||||
|
else:
|
||||||
|
StepByStepDisplay.show_steps(self.steps_mult, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для колонки деления"""
|
||||||
|
if '.' in num1 or '.' in num2:
|
||||||
|
StepByStepDisplay.show_message(self.steps_div,
|
||||||
|
"Пошаговое решение доступно только для целых чисел", "#FFD700")
|
||||||
|
else:
|
||||||
|
StepByStepDisplay.show_steps(self.steps_div, self.solver, num1, num2, operation)
|
||||||
|
|
||||||
|
def test_data(self, e):
|
||||||
|
# Создаем поля ввода с валидацией для шестнадцатеричных чисел
|
||||||
|
self.input_left_1 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"), # только 0-9, A-F, точка и минус
|
||||||
|
hint_text="Например: A (10) или -A (-10) или 1A.C (26.75)"
|
||||||
|
)
|
||||||
|
self.input_left_2 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: 5 (5) или F.8 (15.5)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_right_1 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: A (10) или FF (255)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_right_2 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: 5 (5) или 1F (31)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_mult_1 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: A (10) или 1A (26)"
|
||||||
|
)
|
||||||
|
self.input_mult_2 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: 5 (5) или B (11)"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_div_1 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 1",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: 64 (100) или FF (255)"
|
||||||
|
)
|
||||||
|
self.input_div_2 = ft.TextField(
|
||||||
|
label="Шестнадцатеричное число 2",
|
||||||
|
width=250,
|
||||||
|
text_align=ft.TextAlign.RIGHT,
|
||||||
|
value="0",
|
||||||
|
input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"),
|
||||||
|
hint_text="Например: 2 (2) или A (10)"
|
||||||
|
)
|
||||||
|
|
||||||
|
right_column.controls = [ft.Container(
|
||||||
|
bgcolor="#55257D",
|
||||||
|
padding=ft.padding.all(10),
|
||||||
|
border_radius=10,
|
||||||
|
expand=True,
|
||||||
|
content=ft.Column(
|
||||||
|
scroll=ft.ScrollMode.AUTO,
|
||||||
|
expand=True,
|
||||||
|
controls=[
|
||||||
|
ft.Text("Шестнадцатеричный калькулятор", size=20, color="white", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text("Поддерживает отрицательные числа и дроби", size=11, color="#B0B0B0", italic=True),
|
||||||
|
ft.Text("Используйте A-F для цифр 10-15, точку для дробей, минус для отрицательных", size=11, color="#B0B0B0"),
|
||||||
|
ft.Row(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
||||||
|
controls=[
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_left_1,
|
||||||
|
ft.Text("——— Вычитание", size=12, color="white"),
|
||||||
|
self.input_left_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_left,
|
||||||
|
self.decimal_left
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_subtraction),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_left,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_right_1,
|
||||||
|
ft.Text("—|— Сложение", size=12, color="white"),
|
||||||
|
self.input_right_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_right,
|
||||||
|
self.decimal_right
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_addition),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_right,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_mult_1,
|
||||||
|
ft.Text("——— Умножение", size=12, color="white"),
|
||||||
|
self.input_mult_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_mult,
|
||||||
|
self.decimal_mult
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_multiplication),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_mult,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
controls=[
|
||||||
|
self.input_div_1,
|
||||||
|
ft.Text("——— Деление", size=12, color="white"),
|
||||||
|
self.input_div_2,
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column([
|
||||||
|
self.result_div,
|
||||||
|
self.decimal_div
|
||||||
|
], horizontal_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
bgcolor="#3A1E51",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
),
|
||||||
|
ft.CupertinoButton("=", on_click=self.calculate_division),
|
||||||
|
ft.Container(
|
||||||
|
expand=True,
|
||||||
|
content=self.steps_div,
|
||||||
|
bgcolor="#1A0A2A",
|
||||||
|
padding=10,
|
||||||
|
border_radius=5,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
e.page.update()
|
||||||
786
app/src/step_by_step_solver.py
Normal file
786
app/src/step_by_step_solver.py
Normal file
@@ -0,0 +1,786 @@
|
|||||||
|
"""
|
||||||
|
Модуль для пошагового решения задач с системами счисления (2, 8, 16, n-ричные)
|
||||||
|
Алгоритм работает аналогично Photomath - показывает каждый шаг решения
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Tuple, Dict
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Operation(Enum):
|
||||||
|
"""Типы операций"""
|
||||||
|
ADDITION = "сложение"
|
||||||
|
SUBTRACTION = "вычитание"
|
||||||
|
MULTIPLICATION = "умножение"
|
||||||
|
DIVISION = "деление"
|
||||||
|
|
||||||
|
|
||||||
|
class NumberSystem(Enum):
|
||||||
|
"""Поддерживаемые системы счисления"""
|
||||||
|
BINARY = 2
|
||||||
|
OCTAL = 8
|
||||||
|
DECIMAL = 10
|
||||||
|
HEXADECIMAL = 16
|
||||||
|
|
||||||
|
|
||||||
|
class Step:
|
||||||
|
"""Класс для представления одного шага решения"""
|
||||||
|
|
||||||
|
def __init__(self, title: str, description: str, visual: str = "", result: str = ""):
|
||||||
|
self.title = title # Название шага
|
||||||
|
self.description = description # Описание что делается
|
||||||
|
self.visual = visual # Визуальное представление
|
||||||
|
self.result = result # Результат этого шага
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
output = f"\n{'='*60}\n"
|
||||||
|
output += f"ШАГ: {self.title}\n"
|
||||||
|
output += f"{'='*60}\n"
|
||||||
|
output += f"{self.description}\n"
|
||||||
|
if self.visual:
|
||||||
|
output += f"\n{self.visual}\n"
|
||||||
|
if self.result:
|
||||||
|
output += f"\n➤ Результат: {self.result}\n"
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class StepByStepSolver:
|
||||||
|
"""
|
||||||
|
Класс для пошагового решения задач с системами счисления
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, base: int = 10, digits: str = None):
|
||||||
|
"""
|
||||||
|
Инициализация решателя
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base: основание системы счисления (2, 8, 10, 16 или любое n)
|
||||||
|
digits: строка с символами для представления цифр (по умолчанию 0-9A-Z)
|
||||||
|
"""
|
||||||
|
self.base = base
|
||||||
|
self.steps: List[Step] = []
|
||||||
|
# Используем пользовательский набор символов или стандартный
|
||||||
|
if digits is not None:
|
||||||
|
self.digits = digits
|
||||||
|
else:
|
||||||
|
self.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
def clear_steps(self):
|
||||||
|
"""Очистить все шаги решения"""
|
||||||
|
self.steps = []
|
||||||
|
|
||||||
|
def get_system_name(self) -> str:
|
||||||
|
"""Получить название системы счисления"""
|
||||||
|
names = {
|
||||||
|
2: "двоичная",
|
||||||
|
8: "восьмеричная",
|
||||||
|
10: "десятичная",
|
||||||
|
16: "шестнадцатеричная"
|
||||||
|
}
|
||||||
|
return names.get(self.base, f"{self.base}-ричная")
|
||||||
|
|
||||||
|
def validate_number(self, number: str) -> bool:
|
||||||
|
"""
|
||||||
|
Проверка корректности числа в данной системе счисления
|
||||||
|
|
||||||
|
Args:
|
||||||
|
number: строка с числом
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если число корректно, False иначе
|
||||||
|
"""
|
||||||
|
number = number.upper().replace('-', '')
|
||||||
|
valid_digits = self.digits[:self.base]
|
||||||
|
return all(c in valid_digits for c in number)
|
||||||
|
|
||||||
|
def to_decimal(self, number: str) -> int:
|
||||||
|
"""
|
||||||
|
Преобразование числа из n-ричной системы в десятичную
|
||||||
|
|
||||||
|
Args:
|
||||||
|
number: число в n-ричной системе
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
число в десятичной системе
|
||||||
|
"""
|
||||||
|
is_negative = number.startswith('-')
|
||||||
|
number = number.replace('-', '').upper()
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
for i, digit in enumerate(reversed(number)):
|
||||||
|
digit_value = self.digits.index(digit)
|
||||||
|
result += digit_value * (self.base ** i)
|
||||||
|
|
||||||
|
return -result if is_negative else result
|
||||||
|
|
||||||
|
def from_decimal(self, number: int) -> str:
|
||||||
|
"""
|
||||||
|
Преобразование из десятичной системы в n-ричную
|
||||||
|
|
||||||
|
Args:
|
||||||
|
number: число в десятичной системе
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
число в n-ричной системе
|
||||||
|
"""
|
||||||
|
if number == 0:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
is_negative = number < 0
|
||||||
|
number = abs(number)
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
while number > 0:
|
||||||
|
result = self.digits[number % self.base] + result
|
||||||
|
number //= self.base
|
||||||
|
|
||||||
|
return ("-" + result) if is_negative else result
|
||||||
|
|
||||||
|
def convert_with_steps(self, number: str, target_base: int) -> List[Step]:
|
||||||
|
"""
|
||||||
|
Преобразование числа в другую систему счисления с пошаговым объяснением
|
||||||
|
|
||||||
|
Args:
|
||||||
|
number: исходное число
|
||||||
|
target_base: целевая система счисления
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
список шагов решения
|
||||||
|
"""
|
||||||
|
steps = []
|
||||||
|
is_negative = number.startswith('-')
|
||||||
|
number = number.replace('-', '').upper()
|
||||||
|
|
||||||
|
# Шаг 1: Исходные данные
|
||||||
|
step1 = Step(
|
||||||
|
"Исходные данные",
|
||||||
|
f"Необходимо преобразовать число {number} из {self.get_system_name()} " +
|
||||||
|
f"({self.base}-ричной) системы в {target_base}-ричную систему.\n" +
|
||||||
|
f"Это как перевод с одного языка на другой - число остаётся тем же, меняется только способ записи!",
|
||||||
|
f"Исходное число: {'-' if is_negative else ''}{number}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step1)
|
||||||
|
|
||||||
|
# Шаг 2: Перевод в десятичную систему (если нужно)
|
||||||
|
if self.base != 10:
|
||||||
|
decimal_value = 0
|
||||||
|
calculation = []
|
||||||
|
position_table = []
|
||||||
|
|
||||||
|
# Создаём таблицу разрядов
|
||||||
|
digits_list = list(reversed(number))
|
||||||
|
position_table.append("┌─" + "─┬─".join(["─"*5 for _ in digits_list]) + "─┐")
|
||||||
|
position_table.append("│ " + " │ ".join([f"Поз {i}" for i in range(len(digits_list))]) + " │")
|
||||||
|
position_table.append("├─" + "─┼─".join(["─"*5 for _ in digits_list]) + "─┤")
|
||||||
|
position_table.append("│ " + " │ ".join([f"{self.base}^{i}={self.base**i}".center(5) for i in range(len(digits_list))]) + " │")
|
||||||
|
position_table.append("├─" + "─┼─".join(["─"*5 for _ in digits_list]) + "─┤")
|
||||||
|
position_table.append("│ " + " │ ".join([f"{d}".center(5) for d in digits_list]) + " │")
|
||||||
|
position_table.append("└─" + "─┴─".join(["─"*5 for _ in digits_list]) + "─┘")
|
||||||
|
|
||||||
|
for i, digit in enumerate(reversed(number)):
|
||||||
|
digit_value = self.digits.index(digit)
|
||||||
|
power = self.base ** i
|
||||||
|
term_value = digit_value * power
|
||||||
|
decimal_value += term_value
|
||||||
|
calculation.append(f"Позиция {i}: {digit} × {self.base}^{i} = {digit_value} × {power} = {term_value}")
|
||||||
|
|
||||||
|
visual = "\n".join(position_table)
|
||||||
|
visual += "\n\nПОШАГОВЫЙ РАСЧЁТ:\n"
|
||||||
|
visual += "Каждая позиция (справа налево) имеет свой \"вес\" - степень основания.\n"
|
||||||
|
visual += f"В {self.base}-ричной системе веса позиций: 1, {self.base}, {self.base**2}, {self.base**3}...\n\n"
|
||||||
|
visual += "\n".join(calculation)
|
||||||
|
|
||||||
|
sum_parts = [str(self.digits.index(digit) * (self.base ** i)) for i, digit in enumerate(reversed(number))]
|
||||||
|
visual += f"\n\nИТОГОВАЯ СУММА: {' + '.join(sum_parts)} = {decimal_value}"
|
||||||
|
|
||||||
|
step2 = Step(
|
||||||
|
"Перевод в десятичную систему",
|
||||||
|
f"Представьте, что каждая позиция - это \"кармашек\" с определённой ценностью.\n" +
|
||||||
|
f"Самый правый \"стоит\" 1, следующий - {self.base}, потом {self.base**2} и так далее.\n" +
|
||||||
|
f"Мы умножаем цифру на \"цену\" её позиции и складываем всё вместе.",
|
||||||
|
visual,
|
||||||
|
f"{decimal_value}₍₁₀₎"
|
||||||
|
)
|
||||||
|
steps.append(step2)
|
||||||
|
else:
|
||||||
|
decimal_value = int(number)
|
||||||
|
|
||||||
|
# Шаг 3: Перевод из десятичной в целевую систему (если нужно)
|
||||||
|
if target_base != 10:
|
||||||
|
divisions = []
|
||||||
|
temp = decimal_value
|
||||||
|
result_digits = []
|
||||||
|
step_num = 1
|
||||||
|
|
||||||
|
visual = f"МЕТОД: Делим число на {target_base} до тех пор, пока не получим 0.\n"
|
||||||
|
visual += "Каждый остаток от деления - это одна цифра результата.\n"
|
||||||
|
visual += "Важно! Первый остаток будет ПОСЛЕДНЕЙ цифрой ответа.\n\n"
|
||||||
|
|
||||||
|
while temp > 0:
|
||||||
|
remainder = temp % target_base
|
||||||
|
quotient = temp // target_base
|
||||||
|
result_digits.insert(0, self.digits[remainder])
|
||||||
|
divisions.append(f"Шаг {step_num}: {temp} ÷ {target_base} = {quotient}, остаток: {remainder} → цифра '{self.digits[remainder]}'")
|
||||||
|
step_num += 1
|
||||||
|
temp = quotient
|
||||||
|
|
||||||
|
result_number = ''.join(result_digits) if result_digits else "0"
|
||||||
|
|
||||||
|
visual += "\n".join(divisions)
|
||||||
|
visual += "\n\n" + "="*50
|
||||||
|
visual += f"\nСобираем остатки СНИЗУ ВВЕРХ (от последнего к первому):\n"
|
||||||
|
visual += "═"*50 + "\n"
|
||||||
|
for i, digit in enumerate(result_digits):
|
||||||
|
visual += f" ↑ {digit} "
|
||||||
|
visual += f"\n\nОтвет: {result_number}"
|
||||||
|
|
||||||
|
step3 = Step(
|
||||||
|
f"Перевод в {target_base}-ричную систему",
|
||||||
|
f"Алгоритм деления с остатком - классический способ перевода.\n" +
|
||||||
|
f"Представьте, что вы раскладываете конфеты по коробкам: в каждой {target_base} штук.\n" +
|
||||||
|
f"Остатки показывают, сколько конфет не поместилось в полные коробки на каждом этапе.",
|
||||||
|
visual,
|
||||||
|
f"{'-' if is_negative else ''}{result_number}₍{target_base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step3)
|
||||||
|
|
||||||
|
final_result = ('-' if is_negative else '') + result_number
|
||||||
|
else:
|
||||||
|
final_result = str(decimal_value)
|
||||||
|
|
||||||
|
# Финальный шаг
|
||||||
|
final_step = Step(
|
||||||
|
"Итоговый результат",
|
||||||
|
f"Число {'-' if is_negative else ''}{number}₍{self.base}₎ = {final_result}₍{target_base}₎",
|
||||||
|
"",
|
||||||
|
f"{final_result}₍{target_base}₎"
|
||||||
|
)
|
||||||
|
steps.append(final_step)
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def add_with_steps(self, num1: str, num2: str) -> List[Step]:
|
||||||
|
"""
|
||||||
|
Сложение двух чисел в n-ричной системе с пошаговым объяснением
|
||||||
|
|
||||||
|
Args:
|
||||||
|
num1: первое число
|
||||||
|
num2: второе число
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
список шагов решения
|
||||||
|
"""
|
||||||
|
steps = []
|
||||||
|
|
||||||
|
# Шаг 1: Условие задачи
|
||||||
|
step1 = Step(
|
||||||
|
"Условие задачи",
|
||||||
|
f"Необходимо сложить два числа в {self.get_system_name()} ({self.base}-ричной) системе счисления.\n" +
|
||||||
|
f"Принцип такой же, как в десятичной системе, но \"переполнение\" происходит при {self.base}, а не при 10!\n" +
|
||||||
|
f"Пример: если в десятичной 9+1=10 (переносим 1), то в двоичной 1+1=10₍₂₎ (тоже переносим 1).",
|
||||||
|
f"{num1}₍{self.base}₎ + {num2}₍{self.base}₎ = ?",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
steps.append(step1)
|
||||||
|
|
||||||
|
# Проверка на отрицательные числа
|
||||||
|
is_neg1 = num1.startswith('-')
|
||||||
|
is_neg2 = num2.startswith('-')
|
||||||
|
num1_clean = num1.replace('-', '').upper()
|
||||||
|
num2_clean = num2.replace('-', '').upper()
|
||||||
|
|
||||||
|
# Шаг 2: Выравнивание разрядов
|
||||||
|
max_len = max(len(num1_clean), len(num2_clean))
|
||||||
|
num1_aligned = num1_clean.zfill(max_len)
|
||||||
|
num2_aligned = num2_clean.zfill(max_len)
|
||||||
|
|
||||||
|
visual = f" {num1_aligned}\n+ {num2_aligned}\n" + "-" * (max_len + 2)
|
||||||
|
|
||||||
|
step2 = Step(
|
||||||
|
"Выравнивание чисел",
|
||||||
|
f"Дополняем числа нулями слева до одинаковой длины ({max_len} разрядов)",
|
||||||
|
visual,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
steps.append(step2)
|
||||||
|
|
||||||
|
# Шаг 3: Сложение в столбик
|
||||||
|
result = []
|
||||||
|
carry = 0
|
||||||
|
detailed_calc = []
|
||||||
|
|
||||||
|
for i in range(max_len - 1, -1, -1):
|
||||||
|
d1 = self.digits.index(num1_aligned[i])
|
||||||
|
d2 = self.digits.index(num2_aligned[i])
|
||||||
|
|
||||||
|
total = d1 + d2 + carry
|
||||||
|
digit_result = total % self.base
|
||||||
|
new_carry = total // self.base
|
||||||
|
|
||||||
|
result.insert(0, self.digits[digit_result])
|
||||||
|
|
||||||
|
position = max_len - i - 1
|
||||||
|
explanation = f"Позиция {position}: {num1_aligned[i]}({d1}) + {num2_aligned[i]}({d2})"
|
||||||
|
if carry > 0:
|
||||||
|
explanation += f" + перенос({carry})"
|
||||||
|
explanation += f" = {total}"
|
||||||
|
|
||||||
|
if new_carry > 0:
|
||||||
|
explanation += f" = {digit_result} (перенос {new_carry})"
|
||||||
|
else:
|
||||||
|
explanation += f" = {digit_result}"
|
||||||
|
|
||||||
|
detailed_calc.append(explanation)
|
||||||
|
carry = new_carry
|
||||||
|
|
||||||
|
if carry > 0:
|
||||||
|
result.insert(0, self.digits[carry])
|
||||||
|
detailed_calc.append(f"Финальный перенос: {carry}")
|
||||||
|
|
||||||
|
result_str = ''.join(result)
|
||||||
|
|
||||||
|
visual = "\n".join(detailed_calc)
|
||||||
|
visual += f"\n\nВизуализация:\n {num1_aligned}\n+ {num2_aligned}\n" + "-" * (max_len + 2) + f"\n {result_str}"
|
||||||
|
|
||||||
|
step3 = Step(
|
||||||
|
"Сложение в столбик",
|
||||||
|
f"Складываем числа справа налево, учитывая переносы.\n" +
|
||||||
|
f"ПРАВИЛО: Если сумма ≥ {self.base}, то делим на {self.base}:\n" +
|
||||||
|
f" • Остаток от деления → записываем в результат\n" +
|
||||||
|
f" • Частное → переносим в следующий разряд\n" +
|
||||||
|
f"Например: если {self.base-1} + 1 = {self.base}, пишем 0 и переносим 1 (как 9+1=10 в десятичной).",
|
||||||
|
visual,
|
||||||
|
f"{result_str}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step3)
|
||||||
|
|
||||||
|
# Шаг 4: Проверка через десятичную систему
|
||||||
|
dec1 = self.to_decimal(num1)
|
||||||
|
dec2 = self.to_decimal(num2)
|
||||||
|
dec_result = dec1 + dec2
|
||||||
|
|
||||||
|
step4 = Step(
|
||||||
|
"Проверка результата",
|
||||||
|
"Проверим правильность через перевод в десятичную систему",
|
||||||
|
f"{num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" +
|
||||||
|
f"{num2}₍{self.base}₎ = {dec2}₍₁₀₎\n" +
|
||||||
|
f"{dec1} + {dec2} = {dec_result}\n" +
|
||||||
|
f"{dec_result}₍₁₀₎ = {result_str}₍{self.base}₎",
|
||||||
|
f"✓ Ответ верен: {result_str}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step4)
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def subtract_with_steps(self, num1: str, num2: str) -> List[Step]:
|
||||||
|
"""
|
||||||
|
Вычитание двух чисел в n-ричной системе с пошаговым объяснением
|
||||||
|
|
||||||
|
Args:
|
||||||
|
num1: уменьшаемое
|
||||||
|
num2: вычитаемое
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
список шагов решения
|
||||||
|
"""
|
||||||
|
steps = []
|
||||||
|
|
||||||
|
# Шаг 1: Условие задачи
|
||||||
|
step1 = Step(
|
||||||
|
"Условие задачи",
|
||||||
|
f"Необходимо вычесть два числа в {self.get_system_name()} ({self.base}-ричной) системе счисления.\n" +
|
||||||
|
f"Вычитание работает как в десятичной, но занимаем не 10, а {self.base}.\n" +
|
||||||
|
f"Например: в десятичной при 3-5 занимаем 10, получается 13-5=8.\n" +
|
||||||
|
f"В {self.base}-ричной системе занимаем {self.base} из следующего разряда.",
|
||||||
|
f"{num1}₍{self.base}₎ - {num2}₍{self.base}₎ = ?",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
steps.append(step1)
|
||||||
|
|
||||||
|
# Определяем знак результата
|
||||||
|
dec1 = self.to_decimal(num1)
|
||||||
|
dec2 = self.to_decimal(num2)
|
||||||
|
is_negative = dec1 < dec2
|
||||||
|
|
||||||
|
# Работаем с абсолютными значениями
|
||||||
|
if is_negative:
|
||||||
|
num1, num2 = num2, num1
|
||||||
|
num1 = num1.replace('-', '')
|
||||||
|
num2 = num2.replace('-', '')
|
||||||
|
|
||||||
|
num1_clean = num1.replace('-', '').upper()
|
||||||
|
num2_clean = num2.replace('-', '').upper()
|
||||||
|
|
||||||
|
# Шаг 2: Выравнивание разрядов
|
||||||
|
max_len = max(len(num1_clean), len(num2_clean))
|
||||||
|
num1_aligned = num1_clean.zfill(max_len)
|
||||||
|
num2_aligned = num2_clean.zfill(max_len)
|
||||||
|
|
||||||
|
visual = f" {num1_aligned}\n- {num2_aligned}\n" + "-" * (max_len + 2)
|
||||||
|
|
||||||
|
step2 = Step(
|
||||||
|
"Выравнивание чисел",
|
||||||
|
f"Дополняем числа нулями слева до одинаковой длины ({max_len} разрядов)",
|
||||||
|
visual,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
steps.append(step2)
|
||||||
|
|
||||||
|
# Шаг 3: Вычитание в столбик
|
||||||
|
result = []
|
||||||
|
borrow = 0
|
||||||
|
detailed_calc = []
|
||||||
|
|
||||||
|
num1_list = list(num1_aligned)
|
||||||
|
|
||||||
|
for i in range(max_len - 1, -1, -1):
|
||||||
|
d1 = self.digits.index(num1_list[i])
|
||||||
|
d2 = self.digits.index(num2_aligned[i])
|
||||||
|
|
||||||
|
# Учитываем заем
|
||||||
|
d1 -= borrow
|
||||||
|
|
||||||
|
position = max_len - i - 1
|
||||||
|
explanation = f"Позиция {position}: {num1_list[i]}({self.digits.index(num1_list[i])})"
|
||||||
|
|
||||||
|
if borrow > 0:
|
||||||
|
explanation += f" - заем({borrow})"
|
||||||
|
explanation += f" = {d1}"
|
||||||
|
|
||||||
|
explanation += f" - {num2_aligned[i]}({d2})"
|
||||||
|
|
||||||
|
if d1 < d2:
|
||||||
|
# Нужен заем
|
||||||
|
d1 += self.base
|
||||||
|
borrow = 1
|
||||||
|
explanation += f" → занимаем {self.base}, получаем {d1} - {d2} = {d1 - d2}"
|
||||||
|
else:
|
||||||
|
borrow = 0
|
||||||
|
explanation += f" = {d1 - d2}"
|
||||||
|
|
||||||
|
digit_result = d1 - d2
|
||||||
|
result.insert(0, self.digits[digit_result])
|
||||||
|
detailed_calc.append(explanation)
|
||||||
|
|
||||||
|
# Убираем ведущие нули
|
||||||
|
result_str = ''.join(result).lstrip('0') or '0'
|
||||||
|
|
||||||
|
visual = "\n".join(detailed_calc)
|
||||||
|
visual += f"\n\nВизуализация:\n {num1_aligned}\n- {num2_aligned}\n" + "-" * (max_len + 2)
|
||||||
|
visual += f"\n {result_str.zfill(max_len)}"
|
||||||
|
|
||||||
|
if is_negative:
|
||||||
|
result_str = '-' + result_str
|
||||||
|
|
||||||
|
step3 = Step(
|
||||||
|
"Вычитание в столбик",
|
||||||
|
f"Вычитаем числа справа налево.\n" +
|
||||||
|
f"ПРАВИЛО ЗАИМСТВОВАНИЯ:\n" +
|
||||||
|
f" • Если верхняя цифра < нижней, занимаем {self.base} из следующего разряда\n" +
|
||||||
|
f" • К верхней цифре прибавляем {self.base}, вычитаем нижнюю\n" +
|
||||||
|
f" • Из следующего разряда вычитаем 1 (это \"долг\")\n" +
|
||||||
|
f"Это как занять 10₽ у друга, чтобы хватило на покупку!",
|
||||||
|
visual,
|
||||||
|
f"{result_str}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step3)
|
||||||
|
|
||||||
|
# Шаг 4: Проверка
|
||||||
|
dec_result = dec1 - dec2
|
||||||
|
|
||||||
|
step4 = Step(
|
||||||
|
"Проверка результата",
|
||||||
|
"Проверим правильность через перевод в десятичную систему",
|
||||||
|
f"{num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" +
|
||||||
|
f"{num2}₍{self.base}₎ = {dec2}₍₁₀₎\n" +
|
||||||
|
f"{dec1} - {dec2} = {dec_result}\n" +
|
||||||
|
f"{dec_result}₍₁₀₎ = {result_str}₍{self.base}₎",
|
||||||
|
f"✓ Ответ верен: {result_str}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step4)
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def multiply_with_steps(self, num1: str, num2: str) -> List[Step]:
|
||||||
|
"""
|
||||||
|
Умножение двух чисел в n-ричной системе с пошаговым объяснением
|
||||||
|
|
||||||
|
Args:
|
||||||
|
num1: первый множитель
|
||||||
|
num2: второй множитель
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
список шагов решения
|
||||||
|
"""
|
||||||
|
steps = []
|
||||||
|
|
||||||
|
# Шаг 1: Исходные данные
|
||||||
|
step1 = Step(
|
||||||
|
"Условие задачи",
|
||||||
|
f"Необходимо умножить два числа в {self.get_system_name()} ({self.base}-ричной) системе счисления.\n" +
|
||||||
|
f"\nСПОСОБ РЕШЕНИЯ:\n" +
|
||||||
|
f"Умножение напрямую в {self.base}-ричной системе сложное, поэтому используем \"переводчика\":\n" +
|
||||||
|
f" 1. Переводим оба числа в привычную десятичную систему\n" +
|
||||||
|
f" 2. Умножаем в десятичной системе (это мы умеем!)\n" +
|
||||||
|
f" 3. Переводим результат обратно в {self.base}-ричную систему\n" +
|
||||||
|
f"Это как перевести иностранный текст на русский, решить задачу и перевести ответ обратно.",
|
||||||
|
f"{num1}₍{self.base}₎ × {num2}₍{self.base}₎ = ?",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
steps.append(step1)
|
||||||
|
|
||||||
|
# Определяем знак результата
|
||||||
|
dec1 = self.to_decimal(num1)
|
||||||
|
dec2 = self.to_decimal(num2)
|
||||||
|
|
||||||
|
num1_clean = num1.replace('-', '').upper()
|
||||||
|
num2_clean = num2.replace('-', '').upper()
|
||||||
|
|
||||||
|
# Шаг 2: Умножение через десятичную систему (простой подход)
|
||||||
|
result_dec = dec1 * dec2
|
||||||
|
result_str = self.from_decimal(result_dec)
|
||||||
|
|
||||||
|
step2 = Step(
|
||||||
|
"Шаг 1: Преобразование в десятичную систему",
|
||||||
|
f"Переводим множители из {self.base}-ричной в десятичную систему.\n" +
|
||||||
|
f"Используем метод разложения по разрядам (см. примеры выше).",
|
||||||
|
f"{num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" +
|
||||||
|
f"{num2}₍{self.base}₎ = {dec2}₍₁₀₎\n\n" +
|
||||||
|
f"Теперь работаем с привычными десятичными числами!",
|
||||||
|
f"Множители: {dec1} и {dec2}"
|
||||||
|
)
|
||||||
|
steps.append(step2)
|
||||||
|
|
||||||
|
# Шаг 3: Умножение в десятичной системе
|
||||||
|
step3 = Step(
|
||||||
|
"Шаг 2: Умножение в десятичной системе",
|
||||||
|
f"Перемножаем числа обычным способом (как учили в школе).\n" +
|
||||||
|
f"Это самая простая часть - обычное умножение!",
|
||||||
|
f"{dec1} × {dec2} = {result_dec}\n\n" +
|
||||||
|
f"Получили результат в десятичной системе: {result_dec}",
|
||||||
|
f"{result_dec}₍₁₀₎"
|
||||||
|
)
|
||||||
|
steps.append(step3)
|
||||||
|
|
||||||
|
# Шаг 4: Обратное преобразование
|
||||||
|
step4 = Step(
|
||||||
|
f"Шаг 3: Перевод результата в {self.base}-ричную систему",
|
||||||
|
f"Переводим полученный результат обратно в {self.base}-ричную систему.\n" +
|
||||||
|
f"Используем метод деления с остатком (см. примеры выше).",
|
||||||
|
f"{result_dec}₍₁₀₎ = {result_str}₍{self.base}₎\n\n" +
|
||||||
|
f"Вернули ответ в исходную систему счисления!",
|
||||||
|
f"{result_str}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step4)
|
||||||
|
|
||||||
|
# Шаг 5: Проверка
|
||||||
|
step5 = Step(
|
||||||
|
"Итоговый результат и проверка",
|
||||||
|
f"Проверяем логику решения:\n" +
|
||||||
|
f" • Исходная задача: {num1}₍{self.base}₎ × {num2}₍{self.base}₎\n" +
|
||||||
|
f" • Перевели в десятичную: {dec1} × {dec2}\n" +
|
||||||
|
f" • Получили результат: {result_dec}\n" +
|
||||||
|
f" • Вернули в {self.base}-ричную: {result_str}₍{self.base}₎",
|
||||||
|
f"┌─────────────────────────────────┐\n" +
|
||||||
|
f"│ {num1}₍{self.base}₎ × {num2}₍{self.base}₎ = {result_str}₍{self.base}₎ │\n" +
|
||||||
|
f"└─────────────────────────────────┘\n" +
|
||||||
|
f"Проверка: {dec1} × {dec2} = {result_dec}₍₁₀₎ = {result_str}₍{self.base}₎",
|
||||||
|
f"✓ Ответ: {result_str}₍{self.base}₎"
|
||||||
|
)
|
||||||
|
steps.append(step5)
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def divide_with_steps(self, num1: str, num2: str) -> List[Step]:
|
||||||
|
"""
|
||||||
|
Деление двух чисел в n-ричной системе с пошаговым объяснением
|
||||||
|
|
||||||
|
Args:
|
||||||
|
num1: делимое
|
||||||
|
num2: делитель
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
список шагов решения
|
||||||
|
"""
|
||||||
|
steps = []
|
||||||
|
|
||||||
|
# Шаг 1: Исходные данные
|
||||||
|
step1 = Step(
|
||||||
|
"Условие задачи",
|
||||||
|
f"Необходимо разделить два числа в {self.get_system_name()} ({self.base}-ричной) системе счисления.\n" +
|
||||||
|
f"\nСПОСОБ РЕШЕНИЯ:\n" +
|
||||||
|
f"Деление в {self.base}-ричной системе очень трудоёмкое, поэтому используем метод \"переводчика\":\n" +
|
||||||
|
f" 1. Переводим делимое и делитель в десятичную систему\n" +
|
||||||
|
f" 2. Делим в десятичной системе (получаем частное и остаток)\n" +
|
||||||
|
f" 3. Переводим частное и остаток обратно в {self.base}-ричную систему\n" +
|
||||||
|
f"Проще решить на \"родном языке\" математики - в десятичной системе!",
|
||||||
|
f"{num1}₍{self.base}₎ ÷ {num2}₍{self.base}₎ = ?",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
steps.append(step1)
|
||||||
|
|
||||||
|
# Проверка деления на ноль
|
||||||
|
dec2 = self.to_decimal(num2)
|
||||||
|
if dec2 == 0:
|
||||||
|
step_error = Step(
|
||||||
|
"Ошибка",
|
||||||
|
"Деление на ноль невозможно!",
|
||||||
|
"",
|
||||||
|
"❌ Деление на ноль"
|
||||||
|
)
|
||||||
|
steps.append(step_error)
|
||||||
|
return steps
|
||||||
|
|
||||||
|
dec1 = self.to_decimal(num1)
|
||||||
|
|
||||||
|
num1_clean = num1.replace('-', '').upper()
|
||||||
|
num2_clean = num2.replace('-', '').upper()
|
||||||
|
|
||||||
|
# Шаг 2: Преобразование в десятичную систему
|
||||||
|
step2 = Step(
|
||||||
|
"Шаг 1: Преобразование в десятичную систему",
|
||||||
|
f"Переводим делимое и делитель из {self.base}-ричной в десятичную систему.\n" +
|
||||||
|
f"Используем знакомый метод разложения по разрядам.",
|
||||||
|
f"Делимое: {num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" +
|
||||||
|
f"Делитель: {num2}₍{self.base}₎ = {dec2}₍₁₀₎\n\n" +
|
||||||
|
f"Теперь задача выглядит просто: {dec1} ÷ {dec2}",
|
||||||
|
f"Числа в десятичной: {dec1} ÷ {dec2}"
|
||||||
|
)
|
||||||
|
steps.append(step2)
|
||||||
|
|
||||||
|
# Шаг 3: Деление в десятичной системе (целочисленное)
|
||||||
|
quotient = dec1 // dec2
|
||||||
|
remainder = dec1 % dec2
|
||||||
|
|
||||||
|
step3 = Step(
|
||||||
|
"Шаг 2: Деление в десятичной системе",
|
||||||
|
f"Выполняем целочисленное деление (деление нацело).\n" +
|
||||||
|
f"\nЧТО ПОЛУЧАЕМ:\n" +
|
||||||
|
f" • Частное (сколько раз {dec2} помещается в {dec1})\n" +
|
||||||
|
f" • Остаток (что осталось после деления)\n" +
|
||||||
|
f"\nПример: 17 ÷ 5 = 3 (остаток 2), потому что 5×3=15, а 17-15=2",
|
||||||
|
f"{dec1} ÷ {dec2} = {quotient} (остаток {remainder})\n\n" +
|
||||||
|
f"Проверка: {dec2} × {quotient} + {remainder} = {dec2 * quotient} + {remainder} = {dec1} ✓",
|
||||||
|
f"Частное: {quotient}₍₁₀₎, Остаток: {remainder}₍₁₀₎"
|
||||||
|
)
|
||||||
|
steps.append(step3)
|
||||||
|
|
||||||
|
# Шаг 4: Обратное преобразование
|
||||||
|
quotient_str = self.from_decimal(quotient)
|
||||||
|
remainder_str = self.from_decimal(remainder) if remainder != 0 else "0"
|
||||||
|
|
||||||
|
step4 = Step(
|
||||||
|
f"Шаг 3: Перевод результата в {self.base}-ричную систему",
|
||||||
|
f"Переводим частное и остаток обратно в {self.base}-ричную систему.\n" +
|
||||||
|
f"Используем метод деления с остатком для перевода.",
|
||||||
|
f"Частное: {quotient}₍₁₀₎ → {quotient_str}₍{self.base}₎\n" +
|
||||||
|
f"Остаток: {remainder}₍₁₀₎ → {remainder_str}₍{self.base}₎\n\n" +
|
||||||
|
f"Вернули ответ в исходную систему счисления!",
|
||||||
|
f"{quotient_str}₍{self.base}₎ (остаток {remainder_str}₍{self.base}₎)"
|
||||||
|
)
|
||||||
|
steps.append(step4)
|
||||||
|
|
||||||
|
# Шаг 5: Проверка
|
||||||
|
check_result = dec2 * quotient + remainder
|
||||||
|
step5 = Step(
|
||||||
|
"Итоговый результат и проверка",
|
||||||
|
f"Проверяем правильность по формуле:\n" +
|
||||||
|
f" Делимое = Делитель × Частное + Остаток\n" +
|
||||||
|
f"Это основное правило деления с остатком!",
|
||||||
|
f"┌──────────────────────────────────────┐\n" +
|
||||||
|
f"│ {num1}₍{self.base}₎ ÷ {num2}₍{self.base}₎ = {quotient_str}₍{self.base}₎" +
|
||||||
|
(f" (ост. {remainder_str}₍{self.base}₎)" if remainder != 0 else "") + " │\n" +
|
||||||
|
f"└──────────────────────────────────────┘\n\n" +
|
||||||
|
f"Проверка в десятичной: {dec2} × {quotient} + {remainder} = {check_result} ✓\n" +
|
||||||
|
f"Проверка в {self.base}-ричной: {num2}₍{self.base}₎ × {quotient_str}₍{self.base}₎" +
|
||||||
|
(f" + {remainder_str}₍{self.base}₎" if remainder != 0 else "") + f" = {num1}₍{self.base}₎ ✓",
|
||||||
|
f"✓ Ответ: {quotient_str}₍{self.base}₎" + (f" (остаток {remainder_str}₍{self.base}₎)" if remainder != 0 else "")
|
||||||
|
)
|
||||||
|
steps.append(step5)
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def solve(self, num1: str, num2: str, operation: Operation) -> List[Step]:
|
||||||
|
"""
|
||||||
|
Решить задачу с пошаговым объяснением
|
||||||
|
|
||||||
|
Args:
|
||||||
|
num1: первое число
|
||||||
|
num2: второе число
|
||||||
|
operation: тип операции (сложение/вычитание/умножение/деление)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
список шагов решения
|
||||||
|
"""
|
||||||
|
if operation == Operation.ADDITION:
|
||||||
|
return self.add_with_steps(num1, num2)
|
||||||
|
elif operation == Operation.SUBTRACTION:
|
||||||
|
return self.subtract_with_steps(num1, num2)
|
||||||
|
elif operation == Operation.MULTIPLICATION:
|
||||||
|
return self.multiply_with_steps(num1, num2)
|
||||||
|
elif operation == Operation.DIVISION:
|
||||||
|
return self.divide_with_steps(num1, num2)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Неподдерживаемая операция: {operation}")
|
||||||
|
|
||||||
|
def print_solution(self, steps: List[Step]):
|
||||||
|
"""
|
||||||
|
Вывести решение в консоль
|
||||||
|
|
||||||
|
Args:
|
||||||
|
steps: список шагов решения
|
||||||
|
"""
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("ПОШАГОВОЕ РЕШЕНИЕ")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
print(f"\n{'█'*60}")
|
||||||
|
print(f"ШАГ {i}: {step.title}")
|
||||||
|
print(f"{'█'*60}")
|
||||||
|
print(f"\n{step.description}")
|
||||||
|
|
||||||
|
if step.visual:
|
||||||
|
print(f"\n{step.visual}")
|
||||||
|
|
||||||
|
if step.result:
|
||||||
|
print(f"\n➤ Результат: {step.result}")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("РЕШЕНИЕ ЗАВЕРШЕНО")
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Пример использования
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Пример 1: Сложение в двоичной системе
|
||||||
|
print("ПРИМЕР 1: СЛОЖЕНИЕ В ДВОИЧНОЙ СИСТЕМЕ")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
solver = StepByStepSolver(base=2)
|
||||||
|
steps = solver.solve("1011", "110", Operation.ADDITION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Пример 2: Вычитание в восьмеричной системе
|
||||||
|
print("\n\nПРИМЕР 2: ВЫЧИТАНИЕ В ВОСЬМЕРИЧНОЙ СИСТЕМЕ")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
solver = StepByStepSolver(base=8)
|
||||||
|
steps = solver.solve("756", "237", Operation.SUBTRACTION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Пример 3: Сложение в шестнадцатеричной системе
|
||||||
|
print("\n\nПРИМЕР 3: СЛОЖЕНИЕ В ШЕСТНАДЦАТЕРИЧНОЙ СИСТЕМЕ")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
solver = StepByStepSolver(base=16)
|
||||||
|
steps = solver.solve("A5", "3F", Operation.ADDITION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Пример 4: Сложение в пятеричной системе
|
||||||
|
print("\n\nПРИМЕР 4: СЛОЖЕНИЕ В ПЯТЕРИЧНОЙ СИСТЕМЕ")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
solver = StepByStepSolver(base=5)
|
||||||
|
steps = solver.solve("234", "142", Operation.ADDITION)
|
||||||
|
solver.print_solution(steps)
|
||||||
14
app/src/test.py
Normal file
14
app/src/test.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
v=20#скорость
|
||||||
|
p=21.5#необходимы путь
|
||||||
|
g=10#ускорение свободного падения
|
||||||
|
|
||||||
|
t_par=v/g
|
||||||
|
h_pod=(v**2)/(2*g)
|
||||||
|
h_main=p-h_pod
|
||||||
|
t_pod1=g/2
|
||||||
|
t_pod2=math.sqrt(h_main/t_pod1)
|
||||||
|
t=t_pod2-t_par
|
||||||
|
|
||||||
|
pass
|
||||||
67
app/src/test_base50.py
Normal file
67
app/src/test_base50.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"""
|
||||||
|
Тестирование 50-ричного калькулятора
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from step_by_step_solver import StepByStepSolver, Operation
|
||||||
|
|
||||||
|
# Тест 50-ричной системы
|
||||||
|
print("="*60)
|
||||||
|
print("ТЕСТИРОВАНИЕ 50-РИЧНОЙ СИСТЕМЫ СЧИСЛЕНИЯ")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
solver = StepByStepSolver(base=50)
|
||||||
|
|
||||||
|
# Пример 1: Простое сложение
|
||||||
|
print("\n\nПРИМЕР 1: Сложение A + B в 50-ричной системе")
|
||||||
|
print("="*60)
|
||||||
|
print("A₍₅₀₎ = 10₍₁₀₎")
|
||||||
|
print("B₍₅₀₎ = 11₍₁₀₎")
|
||||||
|
print("Ожидаемый результат: V₍₅₀₎ = 21₍₁₀₎")
|
||||||
|
print()
|
||||||
|
steps = solver.solve("A", "B", Operation.ADDITION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Пример 2: Сложение с переносом
|
||||||
|
print("\n\nПРИМЕР 2: Сложение N + 1 в 50-ричной системе")
|
||||||
|
print("="*60)
|
||||||
|
print("N₍₅₀₎ = 49₍₁₀₎")
|
||||||
|
print("1₍₅₀₎ = 1₍₁₀₎")
|
||||||
|
print("Ожидаемый результат: 10₍₅₀₎ = 50₍₁₀₎")
|
||||||
|
print()
|
||||||
|
steps = solver.solve("N", "1", Operation.ADDITION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Пример 3: Большие числа
|
||||||
|
print("\n\nПРИМЕР 3: Сложение 1A + 2B в 50-ричной системе")
|
||||||
|
print("="*60)
|
||||||
|
print("1A₍₅₀₎ = 1×50 + 10 = 60₍₁₀₎")
|
||||||
|
print("2B₍₅₀₎ = 2×50 + 11 = 111₍₁₀₎")
|
||||||
|
print("Ожидаемый результат: 3V₍₅₀₎ = 171₍₁₀₎")
|
||||||
|
print()
|
||||||
|
steps = solver.solve("1A", "2B", Operation.ADDITION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Пример 4: Вычитание
|
||||||
|
print("\n\nПРИМЕР 4: Вычитание 2A - B в 50-ричной системе")
|
||||||
|
print("="*60)
|
||||||
|
print("2A₍₅₀₎ = 2×50 + 10 = 110₍₁₀₎")
|
||||||
|
print("B₍₅₀₎ = 11₍₁₀₎")
|
||||||
|
print("Ожидаемый результат: 1N₍₅₀₎ = 99₍₁₀₎")
|
||||||
|
print()
|
||||||
|
steps = solver.solve("2A", "B", Operation.SUBTRACTION)
|
||||||
|
solver.print_solution(steps)
|
||||||
|
|
||||||
|
# Проверка правильности конвертации
|
||||||
|
print("\n\nПРОВЕРКА ТАБЛИЦЫ СООТВЕТСТВИЙ (50-ричная → десятичная)")
|
||||||
|
print("="*60)
|
||||||
|
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
print("Первые 50 символов:")
|
||||||
|
for i in range(50):
|
||||||
|
if i % 10 == 0:
|
||||||
|
print()
|
||||||
|
print(f"{digits[i]}={i:2d}", end=" ")
|
||||||
|
print("\n")
|
||||||
163
complete_update.py
Normal file
163
complete_update.py
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Скрипт для завершения обновления калькуляторов
|
||||||
|
Добавляет функции умножения и деления в sedecim_calculeator8.py и pentecostal_calculeator10.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
def add_hex_multiplication_division():
|
||||||
|
"""Добавляет умножение и деление в шестнадцатеричный калькулятор"""
|
||||||
|
|
||||||
|
# Методы для добавления после calculate_addition
|
||||||
|
methods_to_add = '''
|
||||||
|
def calculate_multiplication(self, e):
|
||||||
|
try:
|
||||||
|
num1 = self.hex_to_decimal(self.input_mult_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_mult_2.value.upper())
|
||||||
|
result_decimal = num1 * num2
|
||||||
|
result_hex = self.decimal_to_hex(result_decimal)
|
||||||
|
|
||||||
|
self.result_mult.value = f"HEX: {result_hex}"
|
||||||
|
self.decimal_mult.value = f"DEC: {result_decimal}"
|
||||||
|
|
||||||
|
if '.' not in self.input_mult_1.value and '.' not in self.input_mult_2.value:
|
||||||
|
hex_val1 = self.input_mult_1.value.upper().lstrip('-+') if self.input_mult_1.value else "0"
|
||||||
|
hex_val2 = self.input_mult_2.value.upper().lstrip('-+') if self.input_mult_2.value else "0"
|
||||||
|
self.show_steps_mult(hex_val1, hex_val2, Operation.MULTIPLICATION)
|
||||||
|
else:
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_mult.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_mult.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_mult.value = "DEC: ошибка"
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def calculate_division(self, e):
|
||||||
|
try:
|
||||||
|
num1 = self.hex_to_decimal(self.input_div_1.value.upper())
|
||||||
|
num2 = self.hex_to_decimal(self.input_div_2.value.upper())
|
||||||
|
|
||||||
|
if num2 == 0:
|
||||||
|
self.result_div.value = "HEX: Деление на 0!"
|
||||||
|
self.decimal_div.value = "DEC: ошибка"
|
||||||
|
self.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
quotient = int(num1 // num2)
|
||||||
|
remainder = int(num1 % num2)
|
||||||
|
quot_hex = self.decimal_to_hex(quotient)
|
||||||
|
|
||||||
|
if remainder == 0:
|
||||||
|
self.result_div.value = f"HEX: {quot_hex}"
|
||||||
|
self.decimal_div.value = f"DEC: {quotient}"
|
||||||
|
else:
|
||||||
|
self.result_div.value = f"HEX: {quot_hex}"
|
||||||
|
self.decimal_div.value = f"DEC: {quotient} (ост. {remainder})"
|
||||||
|
|
||||||
|
if '.' not in self.input_div_1.value and '.' not in self.input_div_2.value:
|
||||||
|
hex_val1 = self.input_div_1.value.upper().lstrip('-+') if self.input_div_1.value else "0"
|
||||||
|
hex_val2 = self.input_div_2.value.upper().lstrip('-+') if self.input_div_2.value else "0"
|
||||||
|
self.show_steps_div(hex_val1, hex_val2, Operation.DIVISION)
|
||||||
|
else:
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12)
|
||||||
|
)
|
||||||
|
self.steps_div.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.result_div.value = f"Ошибка: {str(ex)}"
|
||||||
|
self.decimal_div.value = "DEC: ошибка"
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def show_steps_mult(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для умножения"""
|
||||||
|
try:
|
||||||
|
steps = self.solver.solve(num1, num2, operation)
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_content = []
|
||||||
|
step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step_content.append(ft.Text(step.description, size=10, color="#E0E0E0"))
|
||||||
|
if step.visual:
|
||||||
|
step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True))
|
||||||
|
if step.result:
|
||||||
|
step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
|
||||||
|
self.steps_mult.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.steps_mult.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.steps_mult.controls.clear()
|
||||||
|
self.steps_mult.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10))
|
||||||
|
self.steps_mult.update()
|
||||||
|
|
||||||
|
def show_steps_div(self, num1: str, num2: str, operation: Operation):
|
||||||
|
"""Отображает пошаговое решение для деления"""
|
||||||
|
try:
|
||||||
|
steps = self.solver.solve(num1, num2, operation)
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700")
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, step in enumerate(steps, 1):
|
||||||
|
step_content = []
|
||||||
|
step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF"))
|
||||||
|
step_content.append(ft.Text(step.description, size=10, color="#E0E0E0"))
|
||||||
|
if step.visual:
|
||||||
|
step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True))
|
||||||
|
if step.result:
|
||||||
|
step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00"))
|
||||||
|
|
||||||
|
self.steps_div.controls.append(
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(step_content, spacing=3),
|
||||||
|
bgcolor="#2A1A3A",
|
||||||
|
padding=8,
|
||||||
|
border_radius=5,
|
||||||
|
border=ft.border.all(1, "#4A3A5A")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.steps_div.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.steps_div.controls.clear()
|
||||||
|
self.steps_div.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10))
|
||||||
|
self.steps_div.update()
|
||||||
|
'''
|
||||||
|
|
||||||
|
print("✓ Шаблон методов для шестнадцатеричного калькулятора создан")
|
||||||
|
print("\nСкопируйте этот код и вставьте после метода calculate_addition в sedecim_calculeator8.py")
|
||||||
|
print(methods_to_add)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("="*60)
|
||||||
|
print("ИНСТРУКЦИЯ ПО ЗАВЕРШЕНИЮ ОБНОВЛЕНИЯ КАЛЬКУЛЯТОРОВ")
|
||||||
|
print("="*60)
|
||||||
|
print("\n✅ УЖЕ ОБНОВЛЕНО:")
|
||||||
|
print(" • step_by_step_solver.py - добавлены умножение и деление")
|
||||||
|
print(" • calculater2.py - 4 колонки (+, -, ×, ÷)")
|
||||||
|
print(" • binary_calculeator4.py - 4 колонки (+, -, ×, ÷)")
|
||||||
|
print(" • octogon_calculeator6.py - 4 колонки (+, -, ×, ÷)")
|
||||||
|
print("\n⏳ НУЖНО ЗАВЕРШИТЬ:")
|
||||||
|
print(" • sedecim_calculeator8.py (Шестнадцатеричный)")
|
||||||
|
print(" • pentecostal_calculeator10.py (50-ричный)")
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("РУКОВОДСТВО НАХОДИТСЯ В: UPGRADE_GUIDE.md")
|
||||||
|
print("="*60)
|
||||||
28
main.py
28
main.py
@@ -0,0 +1,28 @@
|
|||||||
|
import math as m
|
||||||
|
|
||||||
|
def decimal_to_base50(n):
|
||||||
|
if n == 0:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
# Символы для 50-ичной системы: 0-9, A-Z, a-m (всего 10+26+14=50)
|
||||||
|
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
while n > 0:
|
||||||
|
remainder = n % 50
|
||||||
|
result = digits[remainder] + result
|
||||||
|
n = n // 50
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def base50_to_decimal(s):
|
||||||
|
# Словарь для преобразования символов в числа
|
||||||
|
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
|
||||||
|
char_to_value = {char: i for i, char in enumerate(digits)}
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
for char in s:
|
||||||
|
result = result * 50 + char_to_value[char]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user