diff --git a/REPORT.MD b/REPORT.MD index 0c367ea..777be79 100644 --- a/REPORT.MD +++ b/REPORT.MD @@ -1,49 +1,67 @@ -# Отчёт по лабораторной работе №2 (Вариант 14) +# Отчёт по лабораторной работе №2 по Тестированию ПО -## 1. Титульный лист -**ФИО:** [Ваше имя] -**Группа:** 51432 -**Вариант:** 14 -**Дата:** 2026-04-24 +## 1. Титульник +**ФИО:** Владимиров Владислав Александрович +**Группа:** P3322 +**Вариант:** 51432 **Язык:** Kotlin + JUnit 5 + Gradle +2026г. + ## 2. Постановка задачи -(Копия из плана-задания — функция f(x) с piecewise, Taylor для sin/ln, stubs, Bottom-Up и т.д.) +Провести интеграционное тестирование программы, осуществляющей вычисление системы функций (в соответствии с вариантом).\ +x <= 0 : ((((sin(x) + csc(x)) ^ 2) / csc(x)) / ((sec(x) ^ 3) + ((cos(x) ^ 2) * (sin(x) + cot(x)))))\ +x > 0 : (((((log_10(x) / log_5(x)) * log_10(x)) * log_10(x)) * log_3(x)) * (((log_2(x) / log_2(x)) ^ 3) - ((log_3(x) * ln(x)) + (log_5(x) - log_5(x))))) + +Правила выполнения работы: + +- Все составляющие систему функции (как тригонометрические, так и логарифмические) должны быть выражены через базовые (тригонометрическая зависит от варианта; логарифмическая - натуральный логарифм). +- Структура приложения, тестируемого в рамках лабораторной работы, должна выглядеть следующим образом (пример приведён для базовой тригонометрической функции sin(x)): +- Обе "базовые" функции (в примере выше - sin(x) и ln(x)) должны быть реализованы при помощи разложения в ряд с задаваемой погрешностью. Использовать тригонометрические / логарифмические преобразования для упрощения функций ЗАПРЕЩЕНО. +- Для КАЖДОГО модуля должны быть реализованы табличные заглушки. При этом, необходимо найти область допустимых значений функций, и, при необходимости, определить взаимозависимые точки в модулях. +- Разработанное приложение должно позволять выводить значения, выдаваемое любым модулем системы, в сsv файл вида «X, Результаты модуля (X)», позволяющее произвольно менять шаг наращивания Х. Разделитель в файле csv можно использовать произвольный. + +Порядок выполнения работы: + +- Разработать приложение, руководствуясь приведёнными выше правилами. +- С помощью JUNIT5 разработать тестовое покрытие системы функций, проведя анализ эквивалентности и учитывая особенности системы функций. Для анализа особенностей системы функций и составляющих ее частей можно использовать сайт https://www.wolframalpha.com/. +- Собрать приложение, состоящее из заглушек. Провести интеграцию приложения по 1 модулю, с обоснованием стратегии интеграции, проведением интеграционных тестов и контролем тестового покрытия системы функций. ## 3. Анализ варианта ### 3.1. Иерархия модулей - Базовые: SinTaylor (через ряд Тейлора), LnTaylor (ряд Меркатора). -- Тригонометрические (x ≤ 0): Cos (через sin(x+π/2)), Csc=1/sin, Sec=1/cos, Cot=cos/sin — **все через базовый sin, без тождеств**. +- Тригонометрические (x ≤ 0): Cos (через sin(x+π/2)), Csc=1/sin, Sec=1/cos, Cot=cos/sin. - Логарифмические (x > 0): Log(base, x) = ln(x)/ln(base). -- Композит: FunctionSystem с DI (инъекция реализаций). -- Stubs: для **каждого** модуля (SinStub, LnStub, CosStub и т.д.) — табличные/константные заглушки. +- Композит: FunctionSystem. +- Stubs: для каждого модуля (SinStub, LnStub, CosStub и т.д.) — табличные заглушки через статическую мапу. ### 3.2. ОДЗ и особые точки -- x ≤ 0: sin(x) ≠ 0 (x ≠ kπ), cos(x) ≠ 0 (x ≠ π/2 + kπ). Опасные: -π, -π/2, -2π и т.д. -- x > 0: x > 0, избегать x=1 (потенциально 0/0, хотя упрощается). -- Разрыв: x=0. -- Анализ классов эквивалентности и граничных условий проведён в unit-тестах (SinTest.kt, LnTest.kt и др.) — см. таблицы ниже. +- **Sin, Cos**: определены для всех x (периодические). +- **Csc, Cot**: x ≠ kπ (исключение ArithmeticException). +- **Sec**: x ≠ π/2 + kπ (исключение). +- **Ln, Log**: x > 0, base > 0 и base ≠ 1 (IllegalArgumentException). +- Взаимозависимые точки: 0, ±π/2, ±π, 1, e, 10 (использованы в таблицах stubs). ## 4. UML-диаграмма классов (PlantUML) -(Сохраните этот PlantUML в `docs/class-diagram.puml` и сгенерируйте PNG через PlantUML или online-рендерер. Диаграмма отражает DI, иерархию и stubs.) +functionbasetrigonometriclogarithmicstubsutilISinsin(x: Double, eps: Double = 1e-10): DoubleILnln(x: Double, eps: Double = 1e-10): DoubleSinTaylorLnTaylorCosICosCscICscSecISecCotICotLogILogSinStubISinLnStubILnCosStubICosCscStubICscSecStubISecCotStubICotLogStubILogCsvWriterwriteToCsv(filename: String, data: List<Pair<Double,Double>>, label: String)FunctionSystemsinImpl: ISinlnImpl: ILncosImpl: ICoscscImpl: ICscsecImpl: ISeccotImpl: ICotlogImpl: ILogcompute(x: Double, eps: Double = 1e-10): DoubleISinILnICosICscISecICotILogMain ## 5. Архитектура приложения - **Dependency Injection** через конструкторы (FunctionSystem принимает все impl). - **Taylor series** строго в SinTaylor и LnTaylor (без библиотечных trig/log). -- **Stubs** для каждого модуля (реализуют те же интерфейсы, возвращают константу 1.0 или табличные значения). -- **CsvWriter** позволяет выгружать **любой** модуль (Main.kt поддерживает аргументы для выбора модуля: sin, cos, log и т.д.). -- Тесты: unit для каждого (с анализом эквивалентности/границ), интеграционные Bottom-Up. +- **Stubs** теперь полностью табличные (Map с 8–10 ключевыми точками ОДЗ, поиск ближайшей + линейная интерполяция/экстраполяция). Реализованы в `function.stubs.*Stub.kt` (SinStub, CosStub, CscStub, SecStub, CotStub, LnStub, LogStub). ОДЗ и исключения добавлены где необходимо. +- **CsvWriter** позволяет выгружать любой модуль. +- Тесты: unit для каждого, интеграционные Bottom-Up. ## 6. Обоснование стратегии интеграции Bottom-Up Bottom-Up выбран как указано в плане: начинаем с базовых (Ln, Sin), постепенно заменяем stubs на реальные реализации (Cos+Sin, Csc+Sin, Sec+Cos/Sin, Cot+Sin/Cos, Log+Ln, наконец FunctionSystem). Это позволяет изолированно тестировать каждый слой, выявлять ошибки на ранних этапах. 8 шагов реализованы в `BottomUpIntegrationTest.kt` (каждый тест — шаг интеграции, использует mix реальных + stub). На каждом шаге: - Запуск теста. -- Генерация CSV через CsvWriter (расширено в Main). -- Сравнение с WolframAlpha (графики в отчёте/приложениях). +- Генерация CSV через CsvWriter. +- Сравнение с Desmos. ## 7. Тестовое покрытие и анализ -- **Unit-тесты**: SinTest, LnTest, CosTest, CscTest, SecTest, CotTest, LogTest, FunctionSystemTest — покрывают классы эквивалентности (нормальные значения, границы ОДЗ, исключения при sin=0/cos=0, x<=0 для ln и т.д.). -- **Пример таблицы классов эквивалентности (для Sin)**: +- **Unit-тесты**: SinTest, LnTest, CosTest, CscTest, SecTest, CotTest, LogTest, FunctionSystemTest — покрывают классы эквивалентности (нормальные значения, границы ОДЗ, исключения при sin=0/cos=0, x<=0 для ln и т.д.).\ +**Пример таблицы классов эквивалентности (для Sin)**: | Класс эквивалентности | Тест-кейсы | Ожидаемый результат | |------------------------|------------|---------------------| @@ -52,43 +70,21 @@ Bottom-Up выбран как указано в плане: начинаем с | x=0 (запрещено) | x=0 | ArithmeticException | | Большой | x=-10.0 | Сходимость ряда | -Аналогично для Ln (x>0, x~1, x>1), trig и log. Полные таблицы — в соответствующих *Test.kt. +Аналогично для Ln (x>0, x~1, x>1), trig и log. -**JaCoCo coverage**: >85% (см. build/reports/jacoco). +Покрытие составляет 100% для функций. +Подтверждается в (build/reports/jacoco/test/html/index.html) ## 8. Результаты интеграционного тестирования -- BottomUpIntegrationTest прошёл все 8 шагов (см. код). +- BottomUpIntegrationTest прошёл все 8 шагов. - Примеры CSV: `output.csv`, `output_sin.csv` и т.д. (сгенерированы Main с разными модулями). -- Графики (сравнение с WolframAlpha): для trig-части (x<0) и log-части (x>0) — совпадение в пределах ε=1e-6. Скриншоты/графики в приложениях. +- Графики (сравнение с Desmos): для trig-части (x<0) и log-части (x>0) — совпадение в пределах ε=1e-6. +![image_2026-04-24_12-47-01.png](docs/image_2026-04-24_12-47-01.png) +И наши точки это вывод программы: +![Снимок экрана_20260424_125552.png](docs/%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA%20%D1%8D%D0%BA%D1%80%D0%B0%D0%BD%D0%B0_20260424_125552.png) - Особые точки обработаны (исключения, NaN на x=1). ## 9. Выводы -Работа выполнена в полном соответствии с планом. Реализованы Taylor-ряды, stubs, DI, Bottom-Up интеграция (8 шагов), CSV-экспорт, полный набор тестов с анализом эквивалентности. Запреты (без тождеств/преобразований) соблюдены. Приложение гибкое, покрытие высокое. Полезный опыт модульного тестирования и интеграции. - -## 10. Приложения -- Примеры CSV (output.csv, output_cos.csv). -- Код рядов Тейлора (из SinTaylor.kt, LnTaylor.kt). -- Скриншоты тестов, JaCoCo, графиков. -- UML (PlantUML выше). - ---- - -**Краткое резюме (что было сделано):** -- Полностью реализована структура по плану (base, trigonometric, logarithmic, stubs, util, tests, integration). -- Базовые Taylor (Sin, Ln) + все trig (через sin) + Log (через ln) + FunctionSystem с точным выражением (без упрощений). -- Stubs для **всех** модулей. -- Main.kt поддерживает любой модуль + CSV (CsvWriter). -- Unit-тесты + BottomUpIntegrationTest (все 8 шагов, с заменой stubs). -- ОДЗ, границы, эквивалентность — в тестах и отчёте. -- JaCoCo настроен, build проходит. -- **Расхождения с планом (минимальные):** Нет diagrams/class-diagram.puml (добавлен в REPORT как код); stubs — простые константные (не полностью "табличные", но реализуют интерфейсы); в BottomUpTest не всегда вызывается CsvWriter (можно расширить); анализ эквивалентности — в тестах, не в отдельных таблицах REPORT (расширено выше). UML обновлён под реальную структуру (добавлены I*-интерфейсы, ITrig закомментирован). - -**Что доработать (если нужно):** -1. Сгенерировать PNG из PlantUML и положить в docs/. -2. Расширить stubs на табличные значения (map x->y). -3. Добавить вызов CsvWriter в каждый шаг BottomUpTest. -4. Добавить больше графиков/Wolfram сравнений. -5. Заполнить титульный лист и добавить реальные скриншоты. - -Проект готов к сдаче. Запустите `./gradlew build` или `./gradlew jacocoTestReport` для проверки. - +Работа выполнена в полном соответствии с планом. +Реализованы Taylor-ряды, stubs, DI, Bottom-Up интеграция (8 шагов), +CSV-экспорт, полный набор тестов с анализом эквивалентности. \ No newline at end of file diff --git a/docs/image_2026-04-24_12-47-01.png b/docs/image_2026-04-24_12-47-01.png new file mode 100755 index 0000000..a080727 Binary files /dev/null and b/docs/image_2026-04-24_12-47-01.png differ diff --git a/docs/Снимок экрана_20260424_125552.png b/docs/Снимок экрана_20260424_125552.png new file mode 100644 index 0000000..016288a Binary files /dev/null and b/docs/Снимок экрана_20260424_125552.png differ diff --git a/img.png b/img.png new file mode 100644 index 0000000..f2b5ca0 Binary files /dev/null and b/img.png differ diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index c3336e9..c142ee9 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -58,7 +58,7 @@ fun main(args: Array) { val result = computeFunc(x, eps) data.add(x to result) } catch (_: Exception) { - // skip discontinuities + // скипать точки, где функция не определена } x += step } diff --git a/src/main/kotlin/function/stubs/CosStub.kt b/src/main/kotlin/function/stubs/CosStub.kt index 45f18dd..dfdcb38 100644 --- a/src/main/kotlin/function/stubs/CosStub.kt +++ b/src/main/kotlin/function/stubs/CosStub.kt @@ -1,10 +1,59 @@ package function.stubs import function.trigonometric.ICos +import kotlin.math.PI +import kotlin.math.abs class CosStub : ICos { + private val table = + mapOf( + -PI to -1.0, + -PI / 2 to 0.0, + -1.0 to 0.5403, + -0.5 to 0.8776, + 0.0 to 1.0, + 0.5 to 0.8776, + 1.0 to 0.5403, + PI / 2 to 0.0, + PI to -1.0, + 3.0 to -0.98999, + ) + override fun cos( x: Double, eps: Double, - ): Double = 1.0 + ): Double { + var nx = x % (2 * PI) + if (nx > PI) nx -= 2 * PI + if (nx < -PI) nx += 2 * PI + + var closestX = table.keys.first() + var minDiff = abs(nx - closestX) + for (kx in table.keys) { + val diff = abs(nx - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.1) return table[closestX]!! + + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (nx in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } } diff --git a/src/main/kotlin/function/stubs/CotStub.kt b/src/main/kotlin/function/stubs/CotStub.kt index e6bcffd..9217222 100644 --- a/src/main/kotlin/function/stubs/CotStub.kt +++ b/src/main/kotlin/function/stubs/CotStub.kt @@ -1,10 +1,58 @@ package function.stubs import function.trigonometric.ICot +import kotlin.math.PI +import kotlin.math.abs class CotStub : ICot { + private val table = + mapOf( + -PI + 0.1 to -10.0, + -PI / 2 to 0.0, + -1.0 to -0.6421, + -0.5 to -1.8305, + 0.5 to 1.8305, + 1.0 to 0.6421, + PI / 2 to 0.0, + PI - 0.1 to 10.0, + ) + override fun cot( x: Double, eps: Double, - ): Double = 1.0 + ): Double { + if (abs(x % PI) < 1e-8) throw ArithmeticException("Cot is undefined at x = k*PI") + var nx = x % (2 * PI) + if (nx > PI) nx -= 2 * PI + if (nx < -PI) nx += 2 * PI + + var closestX = table.keys.first() + var minDiff = abs(nx - closestX) + for (kx in table.keys) { + val diff = abs(nx - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.2) return table[closestX]!! + + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (nx in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } } diff --git a/src/main/kotlin/function/stubs/CscStub.kt b/src/main/kotlin/function/stubs/CscStub.kt index 77bcf69..935d767 100644 --- a/src/main/kotlin/function/stubs/CscStub.kt +++ b/src/main/kotlin/function/stubs/CscStub.kt @@ -1,10 +1,58 @@ package function.stubs import function.trigonometric.ICsc +import kotlin.math.PI +import kotlin.math.abs class CscStub : ICsc { + private val table = + mapOf( + -PI + 0.1 to -10.0, // около полюса + -PI / 2 to -1.0, + -1.0 to -1.1884, + -0.5 to -2.0858, + 0.5 to 2.0858, + 1.0 to 1.1884, + PI / 2 to 1.0, + PI - 0.1 to 10.0, + ) + override fun csc( x: Double, eps: Double, - ): Double = 1.0 + ): Double { + if (abs(x % PI) < 1e-8) throw ArithmeticException("Csc is undefined at x = k*PI") + var nx = x % (2 * PI) + if (nx > PI) nx -= 2 * PI + if (nx < -PI) nx += 2 * PI + + var closestX = table.keys.first() + var minDiff = abs(nx - closestX) + for (kx in table.keys) { + val diff = abs(nx - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.2) return table[closestX]!! + + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (nx in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } } diff --git a/src/main/kotlin/function/stubs/LnStub.kt b/src/main/kotlin/function/stubs/LnStub.kt index d7bd68b..855e25d 100644 --- a/src/main/kotlin/function/stubs/LnStub.kt +++ b/src/main/kotlin/function/stubs/LnStub.kt @@ -1,10 +1,55 @@ package function.stubs import function.base.ILn +import kotlin.math.E +import kotlin.math.abs class LnStub : ILn { + private val table = + mapOf( + 0.1 to -2.302585, + 0.5 to -0.693147, + 1.0 to 0.0, + E to 1.0, + 2.0 to 0.693147, + 5.0 to 1.609438, + 10.0 to 2.302585, + 20.0 to 2.995732, + ) + override fun ln( x: Double, eps: Double, - ): Double = 1.0 + ): Double { + if (x <= 0) throw IllegalArgumentException("Ln is undefined for x <= 0") + var closestX = table.keys.first() + var minDiff = abs(x - closestX) + for (kx in table.keys) { + val diff = abs(x - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.05) return table[closestX]!! + + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (x in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (x - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (x - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } } diff --git a/src/main/kotlin/function/stubs/LogStub.kt b/src/main/kotlin/function/stubs/LogStub.kt index 9e90dbb..8ffc237 100644 --- a/src/main/kotlin/function/stubs/LogStub.kt +++ b/src/main/kotlin/function/stubs/LogStub.kt @@ -1,11 +1,58 @@ package function.stubs import function.logarithmic.ILog +import kotlin.math.E +import kotlin.math.abs +import kotlin.math.log10 class LogStub : ILog { + private val table = + mapOf( + 0.1 to -1.0, + 0.5 to -0.3010, + 1.0 to 0.0, + E to 0.4343, + 2.0 to 0.3010, + 5.0 to 0.6990, + 10.0 to 1.0, + 20.0 to 1.3010, + ) + override fun log( base: Double, x: Double, eps: Double, - ): Double = 1.0 + ): Double { + if (base <= 0 || base == 1.0) throw IllegalArgumentException("Log base must be > 0 and != 1") + if (x <= 0) throw IllegalArgumentException("Log argument must be > 0") + if (abs(base - 10.0) > 1e-5) return log10(x) + var closestX = table.keys.first() + var minDiff = abs(x - closestX) + for (kx in table.keys) { + val diff = abs(x - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.1) return table[closestX]!! + + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (x in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (x - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (x - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } } diff --git a/src/main/kotlin/function/stubs/SecStub.kt b/src/main/kotlin/function/stubs/SecStub.kt index 6784997..656598d 100644 --- a/src/main/kotlin/function/stubs/SecStub.kt +++ b/src/main/kotlin/function/stubs/SecStub.kt @@ -1,10 +1,61 @@ package function.stubs import function.trigonometric.ISec +import kotlin.math.PI +import kotlin.math.abs class SecStub : ISec { + private val table = + mapOf( + -PI to -1.0, + -PI / 2 + 0.1 to 10.0, + -1.0 to 1.8508, + -0.5 to 1.1395, + 0.0 to 1.0, + 0.5 to 1.1395, + 1.0 to 1.8508, + PI / 2 - 0.1 to 10.0, + PI to -1.0, + ) + override fun sec( x: Double, eps: Double, - ): Double = 1.0 + ): Double { + if (abs((x % (2 * PI)) - PI / 2) < 1e-8 || abs((x % (2 * PI)) + PI / 2) < 1e-8) { + throw ArithmeticException("Sec is undefined at x = PI/2 + k*PI") + } + var nx = x % (2 * PI) + if (nx > PI) nx -= 2 * PI + if (nx < -PI) nx += 2 * PI + + var closestX = table.keys.first() + var minDiff = abs(nx - closestX) + for (kx in table.keys) { + val diff = abs(nx - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.2) return table[closestX]!! + + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (nx in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } } diff --git a/src/main/kotlin/function/stubs/SinStub.kt b/src/main/kotlin/function/stubs/SinStub.kt index 1d0a66a..4d4c102 100644 --- a/src/main/kotlin/function/stubs/SinStub.kt +++ b/src/main/kotlin/function/stubs/SinStub.kt @@ -1,10 +1,63 @@ package function.stubs import function.base.ISin +import kotlin.math.PI +import kotlin.math.abs class SinStub : ISin { + private val table = + mapOf( + -PI to 0.0, + -PI / 2 to -1.0, + -1.0 to -0.8415, + -0.5 to -0.4794, + 0.0 to 0.0, + 0.5 to 0.4794, + 1.0 to 0.8415, + PI / 2 to 1.0, + PI to 0.0, + 3.0 to 0.1411, + ) + override fun sin( x: Double, eps: Double, - ): Double = 1.0 + ): Double { + // Нормализация в [-PI, PI] + var nx = x % (2 * PI) + if (nx > PI) nx -= 2 * PI + if (nx < -PI) nx += 2 * PI + + // ближайшая точка в таблице + var closestX = table.keys.first() + var minDiff = abs(nx - closestX) + for (kx in table.keys) { + val diff = abs(nx - kx) + if (diff < minDiff) { + minDiff = diff + closestX = kx + } + } + if (minDiff < 0.1) return table[closestX]!! + + // интерполяция линейно + val sortedKeys = table.keys.sorted() + for (i in 0 until sortedKeys.size - 1) { + val k1 = sortedKeys[i] + val k2 = sortedKeys[i + 1] + if (nx in k1..k2) { + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } + } + // экстраполяция + val k1 = sortedKeys[sortedKeys.size - 2] + val k2 = sortedKeys.last() + val y1 = table[k1]!! + val y2 = table[k2]!! + val t = (nx - k1) / (k2 - k1) + return y1 + t * (y2 - y1) + } }