Compare commits

..

53 Commits

Author SHA1 Message Date
me
238f8048e0 add notes for lecture 2
Some checks failed
Fast Reverse Tests / test (push) Successful in 23s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 9s
Word Stat Tests / test (push) Successful in 7s
Word Stat++ Tests / test (push) Failing after 12s
2026-02-02 21:51:52 +05:00
me
231a37be9e add notes for lecture 2
Some checks failed
Fast Reverse Tests / test (push) Successful in 47s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 9s
Word Stat Tests / test (push) Has been cancelled
Word Stat++ Tests / test (push) Has been cancelled
2026-02-02 21:50:41 +05:00
me
3678af68c0 add solutions for hw6:3233
Some checks failed
Fast Reverse Tests / test (push) Successful in 24s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 8s
Word Stat Tests / test (push) Successful in 7s
Word Stat++ Tests / test (push) Failing after 10s
2026-02-02 14:31:39 +03:00
me
d8154da7a9 add solution for hw6:3233
Some checks failed
Fast Reverse Tests / test (push) Failing after 4s
Reverse Tests / test (push) Failing after 4s
Sum Tests / test (push) Failing after 4s
Word Stat Tests / test (push) Failing after 4s
Word Stat++ Tests / test (push) Failing after 4s
2026-02-02 14:27:24 +03:00
me
ddba0fc8b5 add solutions for hw6:Base
Some checks failed
Fast Reverse Tests / test (push) Successful in 24s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Successful in 7s
Word Stat++ Tests / test (push) Failing after 11s
2026-02-02 14:32:45 +05:00
me
dec9762ba2 add tests for hw6:Base
All checks were successful
Fast Reverse Tests / test (push) Successful in 21s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Successful in 7s
Word Stat++ Tests / test (push) Successful in 12s
2026-02-02 13:27:07 +05:00
me
380611c4df add solutions for hw6:Base
All checks were successful
Fast Reverse Tests / test (push) Successful in 24s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 9s
Word Stat Tests / test (push) Successful in 8s
2026-02-02 13:25:10 +05:00
me
1e5c8fab61 add solutions for hw5
All checks were successful
Fast Reverse Tests / test (push) Successful in 24s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 10s
Word Stat Tests / test (push) Successful in 9s
2026-02-02 13:03:16 +05:00
me
ab6616b619 add solutions for hw5
All checks were successful
Fast Reverse Tests / test (push) Successful in 26s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Successful in 7s
2026-02-02 13:02:15 +05:00
me
6f5907b244 add solutions for hw5
Some checks failed
Fast Reverse Tests / test (push) Has been cancelled
Reverse Tests / test (push) Successful in 11s
Sum Tests / test (push) Successful in 10s
Word Stat Tests / test (push) Successful in 9s
2026-02-02 12:21:38 +05:00
me
304f238a8a add tests for hw5
Some checks failed
Fast Reverse Tests / test (push) Failing after 35s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Successful in 8s
2026-02-02 12:06:48 +05:00
me
b3dd5ae4c7 add solution for hw4:4749
All checks were successful
Fast Reverse Tests / test (push) Successful in 14s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 8s
Word Stat Tests / test (push) Successful in 8s
2026-02-02 12:02:40 +05:00
me
29c361725e add solution for hw4:3839
Some checks failed
Fast Reverse Tests / test (push) Successful in 15s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 9s
Word Stat Tests / test (push) Failing after 8s
2026-02-02 11:59:24 +05:00
me
868a206eb1 add solution for hw4:3637
Some checks failed
Fast Reverse Tests / test (push) Successful in 15s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 10s
Word Stat Tests / test (push) Failing after 7s
2026-02-02 11:50:52 +05:00
me
21d9d2c105 add solution for hw4:3637
Some checks failed
Fast Reverse Tests / test (push) Successful in 16s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 9s
Word Stat Tests / test (push) Failing after 7s
2026-02-02 11:47:58 +05:00
me
5ef52d793b add notes for lecture 1
Some checks failed
Fast Reverse Tests / test (push) Successful in 37s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 10s
Word Stat Tests / test (push) Failing after 7s
2026-02-02 10:40:50 +05:00
me
5945b021f2 add solution for hw4:3435
Some checks failed
Fast Reverse Tests / test (push) Successful in 11s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 5s
2026-01-31 15:54:01 +05:00
me
e197eb2690 add solution for hw4:3435
Some checks failed
Fast Reverse Tests / test (push) Successful in 13s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 4s
2026-01-31 15:49:07 +05:00
me
7b3f22216f add solution for hw4:3435
Some checks failed
Fast Reverse Tests / test (push) Successful in 12s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 5s
2026-01-31 15:47:25 +05:00
me
6c42ebab03 add solution for hw4:3435
Some checks failed
Fast Reverse Tests / test (push) Successful in 12s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 5s
2026-01-31 15:43:42 +05:00
me
8173cf4dab add solution for hw4:3435
Some checks failed
Fast Reverse Tests / test (push) Successful in 12s
Reverse Tests / test (push) Successful in 6s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 5s
2026-01-31 15:39:59 +05:00
me
971119da82 add solution for hw4:3435
Some checks failed
Fast Reverse Tests / test (push) Failing after 4s
Reverse Tests / test (push) Failing after 4s
Sum Tests / test (push) Failing after 4s
Word Stat Tests / test (push) Failing after 4s
2026-01-31 15:37:58 +05:00
me
ab51c1a3dc add solution for hw4:3233
Some checks failed
Fast Reverse Tests / test (push) Successful in 13s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 8s
Word Stat Tests / test (push) Failing after 5s
2026-01-31 15:06:18 +05:00
me
826ab5a4d9 update README.md
Some checks failed
Fast Reverse Tests / test (push) Successful in 13s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 5s
2026-01-31 14:39:57 +05:00
me
03dabc9dee add Base solution for hw4
All checks were successful
Fast Reverse Tests / test (push) Successful in 14s
Reverse Tests / test (push) Successful in 7s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Successful in 5s
2026-01-31 14:30:54 +05:00
me
de97058fe9 add tests for hw4
Some checks failed
Fast Reverse Tests / test (push) Failing after 16s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 7s
Word Stat Tests / test (push) Failing after 4s
2026-01-31 13:26:06 +05:00
me
4807af64d9 upd 2026-01-31 13:25:49 +05:00
me
4be68698dd add solution for hw3:4142 & tests
Some checks failed
Fast Reverse Tests / test (push) Failing after 15s
Reverse Tests / test (push) Successful in 9s
Sum Tests / test (push) Successful in 10s
2026-01-30 23:58:25 +05:00
me
4488a76813 update runners
Some checks failed
Fast Reverse Tests / test (push) Failing after 15s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 9s
2026-01-30 23:19:37 +05:00
me
02aefc2a89 add solution for hw3:3839 & tests
Some checks failed
Fast Reverse Tests / test (push) Failing after 15s
Reverse Tests / test (push) Successful in 8s
Sum Tests / test (push) Successful in 8s
2026-01-30 23:17:36 +05:00
me
8b7b78ee4d add solution for hw3:3637 & tests in runner
Some checks failed
Fast Reverse Tests / test (push) Failing after 16s
Reverse Tests / test (push) Successful in 5s
Sum Tests / test (push) Successful in 8s
2026-01-30 23:04:13 +05:00
me
26ce449302 add Base tests for hw5
Some checks failed
Fast Reverse Tests / test (push) Failing after 26s
Reverse Tests / test (push) Successful in 6s
Sum Tests / test (push) Successful in 8s
2026-01-30 16:39:36 +05:00
me
04af91b4a7 update tests
All checks were successful
Reverse Tests / test (push) Successful in 6s
Sum Tests / test (push) Successful in 7s
2026-01-30 16:36:08 +05:00
me
8414aeb924 update README.md
All checks were successful
Reverse Tests / test (push) Successful in 6s
Sum Tests / test (push) Successful in 10s
2026-01-30 15:52:51 +05:00
me
f655cc480c add fastscanner
All checks were successful
Reverse Tests / test (push) Successful in 5s
Sum Tests / test (push) Successful in 8s
2026-01-30 15:50:09 +05:00
me
d1eac5b8b5 add Base solution for hw3
All checks were successful
Reverse Tests / test (push) Successful in 6s
Sum Tests / test (push) Successful in 9s
2026-01-30 14:32:03 +05:00
me
8a5c2a28c6 add Base tests for hw3
Some checks failed
Reverse Tests / test (push) Failing after 5s
Sum Tests / test (push) Successful in 7s
2026-01-30 13:43:25 +05:00
me
c411372ec7 add Base tests for hw3 2026-01-30 13:42:43 +05:00
me
f211ef6073 add hw3 reverse to README.md
All checks were successful
Sum Tests / test (push) Successful in 7s
2026-01-30 13:40:55 +05:00
me
3bc865b923 add badge for tests
All checks were successful
Sum Tests / test (push) Successful in 9s
2026-01-30 13:36:57 +05:00
me
0622e7e215 add badge for tests
All checks were successful
Sum Tests / test (push) Successful in 8s
2026-01-30 13:34:31 +05:00
me
ade99c8153 testing comments in readme
All checks were successful
Sum Tests / test (push) Successful in 8s
2026-01-30 13:31:26 +05:00
me
46d9e93e13 deletion
All checks were successful
Sum Tests / test (push) Successful in 9s
2026-01-30 13:26:18 +05:00
me
ffb7d736ca Add CI for Sum tests
Some checks failed
Sum Tests / test (push) Failing after 1s
2026-01-30 13:22:08 +05:00
me
8bb1a8ac0a Add CI for Sum tests
Some checks failed
Sum Tests / test (push) Failing after 14s
2026-01-30 13:19:10 +05:00
me
cb731abee6 Add CI for Sum tests
Some checks failed
Sum Tests / test (push) Failing after 43s
2026-01-30 13:11:59 +05:00
me
3b830c7dd6 Add CI for Sum tests 2026-01-30 13:08:57 +05:00
me
5fe95f078f update .gitignore 2026-01-30 12:49:32 +05:00
me
f3c258a2fc add all variants for 'Sum' task (hw2) 2026-01-30 12:41:01 +05:00
me
6e604740a5 upd 2026-01-29 22:10:58 +03:00
me
d407f92497 upd 2026-01-29 22:09:45 +03:00
me
e458f6e51e add .gitignore 2026-01-29 22:07:46 +03:00
me
3fbdccb52a upd 2026-01-29 22:07:37 +03:00
70 changed files with 3286 additions and 3540 deletions

View File

@@ -0,0 +1,22 @@
name: Fast Reverse Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Compile Java
run: |
mkdir -p out
javac -d out $(find java -name "*.java")
- name: Run Fast Reverse tests
run: |
java -ea -cp out reverse.FastReverseTest Base 3233

View File

@@ -0,0 +1,22 @@
name: Reverse Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Compile Java
run: |
mkdir -p out
javac -d out $(find java -name "*.java")
- name: Run Reverse tests
run: |
java -ea -cp out reverse.ReverseTest Base 3233 3435 3637 3839 4142

22
.gitea/workflows/sum.yml Normal file
View File

@@ -0,0 +1,22 @@
name: Sum Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Compile Java
run: |
mkdir -p out
javac -d out $(find java -name "*.java")
- name: Run Sum tests
run: |
java -ea -cp out sum.SumTest Base 3233 3435 3637 3839 4142 4749

View File

@@ -0,0 +1,22 @@
name: Word Stat Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Compile Java
run: |
mkdir -p out
javac -d out $(find java -name "*.java")
- name: Run Word Stat tests
run: |
java -ea -cp out wordStat.WordStatTest Base 3233 3435 3637 3839 4142 4749

22
.gitea/workflows/wspp.yml Normal file
View File

@@ -0,0 +1,22 @@
name: Word Stat++ Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Compile Java
run: |
mkdir -p out
javac -d out $(find java -name "*.java")
- name: Run Word Stat++ tests
run: |
java -ea -cp out wspp.WsppTest Base 3233

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
*.xml
java/out/
*.iml
*.idea*
*out*

View File

