|
|
% ===============================================
|
|
|
% ТЕСТЫ ДЛЯ LAB9 TASK2
|
|
|
% Автоматические тесты всех предикатов
|
|
|
% ===============================================
|
|
|
|
|
|
% Запуск всех тестов
|
|
|
run_tests :-
|
|
|
write('=== ЗАПУСК АВТОМАТИЧЕСКИХ ТЕСТОВ TASK2 ==='), nl,
|
|
|
test_digit_product,
|
|
|
test_max_digit_not_div3,
|
|
|
test_divisor_count,
|
|
|
test_recursion_equivalence,
|
|
|
test_edge_cases,
|
|
|
test_performance,
|
|
|
write('=== ВСЕ ТЕСТЫ TASK2 ЗАВЕРШЕНЫ ==='), nl.
|
|
|
|
|
|
% ===============================================
|
|
|
% ТЕСТЫ ДЛЯ ПРОИЗВЕДЕНИЯ ЦИФР
|
|
|
% ===============================================
|
|
|
test_digit_product :-
|
|
|
write('--- Тестирование произведения цифр ---'), nl,
|
|
|
|
|
|
% Тест базового случая
|
|
|
(digit_product_up(0, 1) ->
|
|
|
write('✓ digit_product_up(0,1) - ПРОЙДЕН') ;
|
|
|
write('✗ digit_product_up(0,1) - ПРОВАЛЕН')), nl,
|
|
|
|
|
|
(digit_product_down(0, 1) ->
|
|
|
write('✓ digit_product_down(0,1) - ПРОЙДЕН') ;
|
|
|
write('✗ digit_product_down(0,1) - ПРОВАЛЕН')), nl,
|
|
|
|
|
|
% Тест известных значений
|
|
|
TestCases = [
|
|
|
(123, 6), % 1*2*3 = 6
|
|
|
(456, 120), % 4*5*6 = 120
|
|
|
(789, 504), % 7*8*9 = 504
|
|
|
(102, 0), % 1*0*2 = 0
|
|
|
(555, 125), % 5*5*5 = 125
|
|
|
(1, 1), % 1 = 1
|
|
|
(9, 9) % 9 = 9
|
|
|
],
|
|
|
forall(member((N, Expected), TestCases), test_digit_product_case(N, Expected)),
|
|
|
|
|
|
% Тест эквивалентности рекурсий
|
|
|
test_digit_product_equivalence([0, 1, 12, 123, 1234, 5555, 102, 9876]),
|
|
|
nl.
|
|
|
|
|
|
test_digit_product_case(N, Expected) :-
|
|
|
digit_product_up(N, P1),
|
|
|
digit_product_down(N, P2),
|
|
|
write('Произведение цифр '), write(N), write(': up='), write(P1),
|
|
|
write(', down='), write(P2), write(', ожидалось='), write(Expected),
|
|
|
((P1 =:= Expected, P2 =:= Expected) -> write(' ✓') ; write(' ✗')), nl.
|
|
|
|
|
|
test_digit_product_equivalence([]).
|
|
|
test_digit_product_equivalence([N|Rest]) :-
|
|
|
digit_product_up(N, P1),
|
|
|
digit_product_down(N, P2),
|
|
|
(P1 =:= P2 ->
|
|
|
write('✓ Произведения цифр '), write(N), write(' эквивалентны') ;
|
|
|
write('✗ Произведения цифр '), write(N), write(' НЕ эквивалентны')), nl,
|
|
|
test_digit_product_equivalence(Rest).
|
|
|
|
|
|
% ===============================================
|
|
|
% ТЕСТЫ ДЛЯ МАКСИМАЛЬНОЙ ЦИФРЫ НЕ ДЕЛЯЩЕЙСЯ НА 3
|
|
|
% ===============================================
|
|
|
test_max_digit_not_div3 :-
|
|
|
write('--- Тестирование максимальной цифры не делящейся на 3 ---'), nl,
|
|
|
|
|
|
% Тест базового случая
|
|
|
(max_digit_not_div3_up(0, -1) ->
|
|
|
write('✓ max_digit_not_div3_up(0,-1) - ПРОЙДЕН') ;
|
|
|
write('✗ max_digit_not_div3_up(0,-1) - ПРОВАЛЕН')), nl,
|
|
|
|
|
|
(max_digit_not_div3_down(0, -1) ->
|
|
|
write('✓ max_digit_not_div3_down(0,-1) - ПРОЙДЕН') ;
|
|
|
write('✗ max_digit_not_div3_down(0,-1) - ПРОВАЛЕН')), nl,
|
|
|
|
|
|
% Тест известных значений
|
|
|
TestCases = [
|
|
|
(123, 2), % цифры: 1,2,3 -> не дел. на 3: 1,2 -> макс: 2
|
|
|
(456, 5), % цифры: 4,5,6 -> не дел. на 3: 4,5 -> макс: 5
|
|
|
(789, 8), % цифры: 7,8,9 -> не дел. на 3: 7,8 -> макс: 8
|
|
|
(369, -1), % цифры: 3,6,9 -> все дел. на 3 -> -1
|
|
|
(147, 7), % цифры: 1,4,7 -> не дел. на 3: 1,4,7 -> макс: 7
|
|
|
(1, 1), % цифра: 1 -> не дел. на 3: 1 -> макс: 1
|
|
|
(39, -1), % цифры: 3,9 -> все дел. на 3 -> -1
|
|
|
(12457, 7), % цифры: 1,2,4,5,7 -> не дел. на 3: 1,2,4,5,7 -> макс: 7
|
|
|
(36960, -1) % цифры: 3,6,9,6,0 -> все дел. на 3 -> -1
|
|
|
],
|
|
|
forall(member((N, Expected), TestCases), test_max_digit_not_div3_case(N, Expected)),
|
|
|
|
|
|
% Тест эквивалентности рекурсий
|
|
|
test_max_digit_not_div3_equivalence([0, 123, 456, 369, 147, 12457, 98765]),
|
|
|
nl.
|
|
|
|
|
|
test_max_digit_not_div3_case(N, Expected) :-
|
|
|
max_digit_not_div3_up(N, M1),
|
|
|
max_digit_not_div3_down(N, M2),
|
|
|
write('Макс. цифра не дел. на 3 для '), write(N), write(': up='), write(M1),
|
|
|
write(', down='), write(M2), write(', ожидалось='), write(Expected),
|
|
|
((M1 =:= Expected, M2 =:= Expected) -> write(' ✓') ; write(' ✗')), nl.
|
|
|
|
|
|
test_max_digit_not_div3_equivalence([]).
|
|
|
test_max_digit_not_div3_equivalence([N|Rest]) :-
|
|
|
max_digit_not_div3_up(N, M1),
|
|
|
max_digit_not_div3_down(N, M2),
|
|
|
(M1 =:= M2 ->
|
|
|
write('✓ Макс. цифры не дел. на 3 для '), write(N), write(' эквивалентны') ;
|
|
|
write('✗ Макс. цифры не дел. на 3 для '), write(N), write(' НЕ эквивалентны')), nl,
|
|
|
test_max_digit_not_div3_equivalence(Rest).
|
|
|
|
|
|
% ===============================================
|
|
|
% ТЕСТЫ ДЛЯ КОЛИЧЕСТВА ДЕЛИТЕЛЕЙ
|
|
|
% ===============================================
|
|
|
test_divisor_count :-
|
|
|
write('--- Тестирование количества делителей ---'), nl,
|
|
|
|
|
|
% Тест известных значений
|
|
|
TestCases = [
|
|
|
(1, 1), % делители: 1
|
|
|
(2, 2), % делители: 1, 2
|
|
|
(6, 4), % делители: 1, 2, 3, 6
|
|
|
(12, 6), % делители: 1, 2, 3, 4, 6, 12
|
|
|
(16, 5), % делители: 1, 2, 4, 8, 16
|
|
|
(24, 8), % делители: 1, 2, 3, 4, 6, 8, 12, 24
|
|
|
(100, 9), % делители: 1, 2, 4, 5, 10, 20, 25, 50, 100
|
|
|
(7, 2), % делители: 1, 7 (простое число)
|
|
|
(13, 2) % делители: 1, 13 (простое число)
|
|
|
],
|
|
|
forall(member((N, Expected), TestCases), test_divisor_count_case(N, Expected)),
|
|
|
|
|
|
% Тест эквивалентности всех методов
|
|
|
test_divisor_count_equivalence([1, 2, 6, 12, 16, 24, 36, 48]),
|
|
|
nl.
|
|
|
|
|
|
test_divisor_count_case(N, Expected) :-
|
|
|
divisor_count_up(N, C1),
|
|
|
divisor_count_down(N, C2),
|
|
|
divisor_count_optimized(N, C3),
|
|
|
write('Количество делителей '), write(N), write(': up='), write(C1),
|
|
|
write(', down='), write(C2), write(', opt='), write(C3),
|
|
|
write(', ожидалось='), write(Expected),
|
|
|
((C1 =:= Expected, C2 =:= Expected, C3 =:= Expected) -> write(' ✓') ; write(' ✗')), nl.
|
|
|
|
|
|
test_divisor_count_equivalence([]).
|
|
|
test_divisor_count_equivalence([N|Rest]) :-
|
|
|
divisor_count_up(N, C1),
|
|
|
divisor_count_down(N, C2),
|
|
|
divisor_count_optimized(N, C3),
|
|
|
((C1 =:= C2, C2 =:= C3) ->
|
|
|
write('✓ Количества делителей '), write(N), write(' эквивалентны') ;
|
|
|
write('✗ Количества делителей '), write(N), write(' НЕ эквивалентны')), nl,
|
|
|
test_divisor_count_equivalence(Rest).
|
|
|
|
|
|
% ===============================================
|
|
|
% ТЕСТЫ ЭКВИВАЛЕНТНОСТИ РЕКУРСИЙ
|
|
|
% ===============================================
|
|
|
test_recursion_equivalence :-
|
|
|
write('--- Тестирование эквивалентности рекурсий ---'), nl,
|
|
|
|
|
|
TestNumbers = [0, 1, 12, 123, 1234, 5555, 9876, 102030],
|
|
|
write('Проверка эквивалентности для чисел: '), write(TestNumbers), nl,
|
|
|
|
|
|
forall(member(N, TestNumbers), test_single_number_equivalence(N)),
|
|
|
nl.
|
|
|
|
|
|
test_single_number_equivalence(N) :-
|
|
|
% Произведение цифр
|
|
|
digit_product_up(N, P1),
|
|
|
digit_product_down(N, P2),
|
|
|
ProductOK = (P1 =:= P2),
|
|
|
|
|
|
% Максимальная цифра не делящаяся на 3
|
|
|
max_digit_not_div3_up(N, M1),
|
|
|
max_digit_not_div3_down(N, M2),
|
|
|
MaxDigitOK = (M1 =:= M2),
|
|
|
|
|
|
% Количество делителей (только для положительных)
|
|
|
( N > 0 ->
|
|
|
divisor_count_up(N, C1),
|
|
|
divisor_count_down(N, C2),
|
|
|
divisor_count_optimized(N, C3),
|
|
|
DivisorOK = (C1 =:= C2, C2 =:= C3)
|
|
|
; DivisorOK = true
|
|
|
),
|
|
|
|
|
|
% Общий результат
|
|
|
( (ProductOK, MaxDigitOK, DivisorOK) ->
|
|
|
write('✓ ')
|
|
|
; write('✗ ')
|
|
|
),
|
|
|
write('Число '), write(N), write(' - все рекурсии эквивалентны'), nl.
|
|
|
|
|
|
% ===============================================
|
|
|
% ТЕСТЫ ГРАНИЧНЫХ СЛУЧАЕВ
|
|
|
% ===============================================
|
|
|
test_edge_cases :-
|
|
|
write('--- Тестирование граничных случаев ---'), nl,
|
|
|
|
|
|
% Тест с числом, содержащим только нули
|
|
|
write('Тест с числом 1000:'), nl,
|
|
|
digit_product_up(1000, P1000),
|
|
|
write(' Произведение цифр 1000 = '), write(P1000),
|
|
|
(P1000 =:= 0 -> write(' ✓') ; write(' ✗')), nl,
|
|
|
|
|
|
% Тест с числом, все цифры которого делятся на 3
|
|
|
write('Тест с числом 3699 (все цифры дел. на 3):'), nl,
|
|
|
max_digit_not_div3_up(3699, M3699),
|
|
|
write(' Макс. цифра не дел. на 3 для 3699 = '), write(M3699),
|
|
|
(M3699 =:= -1 -> write(' ✓') ; write(' ✗')), nl,
|
|
|
|
|
|
% Тест с простыми числами
|
|
|
write('Тест с простыми числами:'), nl,
|
|
|
PrimeNumbers = [2, 3, 5, 7, 11, 13, 17, 19],
|
|
|
forall(member(P, PrimeNumbers), test_prime_divisors(P)),
|
|
|
|
|
|
% Тест с точными квадратами
|
|
|
write('Тест с точными квадратами:'), nl,
|
|
|
Squares = [1, 4, 9, 16, 25, 36, 49],
|
|
|
forall(member(S, Squares), test_square_divisors(S)),
|
|
|
nl.
|
|
|
|
|
|
test_prime_divisors(P) :-
|
|
|
divisor_count_optimized(P, Count),
|
|
|
write(' Простое число '), write(P), write(' имеет '), write(Count), write(' делителей'),
|
|
|
(Count =:= 2 -> write(' ✓') ; write(' ✗')), nl.
|
|
|
|
|
|
test_square_divisors(S) :-
|
|
|
divisor_count_optimized(S, Count),
|
|
|
Sqrt is floor(sqrt(S)),
|
|
|
( Sqrt * Sqrt =:= S ->
|
|
|
write(' Точный квадрат '), write(S), write(' имеет '), write(Count), write(' делителей ✓')
|
|
|
; write(' Ошибка: '), write(S), write(' не является точным квадратом ✗')
|
|
|
), nl.
|
|
|
|
|
|
% ===============================================
|
|
|
% ТЕСТЫ ПРОИЗВОДИТЕЛЬНОСТИ
|
|
|
% ===============================================
|
|
|
test_performance :-
|
|
|
write('--- Тесты производительности ---'), nl,
|
|
|
|
|
|
write('Сравнение производительности для больших чисел:'), nl,
|
|
|
TestNumbers = [12345, 123456, 1234567],
|
|
|
forall(member(N, TestNumbers), test_performance_single(N)),
|
|
|
|
|
|
write('Сравнение оптимизированного алгоритма делителей:'), nl,
|
|
|
LargeNumbers = [100, 1000, 10000],
|
|
|
forall(member(N, LargeNumbers), test_divisor_performance(N)),
|
|
|
nl.
|
|
|
|
|
|
test_performance_single(N) :-
|
|
|
write('Тестирование производительности для N = '), write(N), write(':'), nl,
|
|
|
|
|
|
% Произведение цифр
|
|
|
get_time(T1),
|
|
|
digit_product_up(N, _),
|
|
|
get_time(T2),
|
|
|
digit_product_down(N, _),
|
|
|
get_time(T3),
|
|
|
|
|
|
TimeProductUp is T2 - T1,
|
|
|
TimeProductDown is T3 - T2,
|
|
|
|
|
|
write(' Произведение цифр: up='), write(TimeProductUp),
|
|
|
write('с, down='), write(TimeProductDown), write('с'), nl,
|
|
|
|
|
|
% Максимальная цифра
|
|
|
get_time(T4),
|
|
|
max_digit_not_div3_up(N, _),
|
|
|
get_time(T5),
|
|
|
max_digit_not_div3_down(N, _),
|
|
|
get_time(T6),
|
|
|
|
|
|
TimeMaxUp is T5 - T4,
|
|
|
TimeMaxDown is T6 - T5,
|
|
|
|
|
|
write(' Макс. цифра: up='), write(TimeMaxUp),
|
|
|
write('с, down='), write(TimeMaxDown), write('с'), nl.
|
|
|
|
|
|
test_divisor_performance(N) :-
|
|
|
write('Тестирование делителей для N = '), write(N), write(':'), nl,
|
|
|
|
|
|
get_time(T1),
|
|
|
divisor_count_up(N, C1),
|
|
|
get_time(T2),
|
|
|
divisor_count_down(N, C2),
|
|
|
get_time(T3),
|
|
|
divisor_count_optimized(N, C3),
|
|
|
get_time(T4),
|
|
|
|
|
|
TimeUp is T2 - T1,
|
|
|
TimeDown is T3 - T2,
|
|
|
TimeOpt is T4 - T3,
|
|
|
|
|
|
write(' Результаты: up='), write(C1), write(', down='), write(C2),
|
|
|
write(', opt='), write(C3), nl,
|
|
|
write(' Времена: up='), write(TimeUp), write('с, down='), write(TimeDown),
|
|
|
write('с, opt='), write(TimeOpt), write('с'), nl,
|
|
|
|
|
|
% Проверка ускорения оптимизированной версии
|
|
|
( TimeOpt < TimeUp ->
|
|
|
SpeedUp is TimeUp / TimeOpt,
|
|
|
write(' Ускорение оптимизированной версии: '), write(SpeedUp), write('x ✓')
|
|
|
; write(' Оптимизированная версия медленнее ✗')
|
|
|
), nl.
|
|
|
|
|
|
% ===============================================
|
|
|
% СТРЕСС-ТЕСТЫ
|
|
|
% ===============================================
|
|
|
stress_test :-
|
|
|
write('--- Стресс-тесты ---'), nl,
|
|
|
|
|
|
write('Тестирование 50 случайных чисел:'), nl,
|
|
|
stress_test_random_numbers(50),
|
|
|
|
|
|
write('Тестирование больших чисел:'), nl,
|
|
|
LargeNumbers = [99999, 123456789, 987654321],
|
|
|
forall(member(N, LargeNumbers), stress_test_large_number(N)),
|
|
|
nl.
|
|
|
|
|
|
stress_test_random_numbers(0) :- !.
|
|
|
stress_test_random_numbers(N) :-
|
|
|
N > 0,
|
|
|
random(0, 10000, TestNumber),
|
|
|
|
|
|
% Быстрая проверка корректности
|
|
|
( TestNumber > 0 ->
|
|
|
divisor_count_up(TestNumber, C1),
|
|
|
divisor_count_optimized(TestNumber, C2),
|
|
|
(C1 =:= C2 -> Status = 'OK' ; Status = 'ERROR')
|
|
|
; Status = 'SKIP'
|
|
|
),
|
|
|
|
|
|
(N mod 10 =:= 0 ->
|
|
|
(write('Тест '), write(N), write(': '), write(TestNumber),
|
|
|
write(' - '), write(Status), nl)
|
|
|
; true),
|
|
|
|
|
|
N1 is N - 1,
|
|
|
stress_test_random_numbers(N1).
|
|
|
|
|
|
stress_test_large_number(N) :-
|
|
|
write('Тестирование большого числа '), write(N), write(':'), nl,
|
|
|
|
|
|
get_time(T1),
|
|
|
digit_product_down(N, Product),
|
|
|
get_time(T2),
|
|
|
max_digit_not_div3_down(N, MaxDigit),
|
|
|
get_time(T3),
|
|
|
|
|
|
TimeProduct is T2 - T1,
|
|
|
TimeMaxDigit is T3 - T2,
|
|
|
|
|
|
write(' Произведение цифр: '), write(Product),
|
|
|
write(' (время: '), write(TimeProduct), write('с)'), nl,
|
|
|
write(' Макс. цифра не дел. на 3: '), write(MaxDigit),
|
|
|
write(' (время: '), write(TimeMaxDigit), write('с)'), nl.
|
|
|
|
|
|
% ===============================================
|
|
|
% ДОПОЛНИТЕЛЬНЫЕ ТЕСТЫ
|
|
|
% ===============================================
|
|
|
|
|
|
% Тест специальных случаев для произведения цифр
|
|
|
test_digit_product_special :-
|
|
|
write('--- Специальные тесты произведения цифр ---'), nl,
|
|
|
|
|
|
% Числа с нулями
|
|
|
ZeroNumbers = [10, 102, 1020, 10203],
|
|
|
forall(member(N, ZeroNumbers), test_zero_product(N)),
|
|
|
|
|
|
% Числа из одинаковых цифр
|
|
|
SameDigitNumbers = [111, 222, 3333, 55555],
|
|
|
forall(member(N, SameDigitNumbers), test_same_digit_product(N)),
|
|
|
nl.
|
|
|
|
|
|
test_zero_product(N) :-
|
|
|
digit_product_up(N, Product),
|
|
|
write('Произведение цифр '), write(N), write(' = '), write(Product),
|
|
|
(Product =:= 0 -> write(' ✓') ; write(' ✗')), nl.
|
|
|
|
|
|
test_same_digit_product(N) :-
|
|
|
digit_product_up(N, Product),
|
|
|
% Получаем первую цифру
|
|
|
FirstDigit is N mod 10,
|
|
|
% Подсчитываем количество цифр
|
|
|
atom_chars(N, Chars),
|
|
|
length(Chars, DigitCount),
|
|
|
% Ожидаемое произведение
|
|
|
Expected is FirstDigit ^ DigitCount,
|
|
|
write('Произведение цифр '), write(N), write(' = '), write(Product),
|
|
|
write(', ожидалось '), write(Expected),
|
|
|
(Product =:= Expected -> write(' ✓') ; write(' ✗')), nl. |