@@ -7,7 +7,6 @@ include_toc: true
[Условия домашних заданий](https://www.kgeorgiy.info/courses/prog-intro/homeworks.html) [Условия домашних заданий](https://www.kgeorgiy.info/courses/prog-intro/homeworks.html)
## Домашнее задание 14. Обработка ошибок ## Домашнее задание 14. Обработка ошибок
Модификации Модификации
@@ -276,10 +275,11 @@ include_toc: true
* Класс должен иметь имя `WsppPosition` * Класс должен иметь имя `WsppPosition`
## Домашнее задание 5. Свой сканнер ## Домашнее задание 5. Свой сканнер [![Fast Reverse Tests](https://git.fymio.us/me/prog-intro-2025/actions/workflows/fast-reverse.yml/badge.svg)](https://git.fymio.us/me/prog-intro-2025/actions)
Модификации Модификации
* *Base* * *Base*
* Исходный код тестов: [FastReverseTest.java](java/reverse/FastReverseTest.java) * Исходный код тестов: [FastReverseTest.java](java/reverse/FastReverseTest.java)
* Откомпилированные тесты: [FastReverseTest.jar](artifacts/FastReverseTest.jar) * Откомпилированные тесты: [FastReverseTest.jar](artifacts/FastReverseTest.jar)
* Аргументы командной строки: модификации * Аргументы командной строки: модификации
@@ -311,7 +311,7 @@ include_toc: true
и [закрывающими](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#END_PUNCTUATION) и [закрывающими](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#END_PUNCTUATION)
скобками скобками
* Класс должен иметь имя `ReverseRotate` * Класс должен иметь имя `ReverseRotate`
* *3233* * *3233*
* Выведите (в реверсивном порядке) только числа, * Выведите (в реверсивном порядке) только числа,
у которых сумма номеров строки и столбца четная у которых сумма номеров строки и столбца четная
* Числа дополнительно могут разделяться * Числа дополнительно могут разделяться
@@ -337,10 +337,11 @@ include_toc: true
* Класс должен иметь имя `ReverseSum` * Класс должен иметь имя `ReverseSum`
## Домашнее задание 4. Подсчет слов ## Домашнее задание 4. Подсчет слов [![Word Stat](https://git.fymio.us/me/prog-intro-2025/actions/workflows/word-stat.yml/badge.svg)](https://git.fymio.us/me/prog-intro-2025/actions)
Модификации Модификации
* *Base* * *Base*
* Класс должен иметь имя `WordStat` * Класс должен иметь имя `WordStat`
* Исходный код тестов: * Исходный код тестов:
[WordStatTest.java](java/wordStat/WordStatTest.java), [WordStatTest.java](java/wordStat/WordStatTest.java),
@@ -348,10 +349,10 @@ include_toc: true
[WordStatChecker.java](java/wordStat/WordStatChecker.java) [WordStatChecker.java](java/wordStat/WordStatChecker.java)
* Откомпилированные тесты: [WordStatTest.jar](artifacts/WordStatTest.jar) * Откомпилированные тесты: [WordStatTest.jar](artifacts/WordStatTest.jar)
* Аргументы командной строки: модификации * Аргументы командной строки: модификации
* *FastSort* * *FastSort*
* Пусть _n_ число слов во входном файле, * Пусть _n_ число слов во входном файле,
тогда программа должна работать за O(_n_ log _n_). тогда программа должна работать за O(_n_ log _n_).
* *3637* * *3637*
* Назовём _серединой слова_ подстроку, полученную удалением * Назовём _серединой слова_ подстроку, полученную удалением
первых и последних 3 символов слова. первых и последних 3 символов слова.
Слова длины меньшей 7 игнорируются. Слова длины меньшей 7 игнорируются.
@@ -359,7 +360,7 @@ include_toc: true
середины слов, встречающихся во входном файле, середины слов, встречающихся во входном файле,
упорядоченные по возрастанию длины (при равенстве по первому вхождению). упорядоченные по возрастанию длины (при равенстве по первому вхождению).
* Класс должен иметь имя `WordStatLengthMiddle` * Класс должен иметь имя `WordStatLengthMiddle`
* *3839* * *3839*
* Назовём _аффиксами слова_ * Назовём _аффиксами слова_
его префикс и суффикс длины `n / 2`, где `n` — длина слова. его префикс и суффикс длины `n / 2`, где `n` — длина слова.
Слова длины один игнорируются. Слова длины один игнорируются.
@@ -367,7 +368,7 @@ include_toc: true
аффиксы слов, встречающихся во входном файле, аффиксы слов, встречающихся во входном файле,
упорядоченные по возрастанию длины (при равенстве по первому вхождению). упорядоченные по возрастанию длины (при равенстве по первому вхождению).
* Класс должен иметь имя `WordStatLengthAffix` * Класс должен иметь имя `WordStatLengthAffix`
* *3435* * *3435*
* Назовём _суффиксом слова_ подстроку, * Назовём _суффиксом слова_ подстроку,
состоящую из `n / 2` последних символов слова, где `n` — длина слова. состоящую из `n / 2` последних символов слова, где `n` — длина слова.
Слова длины один игнорируются. Слова длины один игнорируются.
@@ -375,12 +376,12 @@ include_toc: true
суффиксы слов, встречающихся во входном файле, суффиксы слов, встречающихся во входном файле,
упорядоченные по возрастанию длины (при равенстве по первому вхождению). упорядоченные по возрастанию длины (при равенстве по первому вхождению).
* Класс должен иметь имя `WordStatLengthSuffix` * Класс должен иметь имя `WordStatLengthSuffix`
* *3233* * *3233*
* Выходной файл должен содержать все различные * Выходной файл должен содержать все различные
слова встречающиеся во входном файле, слова встречающиеся во входном файле,
упорядоченные по возрастанию длины (при равенстве по первому вхождению). упорядоченные по возрастанию длины (при равенстве по первому вхождению).
* Класс должен иметь имя `WordStatLength` * Класс должен иметь имя `WordStatLength`
* *4142* * *4142*
* Назовём _серединой слова_ подстроку, полученную удалением * Назовём _серединой слова_ подстроку, полученную удалением
первых и последних 3 символов слова. первых и последних 3 символов слова.
Слова длины меньшей 7 игнорируются. Слова длины меньшей 7 игнорируются.
@@ -388,7 +389,7 @@ include_toc: true
середины слов, встречающихся во входном файле, середины слов, встречающихся во входном файле,
упорядоченные по возрастанию длины (при равенстве по первому вхождению). упорядоченные по возрастанию длины (при равенстве по первому вхождению).
* Класс должен иметь имя `WordStatLengthMiddle` * Класс должен иметь имя `WordStatLengthMiddle`
* *4749* * *4749*
* Назовём _префиксом слова_ подстроку, * Назовём _префиксом слова_ подстроку,
состоящую из `n / 2` первых символов слова, где `n` — длина слова. состоящую из `n / 2` первых символов слова, где `n` — длина слова.
Слова длины один игнорируются. Слова длины один игнорируются.
@@ -397,24 +398,24 @@ include_toc: true
упорядоченные по возрастанию длины (при равенстве по первому вхождению). упорядоченные по возрастанию длины (при равенстве по первому вхождению).
* Класс должен иметь имя `WordStatLengthPrefix` * Класс должен иметь имя `WordStatLengthPrefix`
## Домашнее задание 3. Реверс [![Reverse Tests](https://git.fymio.us/me/prog-intro-2025/actions/workflows/reverse.yml/badge.svg)](https://git.fymio.us/me/prog-intro-2025/actions)
## Домашнее задание 3. Реверс
Модификации Модификации
* *Base* * *Base*
* Исходный код тестов: * Исходный код тестов:
[ReverseTest.java](java/reverse/ReverseTest.java), [ReverseTest.java](java/reverse/ReverseTest.java),
[ReverseTester.java](java/reverse/ReverseTester.java) [ReverseTester.java](java/reverse/ReverseTester.java)
* Откомпилированные тесты: [ReverseTest.jar](artifacts/ReverseTest.jar) * Откомпилированные тесты: [ReverseTest.jar](artifacts/ReverseTest.jar)
* Аргументы командной строки: модификации * Аргументы командной строки: модификации
* *Memory* * *Memory*
* Программа должна сначала считывать все данные в память, * Программа должна сначала считывать все данные в память,
и только потом обрабатывать их. и только потом обрабатывать их.
* Пусть _M_ объём памяти, необходимый для сохранения ввода * Пусть _M_ объём памяти, необходимый для сохранения ввода
в двумерном массиве `int` минимального размера. в двумерном массиве `int` минимального размера.
Ваша программа должна использовать не более 4_M_ + 1024 байт памяти. Ваша программа должна использовать не более 4_M_ + 1024 байт памяти.
* Накладные расходы на запуск вашей программы JVM не учитываются. * Накладные расходы на запуск вашей программы JVM не учитываются.
* *3637* * *3637*
* Рассмотрим входные данные как (не полностью определенную) матрицу, * Рассмотрим входные данные как (не полностью определенную) матрицу,
вместо каждого числа выведите максимум из чисел, вместо каждого числа выведите максимум из чисел,
находящихся в его столбце в последующих строках, и его самого находящихся в его столбце в последующих строках, и его самого
@@ -424,7 +425,7 @@ include_toc: true
вместо каждого числа выведите максимум из чисел вместо каждого числа выведите максимум из чисел
текущее число — правый нижний угол матрицы текущее число — правый нижний угол матрицы
* Класс должен иметь имя `ReverseMax` * Класс должен иметь имя `ReverseMax`
* *3435* * *3435*
* Рассмотрим входные данные как (не полностью определенную) матрицу, * Рассмотрим входные данные как (не полностью определенную) матрицу,
выведите ее поворот по часовой стрелке, например для ввода выведите ее поворот по часовой стрелке, например для ввода
``` ```
@@ -440,38 +441,37 @@ include_toc: true
4 4
``` ```
* Класс должен иметь имя `ReverseRotate` * Класс должен иметь имя `ReverseRotate`
* *3233* * *3233*
* Выведите (в реверсивном порядке) только числа, * Выведите (в реверсивном порядке) только числа,
у которых сумма номеров строки и столбца четная у которых сумма номеров строки и столбца четная
* Класс должен иметь имя `ReverseEven` * Класс должен иметь имя `ReverseEven`
* *4142* * *4142*
* Рассмотрим входные данные как (не полностью определенную) матрицу, * Рассмотрим входные данные как (не полностью определенную) матрицу,
вместо каждого числа выведите среднее из чисел в его столбце и строке вместо каждого числа выведите среднее из чисел в его столбце и строке
* Класс должен иметь имя `ReverseAvg` * Класс должен иметь имя `ReverseAvg`
* *4749* * *4749*
* Рассмотрим входные данные как (не полностью определенную) матрицу, * Рассмотрим входные данные как (не полностью определенную) матрицу,
вместо каждого числа выведите сумму чиселв его столбце и строке вместо каждого числа выведите сумму чиселв его столбце и строке
* Класс должен иметь имя `ReverseSum` * Класс должен иметь имя `ReverseSum`
## Домашнее задание 2. Сумма чисел [![Sum Tests](https://git.fymio.us/me/prog-intro-2025/actions/workflows/sum.yml/badge.svg)](https://git.fymio.us/me/prog-intro-2025/actions)
## Домашнее задание 2. Сумма чисел
Модификации Модификации
* *Base* * *Base*
* Исходный код тестов: * Исходный код тестов:
[SumTest.java](java/sum/SumTest.java), [SumTest.java](java/sum/SumTest.java),
[SumTester.java](java/sum/SumTester.java), [SumTester.java](java/sum/SumTester.java),
[базовые классы](java/base/) [базовые классы](java/base/)
* Откомпилированные тесты: [SumTest.jar](artifacts/SumTest.jar) * Откомпилированные тесты: [SumTest.jar](artifacts/SumTest.jar)
* Аргументы командной строки: модификации * Аргументы командной строки: модификации
* *3637* * *3637*
* Входные данные являются 64-битными числами в формате с плавающей точкой * Входные данные являются 64-битными числами в формате с плавающей точкой
* На вход подаются десятичные и шестнадцатеричные числа * На вход подаются десятичные и шестнадцатеричные числа
* Шестнадцатеричные числа имеют префикс `0x`, * Шестнадцатеричные числа имеют префикс `0x`,
например `0xa.bp2` равно (10+11/16)·4 равно 42.75 например `0xa.bp2` равно (10+11/16)·4 равно 42.75
* Ввод регистронезависим * Ввод регистронезависим
* Класс должен иметь имя `SumDoubleHex` * Класс должен иметь имя `SumDoubleHex`
* *3839* * *3839*
* Входные данные помещаются в тип [BigDecimal](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/math/BigDecimal.html) * Входные данные помещаются в тип [BigDecimal](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/math/BigDecimal.html)
* На вход подаются десятичные и шестнадцатеричные числа * На вход подаются десятичные и шестнадцатеричные числа
* Шестнадцатеричные числа имеют префикс `0x`, * Шестнадцатеричные числа имеют префикс `0x`,
@@ -479,22 +479,23 @@ include_toc: true
(мантисса и порядок являются целыми числами) (мантисса и порядок являются целыми числами)
* Ввод регистронезависим * Ввод регистронезависим
* Класс должен иметь имя `SumBigDecimalHex` * Класс должен иметь имя `SumBigDecimalHex`
* *3435* * *3435*
* На вход подаются десятичные и шестнадцатеричные числа * На вход подаются десятичные и шестнадцатеричные числа
* Шестнадцатеричные числа имеют префикс `0x` * Шестнадцатеричные числа имеют префикс `0x`
* Ввод регистронезависим * Ввод регистронезависим
* Класс должен иметь имя `SumHex` * Класс должен иметь имя `SumHex`
* *3233* * *3233*
* Входные данные являются 64-битными числами в формате с плавающей точкой * Входные данные являются 64-битными числами в формате с плавающей точкой
* Класс должен иметь имя `SumDouble` * Класс должен иметь имя `SumDouble`
* *4142* * *4142*
* Входные данные помещаются в тип [BigInteger](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/BigInteger.html) * Входные данные помещаются в тип [BigInteger](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/BigInteger.html)
* восьмеричные числа имеют суффикс `o` * восьмеричные числа имеют суффикс `o`
* Класс должен иметь имя `SumBigIntegerOctal` * Класс должен иметь имя `SumBigIntegerOctal`
* *4749* * *4749*
* Входные данные являются 64-битными целыми числами * Входные данные являются 64-битными целыми числами
* восьмеричные числа имеют суффикс `o` * восьмеричные числа имеют суффикс `o`
* Класс должен иметь имя `SumLongOctal` * Класс должен иметь имя `SumLongOctal`
<!--
Для того, чтобы протестировать программу: Для того, чтобы протестировать программу:
@@ -548,3 +549,5 @@ include_toc: true
1. Запустите и проверьте, что вы получили ту же ссылку, что и в предыдущем пункте 1. Запустите и проверьте, что вы получили ту же ссылку, что и в предыдущем пункте
1. Сдайте скрипт преподавателю 1. Сдайте скрипт преподавателю
1. Вы можете получить больше плюсиков, модифицируя код `RunMe.java` 1. Вы можете получить больше плюсиков, модифицируя код `RunMe.java`
-->

View File

@@ -1,202 +0,0 @@
package expression;
import base.Asserts;
import base.Pair;
import base.TestCounter;
import expression.common.ExpressionKind;
import expression.common.Type;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.IntStream;
/**
* One-argument arithmetic expression over {@link BigDecimal}s.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
@SuppressWarnings("ClassReferencesSubclass")
public interface BigDecimalListExpression extends ToMiniString {
BigDecimal evaluateBd(List<BigDecimal> variables);
// Tests follow. You may temporarily remove everything til the end.
Add EXAMPLE = new Add(
new Subtract(new Variable(0), new Const(BigDecimal.ONE)),
new Multiply(new Variable(1), new Const(BigDecimal.TEN))
);
Type<BigDecimal> TYPE = new Type<>(
v -> new BigDecimal(v + ".000"),
random -> BigDecimal.valueOf(random.getRandom().nextGaussian()),
BigDecimal.class
);
ExpressionKind<BigDecimalListExpression, BigDecimal> KIND = new ExpressionKind<>(
TYPE,
BigDecimalListExpression.class,
(r, c) -> IntStream.range(0, c)
.mapToObj(name -> Pair.<String, BigDecimalListExpression>of("$" + name, new Variable(name)))
.toList(),
(expr, variables, values) -> expr.evaluateBd(values)
);
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
static ExpressionTester<?, ?> tester(final TestCounter counter) {
Asserts.assertEquals("Example toString()", "(($0 - 1) + ($1 * 10))", EXAMPLE.toString());
Asserts.assertEquals(
EXAMPLE + " at (2, 3)",
BigDecimal.valueOf(31),
EXAMPLE.evaluateBd(List.of(BigDecimal.valueOf(2), BigDecimal.valueOf(3)))
);
final Variable vx = new Variable(0);
final Variable vy = new Variable(1);
return new ExpressionTester<>(
counter, KIND, c -> v -> c,
(op, a, b) -> v -> op.apply(a.evaluateBd(v), b.evaluateBd(v)),
BigDecimal::add, BigDecimal::subtract, BigDecimal::multiply, BigDecimal::divide
)
.basic("10", "10", v -> v(10), c(10))
.basic("$x", "$x", BigDecimalListExpression::x, vx)
.basic("$y", "$y", BigDecimalListExpression::y, vy)
.basic("($x + $y)", "$x + $y", v -> x(v).add(y(v)), new Add(vx, vy))
.basic("($x + 2)", "$x + 2", v -> x(v).add(v(2)), new Add(vx, c(2)))
.basic("(2 - $x)", "2 - $x", v -> v(2).subtract(x(v)), new Subtract(c(2), vx))
.basic("(3 * $x)", "3 * $x", v -> v(3).multiply(x(v)), new Multiply(c(3), vx))
.basic("($x + $x)", "$x + $x", v -> x(v).add(x(v)), new Add(vx, vx))
.basic("($x / -2)", "$x / -2", v -> x(v).divide(v(-2)), new Divide(vx, c(-2)))
.basic("(2 + $x)", "2 + $x", v -> v(2).add(x(v)), new Add(c(2), vx))
.basic("((1 + 2) + 3)", "1 + 2 + 3", v -> v(6), new Add(new Add(c(1), c(2)), c(3)))
.basic("(1 + (2 * 3))", "1 + 2 * 3", v -> v(7), new Add(c(1), new Multiply(c(2), c(3))))
.basic("(1 - (2 * 3))", "1 - 2 * 3", v -> v(-5), new Subtract(c(1), new Multiply(c(2), c(3))))
.basic("(1 + (2 + 3))", "1 + 2 + 3", v -> v(6), new Add(c(1), new Add(c(2), c(3))))
.basic("((1 - 2) - 3)", "1 - 2 - 3", v -> v(-4), new Subtract(new Subtract(c(1), c(2)), c(3)))
.basic("(1 - (2 - 3))", "1 - (2 - 3)", v -> v(2), new Subtract(c(1), new Subtract(c(2), c(3))))
.basic("((1 * 2) * 3)", "1 * 2 * 3", v -> v(6), new Multiply(new Multiply(c(1), c(2)), c(3)))
.basic("(1 * (2 * 3))", "1 * 2 * 3", v -> v(6), new Multiply(c(1), new Multiply(c(2), c(3))))
.basic("((10 / 2) / 3)", "10 / 2 / 3", v -> v(10).divide(v(2)).divide(v(3)), new Divide(new Divide(c(10), c(2)), c(3)))
.basic("(10 / (3 / 2))", "10 / (3 / 2)", v -> v(10).divide(v(3).divide(v(2))), new Divide(c(10), new Divide(c(3), c(2))))
.basic("(($x * $x) + (($x - 1) / 10))",
"$x * $x + ($x - 1) / 10",
v -> x(v).multiply(x(v)).add(x(v).subtract(v(1)).divide(v(10))),
new Add(new Multiply(vx, vx), new Divide(new Subtract(vx, c(1)), c(10)))
)
.basic("($x * -1000000000)", "$x * -1000000000", v -> x(v).multiply(v(-1_000_000_000)), new Multiply(vx, c(-1_000_000_000)))
.basic("($x * -1000000000000000)", "$x * -1000000000000000", v -> x(v).multiply(v(-1_000_000_000_000_000L)), new Multiply(vx, c(-1_000_000_000_000_000L)))
.basic("(10 / $x)", "10 / $x", v -> v(10).divide(x(v)), new Divide(c(10), vx))
.basic("($x / $x)", "$x / $x", v -> x(v).divide(x(v)), new Divide(vx, vx))
.advanced("(2 + 1)", "2 + 1", v -> v(2 + 1), new Add(c(2), c(1)))
.advanced("($x - 1)", "$x - 1", v -> x(v).subtract(v(1)), new Subtract(vx, c(1)))
.advanced("(1 * 2)", "1 * 2", v -> v(1 * 2), new Multiply(c(1), c(2)))
.advanced("($x / 1)", "$x / 1", v -> x(v).divide(v(1)), new Divide(vx, c(1)))
.advanced("(1 + (2 + 1))", "1 + 2 + 1", v -> v(1 + 2 + 1), new Add(c(1), new Add(c(2), c(1))))
.advanced("($x - ($x - 1))", "$x - ($x - 1)", v -> x(v).subtract(x(v).subtract(v(1))), new Subtract(vx, new Subtract(vx, c(1))))
.advanced("(2 * ($x / 1))", "2 * ($x / 1)", v -> v(2).multiply(x(v).divide(v(1))), new Multiply(c(2), new Divide(vx, c(1))))
.advanced("(2 / ($x - 1))", "2 / ($x - 1)", v -> v(2).divide(x(v).subtract(v(1))), new Divide(c(2), new Subtract(vx, c(1))))
.advanced("((1 * 2) + $x)", "1 * 2 + $x", v -> v(1 * 2).add(x(v)), new Add(new Multiply(c(1), c(2)), vx))
.advanced("(($x - 1) - 2)", "$x - 1 - 2", v -> x(v).subtract(v(3)), new Subtract(new Subtract(vx, c(1)), c(2)))
.advanced("(($x / 1) * 2)", "$x / 1 * 2", v -> x(v).multiply(v(2)), new Multiply(new Divide(vx, c(1)), c(2)))
.advanced("((2 + 1) / 1)", "(2 + 1) / 1", v -> v(3), new Divide(new Add(c(2), c(1)), c(1)))
.advanced(
"(1 + (1 + (2 + 1)))",
"1 + 1 + 2 + 1",
v -> v(1 + 1 + 2 + 1),
new Add(c(1), new Add(c(1), new Add(c(2), c(1))))
)
.advanced(
"($x - ((1 * 2) + $x))",
"$x - (1 * 2 + $x)",
v -> x(v).subtract(v(1 * 2).add(x(v))),
new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx))
)
.advanced(
"($x * (2 / ($x - 1)))",
"$x * (2 / ($x - 1))",
v -> x(v).multiply(v(2).divide(x(v).subtract(v(1)))),
new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1))))
)
.advanced(
"($x / (1 + (2 + 1)))",
"$x / (1 + 2 + 1)",
v -> x(v).divide(v(1 + 2 + 1)),
new Divide(vx, new Add(c(1), new Add(c(2), c(1))))
)
.advanced(
"((1 * 2) + (2 + 1))",
"1 * 2 + 2 + 1",
v -> v(1 * 2 + 2 + 1),
new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1)))
)
.advanced(
"((2 + 1) - (2 + 1))",
"2 + 1 - (2 + 1)",
v -> v(2 + 1 - (2 + 1)),
new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1)))
)
.advanced(
"(($x - 1) * ($x / 1))",
"($x - 1) * ($x / 1)",
v -> x(v).subtract(v(1)).multiply(x(v).divide(v(1))),
new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1)))
)
.advanced(
"(($x - 1) / (1 * 2))",
"($x - 1) / (1 * 2)",
v -> x(v).subtract(v(1)).divide(v(2)),
new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2)))
)
.advanced(
"((($x - 1) - 2) + $x)",
"$x - 1 - 2 + $x",
v -> x(v).subtract(v(3)).add(x(v)),
new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx)
)
.advanced(
"(((1 * 2) + $x) - 1)",
"1 * 2 + $x - 1",
v -> v(1).add(x(v)),
new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1))
)
.advanced(
"(((2 + 1) / 1) * $x)",
"(2 + 1) / 1 * $x",
v -> v(3).multiply(x(v)),
new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx)
)
.advanced(
"((2 / ($x - 1)) / 2)",
"2 / ($x - 1) / 2",
v -> v(2).divide(x(v).subtract(v(1))).divide(v(2)),
new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2))
);
}
private static BigDecimal x(final List<BigDecimal> vars) {
return vars.get(0);
}
private static BigDecimal y(final List<BigDecimal> vars) {
return vars.get(1);
}
private static Const c(final BigDecimal v) {
return TYPE.constant(v);
}
private static Const c(final long v) {
return TYPE.constant(v(v));
}
private static BigDecimal v(final long v) {
return BigDecimal.valueOf(v);
}
static void main(final String... args) {
TripleExpression.SELECTOR
.variant("BigDecimalList", ExpressionTest.v(BigDecimalListExpression::tester))
.main(args);
}
}

View File

@@ -1,192 +0,0 @@
package expression;
import base.Asserts;
import base.Pair;
import base.TestCounter;
import expression.common.ExpressionKind;
import expression.common.Type;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.IntStream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
@SuppressWarnings("ClassReferencesSubclass")
public interface BigIntegerListExpression extends ToMiniString {
BigInteger evaluateBi(List<BigInteger> variables);
// Tests follow. You may temporarily remove everything til the end.
Add EXAMPLE = new Add(
new Subtract(new Variable(0), new Const(BigInteger.ONE)),
new Multiply(new Variable(1), new Const(BigInteger.TEN))
);
Type<BigInteger> TYPE = new Type<>(BigInteger::valueOf, random -> v(random.getRandom().nextLong()), BigInteger.class);
ExpressionKind<BigIntegerListExpression, BigInteger> KIND = new ExpressionKind<>(
TYPE,
BigIntegerListExpression.class,
(r, c) -> IntStream.range(0, c)
.mapToObj(name -> Pair.<String, BigIntegerListExpression>of("$" + name, new Variable(name)))
.toList(),
(expr, variables, values) -> expr.evaluateBi(values)
);
@SuppressWarnings("PointlessArithmeticExpression")
static ExpressionTester<?, ?> tester(final TestCounter counter) {
Asserts.assertEquals("Example toString()", "(($0 - 1) + ($1 * 10))", EXAMPLE.toString());
Asserts.assertEquals(
EXAMPLE + " at (2, 3)",
BigInteger.valueOf(31),
EXAMPLE.evaluateBi(List.of(BigInteger.valueOf(2), BigInteger.valueOf(3)))
);
final Variable vx = new Variable(0);
final Variable vy = new Variable(1);
return new ExpressionTester<>(
counter, KIND, c -> v -> c,
(op, a, b) -> v -> op.apply(a.evaluateBi(v), b.evaluateBi(v)),
BigInteger::add, BigInteger::subtract, BigInteger::multiply, BigInteger::divide
)
.basic("10", "10", v -> v(10), c(10))
.basic("$x", "$x", BigIntegerListExpression::x, vx)
.basic("$y", "$y", BigIntegerListExpression::y, vy)
.basic("($x + $y)", "$x + $y", v -> x(v).add(y(v)), new Add(vx, vy))
.basic("($x + 2)", "$x + 2", v -> x(v).add(v(2)), new Add(vx, c(2)))
.basic("(2 - $x)", "2 - $x", v -> v(2).subtract(x(v)), new Subtract(c(2), vx))
.basic("(3 * $x)", "3 * $x", v -> v(3).multiply(x(v)), new Multiply(c(3), vx))
.basic("($x + $x)", "$x + $x", v -> x(v).add(x(v)), new Add(vx, vx))
.basic("($x / -2)", "$x / -2", v -> x(v).divide(v(-2)), new Divide(vx, c(-2)))
.basic("(2 + $x)", "2 + $x", v -> v(2).add(x(v)), new Add(c(2), vx))
.basic("((1 + 2) + 3)", "1 + 2 + 3", v -> v(6), new Add(new Add(c(1), c(2)), c(3)))
.basic("(1 + (2 * 3))", "1 + 2 * 3", v -> v(7), new Add(c(1), new Multiply(c(2), c(3))))
.basic("(1 - (2 * 3))", "1 - 2 * 3", v -> v(-5), new Subtract(c(1), new Multiply(c(2), c(3))))
.basic("(1 + (2 + 3))", "1 + 2 + 3", v -> v(6), new Add(c(1), new Add(c(2), c(3))))
.basic("((1 - 2) - 3)", "1 - 2 - 3", v -> v(-4), new Subtract(new Subtract(c(1), c(2)), c(3)))
.basic("(1 - (2 - 3))", "1 - (2 - 3)", v -> v(2), new Subtract(c(1), new Subtract(c(2), c(3))))
.basic("((1 * 2) * 3)", "1 * 2 * 3", v -> v(6), new Multiply(new Multiply(c(1), c(2)), c(3)))
.basic("(1 * (2 * 3))", "1 * 2 * 3", v -> v(6), new Multiply(c(1), new Multiply(c(2), c(3))))
.basic("((10 / 2) / 3)", "10 / 2 / 3", v -> v(10 / 2 / 3), new Divide(new Divide(c(10), c(2)), c(3)))
.basic("(10 / (3 / 2))", "10 / (3 / 2)", v -> v(10 / (3 / 2)), new Divide(c(10), new Divide(c(3), c(2))))
.basic("(($x * $x) + (($x - 1) / 10))",
"$x * $x + ($x - 1) / 10",
v -> x(v).multiply(x(v)).add(x(v).subtract(v(1)).divide(v(10))),
new Add(new Multiply(vx, vx), new Divide(new Subtract(vx, c(1)), c(10)))
)
.basic("($x * -1000000000)", "$x * -1000000000", v -> x(v).multiply(v(-1_000_000_000)), new Multiply(vx, c(-1_000_000_000)))
.basic("($x * -1000000000000000)", "$x * -1000000000000000", v -> x(v).multiply(v(-1_000_000_000_000_000L)), new Multiply(vx, c(-1_000_000_000_000_000L)))
.basic("(10 / $x)", "10 / $x", v -> v(10).divide(x(v)), new Divide(c(10), vx))
.basic("($x / $x)", "$x / $x", v -> x(v).divide(x(v)), new Divide(vx, vx))
.advanced("(2 + 1)", "2 + 1", v -> v(2 + 1), new Add(c(2), c(1)))
.advanced("($x - 1)", "$x - 1", v -> x(v).subtract(v(1)), new Subtract(vx, c(1)))
.advanced("(1 * 2)", "1 * 2", v -> v(1 * 2), new Multiply(c(1), c(2)))
.advanced("($x / 1)", "$x / 1", v -> x(v).divide(v(1)), new Divide(vx, c(1)))
.advanced("(1 + (2 + 1))", "1 + 2 + 1", v -> v(1 + 2 + 1), new Add(c(1), new Add(c(2), c(1))))
.advanced("($x - ($x - 1))", "$x - ($x - 1)", v -> x(v).subtract(x(v).subtract(v(1))), new Subtract(vx, new Subtract(vx, c(1))))
.advanced("(2 * ($x / 1))", "2 * ($x / 1)", v -> v(2).multiply(x(v).divide(v(1))), new Multiply(c(2), new Divide(vx, c(1))))
.advanced("(2 / ($x - 1))", "2 / ($x - 1)", v -> v(2).divide(x(v).subtract(v(1))), new Divide(c(2), new Subtract(vx, c(1))))
.advanced("((1 * 2) + $x)", "1 * 2 + $x", v -> v(1 * 2).add(x(v)), new Add(new Multiply(c(1), c(2)), vx))
.advanced("(($x - 1) - 2)", "$x - 1 - 2", v -> x(v).subtract(v(3)), new Subtract(new Subtract(vx, c(1)), c(2)))
.advanced("(($x / 1) * 2)", "$x / 1 * 2", v -> x(v).multiply(v(2)), new Multiply(new Divide(vx, c(1)), c(2)))
.advanced("((2 + 1) / 1)", "(2 + 1) / 1", v -> v(3), new Divide(new Add(c(2), c(1)), c(1)))
.advanced(
"(1 + (1 + (2 + 1)))",
"1 + 1 + 2 + 1",
v -> v(1 + 1 + 2 + 1),
new Add(c(1), new Add(c(1), new Add(c(2), c(1))))
)
.advanced(
"($x - ((1 * 2) + $x))",
"$x - (1 * 2 + $x)",
v -> x(v).subtract(v(1 * 2).add(x(v))),
new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx))
)
.advanced(
"($x * (2 / ($x - 1)))",
"$x * (2 / ($x - 1))",
v -> x(v).multiply(v(2).divide(x(v).subtract(v(1)))),
new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1))))
)
.advanced(
"($x / (1 + (2 + 1)))",
"$x / (1 + 2 + 1)",
v -> x(v).divide(v(1 + 2 + 1)),
new Divide(vx, new Add(c(1), new Add(c(2), c(1))))
)
.advanced(
"((1 * 2) + (2 + 1))",
"1 * 2 + 2 + 1",
v -> v(1 * 2 + 2 + 1),
new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1)))
)
.advanced(
"((2 + 1) - (2 + 1))",
"2 + 1 - (2 + 1)",
v -> v(2 + 1 - (2 + 1)),
new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1)))
)
.advanced(
"(($x - 1) * ($x / 1))",
"($x - 1) * ($x / 1)",
v -> x(v).subtract(v(1)).multiply(x(v).divide(v(1))),
new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1)))
)
.advanced(
"(($x - 1) / (1 * 2))",
"($x - 1) / (1 * 2)",
v -> x(v).subtract(v(1)).divide(v(2)),
new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2)))
)
.advanced(
"((($x - 1) - 2) + $x)",
"$x - 1 - 2 + $x",
v -> x(v).subtract(v(3)).add(x(v)),
new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx)
)
.advanced(
"(((1 * 2) + $x) - 1)",
"1 * 2 + $x - 1",
v -> v(1).add(x(v)),
new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1))
)
.advanced(
"(((2 + 1) / 1) * $x)",
"(2 + 1) / 1 * $x",
v -> v(3).multiply(x(v)),
new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx)
)
.advanced(
"((2 / ($x - 1)) / 2)",
"2 / ($x - 1) / 2",
v -> v(2).divide(x(v).subtract(v(1))).divide(v(2)),
new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2))
);
}
private static BigInteger x(final List<BigInteger> vars) {
return vars.get(0);
}
private static BigInteger y(final List<BigInteger> vars) {
return vars.get(1);
}
private static Const c(final long v) {
return TYPE.constant(v(v));
}
private static BigInteger v(final long v) {
return BigInteger.valueOf(v);
}
static void main(final String... args) {
TripleExpression.SELECTOR
.variant("BigIntegerList", ExpressionTest.v(BigIntegerListExpression::tester))
.main(args);
}
}

View File

@@ -1,112 +0,0 @@
package expression;
import base.Asserts;
import base.ExtendedRandom;
import base.Pair;
import base.TestCounter;
import expression.common.ExpressionKind;
import expression.common.Type;
import java.util.List;
/**
* One-argument arithmetic expression over integers.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
@SuppressWarnings("ClassReferencesSubclass")
public interface Expression extends ToMiniString {
int evaluate(int x);
// Tests follow. You may temporarily remove everything til the end.
Subtract EXAMPLE = new Subtract(
new Multiply(new Const(2), new Variable("x")),
new Const(3)
);
Type<Integer> TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class);
ExpressionKind<Expression, Integer> KIND = new ExpressionKind<>(
TYPE,
Expression.class,
List.of(Pair.of("x", new Variable("x"))),
(expr, variables, values) -> expr.evaluate(values.get(0))
);
private static Const c(final int c) {
return new Const(c);
}
@SuppressWarnings({"PointlessArithmeticExpression", "Convert2MethodRef"})
static ExpressionTester<?, ?> tester(final TestCounter counter) {
Asserts.assertEquals("Example toString()", "((2 * x) - 3)", EXAMPLE.toString());
Asserts.assertEquals("Example at 5", 7, EXAMPLE.evaluate(5));
Asserts.assertTrue("Example equals 1",
new Multiply(new Const(2), new Variable("x"))
.equals(new Multiply(new Const(2), new Variable("x"))));
Asserts.assertTrue("Example equals 2",
!new Multiply(new Const(2), new Variable("x"))
.equals(new Multiply(new Variable("x"), new Const(2))));
final Variable vx = new Variable("x");
final Const c1 = c(1);
final Const c2 = c(2);
return new ExpressionTester<>(
counter, KIND, c -> x -> c,
(op, a, b) -> x -> op.apply(a.evaluate(x), b.evaluate(x)),
(a, b) -> a + b, (a, b) -> a - b, (a, b) -> a * b, (a, b) -> a / b
)
.basic("10", "10", x -> 10, c(10))
.basic("x", "x", x -> x, vx)
.basic("(x + 2)", "x + 2", x -> x + 2, new Add(vx, c(2)))
.basic("(2 - x)", "2 - x", x -> 2 - x, new Subtract(c(2), vx))
.basic("(3 * x)", "3 * x", x -> 3*x, new Multiply(c(3), vx))
.basic("(x + x)", "x + x", x -> x + x, new Add(vx, vx))
.basic("(x / -2)", "x / -2", x -> -x / 2, new Divide(vx, c(-2)))
.basic("(2 + x)", "2 + x", x -> 2 + x, new Add(c(2), vx))
.basic("((1 + 2) + 3)", "1 + 2 + 3", x -> 6, new Add(new Add(c(1), c(2)), c(3)))
.basic("(1 + (2 + 3))", "1 + 2 + 3", x -> 6, new Add(c(1), new Add(c(2), c(3))))
.basic("((1 - 2) - 3)", "1 - 2 - 3", x -> -4, new Subtract(new Subtract(c(1), c(2)), c(3)))
.basic("(1 - (2 - 3))", "1 - (2 - 3)", x -> 2, new Subtract(c(1), new Subtract(c(2), c(3))))
.basic("((1 * 2) * 3)", "1 * 2 * 3", x -> 6, new Multiply(new Multiply(c(1), c(2)), c(3)))
.basic("(1 * (2 * 3))", "1 * 2 * 3", x -> 6, new Multiply(c(1), new Multiply(c(2), c(3))))
.basic("((10 / 2) / 3)", "10 / 2 / 3", x -> 10 / 2 / 3, new Divide(new Divide(c(10), c(2)), c(3)))
.basic("(10 / (3 / 2))", "10 / (3 / 2)", x -> 10 / (3 / 2), new Divide(c(10), new Divide(c(3), c(2))))
.basic("(10 * (3 / 2))", "10 * (3 / 2)", x -> 10 * (3 / 2), new Multiply(c(10), new Divide(c(3), c(2))))
.basic("(10 + (3 - 2))", "10 + 3 - 2", x -> 10 + (3 - 2), new Add(c(10), new Subtract(c(3), c(2))))
.basic("((x * x) + ((x - 1) / 10))", "x * x + (x - 1) / 10", x -> x * x + (x - 1) / 10, new Add(
new Multiply(vx, vx),
new Divide(new Subtract(vx, c(1)), c(10))
))
.basic("(x * -1000000000)", "x * -1000000000", x -> x * -1_000_000_000, new Multiply(vx, c(-1_000_000_000)))
.basic("(10 / x)", "10 / x", x -> 10 / x, new Divide(c(10), vx))
.basic("(x / x)", "x / x", x -> x / x, new Divide(vx, vx))
.advanced("(2 + 1)", "2 + 1", x -> 2 + 1, new Add(c2, c1))
.advanced("(x - 1)", "x - 1", x -> x - 1, new Subtract(vx, c1))
.advanced("(1 * 2)", "1 * 2", x -> 1 * 2, new Multiply(c1, c2))
.advanced("(x / 1)", "x / 1", x -> x / 1, new Divide(vx, c1))
.advanced("(1 + (2 + 1))", "1 + 2 + 1", x -> 1 + 2 + 1, new Add(c1, new Add(c2, c1)))
.advanced("(x - (x - 1))", "x - (x - 1)", x -> x - (x - 1), new Subtract(vx, new Subtract(vx, c1)))
.advanced("(2 * (x / 1))", "2 * (x / 1)", x -> 2 * (x / 1), new Multiply(c2, new Divide(vx, c1)))
.advanced("(2 / (x - 1))", "2 / (x - 1)", x -> 2 / (x - 1), new Divide(c2, new Subtract(vx, c1)))
.advanced("((1 * 2) + x)", "1 * 2 + x", x -> 1 * 2 + x, new Add(new Multiply(c1, c2), vx))
.advanced("((x - 1) - 2)", "x - 1 - 2", x -> x - 1 - 2, new Subtract(new Subtract(vx, c1), c2))
.advanced("((x / 1) * 2)", "x / 1 * 2", x -> x / 1 * 2, new Multiply(new Divide(vx, c1), c2))
.advanced("((2 + 1) / 1)", "(2 + 1) / 1", x -> (2 + 1) / 1, new Divide(new Add(c2, c1), c1))
.advanced("(1 + (1 + (2 + 1)))", "1 + 1 + 2 + 1", x -> 1 + 1 + 2 + 1, new Add(c1, new Add(c1, new Add(c2, c1))))
.advanced("(x - ((1 * 2) + x))", "x - (1 * 2 + x)", x -> x - (1 * 2 + x), new Subtract(vx, new Add(new Multiply(c1, c2), vx)))
.advanced("(x * (2 / (x - 1)))", "x * (2 / (x - 1))", x -> x * (2 / (x - 1)), new Multiply(vx, new Divide(c2, new Subtract(vx, c1))))
.advanced("(x / (1 + (2 + 1)))", "x / (1 + 2 + 1)", x -> x / (1 + 2 + 1), new Divide(vx, new Add(c1, new Add(c2, c1))))
.advanced("((1 * 2) + (2 + 1))", "1 * 2 + 2 + 1", x -> 1 * 2 + 2 + 1, new Add(new Multiply(c1, c2), new Add(c2, c1)))
.advanced("((2 + 1) - (2 + 1))", "2 + 1 - (2 + 1)", x -> 2 + 1 - (2 + 1), new Subtract(new Add(c2, c1), new Add(c2, c1)))
.advanced("((x - 1) * (x / 1))", "(x - 1) * (x / 1)", x -> (x - 1) * (x / 1), new Multiply(new Subtract(vx, c1), new Divide(vx, c1)))
.advanced("((x - 1) / (1 * 2))", "(x - 1) / (1 * 2)", x -> (x - 1) / (1 * 2), new Divide(new Subtract(vx, c1), new Multiply(c1, c2)))
.advanced("(((x - 1) - 2) + x)", "x - 1 - 2 + x", x -> x - 1 - 2 + x, new Add(new Subtract(new Subtract(vx, c1), c2), vx))
.advanced("(((1 * 2) + x) - 1)", "1 * 2 + x - 1", x -> 1 * 2 + x - 1, new Subtract(new Add(new Multiply(c1, c2), vx), c1))
.advanced("(((2 + 1) / 1) * x)", "(2 + 1) / 1 * x", x -> (2 + 1) / 1 * x, new Multiply(new Divide(new Add(c2, c1), c1), vx))
.advanced("((2 / (x - 1)) / 2)", "2 / (x - 1) / 2", x -> 2 / (x - 1) / 2, new Divide(new Divide(c2, new Subtract(vx, c1)), c2));
}
}

View File

@@ -1,26 +0,0 @@
package expression;
import base.Selector;
import base.TestCounter;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class ExpressionTest {
public static final Selector SELECTOR = new Selector(ExpressionTest.class, "easy", "hard")
.variant("Base", v(Expression::tester));
private ExpressionTest() {
}
public static Consumer<TestCounter> v(final Function<TestCounter, ? extends ExpressionTester<?, ?>> tester) {
return t -> tester.apply(t).test();
}
public static void main(final String... args) {
SELECTOR.main(args);
}
}

View File

@@ -1,288 +0,0 @@
package expression;
import base.*;
import expression.common.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static base.Asserts.assertTrue;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ExpressionTester<E extends ToMiniString, C> extends Tester {
private final List<Integer> VALUES = IntStream.rangeClosed(-10, 10).boxed().toList();
private final ExpressionKind<E, C> kind;
private final List<Test> basic = new ArrayList<>();
private final List<Test> advanced = new ArrayList<>();
private final Set<String> used = new HashSet<>();
private final GeneratorBuilder generator;
private final List<Pair<ToMiniString, String>> prev = new ArrayList<>();
private final Map<String, C> mappings;
protected ExpressionTester(
final TestCounter counter,
final ExpressionKind<E, C> kind,
final Function<C, E> expectedConstant,
final Binary<C, E> binary,
final BinaryOperator<C> add,
final BinaryOperator<C> sub,
final BinaryOperator<C> mul,
final BinaryOperator<C> div,
final Map<String, C> mappings
) {
super(counter);
this.kind = kind;
this.mappings = mappings;
generator = new GeneratorBuilder(expectedConstant, kind::constant, binary, kind::randomValue);
generator.binary("+", 1600, add, Add.class);
generator.binary("-", 1602, sub, Subtract.class);
generator.binary("*", 2001, mul, Multiply.class);
generator.binary("/", 2002, div, Divide.class);
}
protected ExpressionTester(
final TestCounter counter,
final ExpressionKind<E, C> kind,
final Function<C, E> expectedConstant,
final Binary<C, E> binary,
final BinaryOperator<C> add,
final BinaryOperator<C> sub,
final BinaryOperator<C> mul,
final BinaryOperator<C> div
) {
this(counter, kind, expectedConstant, binary, add, sub, mul, div, Map.of());
}
@Override
public String toString() {
return kind.getName();
}
@Override
public void test() {
counter.scope("Basic tests", () -> basic.forEach(Test::test));
counter.scope("Advanced tests", () -> advanced.forEach(Test::test));
counter.scope("Random tests", generator::testRandom);
}
@SuppressWarnings({"ConstantValue", "EqualsWithItself"})
private void checkEqualsAndToString(final String full, final String mini, final ToMiniString expression, final ToMiniString copy) {
checkToString("toString", full, expression.toString());
if (mode() > 0) {
checkToString("toMiniString", mini, expression.toMiniString());
}
counter.test(() -> {
assertTrue("Equals to this", expression.equals(expression));
assertTrue("Equals to copy", expression.equals(copy));
assertTrue("Equals to null", !expression.equals(null));
assertTrue("Copy equals to null", !copy.equals(null));
});
final String expressionToString = Objects.requireNonNull(expression.toString());
for (final Pair<ToMiniString, String> pair : prev) {
counter.test(() -> {
final ToMiniString prev = pair.first();
final String prevToString = pair.second();
final boolean equals = prevToString.equals(expressionToString);
assertTrue("Equals to " + prevToString, prev.equals(expression) == equals);
assertTrue("Equals to " + prevToString, expression.equals(prev) == equals);
assertTrue("Inconsistent hashCode for " + prev + " and " + expression, (prev.hashCode() == expression.hashCode()) == equals);
});
}
}
private void checkToString(final String method, final String expected, final String actual) {
counter.test(() -> assertTrue(String.format("Invalid %s\n expected: %s\n actual: %s", method, expected, actual), expected.equals(actual)));
}
private void check(
final String full,
final E expected,
final E actual,
final List<String> variables,
final List<C> values
) {
final String vars = IntStream.range(0, variables.size())
.mapToObj(i -> variables.get(i) + "=" + values.get(i))
.collect(Collectors.joining(","));
counter.test(() -> {
final Object expectedResult = evaluate(expected, variables, values);
final Object actualResult = evaluate(actual, variables, values);
final String reason = String.format(
"%s:%n expected `%s`,%n actual `%s`",
String.format("f(%s)\nwhere f is %s", vars, full),
Asserts.toString(expectedResult),
Asserts.toString(actualResult)
);
if (
expectedResult != null && actualResult != null &&
expectedResult.getClass() == actualResult.getClass()
&& (expectedResult.getClass() == Double.class || expectedResult.getClass() == Float.class)
) {
final double expectedValue = ((Number) expectedResult).doubleValue();
final double actualValue = ((Number) actualResult).doubleValue();
Asserts.assertEquals(reason, expectedValue, actualValue, 1e-6);
} else {
assertTrue(reason, Objects.deepEquals(expectedResult, actualResult));
}
});
}
private Object evaluate(final E expression, final List<String> variables, final List<C> values) {
try {
return kind.evaluate(expression, variables, values);
} catch (final Exception e) {
return e.getClass().getName();
}
}
protected ExpressionTester<E, C> basic(final String full, final String mini, final E expected, final E actual) {
return basicF(full, mini, expected, vars -> actual);
}
protected ExpressionTester<E, C> basicF(final String full, final String mini, final E expected, final Function<List<String>, E> actual) {
return basic(new Test(full, mini, expected, actual));
}
private ExpressionTester<E, C> basic(final Test test) {
Asserts.assertTrue(test.full, used.add(test.full));
basic.add(test);
return this;
}
protected ExpressionTester<E, C> advanced(final String full, final String mini, final E expected, final E actual) {
return advancedF(full, mini, expected, vars -> actual);
}
protected ExpressionTester<E, C> advancedF(final String full, final String mini, final E expected, final Function<List<String>, E> actual) {
Asserts.assertTrue(full, used.add(full));
advanced.add(new Test(full, mini, expected, actual));
return this;
}
protected static <E> Named<E> variable(final String name, final E expected) {
return Named.of(name, expected);
}
@FunctionalInterface
public interface Binary<C, E> {
E apply(BinaryOperator<C> op, E a, E b);
}
private final class Test {
private final String full;
private final String mini;
private final E expected;
private final Function<List<String>, E> actual;
private Test(final String full, final String mini, final E expected, final Function<List<String>, E> actual) {
this.full = full;
this.mini = mini;
this.expected = expected;
this.actual = actual;
}
private void test() {
final List<Pair<String, E>> variables = kind.variables().generate(random(), 3);
final List<String> names = Functional.map(variables, Pair::first);
final E actual = kind.cast(this.actual.apply(names));
final String full = mangle(this.full, names);
final String mini = mangle(this.mini, names);
counter.test(() -> {
kind.allValues(variables.size(), VALUES).forEach(values -> check(mini, expected, actual, names, values));
checkEqualsAndToString(full, mini, actual, actual);
prev.add(Pair.of(actual, full));
});
}
private String mangle(String string, final List<String> names) {
for (int i = 0; i < names.size(); i++) {
string = string.replace("$" + (char) ('x' + i), names.get(i));
}
for (final Map.Entry<String, C> mapping : mappings.entrySet()) {
string = string.replace(mapping.getKey(), mapping.getValue().toString());
}
return string;
}
}
private final class GeneratorBuilder {
private final Generator.Builder<C> generator;
private final NodeRendererBuilder<C> renderer = new NodeRendererBuilder<>(random());
private final Renderer.Builder<C, Unit, E> expected;
private final Renderer.Builder<C, Unit, E> actual;
private final Renderer.Builder<C, Unit, E> copy;
private final Binary<C, E> binary;
private GeneratorBuilder(
final Function<C, E> expectedConstant,
final Function<? super C, E> actualConstant,
final Binary<C, E> binary,
final Function<ExtendedRandom, C> randomValue
) {
generator = Generator.builder(() -> randomValue.apply(random()), random());
expected = Renderer.builder(expectedConstant::apply);
actual = Renderer.builder(actualConstant::apply);
copy = Renderer.builder(actualConstant::apply);
this.binary = binary;
}
private void binary(final String name, final int priority, final BinaryOperator<C> op, final Class<?> type) {
generator.add(name, 2);
renderer.binary(name, priority);
expected.binary(name, (unit, a, b) -> binary.apply(op, a, b));
@SuppressWarnings("unchecked") final Constructor<? extends E> constructor = (Constructor<? extends E>) Arrays.stream(type.getConstructors())
.filter(cons -> Modifier.isPublic(cons.getModifiers()))
.filter(cons -> cons.getParameterCount() == 2)
.findFirst()
.orElseGet(() -> counter.fail("%s(..., ...) constructor not found", type.getSimpleName()));
final Renderer.BinaryOperator<Unit, E> actual = (unit, a, b) -> {
try {
return constructor.newInstance(a, b);
} catch (final Exception e) {
return counter.fail(e);
}
};
this.actual.binary(name, actual);
copy.binary(name, actual);
}
private void testRandom() {
final NodeRenderer<C> renderer = this.renderer.build();
final Renderer<C, Unit, E> expectedRenderer = this.expected.build();
final Renderer<C, Unit, E> actualRenderer = this.actual.build();
final expression.common.Generator<C, E> generator = this.generator.build(kind.variables(), List.of());
generator.testRandom(counter, 1, expr -> {
final String full = renderer.render(expr, NodeRenderer.FULL);
final String mini = renderer.render(expr, NodeRenderer.MINI);
final E expected = expectedRenderer.render(expr, Unit.INSTANCE);
final E actual = actualRenderer.render(expr, Unit.INSTANCE);
final List<Pair<String, E>> variables = expr.variables();
final List<String> names = Functional.map(variables, Pair::first);
final List<C> values = Stream.generate(() -> kind.randomValue(random()))
.limit(variables.size())
.toList();
checkEqualsAndToString(full, mini, actual, copy.build().render(expr, Unit.INSTANCE));
check(full, expected, actual, names, values);
});
}
}
}

View File

@@ -1,211 +0,0 @@
package expression;
import base.Asserts;
import base.ExtendedRandom;
import base.Pair;
import base.TestCounter;
import expression.common.ExpressionKind;
import expression.common.Type;
import java.util.List;
import java.util.stream.IntStream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@SuppressWarnings("ClassReferencesSubclass")
@FunctionalInterface
public interface ListExpression extends ToMiniString {
int evaluate(List<Integer> variables);
// Tests follow. You may temporarily remove everything til the end.
Add EXAMPLE = new Add(
new Subtract(new Variable(0), new Const(1)),
new Multiply(new Variable(1), new Const(10))
);
Type<Integer> TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class);
ExpressionKind<ListExpression, Integer> KIND = new ExpressionKind<>(
TYPE,
ListExpression.class,
(r, c) -> IntStream.range(0, c)
.mapToObj(name -> Pair.<String, ListExpression>of("$" + name, new Variable(name)))
.toList(),
(expr, variables, values) -> expr.evaluate(values)
);
private static Const c(final Integer c) {
return TYPE.constant(c);
}
@SuppressWarnings({"PointlessArithmeticExpression", "Convert2MethodRef"})
static ExpressionTester<?, ?> tester(final TestCounter counter) {
Asserts.assertEquals("Example toString()", "(($0 - 1) + ($1 * 10))", EXAMPLE.toString());
Asserts.assertEquals(EXAMPLE + " at (2, 3)", 31, EXAMPLE.evaluate(List.of(2, 3)));
final Variable vx = new Variable(0);
return new ExpressionTester<>(
counter, KIND, c -> vars -> c,
(op, a, b) -> vars -> op.apply(a.evaluate(vars), b.evaluate(vars)),
(a, b) -> a + b, (a, b) -> a - b, (a, b) -> a * b, (a, b) -> a / b
)
.basic("10", "10", vars -> 10, c(10))
.basic("$x", "$x", ListExpression::x, vx)
.basic("($x + 2)", "$x + 2", vars -> x(vars) + 2, new Add(vx, c(2)))
.basic("(2 - $x)", "2 - $x", vars -> 2 - x(vars), new Subtract(c(2), vx))
.basic("(3 * $x)", "3 * $x", vars -> 3 * x(vars), new Multiply(c(3), vx))
.basic("($x + $x)", "$x + $x", vars -> x(vars) + x(vars), new Add(vx, vx))
.basic("($x / -2)", "$x / -2", vars -> -x(vars) / 2, new Divide(vx, c(-2)))
.basic("(2 + $x)", "2 + $x", vars -> 2 + x(vars), new Add(c(2), vx))
.basic("((1 + 2) + 3)", "1 + 2 + 3", vars -> 6, new Add(new Add(c(1), c(2)), c(3)))
.basic("(1 + (2 + 3))", "1 + 2 + 3", vars -> 6, new Add(c(1), new Add(c(2), c(3))))
.basic("((1 - 2) - 3)", "1 - 2 - 3", vars -> -4, new Subtract(new Subtract(c(1), c(2)), c(3)))
.basic("(1 - (2 - 3))", "1 - (2 - 3)", vars -> 2, new Subtract(c(1), new Subtract(c(2), c(3))))
.basic("((1 * 2) * 3)", "1 * 2 * 3", vars -> 6, new Multiply(new Multiply(c(1), c(2)), c(3)))
.basic("(1 * (2 * 3))", "1 * 2 * 3", vars -> 6, new Multiply(c(1), new Multiply(c(2), c(3))))
.basic("((10 / 2) / 3)", "10 / 2 / 3", vars -> 10 / 2 / 3, new Divide(new Divide(c(10), c(2)), c(3)))
.basic("(10 / (3 / 2))", "10 / (3 / 2)", vars -> 10 / (3 / 2), new Divide(c(10), new Divide(c(3), c(2))))
.basic("(10 * (3 / 2))", "10 * (3 / 2)", vars -> 10 * (3 / 2), new Multiply(c(10), new Divide(c(3), c(2))))
.basic("(10 + (3 - 2))", "10 + 3 - 2", vars -> 10 + (3 - 2), new Add(c(10), new Subtract(c(3), c(2))))
.basic(
"(($x * $x) + (($x - 1) / 10))",
"$x * $x + ($x - 1) / 10",
vars -> x(vars) * x(vars) + (x(vars) - 1) / 10,
new Add(new Multiply(vx, vx), new Divide(new Subtract(vx, c(1)), c(10)))
)
.basic(
"($x * -1000000000)",
"$x * -1000000000",
vars -> x(vars) * -1_000_000_000,
new Multiply(vx, c(-1_000_000_000))
)
.basic("(10 / $x)", "10 / $x", vars -> 10 / x(vars), new Divide(c(10), vx))
.basic("($x / $x)", "$x / $x", vars -> x(vars) / x(vars), new Divide(vx, vx))
.advanced("(2 + 1)", "2 + 1", vars -> 2 + 1, new Add(c(2), c(1)))
.advanced("($x - 1)", "$x - 1", vars -> x(vars) - 1, new Subtract(vx, c(1)))
.advanced("(1 * 2)", "1 * 2", vars -> 1 * 2, new Multiply(c(1), c(2)))
.advanced("($x / 1)", "$x / 1", vars -> x(vars) / 1, new Divide(vx, c(1)))
.advanced("(1 + (2 + 1))", "1 + 2 + 1", vars -> 1 + 2 + 1, new Add(c(1), new Add(c(2), c(1))))
.advanced(
"($x - ($x - 1))",
"$x - ($x - 1)",
vars -> x(vars) - (x(vars) - 1),
new Subtract(vx, new Subtract(vx, c(1)))
)
.advanced(
"(2 * ($x / 1))",
"2 * ($x / 1)",
vars -> 2 * (x(vars) / 1),
new Multiply(c(2), new Divide(vx, c(1)))
)
.advanced(
"(2 / ($x - 1))",
"2 / ($x - 1)",
vars -> 2 / (x(vars) - 1),
new Divide(c(2), new Subtract(vx, c(1)))
)
.advanced(
"((1 * 2) + $x)",
"1 * 2 + $x",
vars -> 1 * 2 + x(vars),
new Add(new Multiply(c(1), c(2)), vx)
)
.advanced(
"(($x - 1) - 2)",
"$x - 1 - 2",
vars -> x(vars) - 1 - 2,
new Subtract(new Subtract(vx, c(1)), c(2))
)
.advanced(
"(($x / 1) * 2)",
"$x / 1 * 2",
vars -> x(vars) / 1 * 2,
new Multiply(new Divide(vx, c(1)), c(2))
)
.advanced("((2 + 1) / 1)", "(2 + 1) / 1", vars -> (2 + 1) / 1, new Divide(new Add(c(2), c(1)), c(1)))
.advanced(
"(1 + (1 + (2 + 1)))",
"1 + 1 + 2 + 1",
vars -> 1 + 1 + 2 + 1,
new Add(c(1), new Add(c(1), new Add(c(2), c(1))))
)
.advanced(
"($x - ((1 * 2) + $x))",
"$x - (1 * 2 + $x)",
vars -> x(vars) - (1 * 2 + x(vars)),
new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx))
)
.advanced(
"($x * (2 / ($x - 1)))",
"$x * (2 / ($x - 1))",
vars -> x(vars) * (2 / (x(vars) - 1)),
new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1))))
)
.advanced(
"($x / (1 + (2 + 1)))",
"$x / (1 + 2 + 1)",
vars -> x(vars) / (1 + 2 + 1),
new Divide(vx, new Add(c(1), new Add(c(2), c(1))))
)
.advanced(
"((1 * 2) + (2 + 1))",
"1 * 2 + 2 + 1",
vars -> 1 * 2 + 2 + 1,
new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1)))
)
.advanced(
"((2 + 1) - (2 + 1))",
"2 + 1 - (2 + 1)",
vars -> 2 + 1 - (2 + 1),
new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1)))
)
.advanced(
"(($x - 1) * ($x / 1))",
"($x - 1) * ($x / 1)",
vars -> (x(vars) - 1) * (x(vars) / 1),
new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1)))
)
.advanced(
"(($x - 1) / (1 * 2))",
"($x - 1) / (1 * 2)",
vars -> (x(vars) - 1) / (1 * 2),
new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2)))
)
.advanced(
"((($x - 1) - 2) + $x)",
"$x - 1 - 2 + $x",
vars -> x(vars) - 1 - 2 + x(vars),
new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx)
)
.advanced(
"(((1 * 2) + $x) - 1)",
"1 * 2 + $x - 1",
vars -> 1 * 2 + x(vars) - 1,
new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1))
)
.advanced(
"(((2 + 1) / 1) * $x)",
"(2 + 1) / 1 * $x",
vars -> (2 + 1) / 1 * x(vars),
new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx)
)
.advanced(
"((2 / ($x - 1)) / 2)",
"2 / ($x - 1) / 2",
vars -> 2 / (x(vars) - 1) / 2,
new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2))
);
}
private static Integer x(final List<Integer> vars) {
return vars.get(0);
}
static void main(final String... args) {
TripleExpression.SELECTOR
.variant("List", ExpressionTest.v(ListExpression::tester))
.main(args);
}
}

View File

@@ -1,10 +0,0 @@
package expression;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public interface ToMiniString {
default String toMiniString() {
return toString();
}
}

View File

@@ -1,190 +0,0 @@
package expression;
import base.ExtendedRandom;
import base.Pair;
import base.Selector;
import base.TestCounter;
import expression.common.ExpressionKind;
import expression.common.Type;
import java.util.List;
/**
* Three-argument arithmetic expression over integers.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
@SuppressWarnings("ClassReferencesSubclass")
public interface TripleExpression extends ToMiniString {
int evaluate(int x, int y, int z);
// Tests follow. You may temporarily remove everything til the end.
Type<Integer> TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class);
ExpressionKind<TripleExpression, Integer> KIND = new ExpressionKind<>(
TYPE,
TripleExpression.class,
List.of(
Pair.of("x", new Variable("x")),
Pair.of("y", new Variable("y")),
Pair.of("z", new Variable("z"))
),
(expr, variables, values) -> expr.evaluate(values.get(0), values.get(1), values.get(2))
);
@SuppressWarnings("PointlessArithmeticExpression")
static ExpressionTester<?, ?> tester(final TestCounter counter) {
final Variable vx = new Variable("x");
final Variable vy = new Variable("y");
final Variable vz = new Variable("z");
return new ExpressionTester<>(
counter, KIND, c -> (x, y, z) -> c,
(op, a, b) -> (x, y, z) -> op.apply(a.evaluate(x, y, z), b.evaluate(x, y, z)),
Integer::sum, (a, b) -> a - b, (a, b) -> a * b, (a, b) -> a / b
)
.basic("10", "10", (x, y, z) -> 10, c(10))
.basic("x", "x", (x, y, z) -> x, vx)
.basic("y", "y", (x, y, z) -> y, vy)
.basic("z", "z", (x, y, z) -> z, vz)
.basic("(x + 2)", "x + 2", (x, y, z) -> x + 2, new Add(vx, c(2)))
.basic("(2 - y)", "2 - y", (x, y, z) -> 2 - y, new Subtract(c(2), vy))
.basic("(3 * z)", "3 * z", (x, y, z) -> 3 * z, new Multiply(c(3), vz))
.basic("(x / -2)", "x / -2", (x, y, z) -> -x / 2, new Divide(vx, c(-2)))
.basic("((1 + 2) + 3)", "1 + 2 + 3", (x, y, z) -> 6, new Add(new Add(c(1), c(2)), c(3)))
.basic("(1 + (2 + 3))", "1 + 2 + 3", (x, y, z) -> 6, new Add(c(1), new Add(c(2), c(3))))
.basic("((1 - 2) - 3)", "1 - 2 - 3", (x, y, z) -> -4, new Subtract(new Subtract(c(1), c(2)), c(3)))
.basic("(1 - (2 - 3))", "1 - (2 - 3)", (x, y, z) -> 2, new Subtract(c(1), new Subtract(c(2), c(3))))
.basic("((1 * 2) * 3)", "1 * 2 * 3", (x, y, z) -> 6, new Multiply(new Multiply(c(1), c(2)), c(3)))
.basic("(1 * (2 * 3))", "1 * 2 * 3", (x, y, z) -> 6, new Multiply(c(1), new Multiply(c(2), c(3))))
.basic("((10 / 2) / 3)", "10 / 2 / 3", (x, y, z) -> 10 / 2 / 3, new Divide(new Divide(c(10), c(2)), c(3)))
.basic("(10 / (3 / 2))", "10 / (3 / 2)", (x, y, z) -> 10, new Divide(c(10), new Divide(c(3), c(2))))
.basic("((x * y) + ((z - 1) / 10))", "x * y + (z - 1) / 10", (x, y, z) -> x * y + (z - 1) / 10, new Add(
new Multiply(vx, vy),
new Divide(new Subtract(vz, c(1)), c(10))
))
.basic("(x + y)", "x + y", (x, y, z) -> x + y, new Add(vx, vy))
.basic("(y + x)", "y + x", (x, y, z) -> y + x, new Add(vy, vx))
.advanced("(1 + 1)", "1 + 1", (x, y, z) -> 1 + 1, new Add(c(1), c(1)))
.advanced("(y - x)", "y - x", (x, y, z) -> y - x, new Subtract(vy, vx))
.advanced("(2 * x)", "2 * x", (x, y, z) -> 2 * x, new Multiply(c(2), vx))
.advanced("(2 / x)", "2 / x", (x, y, z) -> 2 / x, new Divide(c(2), vx))
.advanced("(z + (1 + 1))", "z + 1 + 1", (x, y, z) -> z + 1 + 1, new Add(vz, new Add(c(1), c(1))))
.advanced(
"(2 - (y - x))",
"2 - (y - x)",
(x, y, z) -> 2 - (y - x),
new Subtract(c(2), new Subtract(vy, vx))
)
.advanced(
"(z * (2 / x))",
"z * (2 / x)",
(x, y, z) -> z * (2 / x),
new Multiply(vz, new Divide(c(2), vx))
)
.advanced(
"(z / (y - x))",
"z / (y - x)",
(x, y, z) -> z / (y - x),
new Divide(vz, new Subtract(vy, vx))
)
.advanced("((2 * x) + y)", "2 * x + y", (x, y, z) -> 2 * x + y, new Add(new Multiply(c(2), vx), vy))
.advanced(
"((y - x) - 2)",
"y - x - 2",
(x, y, z) -> y - x - 2,
new Subtract(new Subtract(vy, vx), c(2))
)
.advanced("((2 / x) * y)", "2 / x * y", (x, y, z) -> 2 / x * y, new Multiply(new Divide(c(2), vx), vy))
.advanced("((1 + 1) / x)", "(1 + 1) / x", (x, y, z) -> (1 + 1) / x, new Divide(new Add(c(1), c(1)), vx))
.advanced("(1 + (2 * 3))", "1 + 2 * 3", (x, y, z) -> 7, new Add(c(1), new Multiply(c(2), c(3))))
.advanced("(1 - (2 * 3))", "1 - 2 * 3", (x, y, z) -> -5, new Subtract(c(1), new Multiply(c(2), c(3))))
.advanced("(1 + (2 / 3))", "1 + 2 / 3", (x, y, z) -> 1, new Add(c(1), new Divide(c(2), c(3))))
.advanced("(1 - (2 / 3))", "1 - 2 / 3", (x, y, z) -> 1, new Subtract(c(1), new Divide(c(2), c(3))))
.advanced(
"(2 + (z + (1 + 1)))",
"2 + z + 1 + 1",
(x, y, z) -> 2 + z + 1 + 1,
new Add(c(2), new Add(vz, new Add(c(1), c(1))))
)
.advanced(
"(1 - ((2 * x) + y))",
"1 - (2 * x + y)",
(x, y, z) -> 1 - (2 * x + y),
new Subtract(c(1), new Add(new Multiply(c(2), vx), vy))
)
.advanced(
"(1 * (z / (y - x)))",
"1 * (z / (y - x))",
(x, y, z) -> 1 * (z / (y - x)),
new Multiply(c(1), new Divide(vz, new Subtract(vy, vx)))
)
.advanced(
"(z / (z + (1 + 1)))",
"z / (z + 1 + 1)",
(x, y, z) -> z / (z + 1 + 1),
new Divide(vz, new Add(vz, new Add(c(1), c(1))))
)
.advanced(
"((2 * x) + (1 + 1))",
"2 * x + 1 + 1",
(x, y, z) -> 2 * x + 1 + 1,
new Add(new Multiply(c(2), vx), new Add(c(1), c(1)))
)
.advanced(
"((1 + 1) - (1 + 1))",
"1 + 1 - (1 + 1)",
(x, y, z) -> 1 + 1 - (1 + 1),
new Subtract(new Add(c(1), c(1)), new Add(c(1), c(1)))
)
.advanced(
"((y - x) * (2 / x))",
"(y - x) * (2 / x)",
(x, y, z) -> (y - x) * (2 / x),
new Multiply(new Subtract(vy, vx), new Divide(c(2), vx))
)
.advanced(
"((y - x) / (2 * x))",
"(y - x) / (2 * x)",
(x, y, z) -> (y - x) / (2 * x),
new Divide(new Subtract(vy, vx), new Multiply(c(2), vx))
)
.advanced(
"(((y - x) - 2) + 1)",
"y - x - 2 + 1",
(x, y, z) -> y - x - 2 + 1,
new Add(new Subtract(new Subtract(vy, vx), c(2)), c(1))
)
.advanced(
"(((2 * x) + y) - z)",
"2 * x + y - z",
(x, y, z) -> 2 * x + y - z,
new Subtract(new Add(new Multiply(c(2), vx), vy), vz)
)
.advanced(
"(((1 + 1) / x) * 2)",
"(1 + 1) / x * 2",
(x, y, z) -> (1 + 1) / x * 2,
new Multiply(new Divide(new Add(c(1), c(1)), vx), c(2))
)
.advanced(
"((z / (y - x)) / x)",
"z / (y - x) / x",
(x, y, z) -> z / (y - x) / x,
new Divide(new Divide(vz, new Subtract(vy, vx)), vx)
);
}
private static Const c(final Integer c) {
return TYPE.constant(c);
}
Selector SELECTOR = ExpressionTest.SELECTOR
.variant("Triple", ExpressionTest.v(TripleExpression::tester));
static void main(final String... args) {
TripleExpression.SELECTOR.main(args);
}
}

View File

@@ -1,32 +0,0 @@
package expression.common;
import base.Functional;
import base.Pair;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public record Expr<C, V>(Node<C> node, List<Pair<String, V>> variables) {
public <T> List<Pair<String, T>> variables(final BiFunction<String, V, T> f) {
return Functional.map(
variables,
variable -> variable.second(f.apply(variable.first(), variable.second()))
);
}
public <T> Expr<C, T> convert(final BiFunction<String, V, T> f) {
return of(node, variables(f));
}
public Expr<C, V> node(final Function<Node<C>, Node<C>> f) {
return of(f.apply(node), variables);
}
public static <C, V> Expr<C, V> of(final Node<C> node, final List<Pair<String, V>> variables) {
return new Expr<>(node, variables);
}
}

View File

@@ -1,94 +0,0 @@
package expression.common;
import base.ExtendedRandom;
import base.Functional;
import base.Pair;
import expression.ToMiniString;
import java.util.List;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ExpressionKind<E extends ToMiniString, C> {
private final Type<C> type;
private final Class<E> kind;
private final Variables<E> variables;
private final Evaluator<E, C> evaluator;
public ExpressionKind(
final Type<C> type,
final Class<E> kind,
final Variables<E> variables,
final Evaluator<E, C> evaluator
) {
this.type = type;
this.kind = kind;
this.variables = variables;
this.evaluator = evaluator;
}
public ExpressionKind(
final Type<C> type,
final Class<E> kind,
final List<Pair<String, E>> variables,
final Evaluator<E, C> evaluator
) {
this(type, kind, (r, c) -> variables, evaluator);
}
public C evaluate(final E expression, final List<String> variables, final List<C> values) throws Exception {
return evaluator.evaluate(expression, variables, values);
}
public E cast(final Object expression) {
return kind.cast(expression);
}
public String getName() {
return kind.getSimpleName();
}
public E constant(final C value) {
return cast(type.constant(value));
}
public C randomValue(final ExtendedRandom random) {
return type.randomValue(random);
}
public List<List<C>> allValues(final int length, final List<Integer> values) {
return Functional.allValues(fromInts(values), length);
}
public List<C> fromInts(final List<Integer> values) {
return Functional.map(values, this::fromInt);
}
public C fromInt(final int value) {
return type.fromInt(value);
}
@Override
public String toString() {
return kind.getName();
}
public ExpressionKind<E, C> withVariables(final Variables<E> variables) {
return new ExpressionKind<>(type, kind, variables, evaluator);
}
public Variables<E> variables() {
return variables;
}
@FunctionalInterface
public interface Variables<E> {
List<Pair<String, E>> generate(final ExtendedRandom random, final int count);
}
@FunctionalInterface
public interface Evaluator<E, R> {
R evaluate(final E expression, final List<String> vars, final List<R> values) throws Exception;
}
}

View File

@@ -1,173 +0,0 @@
package expression.common;
import base.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Generator<C, E> {
private final Supplier<C> constant;
private final List<Named<Integer>> ops;
private final ExpressionKind.Variables<E> variables;
private final Set<String> forbidden;
private final ExtendedRandom random;
private final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests;
public Generator(
final Supplier<C> constant,
final List<Named<Integer>> ops,
final ExpressionKind.Variables<E> variables,
final Set<String> forbidden,
final ExtendedRandom random,
final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests
) {
this.constant = constant;
this.ops = List.copyOf(ops);
this.variables = variables;
this.forbidden = Set.copyOf(forbidden);
this.random = random;
this.basicTests = List.copyOf(basicTests);
}
public static <C> Builder<C> builder(final Supplier<C> constant, final ExtendedRandom random) {
return new Builder<>(random, constant);
}
public void testRandom(
final TestCounter counter,
final int denominator,
final Consumer<Expr<C, E>> consumer
) {
final int d = Math.max(TestCounter.DENOMINATOR, denominator);
testRandom(counter, consumer, 1, 100, 100 / d, (vars, depth) -> generateFullDepth(vars, Math.min(depth, 3)));
testRandom(counter, consumer, 2, 1000 / d, 1, this::generateSize);
testRandom(counter, consumer, 3, 12, 100 / d, this::generateFullDepth);
testRandom(counter, consumer, 4, 777 / d, 1, this::generatePartialDepth);
}
private void testRandom(
final TestCounter counter,
final Consumer<Expr<C, E>> consumer,
final int seq,
final int levels,
final int perLevel,
final BiFunction<List<Node<C>>, Integer, Node<C>> generator
) {
counter.scope("Random tests #" + seq, () -> {
final int total = levels * perLevel;
int generated = 0;
for (int level = 0; level < levels; level++) {
for (int j = 0; j < perLevel; j++) {
if (generated % 100 == 0) {
progress(counter, total, generated);
}
generated++;
final List<Pair<String, E>> vars = variables(random.nextInt(10) + 1);
consumer.accept(Expr.of(generator.apply(Functional.map(vars, v -> Node.op(v.first())), level), vars));
}
}
progress(counter, generated, total);
});
}
private static void progress(final TestCounter counter, final int total, final int generated) {
counter.format("Completed %4d out of %d%n", generated, total);
}
private Node<C> generate(
final List<Node<C>> variables,
final boolean nullary,
final Supplier<Node<C>> unary,
final Supplier<Pair<Node<C>, Node<C>>> binary
) {
if (nullary || ops.isEmpty()) {
return random.nextBoolean() ? random.randomItem(variables) : Node.constant(constant.get());
} else {
final Named<Integer> op = random.randomItem(ops);
if (Math.abs(op.value()) == 1) {
return Node.op(op.name(), (op.value() + 1) >> 1, unary.get());
} else {
final Pair<Node<C>, Node<C>> pair = binary.get();
return Node.op(op.name(), pair.first(), pair.second());
}
}
}
private Node<C> generate(final List<Node<C>> variables, final boolean nullary, final Supplier<Node<C>> child) {
return generate(variables, nullary, child, () -> Pair.of(child.get(), child.get()));
}
private Node<C> generateFullDepth(final List<Node<C>> variables, final int depth) {
return generate(variables, depth == 0, () -> generateFullDepth(variables, depth - 1));
}
private Node<C> generatePartialDepth(final List<Node<C>> variables, final int depth) {
return generate(variables, depth == 0, () -> generatePartialDepth(variables, random.nextInt(depth)));
}
private Node<C> generateSize(final List<Node<C>> variables, final int size) {
final int first = size <= 1 ? 0 : random.nextInt(size);
return generate(
variables,
size == 0,
() -> generateSize(variables, size - 1),
() -> Pair.of(
generateSize(variables, first),
generateSize(variables, size - 1 - first)
)
);
}
public void testBasic(final Consumer<Expr<C, E>> consumer) {
basicTests.forEach(test -> {
final List<Pair<String, E>> vars = variables(random.nextInt(5) + 3);
test.apply(Functional.map(vars, v -> Node.op(v.first())))
.map(node -> Expr.of(node, vars))
.forEachOrdered(consumer);
});
}
public List<Pair<String, E>> variables(final int count) {
List<Pair<String, E>> vars;
do {
vars = variables.generate(random, count);
} while (vars.stream().map(Pair::first).anyMatch(forbidden::contains));
return vars;
}
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public static final class Builder<C> {
private final ExtendedRandom random;
private final Supplier<C> constant;
private final List<Named<Integer>> ops = new ArrayList<>();
private final Set<String> forbidden = new HashSet<>();
private Builder(final ExtendedRandom random, final Supplier<C> constant) {
this.random = random;
this.constant = constant;
}
public void add(final String name, final int arity) {
ops.add(Named.of(name, arity));
forbidden.add(name);
}
public <E> Generator<C, E> build(
final ExpressionKind.Variables<E> variables,
final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests
) {
return new Generator<>(constant, ops, variables, forbidden, random, basicTests);
}
}
}

View File

@@ -1,106 +0,0 @@
package expression.common;
import java.util.function.Function;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public abstract class Node<T> {
private Node() {
}
public abstract <R> R get(Const<T, R> con, Nullary<R> nul, Unary<Node<T>, R> un, Binary<Node<T>, R> bin);
public abstract <R> R cata(Const<T, R> con, Nullary<R> nul, Unary<R, R> un, Binary<R, R> bin);
public final String toPolish() {
return cata(
T::toString,
name -> name,
(name, priority, a) -> a + " " + name + ":1",
(name, a1, a2) -> a1 + " " + a2 + " " + name + ":2"
);
}
@Override
public final String toString() {
return cata(
T::toString,
name -> name,
(name, priority, a) -> name.equals("[") ? "[" + a + "]" :
(priority & 1) == 1 ? "(" + name + " " + a + ")" : "(" + a + " " + name + ")",
(name, a1, a2) -> "(" + a1 + " " + name + " " + a2 + ")"
);
}
public static <T> Node<T> constant(final T value) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return con.apply(value);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return con.apply(value);
}
};
}
public static <T> Node<T> op(final String name) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return nul.apply(name);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return nul.apply(name);
}
};
}
public static <T> Node<T> op(final String name, final int priority, final Node<T> arg) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return un.apply(name, priority, arg);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return un.apply(name, priority, arg.cata(con, nul, un, bin));
}
};
}
public static <T> Node<T> op(final String name, final Node<T> arg1, final Node<T> arg2) {
return new Node<>() {
@Override
public <R> R get(final Const<T, R> con, final Nullary<R> nul, final Unary<Node<T>, R> un, final Binary<Node<T>, R> bin) {
return bin.apply(name, arg1, arg2);
}
@Override
public <R> R cata(final Const<T, R> con, final Nullary<R> nul, final Unary<R, R> un, final Binary<R, R> bin) {
return bin.apply(name, arg1.cata(con, nul, un, bin), arg2.cata(con, nul, un, bin));
}
};
}
@FunctionalInterface
public interface Const<T, R> extends Function<T, R> {}
@FunctionalInterface
public interface Nullary<R> extends Function<String, R> {}
@FunctionalInterface
public interface Unary<T, R> {
R apply(String name, int priority, T arg);
}
@FunctionalInterface
public interface Binary<T, R> {
R apply(String name, T arg1, T arg2);
}
}

View File

@@ -1,96 +0,0 @@
package expression.common;
import base.ExtendedRandom;
import java.util.List;
import java.util.Map;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class NodeRenderer<C> {
public static final String PAREN = "[";
public static final List<Paren> DEFAULT_PARENS = List.of(paren("(", ")"));
public static final Mode MINI_MODE = Mode.SIMPLE_MINI; // Replace by TRUE_MINI for some challenge;
public static final Settings FULL = Mode.FULL.settings(0);
public static final Settings FULL_EXTRA = Mode.FULL.settings(Integer.MAX_VALUE / 4);
public static final Settings SAME = Mode.SAME.settings(0);
public static final Settings MINI = MINI_MODE.settings(0);
public static final Settings TRUE_MINI = Mode.TRUE_MINI.settings(0);
private final Renderer<C, Settings, Node<C>> renderer;
private final Map<String, String> brackets;
private final ExtendedRandom random;
public NodeRenderer(
final Renderer<C, Settings, Node<C>> renderer,
final Map<String, String> brackets,
final ExtendedRandom random
) {
this.renderer = renderer;
this.brackets = Map.copyOf(brackets);
this.random = random;
}
public static <C> Node<C> paren(final boolean condition, final Node<C> node) {
return condition ? Node.op(PAREN, 1, node) : node;
}
public static Paren paren(final String open, final String close) {
return new Paren(open, close);
}
public Node<C> renderToNode(final Settings settings, final Expr<C, ?> expr) {
final Expr<C, Node<C>> convert = expr.convert((name, variable) -> Node.op(name));
return renderer.render(convert, settings);
}
public String render(final Node<C> node, final List<Paren> parens) {
return node.cata(
String::valueOf,
name -> name,
(name, priority, arg) ->
name == PAREN ? random.randomItem(parens).apply(arg) :
priority == Integer.MAX_VALUE ? name + arg + brackets.get(name) :
(priority & 1) == 1 ? name + arg :
arg + name,
(name, a, b) -> a + " " + name + " " + b
);
}
public String render(final Expr<C, ?> expr, final Settings settings) {
return render(renderToNode(settings, expr), settings.parens());
}
public enum Mode {
FULL, SAME, TRUE_MINI, SIMPLE_MINI;
public Settings settings(final int limit) {
return new Settings(this, limit);
}
}
public record Paren(String open, String close) {
String apply(final String expression) {
return open() + expression + close();
}
}
public record Settings(Mode mode, int limit, List<Paren> parens) {
public Settings(final Mode mode, final int limit) {
this(mode, limit, DEFAULT_PARENS);
}
public <C> Node<C> extra(Node<C> node, final ExtendedRandom random) {
while (random.nextInt(Integer.MAX_VALUE) < limit) {
node = paren(true, node);
}
return node;
}
public Settings withParens(final List<Paren> parens) {
return this.parens.equals(parens) ? this : new Settings(mode, limit, List.copyOf(parens));
}
}
}

View File

@@ -1,145 +0,0 @@
package expression.common;
import base.ExtendedRandom;
import base.Functional;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
public class NodeRendererBuilder<C> {
private final Renderer.Builder<C, NodeRenderer.Settings, Node<C>> nodeRenderer = Renderer.builder(Node::constant);
private final Map<String, Priority> priorities = new HashMap<>();
private final Map<String, String> brackets = new HashMap<>();
private final ExtendedRandom random;
public NodeRendererBuilder(final ExtendedRandom random) {
this.random = random;
nodeRenderer.unary(NodeRenderer.PAREN, (mode, arg) -> NodeRenderer.paren(true, arg));
}
public void unary(final String name, final int priority) {
final String space = name.equals("-") || Character.isLetter(name.charAt(0)) ? " " : "";
nodeRenderer.unary(
name,
(settings, arg) -> settings.extra(Node.op(name, priority, inner(settings, priority, arg, space)), random)
);
}
public void unary(final String left, final String right) {
brackets.put(left, right);
nodeRenderer.unary(
left,
(settings, arg) -> settings.extra(Node.op(left, Integer.MAX_VALUE, arg), random)
);
}
private Node<C> inner(final NodeRenderer.Settings settings, final int priority, final Node<C> arg, final String space) {
if (settings.mode() == NodeRenderer.Mode.FULL) {
return NodeRenderer.paren(true, arg);
} else {
final String op = arg.get(
c -> space,
n -> space,
(n, p, a) ->
priority > unaryPriority(arg) ? NodeRenderer.PAREN :
NodeRenderer.PAREN.equals(n) ? "" :
space,
(n, a, b) -> NodeRenderer.PAREN
);
return op.isEmpty() ? arg : Node.op(op, Priority.MAX.priority | 1, arg);
}
}
private static <C> Integer unaryPriority(final Node<C> node) {
return node.get(c -> Integer.MAX_VALUE, n -> Integer.MAX_VALUE, (n, p, a) -> p, (n, a, b) -> Integer.MIN_VALUE);
}
public void binary(final String name, final int priority) {
final Priority mp = new Priority(name, priority);
priorities.put(name, mp);
nodeRenderer.binary(name, (settings, l, r) -> settings.extra(process(settings, mp, l, r), random));
}
private Node<C> process(final NodeRenderer.Settings settings, final Priority mp, final Node<C> l, final Node<C> r) {
if (settings.mode() == NodeRenderer.Mode.FULL) {
return NodeRenderer.paren(true, op(mp, l, r));
}
final Priority lp = priority(l);
final Priority rp = priority(r);
final int rc = rp.compareLevels(mp);
// :NOTE: Especially ugly code, do not replicate
final boolean advanced = settings.mode() == NodeRenderer.Mode.SAME
|| mp.has(2)
|| mp.has(1) && (mp != rp || (settings.mode() == NodeRenderer.Mode.TRUE_MINI && hasOther(r, rp)));
final Node<C> al = NodeRenderer.paren(lp.compareLevels(mp) < 0, l);
if (rc == 0 && !advanced) {
return get(r, null, (n, a, b) -> rp.op(mp.op(al, a), b));
} else {
return mp.op(al, NodeRenderer.paren(rc == 0 && advanced || rc < 0, r));
}
}
private boolean hasOther(final Node<C> node, final Priority priority) {
return get(node, () -> false, (name, l, r) -> {
final Priority p = Functional.get(priorities, name);
if (p.compareLevels(priority) != 0) {
return false;
}
return p != priority || hasOther(l, priority);
});
}
private Node<C> op(final Priority mp, final Node<C> l, final Node<C> r) {
return mp.op(l, r);
}
private Priority priority(final Node<C> node) {
return get(node, () -> Priority.MAX, (n, a, b) -> Functional.get(priorities, n));
}
private <R> R get(final Node<C> node, final Supplier<R> common, final Node.Binary<Node<C>, R> binary) {
return node.get(
c -> common.get(),
n -> common.get(),
(n, p, a) -> common.get(),
binary
);
}
public NodeRenderer<C> build() {
return new NodeRenderer<>(nodeRenderer.build(), brackets, random);
}
// :NOTE: Especially ugly bit-fiddling, do not replicate
private record Priority(String op, int priority) {
private static final int Q = 3;
private static final Priority MAX = new Priority("MAX", Integer.MAX_VALUE - Q);
private int compareLevels(final Priority that) {
return (priority | Q) - (that.priority | Q);
}
@Override
public String toString() {
return String.format("Priority(%s, %d, %d)", op, priority | Q, priority & Q);
}
public <C> Node<C> op(final Node<C> l, final Node<C> r) {
return Node.op(op, l, r);
}
private boolean has(final int value) {
return (priority & Q) == value;
}
}
}

View File

@@ -1,60 +0,0 @@
package expression.common;
import base.Either;
import java.util.function.LongUnaryOperator;
import java.util.function.Supplier;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class Reason {
public static final Reason OVERFLOW = new Reason("Overflow");
public static final Reason DBZ = new Reason("Division by zero");
private final String description;
public Reason(final String description) {
this.description = description;
}
public static <T> Either<Reason, T> eval(final Supplier<T> action) {
try {
return Either.right(action.get());
} catch (final ReasonException e) {
return Either.left(e.reason);
}
}
public static int overflow(final long value) {
return value < Integer.MIN_VALUE || Integer.MAX_VALUE < value
? OVERFLOW.error()
: (int) value;
}
public <T> T error() {
throw new ReasonException(this);
}
public LongUnaryOperator less(final long limit, final LongUnaryOperator op) {
return a -> a < limit ? error() : op.applyAsLong(a);
}
public LongUnaryOperator greater(final int limit, final LongUnaryOperator op) {
return a -> a > limit ? error() : op.applyAsLong(a);
}
private static class ReasonException extends RuntimeException {
private final Reason reason;
public ReasonException(final Reason reason) {
super(reason.description);
this.reason = reason;
}
}
@Override
public String toString() {
return String.format("Reason(%s)", description);
}
}

View File

@@ -1,60 +0,0 @@
package expression.common;
import base.Functional;
import base.Pair;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public interface Renderer<C, S, R> {
static <C, S, R> Builder<C, S, R> builder(final Node.Const<C, R> constant) {
return new Builder<>(constant);
}
R render(final Expr<C, R> expr, final S settings);
@FunctionalInterface
interface UnaryOperator<S, R> {
R apply(S settings, R arg);
}
@FunctionalInterface
interface BinaryOperator<S, R> {
R apply(S settings, R arg1, R arg2);
}
final class Builder<C, S, R> {
private final Node.Const<C, R> constant;
private final Map<String, UnaryOperator<S, R>> unary = new HashMap<>();
private final Map<String, BinaryOperator<S, R>> binary = new HashMap<>();
private Builder(final Node.Const<C, R> constant) {
this.constant = constant;
}
public void unary(final String name, final UnaryOperator<S, R> op) {
unary.put(name, op);
}
public void binary(final String name, final BinaryOperator<S, R> op) {
binary.put(name, op);
}
public Renderer<C, S, R> build() {
return (expr, settings) -> {
final Map<String, R> vars = expr.variables().stream()
.collect(Collectors.toMap(Pair::first, Pair::second));
return expr.node().cata(
constant,
name -> Functional.get(vars, name),
(name, p, arg) -> Functional.get(unary, name).apply(settings, arg),
(name, arg1, arg2) -> Functional.get(binary, name).apply(settings, arg1, arg2)
);
};
}
}
}

View File

@@ -1,56 +0,0 @@
package expression.common;
import base.Pair;
import base.TestCounter;
import expression.ToMiniString;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
public class TestGenerator<C, E extends ToMiniString> {
private final Generator<C, E> generator;
private final NodeRenderer<C> renderer;
public TestGenerator(final Generator<C, E> generator, final NodeRenderer<C> renderer) {
this.generator = generator;
this.renderer = renderer;
}
public void testBasic(final Consumer<Test<C, E>> test) {
generator.testBasic(consumer(test));
}
public void testRandom(final TestCounter counter, final int denominator, final Consumer<Test<C, E>> test) {
generator.testRandom(counter, denominator, consumer(test));
}
private Consumer<Expr<C, E>> consumer(final Consumer<TestGenerator.Test<C, E>> consumer) {
return expr -> consumer.accept(new TestGenerator.Test<>(expr, renderer));
}
public List<Pair<String, E>> variables(final int count) {
return generator.variables(count);
}
public String render(final Expr<C, ?> expr, final NodeRenderer.Settings settings) {
return renderer.render(expr, settings);
}
public static class Test<C, E> {
public final Expr<C, E> expr;
private final Map<NodeRenderer.Settings, String> rendered = new HashMap<>();
private final NodeRenderer<C> renderer;
public Test(final Expr<C, E> expr, final NodeRenderer<C> renderer) {
this.expr = expr;
this.renderer = renderer;
}
public String render(final NodeRenderer.Settings settings) {
return rendered.computeIfAbsent(settings, s -> renderer.render(expr, s));
}
}
}

View File

@@ -1,145 +0,0 @@
package expression.common;
import base.ExtendedRandom;
import base.Functional;
import expression.ToMiniString;
import expression.common.ExpressionKind.Variables;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class TestGeneratorBuilder<C> {
private final ExtendedRandom random;
private final Generator.Builder<C> generator;
private final NodeRendererBuilder<C> renderer;
private final List<Function<List<Node<C>>, Stream<Node<C>>>> basicTests = new ArrayList<>();
private final List<Node<C>> consts;
private final boolean verbose;
public TestGeneratorBuilder(
final ExtendedRandom random,
final Supplier<C> constant,
final List<C> constants,
final boolean verbose
) {
this.random = random;
this.verbose = verbose;
generator = Generator.builder(constant, random);
renderer = new NodeRendererBuilder<>(random);
consts = Functional.map(constants, Node::constant);
basicTests.add(vars -> consts.stream());
basicTests.add(List::stream);
}
private Node<C> c() {
return random.randomItem(consts);
}
private Node<C> v(final List<Node<C>> variables) {
return random.randomItem(variables);
}
private static <C> Node<C> f(final String name, final int priority, final Node<C> arg) {
return Node.op(name, priority, arg);
}
private static <C> Node<C> f(final String left, final Node<C> arg) {
return Node.op(left, Integer.MAX_VALUE, arg);
}
private static <C> Node<C> f(final String name, final Node<C> arg1, final Node<C> arg2) {
return Node.op(name, arg1, arg2);
}
@SafeVarargs
private void basicTests(final Function<List<Node<C>>, Node<C>>... tests) {
Arrays.stream(tests).map(test -> test.andThen(Stream::of)).forEachOrdered(basicTests::add);
}
public void unary(final String name, final int priority) {
generator.add(name, (priority & 1) * 2 - 1);
renderer.unary(name, priority);
if (verbose) {
basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream()).map(a -> f(name, priority, a)));
} else {
basicTests(vars -> f(name, priority, c()), vars -> f(name, priority, v(vars)));
}
final Function<List<Node<C>>, Node<C>> p1 = vars -> f(name, priority, f(name, priority, f("+", v(vars), c())));
final Function<List<Node<C>>, Node<C>> p2 = vars -> f("*", v(vars), f("*", v(vars), f(name, priority, c())));
basicTests(
vars -> f(name, priority, f("+", v(vars), v(vars))),
vars -> f(name, priority, f(name, priority, v(vars))),
vars -> f(name, priority, f("/", f(name, priority, v(vars)), f("+", v(vars), v(vars)))),
p1,
p2,
vars -> f("+", p1.apply(vars), p2.apply(vars))
);
}
public void unary(final String left, final String right) {
generator.add(left, 1);
renderer.unary(left, right);
if (verbose) {
basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream()).map(a -> f(left, a)));
} else {
basicTests(vars -> f(left, c()), vars -> f(left, v(vars)));
}
final Function<List<Node<C>>, Node<C>> p1 = vars -> f(left, f(left, f("+", v(vars), c())));
final Function<List<Node<C>>, Node<C>> p2 = vars -> f("*", v(vars), f("*", v(vars), f(left, c())));
basicTests(
vars -> f(left, f("+", v(vars), v(vars))),
vars -> f(left, f(left, v(vars))),
vars -> f(left, f("/", f(left, v(vars)), f("+", v(vars), v(vars)))),
p1,
p2,
vars -> f("+", p1.apply(vars), p2.apply(vars))
);
}
public void binary(final String name, final int priority) {
generator.add(name, 2);
renderer.binary(name, priority);
if (verbose) {
basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream().limit(3))
.flatMap(a -> consts.stream().map(b -> f(name, a, b))));
} else {
basicTests(
vars -> f(name, c(), c()),
vars -> f(name, v(vars), c()),
vars -> f(name, c(), v(vars)),
vars -> f(name, v(vars), v(vars))
);
}
final Function<List<Node<C>>, Node<C>> p1 = vars -> f(name, f(name, f("+", v(vars), c()), v(vars)), v(vars));
final Function<List<Node<C>>, Node<C>> p2 = vars -> f("*", v(vars), f("*", v(vars), f(name, c(), v(vars))));
basicTests(
vars -> f(name, f(name, v(vars), v(vars)), v(vars)),
vars -> f(name, v(vars), f(name, v(vars), v(vars))),
vars -> f(name, f(name, v(vars), v(vars)), f(name, v(vars), v(vars))),
vars -> f(name, f("-", f(name, v(vars), v(vars)), c()), f("+", v(vars), v(vars))),
p1,
p2,
vars -> f("+", p1.apply(vars), p2.apply(vars))
);
}
public <E extends ToMiniString> TestGenerator<C,E> build(final Variables<E> variables) {
return new TestGenerator<>(generator.build(variables, basicTests), renderer.build());
}
}

View File

@@ -1,51 +0,0 @@
package expression.common;
import base.Asserts;
import base.ExtendedRandom;
import expression.Const;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;
import java.util.function.IntFunction;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class Type<C> {
private final IntFunction<C> fromInt;
private final Function<ExtendedRandom, C> random;
private final Function<C, Const> constant;
public Type(final IntFunction<C> fromInt, final Function<ExtendedRandom, C> random, final Class<?> type) {
this.fromInt = fromInt;
this.random = random;
try {
final MethodHandle constructor = MethodHandles.publicLookup()
.findConstructor(Const.class, MethodType.methodType(void.class, type));
constant = c -> {
try {
return (Const) constructor.invoke(c);
} catch (final Throwable e) {
throw Asserts.error("Cannot create new Const(%s): %s", c, e);
}
};
} catch (final IllegalAccessException | NoSuchMethodException e) {
throw Asserts.error("Cannot find constructor Const(%s): %s", type, e);
}
}
public Const constant(final C value) {
return constant.apply(value);
}
public C fromInt(final int value) {
return fromInt.apply(value);
}
public C randomValue(final ExtendedRandom random) {
return this.random.apply(random);
}
}

View File

@@ -1,7 +0,0 @@
/**
* Expressions generators for expression-based homeworks
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package expression.common;

View File

@@ -1,30 +0,0 @@
package expression.exceptions;
import base.Selector;
import expression.ListExpression;
import expression.parser.Operations;
import static expression.parser.Operations.*;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class ExceptionsTest {
private static final ExpressionParser PARSER = new ExpressionParser();
private static final Operations.Operation LIST = kind(ListExpression.KIND, PARSER::parse);
public static final Selector SELECTOR = Selector.composite(ExceptionsTest.class, ExceptionsTester::new, "easy", "hard")
.variant("Base", LIST, ADD, SUBTRACT, MULTIPLY, DIVIDE, NEGATE)
.variant("3637", POW, LOG)
.variant("3839", POW, LOG, POW_2, LOG_2)
.variant("3435", POW_2, LOG_2)
.variant("3233", HIGH, LOW)
.selector();
private ExceptionsTest() {
}
public static void main(final String... args) {
SELECTOR.main(args);
}
}

View File

@@ -1,162 +0,0 @@
package expression.exceptions;
import base.Functional;
import base.Named;
import base.Pair;
import expression.ToMiniString;
import expression.Variable;
import expression.common.ExpressionKind;
import expression.parser.ParserTestSet;
import java.util.ArrayList;
import java.util.List;
import java.util.function.LongBinaryOperator;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ExceptionsTestSet<E extends ToMiniString, C> extends ParserTestSet<E, C> {
private static final int D = 5;
private static final List<Integer> OVERFLOW_VALUES = new ArrayList<>();
private final char[] CHARS = "AZ+-*%()[]<>".toCharArray();
static {
Functional.addRange(OVERFLOW_VALUES, D, Integer.MIN_VALUE + D);
Functional.addRange(OVERFLOW_VALUES, D, Integer.MIN_VALUE / 2);
Functional.addRange(OVERFLOW_VALUES, D, (int) -Math.sqrt(Integer.MAX_VALUE));
Functional.addRange(OVERFLOW_VALUES, D, 0);
Functional.addRange(OVERFLOW_VALUES, D, (int) Math.sqrt(Integer.MAX_VALUE));
Functional.addRange(OVERFLOW_VALUES, D, Integer.MAX_VALUE / 2);
Functional.addRange(OVERFLOW_VALUES, D, Integer.MAX_VALUE - D);
}
private final List<Named<String>> parsingTest;
public ExceptionsTestSet(final ExceptionsTester tester, final ParsedKind<E, C> kind) {
super(tester, kind, false);
parsingTest = tester.parsingTest;
}
private void testParsingErrors() {
counter.testForEach(parsingTest, op -> {
final List<String> names = Functional.map(kind.kind().variables().generate(counter.random(), 3), Pair::first);
final String expr = mangle(op.value(), names);
try {
kind.parse(expr, names);
counter.fail("Successfully parsed '%s'", op.value());
} catch (final Exception e) {
counter.format("%-30s %s%n", op.name(), e.getClass().getSimpleName() + ": " + e.getMessage());
}
});
}
private void testOverflow() {
final List<Pair<String, E>> variables = kind.kind().variables().generate(counter.random(), 3);
final List<String> names = Functional.map(variables, Pair::first);
final Variable vx = (Variable) variables.get(0).second();
final Variable vy = (Variable) variables.get(1).second();
//noinspection Convert2MethodRef
testOverflow(names, (a, b) -> a + b, "+", new CheckedAdd(vx, vy));
testOverflow(names, (a, b) -> a - b, "-", new CheckedSubtract(vx, vy));
testOverflow(names, (a, b) -> a * b, "*", new CheckedMultiply(vx, vy));
testOverflow(names, (a, b) -> b == 0 ? Long.MAX_VALUE : a / b, "/", new CheckedDivide(vx, vy));
testOverflow(names, (a, b) -> -b, "<- ignore first argument, unary -", new CheckedNegate(vy));
}
private void testOverflow(final List<String> names, final LongBinaryOperator f, final String op, final Object expression) {
final ExpressionKind<E, C> kind = this.kind.kind();
for (final int a : OVERFLOW_VALUES) {
for (final int b : OVERFLOW_VALUES) {
final long expected = f.applyAsLong(a, b);
final boolean isInt = Integer.MIN_VALUE <= expected && expected <= Integer.MAX_VALUE;
try {
final C actual = kind.evaluate(
kind.cast(expression),
names,
kind.fromInts(List.of(a, b, 0))
);
counter.checkTrue(
isInt && kind.fromInt((int) expected).equals(actual),
"%d %s %d == %d", a, op, b, actual
);
} catch (final Exception e) {
if (isInt) {
counter.fail(e, "Unexpected error in %d %s %d", a, op, b);
}
}
}
}
}
@Override
protected void test() {
counter.scope("Overflow tests", (Runnable) this::testOverflow);
super.test();
counter.scope("Parsing error tests", this::testParsingErrors);
}
@Override
protected E parse(final String expression, final List<String> variables, final boolean reparse) {
final String expr = expression.strip();
if (expr.length() > 10) {
for (final char ch : CHARS) {
for (int i = 0; i < 10; i++) {
final int index = 1 + tester.random().nextInt(expr.length() - 2);
int pi = index - 1;
while (Character.isWhitespace(expr.charAt(pi))) {
pi--;
}
int ni = index;
while (Character.isWhitespace(expr.charAt(ni))) {
ni++;
}
final char pc = expr.charAt(pi);
final char nc = expr.charAt(ni);
if (
"-([{*∛√²³₂₃!‖⎵⎴⌊⌈=?".indexOf(nc) < 0 &&
(!Character.isLetterOrDigit(pc) || !Character.isLetterOrDigit(ch)) &&
nc != ch && pc != ch &&
!Character.isLetterOrDigit(nc) && nc != '$'
) {
shouldFail(
variables,
"Parsing error expected for " + insert(expr, index, "<ERROR_INSERTED -->" + ch + "<-- ERROR_INSERTED>"),
insert(expr, index, String.valueOf(ch))
);
break;
}
}
}
parens(variables, expr, '[', ']');
parens(variables, expr, '{', '}');
}
return counter.testV(() -> counter.call("parse", () -> kind.parse(expr, variables)));
}
private static String insert(final String expr, final int index, final String value) {
return expr.substring(0, index) + value + expr.substring(index);
}
private void parens(final List<String> variables, final String expr, final char open, final char close) {
if (expr.indexOf(open) >= 0) {
replaces(variables, expr, open, '(');
replaces(variables, expr, close, ')');
if (expr.indexOf('(') >= 0) {
replaces(variables, expr, '(', open);
replaces(variables, expr, ')', close);
}
}
}
private void replaces(final List<String> variables, final String expr, final char what, final char by) {
final String input = expr.replace(what, by);
shouldFail(variables, "Unmatched parentheses: " + input, input);
}
private void shouldFail(final List<String> variables, final String message, final String input) {
counter.shouldFail(message, () -> kind.parse(input, variables));
}
}

View File

@@ -1,100 +0,0 @@
package expression.exceptions;
import base.Named;
import base.TestCounter;
import expression.common.Reason;
import expression.parser.ParserTestSet;
import expression.parser.ParserTester;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.LongBinaryOperator;
import java.util.function.LongToIntFunction;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ExceptionsTester extends ParserTester {
/* package-private */ final List<Named<String>> parsingTest = new ArrayList<>(List.of(
Named.of("No first argument", "* $y * $z"),
Named.of("No middle argument", "$x * * $z"),
Named.of("No last argument", "$x * $y * "),
Named.of("No first argument'", "1 + (* $y * $z) + 2"),
Named.of("No middle argument'", "1 + ($x * / 9) + 3"),
Named.of("No last argument'", "1 + ($x * $y - ) + 3"),
Named.of("No opening parenthesis", "$x * $y)"),
Named.of("No closing parenthesis", "($x * $y"),
Named.of("Mismatched closing parenthesis", "($x * $y]"),
Named.of("Mismatched open parenthesis", "[$x * $y)"),
Named.of("Start symbol", "@$x * $y"),
Named.of("Middle symbol", "$x @ * $y"),
Named.of("End symbol", "$x * $y@"),
Named.of("Constant overflow 1", Integer.MIN_VALUE - 1L + ""),
Named.of("Constant overflow 2", Integer.MAX_VALUE + 1L + ""),
Named.of("Bare +", "+"),
Named.of("Bare -", "-"),
Named.of("Bare a", "a"),
Named.of("(())", "(())"),
Named.of("Spaces in numbers", "10 20")
));
public ExceptionsTester(final TestCounter counter) {
super(counter);
}
private void parsingTests(final String... tests) {
for (final String test : tests) {
parsingTest.add(Named.of(test, test));
}
}
@Override
public void unary(final String name, final int priority, final BiFunction<Long, LongToIntFunction, Long> op) {
if (allowed(name)) {
parsingTests(name, "1 * " + name, name + " * 1");
}
parsingTests(name + "()", name + "(1, 2)");
if (name.length() > 1) {
parsingTests(name + "q");
}
if (allLetterAndDigit(name)) {
parsingTests(name + "1", name + "q");
}
super.unary(name, priority, op);
}
private static boolean allowed(final String name) {
return !"xyz".contains(name.substring(0, 1)) && !"xyz".contains(name.substring(name.length() - 1));
}
@Override
public void binary(final String name, final int priority, final LongBinaryOperator op) {
if (allowed(name)) {
parsingTests(name);
}
parsingTests("1 " + name, "1 " + name + " * 3");
if (!"-".equals(name)) {
parsingTests(name + " 1", "1 * " + name + " 2");
}
if (allLetterAndDigit(name)) {
parsingTests("5" + name + "5", "5 " + name + "5", "5 " + name + "5 5", "1" + name + "x 1", "1 " + name + "x 1");
}
super.binary(name, priority, op);
}
private static boolean allLetterAndDigit(final String name) {
return name.chars().allMatch(Character::isLetterOrDigit);
}
@Override
protected void test(final ParserTestSet.ParsedKind<?, ?> kind) {
new ExceptionsTestSet<>(this, kind).test();
}
@Override
protected int cast(final long value) {
return Reason.overflow(value);
}
}

View File

@@ -1,13 +0,0 @@
package expression.exceptions;
import expression.ListExpression;
import java.util.List;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
public interface ListParser {
ListExpression parse(String expression, final List<String> variables) throws Exception;
}

View File

@@ -1,8 +0,0 @@
/**
* Tests for <a href="https://www.kgeorgiy.info/courses/prog-intro/homeworks.html#expressions-exceptions">Expression Error Handling</a> homework
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
*
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package expression.exceptions;

View File

@@ -1,7 +0,0 @@
/**
* Tests for <a href="https://www.kgeorgiy.info/courses/prog-intro/homeworks.html#expressions">Expressions</a> homework
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package expression;

View File

@@ -1,13 +0,0 @@
package expression.parser;
import expression.ListExpression;
import java.util.List;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@FunctionalInterface
public interface ListParser {
ListExpression parse(String expression, List<String> variables);
}

View File

@@ -1,148 +0,0 @@
package expression.parser;
import expression.ToMiniString;
import expression.common.ExpressionKind;
import expression.common.Reason;
import java.math.BigInteger;
import java.util.function.*;
import java.util.stream.LongStream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class Operations {
// === Base
public static final Operation NEGATE = unary("-", 1, a -> -a);
@SuppressWarnings("Convert2MethodRef")
public static final Operation ADD = binary("+", 1600, (a, b) -> a + b);
public static final Operation SUBTRACT = binary("-", 1602, (a, b) -> a - b);
public static final Operation MULTIPLY = binary("*", 2001, (a, b) -> a * b);
public static final Operation DIVIDE = binary("/", 2002, (a, b) -> b == 0 ? Reason.DBZ.error() : a / b);
// === MinMax
public static final Operation MIN = binary("min", 401, Math::min);
public static final Operation MAX = binary("max", 401, Math::max);
// === Reverse
private static Operation digits(final String name, final boolean mask, final int r, final LongBinaryOperator q) {
return unary(name, 1, v -> LongStream.iterate(mask ? v & 0xffff_ffffL : v, n -> n != 0, n -> n / r)
.map(n -> n % r)
.reduce(0, q));
}
public static final Operation REVERSE = digits("reverse", false, 10, (a, b) -> a * 10 + b);
// === Digits
public static final Operation DIGITS = digits("digits", false, 10, Long::sum);
// === Floor and Ceiling
private static long floor(final long a) {
return (a >= 0 ? a : a - FLOOR_CEILING_STEP + 1) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP;
}
private static long ceiling(final long a) {
return (a >= 0 ? a + FLOOR_CEILING_STEP - 1: a) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP;
}
public static final int FLOOR_CEILING_STEP = 1000;
public static final Operation FLOOR = unary("floor", 1, Operations::floor);
public static final Operation CEILING = unary("ceiling", 1, Operations::ceiling);
// === Set, Clear
@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
public static final Operation SET = binary("set", 202, (a, b) -> a | (1 << b));
@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
public static final Operation CLEAR = binary("clear", 202, (a, b) -> a & ~(1 << b));
// === Pow, Log
public static final Operation POW_O = binary("**", 2402, (a, b) ->
b < 0 ? 1 : BigInteger.valueOf(a).modPow(BigInteger.valueOf(b), BigInteger.valueOf(1L << 32)).intValue());
public static final Operation LOG_O = binary("//", 2402, (a, b) ->
a == 0 && b > 0 ? Integer.MIN_VALUE :
a <= 0 || b <= 0 || a == 1 && b == 1 ? 0 :
a > 1 && b == 1 ? Integer.MAX_VALUE
: LongStream.iterate(b, v -> v <= a, v -> v * b).count()
);
private static final Reason INVALID_POW = new Reason("Invalid power");
public static final Operation POW = binary("**", 2402, Operations::powC);
private static long powC(final long a, final long b) {
if (b < 0 || a == 0 && b == 0) {
return INVALID_POW.error();
}
if (Math.abs(a) > 1 && b > 32) {
return Reason.OVERFLOW.error();
}
final BigInteger result = BigInteger.valueOf(a).pow((int) b);
if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || BigInteger.valueOf(Integer.MAX_VALUE).compareTo(result) < 0) {
return Reason.OVERFLOW.error();
}
return result.intValue();
}
private static final Reason INVALID_LOG = new Reason("Invalid log");
public static final Operation LOG = binary("//", 2402, (a, b) ->
a <= 0 || b <= 1 ? INVALID_LOG.error() : (int) (Math.log(a) / Math.log(b)));
// Pow2, Log2
private static final Reason NEG_LOG = new Reason("Logarithm of negative value");
public static final Operation LOG_2
= unary("log₂", 1, NEG_LOG.less(1, a-> (long) (Math.log(a) / Math.log(2))));
private static final Reason NEG_POW = new Reason("Exponentiation to negative power");
public static final Operation POW_2
= unary("pow₂", 1, NEG_POW.less(0, Reason.OVERFLOW.greater(31, a -> (long) Math.pow(2, a))));
// === High, Low
public static final Operation HIGH = unary("high", 1, v -> Integer.highestOneBit((int) v));
public static final Operation LOW = unary("low", 1, v -> Integer.lowestOneBit((int) v));
// === Common
private Operations() {
}
public static Operation unary(final String name, final int priority, final LongUnaryOperator op) {
return unary(name, priority, (a, c) -> op.applyAsLong(a));
}
public static Operation unary(final String left, final String right, final LongUnaryOperator op) {
return unary(left, right, (a, c) -> op.applyAsLong(a));
}
public static Operation unary(final String name, final int priority, final BiFunction<Long, LongToIntFunction, Long> op) {
return tests -> tests.unary(name, priority, op);
}
public static Operation unary(final String left, final String right, final BiFunction<Long, LongToIntFunction, Long> op) {
return tests -> tests.unary(left, right, op);
}
public static Operation binary(final String name, final int priority, final LongBinaryOperator op) {
return tests -> tests.binary(name, priority, op);
}
public static <E extends ToMiniString, C> Operation kind(
final ExpressionKind<E, C> kind,
final ParserTestSet.Parser<E> parser
) {
return factory -> factory.kind(kind, parser);
}
@FunctionalInterface
public interface Operation extends Consumer<ParserTester> {}
}

View File

@@ -1,31 +0,0 @@
package expression.parser;
import base.Selector;
import expression.ListExpression;
import static expression.parser.Operations.*;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class ParserTest {
private static final ExpressionParser PARSER = new ExpressionParser();
private static final Operations.Operation LIST = kind(ListExpression.KIND, PARSER::parse);
// === Common
public static final Selector SELECTOR = Selector.composite(ParserTest.class, ParserTester::new, "easy", "hard")
.variant("Base", LIST, ADD, SUBTRACT, MULTIPLY, DIVIDE, NEGATE)
.variant("3637", MIN, MAX, REVERSE)
.variant("3839", MIN, MAX, REVERSE, DIGITS)
.variant("3435", FLOOR, CEILING, SET, CLEAR)
.variant("3233", FLOOR, CEILING)
.selector();
private ParserTest() {
}
public static void main(final String... args) {
SELECTOR.main(args);
}
}

View File

@@ -1,236 +0,0 @@
package expression.parser;
import base.*;
import expression.ToMiniString;
import expression.common.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ParserTestSet<E extends ToMiniString, C> {
private static final int D = 5;
private static final List<Integer> TEST_VALUES = new ArrayList<>();
static {
Functional.addRange(TEST_VALUES, D, D);
Functional.addRange(TEST_VALUES, D, -D);
}
public static final List<Integer> CONSTS
= List.of(0, 1, -1, 4, -4, 10, -10, 30, -30, 100, -100, Integer.MAX_VALUE, Integer.MIN_VALUE);
protected final ParserTester tester;
protected final ParsedKind<E, C> kind;
private final boolean safe;
protected final TestCounter counter;
public ParserTestSet(final ParserTester tester, final ParsedKind<E, C> kind) {
this(tester, kind, true);
}
protected ParserTestSet(final ParserTester tester, final ParsedKind<E, C> kind, final boolean safe) {
this.tester = tester;
this.kind = kind;
this.safe = safe;
counter = tester.getCounter();
}
private void examples(final TestGenerator<Integer, E> generator) {
example(generator, "$x+2", (x, y, z) -> x + 2);
example(generator, "2-$y", (x, y, z) -> 2 - y);
example(generator, " 3* $z ", (x, y, z) -> 3 * z);
example(generator, "$x/ - 2", (x, y, z) -> -x / 2);
example(generator, "$x*$y+($z-1 )/10", (x, y, z) -> x * y + (int) (z - 1) / 10);
example(generator, "-(-(-\t\t-5 + 16 *$x*$y) + 1 * $z) -(((-11)))", (x, y, z) -> -(-(5 + 16 * x * y) + z) + 11);
example(generator, "" + Integer.MAX_VALUE, (x, y, z) -> (long) Integer.MAX_VALUE);
example(generator, "" + Integer.MIN_VALUE, (x, y, z) -> (long) Integer.MIN_VALUE);
example(generator, "$x--$y--$z", (x, y, z) -> x + y + z);
example(generator, "((2+2))-0/(--2)*555", (x, y, z) -> 4L);
example(generator, "$x-$x+$y-$y+$z-($z)", (x, y, z) -> 0L);
example(generator, "(".repeat(300) + "$x + $y + (-10*-$z)" + ")".repeat(300), (x, y, z) -> x + y + 10 * z);
example(generator, "$x / $y / $z", (x, y, z) -> y == 0 || z == 0 ? Reason.DBZ.error() : (int) x / (int) y / z);
}
private void example(final TestGenerator<Integer, E> generator, final String expr, final ExampleExpression expression) {
final List<String> names = Functional.map(generator.variables(3), Pair::first);
final TExpression expected = vars -> expression.evaluate(vars.get(0), vars.get(1), vars.get(2));
counter.test(() -> {
final String mangled = mangle(expr, names);
final E parsed = parse(mangled, names, true);
Functional.allValues(TEST_VALUES, 3).forEach(values -> check(expected, parsed, names, values, mangled));
});
}
protected static String mangle(final String expr, final List<String> names) {
return expr
.replace("$x", names.get(0))
.replace("$y", names.get(1))
.replace("$z", names.get(2));
}
protected void test() {
final TestGenerator<Integer, E> generator = tester.generator.build(kind.kind.variables());
final Renderer<Integer, Unit, TExpression> renderer = tester.renderer.build();
final Consumer<TestGenerator.Test<Integer, E>> consumer = test -> test(renderer, test);
counter.scope("Basic tests", () -> generator.testBasic(consumer));
counter.scope("Handmade tests", () -> examples(generator));
counter.scope("Random tests", () -> generator.testRandom(counter, 1, consumer));
}
private void test(final Renderer<Integer, Unit, TExpression> renderer, final TestGenerator.Test<Integer, E> test) {
final Expr<Integer, E> expr = test.expr;
final List<Pair<String, E>> vars = expr.variables();
final List<String> variables = Functional.map(vars, Pair::first);
final String full = test.render(NodeRenderer.FULL);
final String mini = test.render(NodeRenderer.MINI);
final E fullParsed = parse(test, variables, NodeRenderer.FULL);
final E miniParsed = parse(test, variables, NodeRenderer.MINI);
final E safeParsed = parse(test, variables, NodeRenderer.SAME);
checkToString(full, mini, "base", fullParsed);
if (tester.mode() > 0) {
counter.test(() -> Asserts.assertEquals("mini.toMiniString", mini, miniParsed.toMiniString()));
counter.test(() -> Asserts.assertEquals("safe.toMiniString", mini, safeParsed.toMiniString()));
}
checkToString(full, mini, "extraParentheses", parse(test, variables, NodeRenderer.FULL_EXTRA));
checkToString(full, mini, "noSpaces", parse(removeSpaces(full), variables, false));
checkToString(full, mini, "extraSpaces", parse(extraSpaces(full), variables, false));
final TExpression expected = renderer.render(
Expr.of(
expr.node(),
Functional.map(vars, (i, var) -> Pair.of(var.first(), args -> args.get(i)))
),
Unit.INSTANCE
);
check(expected, fullParsed, variables, tester.random().random(variables.size(), ExtendedRandom::nextInt), full);
if (this.safe) {
final String safe = test.render(NodeRenderer.SAME);
check(expected, safeParsed, variables, tester.random().random(variables.size(), ExtendedRandom::nextInt), safe);
}
}
private E parse(
final TestGenerator.Test<Integer, E> test,
final List<String> variables,
final NodeRenderer.Settings settings
) {
return parse(test.render(settings.withParens(tester.parens)), variables, false);
}
private static final String LOOKBEHIND = "(?<![a-zA-Z0-9<>*/+=!-])";
private static final String LOOKAHEAD = "(?![a-zA-Z0-9<>*/])";
private static final Pattern SPACES = Pattern.compile(LOOKBEHIND + " | " + LOOKAHEAD + "|" + LOOKAHEAD + LOOKBEHIND);
private String extraSpaces(final String expression) {
return SPACES.matcher(expression).replaceAll(r -> tester.random().randomString(
ExtendedRandom.SPACES,
tester.random().nextInt(5)
));
}
private static String removeSpaces(final String expression) {
return SPACES.matcher(expression).replaceAll("");
}
private void checkToString(final String full, final String mini, final String context, final ToMiniString parsed) {
counter.test(() -> {
assertEquals(context + ".toString", full, full, parsed.toString());
if (tester.mode() > 0) {
assertEquals(context + ".toMiniString", full, mini, parsed.toMiniString());
}
});
}
private static void assertEquals(
final String context,
final String original,
final String expected,
final String actual
) {
final String message = String.format("%s:%n original `%s`,%n expected `%s`,%n actual `%s`",
context, original, expected, actual);
Asserts.assertTrue(message, Objects.equals(expected, actual));
}
private Either<Reason, Integer> eval(final TExpression expression, final List<Integer> vars) {
return Reason.eval(() -> tester.cast(expression.evaluate(vars)));
}
protected E parse(final String expression, final List<String> variables, final boolean reparse) {
return counter.testV(() -> {
final E parsed = counter.testV(() -> counter.call("parse",
() -> kind.parse(expression, variables)));
if (reparse) {
counter.testV(() -> counter.call("parse", () -> kind.parse(parsed.toString(), variables)));
}
return parsed;
});
}
private void check(
final TExpression expectedExpression,
final E expression,
final List<String> variables,
final List<Integer> values,
final String unparsed
) {
counter.test(() -> {
final Either<Reason, Integer> answer = eval(expectedExpression, values);
final String args = IntStream.range(0, variables.size())
.mapToObj(i -> variables.get(i) + "=" + values.get(i))
.collect(Collectors.joining(", "));
final String message = String.format("f(%s)%n\twhere f=%s%n\tyour f=%s", args, unparsed, expression);
try {
final C actual = kind.kind.evaluate(expression, variables, kind.kind.fromInts(values));
counter.checkTrue(answer.isRight(), "Error expected for f(%s)%n\twhere f=%s%n\tyour f=%s", args, unparsed, expression);
Asserts.assertEquals(message, answer.getRight(), actual);
} catch (final Exception e) {
if (answer.isRight()) {
counter.fail(e, "No error expected for %s", message);
}
}
});
}
@FunctionalInterface
public interface TExpression {
long evaluate(List<Integer> vars);
}
@FunctionalInterface
protected interface ExampleExpression {
long evaluate(long x, long y, long z);
}
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public record ParsedKind<E extends ToMiniString, C>(ExpressionKind<E, C> kind, Parser<E> parser) {
public E parse(final String expression, final List<String> variables) throws Exception {
return parser.parse(expression, variables);
}
@Override
public String toString() {
return kind.toString();
}
}
@FunctionalInterface
public interface Parser<E> {
E parse(final String expression, final List<String> variables) throws Exception;
}
}

View File

@@ -1,76 +0,0 @@
package expression.parser;
import base.ExtendedRandom;
import base.TestCounter;
import base.Tester;
import base.Unit;
import expression.ToMiniString;
import expression.common.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.LongBinaryOperator;
import java.util.function.LongToIntFunction;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ParserTester extends Tester {
/* package-private */ final TestGeneratorBuilder<Integer> generator;
/* package-private */ final Renderer.Builder<Integer, Unit, ParserTestSet.TExpression> renderer;
private final List<ParserTestSet.ParsedKind<?, ?>> kinds = new ArrayList<>();
/* package-private */ final List<NodeRenderer.Paren> parens = new ArrayList<>(List.of(NodeRenderer.paren("(", ")")));
public ParserTester(final TestCounter counter) {
super(counter);
renderer = Renderer.builder(c -> vars -> c);
final ExtendedRandom random = counter.random();
generator = new TestGeneratorBuilder<>(random, random::nextInt, ParserTestSet.CONSTS, true);
}
public void unary(final String name, final int priority, final BiFunction<Long, LongToIntFunction, Long> op) {
generator.unary(name, priority);
renderer.unary(name, (unit, a) -> vars -> cast(op.apply(a.evaluate(vars), this::cast)));
}
public void unary(final String left, final String right, final BiFunction<Long, LongToIntFunction, Long> op) {
generator.unary(left, right);
renderer.unary(left, (unit, a) -> vars -> cast(op.apply(a.evaluate(vars), this::cast)));
}
public void binary(final String name, final int priority, final LongBinaryOperator op) {
generator.binary(name, priority);
renderer.binary(name, (unit, a, b) -> vars -> cast(op.applyAsLong(a.evaluate(vars), b.evaluate(vars))));
}
<E extends ToMiniString, C> void kind(final ExpressionKind<E, C> kind, final ParserTestSet.Parser<E> parser) {
kinds.add(new ParserTestSet.ParsedKind<>(kind, parser));
}
@Override
public void test() {
for (final ParserTestSet.ParsedKind<?, ?> kind : kinds) {
counter.scope(kind.toString(), () -> test(kind));
}
}
protected void test(final ParserTestSet.ParsedKind<?, ?> kind) {
new ParserTestSet<>(this, kind).test();
}
public TestCounter getCounter() {
return counter;
}
protected int cast(final long value) {
return (int) value;
}
public void parens(final String... parens) {
assert parens.length % 2 == 0 : "Parens should come in pairs";
for (int i = 0; i < parens.length; i += 2) {
this.parens.add(NodeRenderer.paren(parens[i], parens[i + 1]));
}
}
}

View File

@@ -1,7 +0,0 @@
/**
* Tests for <a href="https://www.kgeorgiy.info/courses/prog-intro/homeworks.html#expressions-parsing">Expressions Parsing</a> homework
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package expression.parser;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,71 +0,0 @@
package markup;
import base.Asserts;
import base.BaseChecker;
import base.TestCounter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class MarkupTester {
private final Map<String, String> mapping;
private final String toString;
private MarkupTester(final Map<String, String> mapping, final String toString) {
this.mapping = mapping;
this.toString = toString;
}
public static Consumer<TestCounter> variant(final Consumer<Checker> checker, final String name, final Map<String, String> mapping) {
return counter -> test(checker).accept(new MarkupTester(mapping, "to" + name), counter);
}
public static BiConsumer<MarkupTester, TestCounter> test(final Consumer<Checker> tester) {
return (checker, counter) -> tester.accept(checker.new Checker(counter));
}
@Override
public String toString() {
return toString;
}
public class Checker extends BaseChecker {
public Checker(final TestCounter counter) {
super(counter);
}
private <T> MethodHandle findMethod(final T value) {
try {
return MethodHandles.publicLookup().findVirtual(value.getClass(), toString, MethodType.methodType(void.class, StringBuilder.class));
} catch (final NoSuchMethodException | IllegalAccessException e) {
throw Asserts.error("Cannot find method 'void %s(StringBuilder)' for %s", toString, value.getClass());
}
}
public <T> void test(final T value, String expectedTemplate) {
final MethodHandle method = findMethod(value);
for (final Map.Entry<String, String> entry : mapping.entrySet()) {
expectedTemplate = expectedTemplate.replace(entry.getKey(), entry.getValue());
}
final String expected = expectedTemplate;
counter.println("Test " + counter.getTestNo());
counter.test(() -> {
final StringBuilder sb = new StringBuilder();
try {
method.invoke(value, sb);
} catch (final Throwable e) {
throw Asserts.error("%s(StringBuilder) for %s thrown exception: %s", toString, value.getClass(), e);
}
Asserts.assertEquals("Result", expected, sb.toString());
});
}
}
}

View File

@@ -1,7 +0,0 @@
/**
* Tests for <a href="https://www.kgeorgiy.info/courses/prog-intro/homeworks.html#markup">Markup</a> homework
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package markup;

View File

@@ -0,0 +1,54 @@
package reverse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class FastScanner {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
int pos = 0;
boolean hasNextLine() throws IOException {
if (line != null && pos < line.length()) return true;
line = br.readLine();
pos = 0;
return line != null;
}
boolean hasNextInt() {
if (line == null) return false;
while (pos < line.length() && (Character.isWhitespace(line.charAt(pos)) ||
Character.getType(line.charAt(pos)) == Character.START_PUNCTUATION ||
Character.getType(line.charAt(pos)) == Character.END_PUNCTUATION)) {
pos++;
}
return pos < line.length() && (Character.isDigit(line.charAt(pos)) || line.charAt(pos) == '-');
}
int nextInt() {
while (pos < line.length() && (Character.isWhitespace(line.charAt(pos)) ||
Character.getType(line.charAt(pos)) == Character.START_PUNCTUATION ||
Character.getType(line.charAt(pos)) == Character.END_PUNCTUATION)) {
pos++;
}
int start = pos;
boolean negative = line.charAt(pos) == '-';
if (negative) pos++;
while (pos < line.length() && Character.isDigit(line.charAt(pos))) {
pos++;
}
int result = 0;
for (int i = negative ? start + 1 : start; i < pos; i++) {
result = result * 10 + (line.charAt(i) - '0');
}
return negative ? -result : result;
}
void nextLine() {
pos = line.length();
}
}

43
java/reverse/Reverse.java Normal file
View File

@@ -0,0 +1,43 @@
package reverse;
import java.io.*;
import java.util.Arrays;
public class Reverse {
public static void main(String[] args) throws IOException {
FastScanner sc = new FastScanner();
int[][] lines = new int[8][];
int linesCount = 0;
while (sc.hasNextLine()) {
int[] line = new int[8];
int count = 0;
while (sc.hasNextInt()) {
if (count >= line.length) {
line = Arrays.copyOf(line, line.length * 2);
}
line[count++] = sc.nextInt();
}
sc.nextLine();
line = Arrays.copyOf(line, count);
if (linesCount >= lines.length) {
lines = Arrays.copyOf(lines, lines.length * 2);
}
lines[linesCount++] = line;
}
PrintWriter out = new PrintWriter(System.out);
for (int i = linesCount - 1; i >= 0; i--) {
int[] line = lines[i];
for (int j = line.length - 1; j >= 0; j--) {
if (j < line.length - 1) out.print(" ");
out.print(line[j]);
}
out.println();
}
out.flush();
}
}

View File

@@ -0,0 +1,67 @@
package reverse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
public class ReverseAvg {
public static void main(String[] args) throws IOException {
FastScanner sc = new FastScanner();
int[][] lines = new int[8][];
int linesCount = 0;
while (sc.hasNextLine()) {
int[] line = new int[8];
int count = 0;
while (sc.hasNextInt()) {
if (count >= line.length) {
line = Arrays.copyOf(line, line.length * 2);
}
line[count++] = sc.nextInt();
}
sc.nextLine();
line = Arrays.copyOf(line, count);
if (linesCount >= lines.length) {
lines = Arrays.copyOf(lines, lines.length * 2);
}
lines[linesCount++] = line;
}
printResult(linesCount, lines);
}
private static void printResult(int linesCount, int[][] lines) {
PrintWriter out = new PrintWriter(System.out);
for (int i = 0; i < linesCount; i++) {
int[] line = lines[i];
for (int j = 0; j < line.length; j++) {
if (j > 0) out.print(" ");
long sum = 0;
long count = 0;
for (int m = 0; m < lines[i].length; m++) {
sum += lines[i][m];
count++;
}
for (int k = 0; k < linesCount; k++) {
if (k != i && lines[k].length > j) {
sum += lines[k][j];
count++;
}
}
out.print(sum / count);
}
out.println();
}
out.flush();
}
}

View File

@@ -0,0 +1,46 @@
package reverse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
public class ReverseEven {
public static void main(String[] args) throws IOException {
FastScanner sc = new FastScanner();
int[][] lines = new int[8][];
int linesCount = 0;
while (sc.hasNextLine()) {
int[] line = new int[8];
int count = 0;
while (sc.hasNextInt()) {
if (count >= line.length) {
line = Arrays.copyOf(line, line.length * 2);
}
line[count++] = sc.nextInt();
}
sc.nextLine();
line = Arrays.copyOf(line, count);
if (linesCount >= lines.length) {
lines = Arrays.copyOf(lines, lines.length * 2);
}
lines[linesCount++] = line;
}
PrintWriter out = new PrintWriter(System.out);
for (int i = linesCount - 1; i >= 0; i--) {
int[] line = lines[i];
for (int j = line.length - 1; j >= 0; j--) {
if ((i + j) % 2 == 0) {
if (j < line.length - 1) out.print(" ");
out.print(line[j]);
}
}
out.println();
}
out.flush();
}
}

View File

@@ -0,0 +1,63 @@
package reverse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
public class ReverseMax {
public static void main(String[] args) throws IOException {
FastScanner sc = new FastScanner();
int[][] lines = new int[8][];
int linesCount = 0;
while (sc.hasNextLine()) {
int[] line = new int[8];
int count = 0;
while (sc.hasNextInt()) {
if (count >= line.length) {
line = Arrays.copyOf(line, line.length * 2);
}
line[count++] = sc.nextInt();
}
sc.nextLine();
line = Arrays.copyOf(line, count);
if (linesCount >= lines.length) {
lines = Arrays.copyOf(lines, lines.length * 2);
}
lines[linesCount++] = line;
}
PrintWriter out = getPrintWriter(linesCount, lines);
out.flush();
}
private static PrintWriter getPrintWriter(int linesCount, int[][] lines) {
PrintWriter out = new PrintWriter(System.out);
for (int i = 0; i < linesCount; i++) {
int[] line = lines[i];
for (int j = 0; j < line.length; j++) {
if (j > 0) out.print(" ");
int maxValue = lines[i][j];
for (int k = i; k < linesCount; k++) {
for (int m = j; m < lines[k].length; m++) {
if (lines[k][m] > maxValue) {
maxValue = lines[k][m];
}
}
}
out.print(maxValue);
}
out.println();
}
return out;
}
}

View File

@@ -0,0 +1,61 @@
package reverse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
public class ReverseMaxC {
public static void main(String[] args) throws IOException {
FastScanner sc = new FastScanner();
int[][] lines = new int[8][];
int linesCount = 0;
while (sc.hasNextLine()) {
int[] line = new int[8];
int count = 0;
while (sc.hasNextInt()) {
if (count >= line.length) {
line = Arrays.copyOf(line, line.length * 2);
}
line[count++] = sc.nextInt();
}
sc.nextLine();
line = Arrays.copyOf(line, count);
if (linesCount >= lines.length) {
lines = Arrays.copyOf(lines, lines.length * 2);
}
lines[linesCount++] = line;
}
PrintWriter out = getPrintWriter(linesCount, lines);
out.flush();
}
private static PrintWriter getPrintWriter(int linesCount, int[][] lines) {
PrintWriter out = new PrintWriter(System.out);
for (int i = 0; i < linesCount; i++) {
int[] line = lines[i];
for (int j = 0; j < line.length; j++) {
if (j > 0) out.print(" ");
int maxRow = lines[i][j];
for (int k = i + 1; k < linesCount; k++) {
if (lines[k].length > j && lines[k][j] > maxRow) {
maxRow = lines[k][j];
}
}
out.print(maxRow);
}
out.println();
}
return out;
}
}

View File

@@ -0,0 +1,52 @@
package reverse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
public class ReverseRotate {
public static void main(String[] args) throws IOException {
FastScanner sc = new FastScanner();
int[][] lines = new int[8][];
int linesCount = 0;
int maxCols = 0;
while (sc.hasNextLine()) {
int[] line = new int[8];
int count = 0;
while (sc.hasNextInt()) {
if (count >= line.length) {
line = Arrays.copyOf(line, line.length * 2);
}
line[count++] = sc.nextInt();
}
sc.nextLine();
line = Arrays.copyOf(line, count);
if (linesCount >= lines.length) {
lines = Arrays.copyOf(lines, lines.length * 2);
}
lines[linesCount++] = line;
if (count > maxCols) {
maxCols = count;
}
}
PrintWriter out = new PrintWriter(System.out);
for (int j = 0; j < maxCols; j++) {
for (int i = linesCount - 1; i >= 0; i--) {
if (lines[i].length > j) {
out.print(lines[i][j] + " ");
}
}
out.println();
}
out.flush();
}
}

23
java/sum/Sum.java Normal file
View File

@@ -0,0 +1,23 @@
package sum;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class Sum {
public static void main(String[] args) {
int res = 0;
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res += (!builder.toString().isEmpty()) ? Integer.parseInt(builder.toString()) : 0;
builder = new StringBuilder();
}
}
res += (!builder.toString().isEmpty()) ? Integer.parseInt(builder.toString()) : 0;
}
System.out.println(res);
}
}

View File

@@ -0,0 +1,57 @@
package sum;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class SumBigDecimalHex {
public static void main(String[] args) {
BigDecimal res = new BigDecimal("0");
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res = res.add(compute(builder.toString()));
builder = new StringBuilder();
}
}
res = res.add(compute(builder.toString()));
}
System.out.println(res);
}
static BigDecimal compute(String num) {
BigDecimal res = BigDecimal.ZERO;
if (num == null || num.isEmpty()) {
return res;
} else if (num.startsWith("0x") || num.startsWith("0X")) {
num = num.toLowerCase();
num = num.substring(2);
if (num.contains("s")) {
int sIndex = num.indexOf('s');
String mantissaHex = num.substring(0, sIndex);
String exponentHex = num.substring(sIndex + 1);
BigInteger mantissa = new BigInteger(mantissaHex, 16);
BigInteger exponent = new BigInteger(exponentHex, 16);
res = res.add(new BigDecimal(mantissa).scaleByPowerOfTen(exponent.negate().intValueExact()));
} else {
res = res.add(new BigDecimal(new BigInteger(num, 16)));
}
} else {
res = new BigDecimal(num);
}
return res;
}
}

View File

@@ -0,0 +1,40 @@
package sum;
import java.math.BigInteger;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class SumBigIntegerOctal {
public static void main(String[] args) {
BigInteger res = new BigInteger("0");
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res = res.add(compute(builder.toString()));
builder = new StringBuilder();
}
}
res = res.add(compute(builder.toString()));
}
System.out.println(res);
}
static BigInteger compute(String num) {
BigInteger res = new BigInteger("0");
if (num.isEmpty()) {
res = res.add(BigInteger.ZERO);
} else if (num.endsWith("o") || num.endsWith("O")) {
res = res.add(new BigInteger(num.substring(0, num.length() - 1), 8));
} else {
res = res.add(new BigInteger(num));
}
return res;
}
}

23
java/sum/SumDouble.java Normal file
View File

@@ -0,0 +1,23 @@
package sum;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class SumDouble {
public static void main(String[] args) {
double res = 0.0; // 0_0
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res += (!builder.toString().isEmpty()) ? Double.parseDouble(builder.toString()) : 0;
builder = new StringBuilder();
}
}
res += (!builder.toString().isEmpty()) ? Double.parseDouble(builder.toString()) : 0;
}
System.out.println(res);
}
}

View File

@@ -0,0 +1,37 @@
package sum;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class SumDoubleHex {
public static void main(String[] args) {
double res = 0;
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res += compute(builder.toString());
builder = new StringBuilder();
}
}
res += compute(builder.toString());
}
System.out.println(res);
}
static double compute(String num) {
double res = 0;
if (num.isEmpty()) {
res += 0;
} else if (num.startsWith("0x") || num.startsWith("0X")) {
res += (num.contains(".")) ? Double.parseDouble(num) : Long.decode(num);
} else {
res += Double.parseDouble(num);
}
return res;
}
}

37
java/sum/SumHex.java Normal file
View File

@@ -0,0 +1,37 @@
package sum;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class SumHex {
public static void main(String[] args) {
int res = 0;
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res += compute(builder.toString());
builder = new StringBuilder();
}
}
res += compute(builder.toString());
}
System.out.println(res);
}
static int compute(String num) {
int res = 0;
if (num.isEmpty()) {
res += 0;
} else if (num.startsWith("0x") || num.startsWith("0X")) {
res += Long.decode(num);
} else {
res += Integer.parseInt(num);
}
return res;
}
}

View File

@@ -0,0 +1,41 @@
package sum;
import java.math.BigInteger;
/**
* @author Nikita Doschennikov (me@fymio.us)
*/
public class SumLongOctal {
public static void main(String[] args) {
long res = 0;
for (String arg : args) {
StringBuilder builder = new StringBuilder();
for (char c : arg.toCharArray()) {
if (!Character.isWhitespace(c)) {
builder.append(c);
} else {
res += compute(builder.toString());
builder = new StringBuilder();
}
}
res += compute(builder.toString());
}
System.out.println(res);
}
static long compute(String num) {
if (num.isEmpty()) {
return 0L;
}
if (num.endsWith("o") || num.endsWith("O")) {
String oct = num.substring(0, num.length() - 1);
BigInteger bi = new BigInteger(oct, 8);
return bi.longValue();
} else {
return Long.parseLong(num);
}
}
}

View File

@@ -0,0 +1,11 @@
public class WordInfo {
String word;
int count;
int firstIndex;
WordInfo(String word, int count, int firstIndex) {
this.word = word;
this.count = count;
this.firstIndex = firstIndex;
}
}

View File

@@ -0,0 +1,54 @@
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class WordStat {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("incorrect input!");
System.err.println("usage: java WordStat inputFile outputFile");
}
String inputFileName = args[0];
String outputFileName = args[1];
try {
BufferedReader r = new BufferedReader(new FileReader(inputFileName));
LinkedHashMap<String, Integer> wordCount = new LinkedHashMap<>();
StringBuilder sb = new StringBuilder();
int data = r.read();
while (data != -1) {
char c = (char) data;
if (Character.getType(c) == Character.DASH_PUNCTUATION ||
Character.isLetter(c) || c == '\'') {
sb.append(c);
} else {
if (!sb.isEmpty()) {
String word = sb.toString().toLowerCase();
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
sb.setLength(0);
}
}
data = r.read();
}
r.close();
PrintWriter writer = new PrintWriter(outputFileName, StandardCharsets.UTF_8);
for (Map.Entry<String, Integer> entry : wordCount.entrySet()) {
String key = entry.getKey();
int value = entry.getValue();
writer.println(key + " " + value);
}
writer.close();
} catch (Exception ex) {
System.err.println("An error occured: " + ex.getMessage());
}
}
}

View File

@@ -0,0 +1,71 @@
import java.io.*;
import java.util.*;
public class WordStatLength {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("incorrect input!");
System.err.println("usage: java WordStat inputFile outputFile");
}
String inputFileName = args[0];
String outputFileName = args[1];
try {
BufferedReader r = new BufferedReader(new FileReader(inputFileName));
Map<String, WordInfo> wordMap = new HashMap<>();
StringBuilder sb = new StringBuilder();
int wordIndex = 0;
int data = r.read();
while (data != -1) {
char c = (char) data;
if (Character.getType(c) == Character.DASH_PUNCTUATION ||
Character.isLetter(c) || c == '\'') {
sb.append(c);
} else {
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (wordMap.containsKey(word)) {
wordMap.get(word).count++;
} else {
wordMap.put(word, new WordInfo(word, 1, wordIndex));
wordIndex++;
}
sb.setLength(0);
}
}
data = r.read();
}
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (wordMap.containsKey(word)) {
wordMap.get(word).count++;
} else {
wordMap.put(word, new WordInfo(word, 1, wordIndex));
}
}
r.close();
List<WordInfo> sortedWords = new ArrayList<>(wordMap.values());
sortedWords.sort(Comparator.comparingInt((WordInfo w) -> w.word.length())
.thenComparingInt(w -> w.firstIndex));
PrintWriter writer = new PrintWriter(outputFileName, "UTF-8");
for (WordInfo info : sortedWords) {
writer.println(info.word + " " + info.count);
}
writer.close();
} catch (Exception ex) {
System.err.println("An error occured: " + ex.getMessage());
}
}
}

View File

@@ -0,0 +1,94 @@
import java.io.*;
import java.util.*;
public class WordStatLengthAffix {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("incorrect input!");
System.err.println("usage: java WordStat inputFile outputFile");
}
String inputFileName = args[0];
String outputFileName = args[1];
try {
BufferedReader r = new BufferedReader(new FileReader(inputFileName));
Map<String, WordInfo> wordMap = new HashMap<>();
StringBuilder sb = new StringBuilder();
int wordIndex = 0;
int data = r.read();
while (data != -1) {
char c = (char) data;
if (Character.getType(c) == Character.DASH_PUNCTUATION ||
Character.isLetter(c) || c == '\'') {
sb.append(c);
} else {
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() != 1) {
String prefix = word.substring(0, word.length() / 2);
String suffix = word.substring(word.length() - word.length() / 2);
if (wordMap.containsKey(prefix)) {
wordMap.get(prefix).count++;
} else {
wordMap.put(prefix, new WordInfo(prefix, 1, wordIndex));
wordIndex++;
}
if (wordMap.containsKey(suffix)) {
wordMap.get(suffix).count++;
} else {
wordMap.put(suffix, new WordInfo(suffix, 1, wordIndex));
wordIndex++;
}
}
sb.setLength(0);
}
}
data = r.read();
}
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() != 1) {
String prefix = word.substring(0, word.length() / 2);
String suffix = word.substring(word.length() - word.length() / 2);
if (wordMap.containsKey(prefix)) {
wordMap.get(prefix).count++;
} else {
wordMap.put(prefix, new WordInfo(prefix, 1, wordIndex));
wordIndex++;
}
if (wordMap.containsKey(suffix)) {
wordMap.get(suffix).count++;
} else {
wordMap.put(suffix, new WordInfo(suffix, 1, wordIndex));
wordIndex++;
}
}
}
r.close();
List<WordInfo> sortedWords = new ArrayList<>(wordMap.values());
sortedWords.sort(Comparator.comparingInt((WordInfo w) -> w.word.length())
.thenComparingInt(w -> w.firstIndex));
PrintWriter writer = new PrintWriter(outputFileName, "UTF-8");
for (WordInfo info : sortedWords) {
writer.println(info.word + " " + info.count);
}
writer.close();
} catch (Exception ex) {
System.err.println("An error occured: " + ex.getMessage());
}
}
}

View File

@@ -0,0 +1,79 @@
import java.io.*;
import java.util.*;
public class WordStatLengthMiddle {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("incorrect input!");
System.err.println("usage: java WordStat inputFile outputFile");
}
String inputFileName = args[0];
String outputFileName = args[1];
try {
BufferedReader r = new BufferedReader(new FileReader(inputFileName));
Map<String, WordInfo> wordMap = new HashMap<>();
StringBuilder sb = new StringBuilder();
int wordIndex = 0;
int data = r.read();
while (data != -1) {
char c = (char) data;
if (Character.getType(c) == Character.DASH_PUNCTUATION ||
Character.isLetter(c) || c == '\'') {
sb.append(c);
} else {
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() >= 7) {
word = word.substring(3, word.length() - 3);
if (wordMap.containsKey(word)) {
wordMap.get(word).count++;
} else {
wordMap.put(word, new WordInfo(word, 1, wordIndex));
wordIndex++;
}
}
sb.setLength(0);
}
}
data = r.read();
}
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() >= 7) {
word = word.substring(3, word.length() - 3);
if (wordMap.containsKey(word)) {
wordMap.get(word).count++;
} else {
wordMap.put(word, new WordInfo(word, 1, wordIndex));
wordIndex++;
}
}
}
r.close();
List<WordInfo> sortedWords = new ArrayList<>(wordMap.values());
sortedWords.sort(Comparator.comparingInt((WordInfo w) -> w.word.length())
.thenComparingInt(w -> w.firstIndex));
PrintWriter writer = new PrintWriter(outputFileName, "UTF-8");
for (WordInfo info : sortedWords) {
writer.println(info.word + " " + info.count);
}
writer.close();
} catch (Exception ex) {
System.err.println("An error occured: " + ex.getMessage());
}
}
}

View File

@@ -0,0 +1,79 @@
import java.io.*;
import java.util.*;
public class WordStatLengthPrefix {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("incorrect input!");
System.err.println("usage: java WordStat inputFile outputFile");
}
String inputFileName = args[0];
String outputFileName = args[1];
try {
BufferedReader r = new BufferedReader(new FileReader(inputFileName));
Map<String, WordInfo> wordMap = new HashMap<>();
StringBuilder sb = new StringBuilder();
int wordIndex = 0;
int data = r.read();
while (data != -1) {
char c = (char) data;
if (Character.getType(c) == Character.DASH_PUNCTUATION ||
Character.isLetter(c) || c == '\'') {
sb.append(c);
} else {
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() != 1) {
String prefix = word.substring(0, word.length() / 2);
if (wordMap.containsKey(prefix)) {
wordMap.get(prefix).count++;
} else {
wordMap.put(prefix, new WordInfo(prefix, 1, wordIndex));
wordIndex++;
}
}
sb.setLength(0);
}
}
data = r.read();
}
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() != 1) {
String prefix = word.substring(0, word.length() / 2);
if (wordMap.containsKey(prefix)) {
wordMap.get(prefix).count++;
} else {
wordMap.put(prefix, new WordInfo(prefix, 1, wordIndex));
wordIndex++;
}
}
}
r.close();
List<WordInfo> sortedWords = new ArrayList<>(wordMap.values());
sortedWords.sort(Comparator.comparingInt((WordInfo w) -> w.word.length())
.thenComparingInt(w -> w.firstIndex));
PrintWriter writer = new PrintWriter(outputFileName, "UTF-8");
for (WordInfo info : sortedWords) {
writer.println(info.word + " " + info.count);
}
writer.close();
} catch (Exception ex) {
System.err.println("An error occured: " + ex.getMessage());
}
}
}

View File

@@ -0,0 +1,79 @@
import java.io.*;
import java.util.*;
public class WordStatLengthSuffix {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("incorrect input!");
System.err.println("usage: java WordStat inputFile outputFile");
}
String inputFileName = args[0];
String outputFileName = args[1];
try {
BufferedReader r = new BufferedReader(new FileReader(inputFileName));
Map<String, WordInfo> wordMap = new HashMap<>();
StringBuilder sb = new StringBuilder();
int wordIndex = 0;
int data = r.read();
while (data != -1) {
char c = (char) data;
if (Character.getType(c) == Character.DASH_PUNCTUATION ||
Character.isLetter(c) || c == '\'') {
sb.append(c);
} else {
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() != 1) {
word = word.substring(word.length() - word.length() / 2);
if (wordMap.containsKey(word)) {
wordMap.get(word).count++;
} else {
wordMap.put(word, new WordInfo(word, 1, wordIndex));
wordIndex++;
}
}
sb.setLength(0);
}
}
data = r.read();
}
if (sb.length() > 0) {
String word = sb.toString().toLowerCase();
if (word.length() != 1) {
word = word.substring(word.length() - word.length() / 2);
if (wordMap.containsKey(word)) {
wordMap.get(word).count++;
} else {
wordMap.put(word, new WordInfo(word, 1, wordIndex));
wordIndex++;
}
}
}
r.close();
List<WordInfo> sortedWords = new ArrayList<>(wordMap.values());
sortedWords.sort(Comparator.comparingInt((WordInfo w) -> w.word.length())
.thenComparingInt(w -> w.firstIndex));
PrintWriter writer = new PrintWriter(outputFileName, "UTF-8");
for (WordInfo info : sortedWords) {
writer.println(info.word + " " + info.count);
}
writer.close();
} catch (Exception ex) {
System.err.println("An error occured: " + ex.getMessage());
}
}
}

28
java/wspp/IntList.java Normal file
View File

@@ -0,0 +1,28 @@
package wspp;
public class IntList {
private int[] array;
private int size;
public IntList() {
array = new int[10];
size = 0;
}
public void add(int value) {
if (size >= array.length) {
int[] newArray = new int[array.length * 2];
System.arraycopy(array, 0, newArray, 0, size);
array = newArray;
}
array[size++] = value;
}
public int get(int index) {
return array[index];
}
public int size() {
return size;
}
}

6
java/wspp/WordInfo.java Normal file
View File

@@ -0,0 +1,6 @@
package wspp;
public class WordInfo {
int count;
IntList positions;
}

View File

@@ -0,0 +1,74 @@
package wspp;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class WordScanner {
private BufferedReader br;
private String line = null;
private int pos = 0;
private int lineNumber = 0;
public WordScanner(String fileName) throws IOException {
br = new BufferedReader(new InputStreamReader(
new FileInputStream(fileName), StandardCharsets.UTF_8));
}
private boolean hasNextLine() throws IOException {
if (line != null && pos < line.length()) return true;
line = br.readLine();
if (line != null) {
lineNumber++;
}
pos = 0;
return line != null;
}
private boolean isWordChar(char c) {
return Character.isLetter(c) || Character.isDigit(c) ||
c == '\'' || c == '$' || c == '_' ||
Character.getType(c) == Character.DASH_PUNCTUATION;
}
public boolean hasNextWord() throws IOException {
while (hasNextLine()) {
while (pos < line.length() && !isWordChar(line.charAt(pos))) {
pos++;
}
if (pos < line.length()) {
return true;
}
}
return false;
}
public String nextWord() {
while (pos < line.length() && !isWordChar(line.charAt(pos))) {
pos++;
}
int start = pos;
while (pos < line.length() && isWordChar(line.charAt(pos))) {
pos++;
}
return line.substring(start, pos).toLowerCase();
}
public int getLineNumber() {
return lineNumber;
}
public void close() throws IOException {
br.close();
}
public void reset(String fileName) throws IOException {
br.close();
br = new BufferedReader(new InputStreamReader(
new FileInputStream(fileName), StandardCharsets.UTF_8));
line = null;
pos = 0;
lineNumber = 0;
}
}

64
java/wspp/Wspp.java Normal file
View File

@@ -0,0 +1,64 @@
package wspp;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class Wspp {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java Wspp inputFile outputFile");
return;
}
String inputFile = args[0];
String outputFile = args[1];
try {
WordScanner scanner = new WordScanner(inputFile);
Map<String, WordInfo> wordMap = new LinkedHashMap<>();
int wordPosition = 1;
while (scanner.hasNextWord()) {
String word = scanner.nextWord();
if (wordMap.containsKey(word)) {
WordInfo info = wordMap.get(word);
info.count++;
info.positions.add(wordPosition);
} else {
WordInfo info = new WordInfo();
info.count = 1;
info.positions = new IntList();
info.positions.add(wordPosition);
wordMap.put(word, info);
}
wordPosition++;
}
scanner.close();
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(outputFile), StandardCharsets.UTF_8));
for (Map.Entry<String, WordInfo> entry : wordMap.entrySet()) {
String word = entry.getKey();
WordInfo info = entry.getValue();
writer.print(word + " " + info.count);
for (int i = 0; i < info.positions.size(); i++) {
writer.print(" " + info.positions.get(i));
}
writer.println();
}
writer.close();
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
}

92
java/wspp/WsppPos.java Normal file
View File

@@ -0,0 +1,92 @@
package wspp;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class WsppPos {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java WsppPos inputFile outputFile");
return;
}
String inputFile = args[0];
String outputFile = args[1];
try {
// Первый проход: считаем количество слов в каждой строке
WordScanner scanner = new WordScanner(inputFile);
Map<Integer, Integer> wordsPerLine = new HashMap<>();
while (scanner.hasNextWord()) {
scanner.nextWord();
int line = scanner.getLineNumber();
wordsPerLine.put(line, wordsPerLine.getOrDefault(line, 0) + 1);
}
scanner.close();
// Второй проход: собираем статистику
scanner = new WordScanner(inputFile);
Map<String, WordInfo> wordMap = new LinkedHashMap<>();
Map<Integer, Integer> currentPosInLine = new HashMap<>();
while (scanner.hasNextWord()) {
String word = scanner.nextWord();
int lineNum = scanner.getLineNumber();
// Позиция с начала строки
int posInLine = currentPosInLine.getOrDefault(lineNum, 0) + 1;
currentPosInLine.put(lineNum, posInLine);
// Пересчитываем в позицию с конца строки
int totalWordsInLine = wordsPerLine.get(lineNum);
int posFromEnd = totalWordsInLine - posInLine + 1;
if (wordMap.containsKey(word)) {
WordInfo info = wordMap.get(word);
info.count++;
info.lineNumbers.add(lineNum);
info.positions.add(posFromEnd);
} else {
WordInfo info = new WordInfo();
info.count = 1;
info.lineNumbers = new IntList();
info.positions = new IntList();
info.lineNumbers.add(lineNum);
info.positions.add(posFromEnd);
wordMap.put(word, info);
}
}
scanner.close();
// Запись результата
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(outputFile), StandardCharsets.UTF_8));
for (Map.Entry<String, WordInfo> entry : wordMap.entrySet()) {
String word = entry.getKey();
WordInfo info = entry.getValue();
writer.print(word + " " + info.count);
for (int i = 0; i < info.lineNumbers.size(); i++) {
writer.print(" " + info.lineNumbers.get(i) + ":" + info.positions.get(i));
}
writer.println();
}
writer.close();
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
static class WordInfo {
int count;
IntList lineNumbers;
IntList positions;
}
}

1763
lectures/README.md Normal file

File diff suppressed because it is too large Load Diff