diff --git a/bench/bench_kotlin/README.md b/bench/bench_kotlin/README.md new file mode 100644 index 0000000..ee833e1 --- /dev/null +++ b/bench/bench_kotlin/README.md @@ -0,0 +1,18 @@ + + ## Run benchmarks + + ```shell +./gradlew bench + ``` + + ## Results of benchmarking + +Note: Warm-up=10 and Iteration=5 + + ``` +Benchmark Mode Cnt Score Error Units +TestFloat.all thrpt 15 3119.965 ± 787.947 ops/s +TestRational.all thrpt 15 7264.325 ± 2229.145 ops/s +TestTrigoFloat.all thrpt 15 324.178 ± 58.267 ops/s +TestTrigoRational.all thrpt 15 0.548 ± 0.015 ops/s + ``` \ No newline at end of file diff --git a/bench/bench_kotlin/build.gradle.kts b/bench/bench_kotlin/build.gradle.kts new file mode 100644 index 0000000..0fe4772 --- /dev/null +++ b/bench/bench_kotlin/build.gradle.kts @@ -0,0 +1,42 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.7.20" + id("org.jetbrains.kotlinx.benchmark") version "0.4.5" +} + +group = "math" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.5") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime-jvm:0.4.5") +} + +tasks.test { + useJUnitPlatform() +} + +tasks.withType { + kotlinOptions.jvmTarget = "1.8" +} + +benchmark { + configurations { + named("main") { + iterations = 5 // number of iterations + iterationTime = 300 + iterationTimeUnit = "ns" + warmups = 10 + advanced("jvmForks", 3) + } + } + targets { + register("test") + } +} \ No newline at end of file diff --git a/bench/bench_kotlin/gradle.properties b/bench/bench_kotlin/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/bench/bench_kotlin/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/bench/bench_kotlin/gradle/wrapper/gradle-wrapper.jar b/bench/bench_kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/bench/bench_kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/bench/bench_kotlin/gradle/wrapper/gradle-wrapper.properties b/bench/bench_kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..60c76b3 --- /dev/null +++ b/bench/bench_kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/bench/bench_kotlin/gradlew b/bench/bench_kotlin/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/bench/bench_kotlin/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/bench/bench_kotlin/gradlew.bat b/bench/bench_kotlin/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/bench/bench_kotlin/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/bench/bench_kotlin/settings.gradle.kts b/bench/bench_kotlin/settings.gradle.kts new file mode 100644 index 0000000..09c32c8 --- /dev/null +++ b/bench/bench_kotlin/settings.gradle.kts @@ -0,0 +1,3 @@ + +rootProject.name = "bench_kotlin" + diff --git a/bench/bench_kotlin/src/main/kotlin/math/Float.kt b/bench/bench_kotlin/src/main/kotlin/math/Float.kt new file mode 100644 index 0000000..c338aa8 --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/Float.kt @@ -0,0 +1,85 @@ +package math + +import math.lib.Math +import math.lib.Math.EIGHTEEN +import java.math.BigInteger +import java.math.BigInteger.ONE +import java.math.BigInteger.TEN +import java.math.BigInteger.ZERO + +interface Float : Repr { + + data class T(val value: BigInteger, val pow: BigInteger) { + override fun toString(): String = "$value*10^$pow" + } + + + fun new(v: Long): T = T(BigInteger.valueOf(v), ZERO) + fun new(v: Long, p: Long): T = T(BigInteger.valueOf(v), BigInteger.valueOf(p)) + + override fun abs(a: T): T = T(a.value.abs(), a.pow) + + override fun inverse(a: T): T = + T(Math.power(TEN, EIGHTEEN) / a.value, (a.pow * -ONE) - EIGHTEEN) + + override fun add(a: T): (T) -> T = { b -> + if (a.pow < b.pow) { + T(b.value * Math.power(TEN, (b.pow - a.pow).abs()) + a.value, a.pow) + } else { + T(a.value * Math.power(TEN, (a.pow - b.pow).abs()) + b.value, b.pow) + } + } + + override fun sub(a: T): (T) -> T = { b -> + if (a.pow < b.pow) { + T(a.value - b.value * Math.power(TEN, (b.pow - a.pow).abs()), a.pow) + } else { + T(a.value * Math.power(TEN, (a.pow - b.pow).abs()) - b.value, b.pow) + } + } + + override fun lt(a: T): (T) -> Boolean = { b -> + if (a.value < ZERO && b.value > ZERO) { + true + } else if (a.value > ZERO && b.value < ZERO) { + false + } else { + sub(a)(b).value < ZERO + } + } + + override fun lte(a: T): (T) -> Boolean = { b -> + if (a.value < ZERO && b.value > ZERO) { + true + } else if (a.value > ZERO && b.value < ZERO) { + false + } else { + sub(a)(b).value <= ZERO + } + } + + override fun mul(a: T): (T) -> T = { b -> + T(a.value * b.value, a.pow + b.pow) + } + + override fun div(a: T): (T) -> T = { b -> + T(a.value * Math.power(TEN, EIGHTEEN) / b.value, a.pow - b.pow - EIGHTEEN) + } + + + override fun resolve(a: T): (BigInteger) -> BigInteger = { prec -> + fun resolve_positive(a: T, prec: BigInteger): BigInteger = + if (a.pow > ZERO) { + a.value * Math.power(TEN, a.pow.abs()) * Math.power(TEN, prec) + } else { + a.value * Math.power(TEN, prec) / Math.power(TEN, (-ONE * a.pow).abs()) + } + + if (a.value < ZERO) { + -ONE * resolve_positive(T(a.value.abs(), a.pow), prec) + } else { + resolve_positive(a, prec) + } + } + +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/main/kotlin/math/Math.kt b/bench/bench_kotlin/src/main/kotlin/math/Math.kt new file mode 100644 index 0000000..b7d57f0 --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/Math.kt @@ -0,0 +1,77 @@ +package math.lib + +import java.math.BigInteger +import java.math.BigInteger.ONE +import java.math.BigInteger.TWO +import java.math.BigInteger.ZERO + +object Math { + + val EIGHTEEN: BigInteger = BigInteger.valueOf(18) + + fun iSqrt(y: BigInteger): BigInteger = + if (y > BigInteger.valueOf(3)) { + tailrec fun iter(x: BigInteger, y: BigInteger, z: BigInteger): BigInteger = + if (x < z) + iter(((y / x) + x) / TWO, y, x) + else + z + + iter((y / TWO) + ONE, y, y) + } else if (y != ZERO) { + ONE + } else { + ZERO + } + + @Suppress("NAME_SHADOWING") + fun power(x: BigInteger, y: BigInteger): BigInteger { + tailrec fun multiply(a: BigInteger, b: BigInteger, result: BigInteger): BigInteger = + if (b == ZERO) { + result + } else { + val result = if (b.and(ONE) == ONE) result * a else result + val b = b.shr(1); + val a = a * a + multiply(a, b, result) + } + + return multiply(x, y, ONE) + } + + fun factorial(n: BigInteger): BigInteger { + tailrec fun factorial(acc: BigInteger, i: BigInteger): BigInteger = + if (i < TWO) { + acc + } else { + factorial(acc * i, i - ONE) + } + + return factorial(ONE, n) + } + + fun min(a: BigInteger, b: BigInteger): BigInteger = + if (a < b) { + a + } else { + b + } + + fun max(a: BigInteger, b: BigInteger): BigInteger = + if (a < b) { + b + } else { + a + } + + fun log10(x: BigInteger): BigInteger { + tailrec fun checkPower(x: BigInteger, i: BigInteger): BigInteger = + if (x % power(BigInteger.TEN, i) > ZERO) { + (i - ONE).abs() + } else { + checkPower(x, i + ONE) + } + + return checkPower(x, ONE) + } +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/main/kotlin/math/Rational.kt b/bench/bench_kotlin/src/main/kotlin/math/Rational.kt new file mode 100644 index 0000000..332efba --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/Rational.kt @@ -0,0 +1,56 @@ +package math + +import math.lib.Math +import java.math.BigInteger + +interface Rational : Repr { + + data class T(val p: BigInteger, val q: BigInteger) { + override fun toString(): String = "$p/$q" + + } + + fun new(p: Long): T = T(BigInteger.valueOf(p), BigInteger.ONE) + + fun new(p: Long, q: Long): T = T(BigInteger.valueOf(p), BigInteger.valueOf(q)) + + fun new(p: BigInteger): T = T(p, BigInteger.ONE) + + override fun abs(a: T): T = T(a.p.abs(), a.q.abs()) + + override fun inverse(a: T): T = T(a.q, a.p) + + override fun add(a: T): (T) -> T = { b -> + T(a.p * b.q + a.q * b.p, a.q * b.q) + } + + override fun sub(a: T): (T) -> T = { b -> + T(a.p * b.q - a.q * b.p, a.q * b.q) + } + + override fun lt(a: T): (T) -> Boolean = { b -> + a.p * b.q < a.q * b.p + } + + override fun lte(a: T): (T) -> Boolean = { b -> + a.p * b.q <= a.q * b.p + } + + override fun mul(a: T): (T) -> T = { b -> + T(a.p * b.p, a.q * b.q) + } + + override fun div(a: T): (T) -> T = { b -> + T(a.p * b.q, a.q * b.p) + } + + override fun resolve(a: T): (BigInteger) -> BigInteger = { prec -> + val input = if (a.p > BigInteger.ZERO) { + T(a.p * -BigInteger.ONE, a.q * -BigInteger.ONE) + } else { + a + } + input.p * Math.power(BigInteger.TEN, prec) / input.q + } + +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/main/kotlin/math/Repr.kt b/bench/bench_kotlin/src/main/kotlin/math/Repr.kt new file mode 100644 index 0000000..637e32a --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/Repr.kt @@ -0,0 +1,40 @@ +package math + +import java.math.BigInteger + +interface Repr { + + fun abs(a: t): t + + fun inverse(a: t): t + + fun add(a: t): (t) -> t + + fun sub(a: t): (t) -> t + + fun lt(a: t): (t) -> Boolean + + fun lte(a: t): (t) -> Boolean + + fun gt(a: t): (t) -> Boolean = { b -> lt(b)(a) } + + fun gte(a: t): (t) -> Boolean = { b -> lte(b)(a) } + + fun mul(a: t): (t) -> t + + fun div(a: t): (t) -> t + + fun modulo(a: t): (t) -> t = { b -> + tailrec fun compute(a: t, b: t): t = + if (lt(a)(b)) { + a + } else { + compute(sub(a)(b), b) + } + + compute(a, b) + } + + fun resolve(a: t): (BigInteger) -> BigInteger + +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/main/kotlin/math/Trigo.kt b/bench/bench_kotlin/src/main/kotlin/math/Trigo.kt new file mode 100644 index 0000000..cc7e2a7 --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/Trigo.kt @@ -0,0 +1,101 @@ +package math + +typealias chebychevIntervals = Pair +typealias chebychevCoef = List +typealias chebychev = Map, chebychevCoef> + +interface Trigo : Repr { + val zero: t + val pi: t + val minus_one: t + val one: t + val two: t + val three: t + val four: t + val five: t + val six: t + val seven: t + val sqrt2: t + val sqrt3: t + + val twoPi get() = mul(pi)(two) + val piHalf get() = div(pi)(two) + val piQuarter get() = div(pi)(four) + val threePiHalf get() = mul(piHalf)(three) + val threePiQuarter get() = mul(piQuarter)(three) + val fivePiQuarter get() = mul(piQuarter)(five) + val sevenPiQuarter get() = mul(piQuarter)(seven) + val piThird get() = div(pi)(three) + val piSixth get() = div(pi)(six) + + val chebychevLookupIntervals: List> get() = listOf((zero to piHalf)) + + val chebychevLookupTable: chebychev + + fun findChebychevInterval(p: t): chebychevIntervals? { + tailrec fun find(x: t, lst: List>, index: Int): chebychevIntervals? = + if (index >= lst.size) { + null + } else if (lte(x)(lst[index].second)) { + lst[index] + } else { + find(x, lst, index + 1) + } + + return find(p, chebychevLookupIntervals, 0) + } + + fun sin(a: t, n: Int): t { + val intervalOpt = findChebychevInterval(a) + val intervalCheby = intervalOpt!! // Force the value extraction from the effect + // u = (a - (c.1+c.2)/2.0 )/ (c.2-c.1)/2.0 where c = intervalCheby + val u = div( + sub(a)(div(add(intervalCheby.first)(intervalCheby.second))(two)) + )( + div(sub(intervalCheby.second)(intervalCheby.first))(two) + ) + + val coef = chebychevLookupTable[intervalCheby]!! // Force the value extraction from the effect + val coef0 = coef[0] + val coef1 = coef[1] + + tailrec fun compute(i: Int, acc: t, t_prev: t, t_prev_prev: t, n: Int, coef: chebychevCoef): t = + if (i <= n) { + val t_next_u = sub(mul(mul(two)(u))(t_prev))(t_prev_prev) + val current_coef = coef[0] + val rest_coef = coef.subList(1, coef.size) + val new_acc = add(acc)(mul(t_next_u)(current_coef)) + compute(i + 1, new_acc, t_next_u, t_prev, n, rest_coef) + } else { + acc + } + + val y0 = add(coef0)(mul(coef1)(u)) + val coefFrom2 = coef.subList(2, coef.size) + + return compute(2, y0, u, one, n, coefFrom2) + } + + fun sinus_symmetry(sign: t, a: t, n: Int): t = + if (lt(a)(zero)) { + sinus_symmetry(mul(sign)(minus_one), mul(a)(minus_one), n) + } else { + val aModTwoPi = modulo(a)(twoPi) + + if (lte(aModTwoPi)(piHalf)) { + mul(sign)(sin(aModTwoPi, n)) + } else if (lte(aModTwoPi)(pi)) { + val theta = sub(aModTwoPi)(piHalf) + val halfPiMinus = sub(piHalf)(theta) + mul(sign)(sin(halfPiMinus, n)) + } else if (lt(pi)(aModTwoPi)) { + val minus_pi = sub(aModTwoPi)(pi) + sinus_symmetry(mul(sign)(minus_one), minus_pi, n) + } else { + throw Exception("Out of bound angle") + } + } + + fun sinus(a: t, n: Int): t = sinus_symmetry(one, a, n) + fun cosinus(a: t, n: Int): t = sinus_symmetry(one, add(a)(piHalf), n) +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/main/kotlin/math/TrigoFloat.kt b/bench/bench_kotlin/src/main/kotlin/math/TrigoFloat.kt new file mode 100644 index 0000000..9ab844c --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/TrigoFloat.kt @@ -0,0 +1,44 @@ +package math + +import math.Float.T +import java.math.BigInteger + +object TrigoFloat : Float, Trigo { + + override val zero: T get() = new(0) + override val pi: T + get() = T( + BigInteger("31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"), + BigInteger("-100") + ) + override val minus_one: T get() = new(-1) + override val one: T get() = new(1) + override val two: T get() = new(2) + override val three: T get() = new(3) + override val four: T get() = new(4) + override val five: T get() = new(5) + override val six: T get() = new(6) + override val seven: T get() = new(7) + override val sqrt2: T get() = T(BigInteger("1414213562373095048801688724209"), BigInteger("-30")) + override val sqrt3: T get() = T(BigInteger("17320508075688772935"), BigInteger("-19")) + + override val chebychevLookupTable: chebychev + get() = mapOf( + (zero to piHalf) to + listOf( + T(BigInteger("6021947012555463"), BigInteger("-16")), + T(BigInteger("513625166679107"), BigInteger("-15")), + T(BigInteger("-10354634426296383"), BigInteger("-17")), + T(BigInteger("-13732034234358675"), BigInteger("-18")), + T(BigInteger("13586698380902013"), BigInteger("-19")), + T(BigInteger("10726309440570181"), BigInteger("-20")), + T(BigInteger("-7046296793891682"), BigInteger("-21")), + T(BigInteger("-3963902510811801"), BigInteger("-22")), + T(BigInteger("194995972671759"), BigInteger("-22")), + T(BigInteger("8522923894416223"), BigInteger("-25")), + T(BigInteger("-3351717514643582"), BigInteger("-26")), + T(BigInteger("-11987008607938776"), BigInteger("-28")), + T(BigInteger("3835820550079916"), BigInteger("-29")) + ) + ) +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/main/kotlin/math/TrigoRational.kt b/bench/bench_kotlin/src/main/kotlin/math/TrigoRational.kt new file mode 100644 index 0000000..250a2f2 --- /dev/null +++ b/bench/bench_kotlin/src/main/kotlin/math/TrigoRational.kt @@ -0,0 +1,46 @@ +package math + +import math.Rational.T +import java.math.BigInteger + +object TrigoRational : Rational, Trigo { + override val zero: T get() = T(BigInteger.ZERO, BigInteger.ONE) + override val pi: T + get() = T( + BigInteger( + "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679" + ), BigInteger( + "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) + ) + override val minus_one: T get() = new(-1) + override val one: T get() = new(1) + override val two: T get() = new(2) + override val three: T get() = new(3) + override val four: T get() = new(4) + override val five: T get() = new(5) + override val six: T get() = new(6) + override val seven: T get() = new(7) + override val sqrt2: T get() = T(BigInteger("1414213562373095048801688724209"), BigInteger("1000000000000000000000000000000")) + override val sqrt3: T get() = T(BigInteger("17320508075688772935"), BigInteger("10000000000000000000")) + + override val chebychevLookupTable: chebychev + get() = mapOf( + (zero to piHalf) to + listOf( + T(BigInteger("6021947012555463"), BigInteger("10000000000000000")), + T(BigInteger("513625166679107"), BigInteger("1000000000000000")), + T(BigInteger("-10354634426296383"), BigInteger("100000000000000000")), + T(BigInteger("-13732034234358675"), BigInteger("1000000000000000000")), + T(BigInteger("13586698380902013"), BigInteger("10000000000000000000")), + T(BigInteger("10726309440570181"), BigInteger("100000000000000000000")), + T(BigInteger("-7046296793891682"), BigInteger("1000000000000000000000")), + T(BigInteger("-3963902510811801"), BigInteger("10000000000000000000000")), + T(BigInteger("194995972671759"), BigInteger("10000000000000000000000")), + T(BigInteger("8522923894416223"), BigInteger("10000000000000000000000000")), + T(BigInteger("-3351717514643582"), BigInteger("100000000000000000000000000")), + T(BigInteger("-11987008607938776"), BigInteger("10000000000000000000000000000")), + T(BigInteger("3835820550079916"), BigInteger("100000000000000000000000000000")) + ) + ) +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/test/kotlin/math/TestCore.kt b/bench/bench_kotlin/src/test/kotlin/math/TestCore.kt new file mode 100644 index 0000000..8059df2 --- /dev/null +++ b/bench/bench_kotlin/src/test/kotlin/math/TestCore.kt @@ -0,0 +1,29 @@ +package math + +import math.lib.Math +import java.math.BigInteger +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class TestCore { + + @Test + fun testISqrt() { + assertEquals(Math.iSqrt(BigInteger.valueOf(4)), BigInteger.TWO) + assertEquals(Math.iSqrt(BigInteger.valueOf(8)), BigInteger.TWO) + assertEquals(Math.iSqrt(BigInteger.valueOf(9)), BigInteger.valueOf(3)) + assertEquals(Math.iSqrt(BigInteger.valueOf(15)), BigInteger.valueOf(3)) + assertEquals(Math.iSqrt(BigInteger.valueOf(16)), BigInteger.valueOf(4)) + } + + @Test + fun testFactorial() { + assertEquals(Math.factorial(BigInteger.ZERO), BigInteger.ONE) + assertEquals(Math.factorial(BigInteger.ONE), BigInteger.ONE) + assertEquals(Math.factorial(BigInteger.TWO), BigInteger.TWO) + assertEquals(Math.factorial(BigInteger.valueOf(3)), BigInteger.valueOf(6)) + assertEquals(Math.factorial(BigInteger.valueOf(4)), BigInteger.valueOf(24)) + assertEquals(Math.factorial(BigInteger.valueOf(5)), BigInteger.valueOf(120)) + assertEquals(Math.factorial(BigInteger.valueOf(6)), BigInteger.valueOf(720)) + } +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/test/kotlin/math/TestFloat.kt b/bench/bench_kotlin/src/test/kotlin/math/TestFloat.kt new file mode 100644 index 0000000..d61ad4a --- /dev/null +++ b/bench/bench_kotlin/src/test/kotlin/math/TestFloat.kt @@ -0,0 +1,90 @@ +package math + +import math.TestFloat.Float.add +import math.TestFloat.Float.inverse +import math.TestFloat.Float.mul +import math.TestFloat.Float.new +import math.TestFloat.Float.sub +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import java.math.BigInteger +import kotlin.test.Test +import kotlin.test.assertEquals + +@State(Scope.Benchmark) +open class TestFloat { + + object Float : math.Float + + @Test + fun testScientific00() { + val a = inverse(new(6)) + val resolved = Float.resolve(a)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(166)) + } + + @Test + fun testScientific01() { + val a = inverse(new(6)) + val b = mul(new(-1))(a) + val resolved = Float.resolve(b)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(-166)) + } + + @Test + fun testScientific02() { + val a = inverse(new(3)) + val b = inverse(new(2)) + val v = add(a)(b) + val resolved = Float.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(833)) + } + + @Test + fun testScientific03() { + val a = inverse(new(3)) + val b = inverse(new(2)) + val v = sub(a)(b) + val resolved = Float.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(-166)) + } + + @Test + fun testScientific04() { + val a = inverse(new(3)) + val b = inverse(new(2)) + val v = mul(a)(b) + val resolved = Float.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(166)) + } + + @Test + fun testScientific05() { + val a = inverse(new(3)) + val b = inverse(new(2)) + val v = Float.div(a)(b) + val resolved = Float.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(666)) // Hell yeah + } + + @Test + fun testScientific06() { + val a = inverse(new(2)) + val b = inverse(new(3)) + val v = Float.modulo(a)(b) + val resolved = Float.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(166)) // Hell yeah + } + + @Benchmark + fun all() { + testScientific00() + testScientific01() + testScientific02() + testScientific03() + testScientific04() + testScientific05() + testScientific06() + } +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/test/kotlin/math/TestRational.kt b/bench/bench_kotlin/src/test/kotlin/math/TestRational.kt new file mode 100644 index 0000000..a3f5ca1 --- /dev/null +++ b/bench/bench_kotlin/src/test/kotlin/math/TestRational.kt @@ -0,0 +1,85 @@ +package math + +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import java.math.BigInteger +import kotlin.test.Test +import kotlin.test.assertEquals + +@State(Scope.Benchmark) +open class TestRational { + + object Rational : math.Rational + + @Test + fun testScientific00() { + val a = Rational.inverse(Rational.new(6)) + val resolved = Rational.resolve(a)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(166)) + } + + @Test + fun testScientific01() { + val a = Rational.inverse(Rational.new(6)) + val b = Rational.mul(Rational.new(-1))(a) + val resolved = Rational.resolve(b)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(-166)) + } + + @Test + fun testScientific02() { + val a = Rational.inverse(Rational.new(3)) + val b = Rational.inverse(Rational.new(2)) + val v = Rational.add(a)(b) + val resolved = Rational.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(833)) + } + + @Test + fun testScientific03() { + val a = Rational.inverse(Rational.new(3)) + val b = Rational.inverse(Rational.new(2)) + val v = Rational.sub(a)(b) + val resolved = Rational.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(-166)) + } + + @Test + fun testScientific04() { + val a = Rational.inverse(Rational.new(3)) + val b = Rational.inverse(Rational.new(2)) + val v = Rational.mul(a)(b) + val resolved = Rational.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(166)) + } + + @Test + fun testScientific05() { + val a = Rational.inverse(Rational.new(3)) + val b = Rational.inverse(Rational.new(2)) + val v = Rational.div(a)(b) + val resolved = Rational.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(666)) // Hell yeah + } + + @Test + fun testScientific06() { + val a = Rational.inverse(Rational.new(2)) + val b = Rational.inverse(Rational.new(3)) + val v = Rational.modulo(a)(b) + val resolved = Rational.resolve(v)(BigInteger.valueOf(3)) + assertEquals(resolved, BigInteger.valueOf(166)) // Hell yeah + } + + @Benchmark + fun all() { + testScientific00() + testScientific01() + testScientific02() + testScientific03() + testScientific04() + testScientific05() + testScientific06() + } +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/test/kotlin/math/TestTrigoFloat.kt b/bench/bench_kotlin/src/test/kotlin/math/TestTrigoFloat.kt new file mode 100644 index 0000000..a6febb8 --- /dev/null +++ b/bench/bench_kotlin/src/test/kotlin/math/TestTrigoFloat.kt @@ -0,0 +1,94 @@ +package math + +import math.Float.T +import math.TrigoFloat.abs +import math.TrigoFloat.add +import math.TrigoFloat.cosinus +import math.TrigoFloat.div +import math.TrigoFloat.inverse +import math.TrigoFloat.lt +import math.TrigoFloat.minus_one +import math.TrigoFloat.mul +import math.TrigoFloat.new +import math.TrigoFloat.one +import math.TrigoFloat.pi +import math.TrigoFloat.piHalf +import math.TrigoFloat.piQuarter +import math.TrigoFloat.piSixth +import math.TrigoFloat.piThird +import math.TrigoFloat.sinus +import math.TrigoFloat.sqrt2 +import math.TrigoFloat.sqrt3 +import math.TrigoFloat.sub +import math.TrigoFloat.threePiHalf +import math.TrigoFloat.two +import math.TrigoFloat.twoPi +import math.TrigoFloat.zero +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import kotlin.test.Test +import kotlin.test.assertEquals + +@State(Scope.Benchmark) +open class TestTrigoFloat { + + private val epsilon = inverse(new(1, 12)) + private val precision = 11 + + private fun testSinus(angle: T, expected: T): Boolean { + val diff = abs(sub(sinus(angle, precision))(expected)) + return lt(diff)(epsilon) + } + + @Test + fun testTrigoSinus() { + assertEquals(testSinus(zero, zero), true) + assertEquals(testSinus(piHalf, one), true) + assertEquals(testSinus(pi, zero), true) + assertEquals(testSinus(threePiHalf, minus_one), true) + assertEquals(testSinus(twoPi, zero), true) + assertEquals(testSinus(piQuarter, div(sqrt2)(two)), true) + assertEquals(testSinus(piSixth, inverse(two)), true) + assertEquals(testSinus(piThird, div(sqrt3)(two)), true) + assertEquals(testSinus(sub(zero)(piHalf), minus_one), true) + assertEquals(testSinus(sub(piHalf)(twoPi), one), true) + } + + private fun testCosinus(angle: T, expected: T): Boolean { + val diff = abs(sub(cosinus(angle, precision))(expected)) + return lt(diff)(epsilon) + } + + @Test + fun testTrigoCosinus() { + assertEquals(testCosinus(zero, one), true) + assertEquals(testCosinus(piHalf, zero), true) + assertEquals(testCosinus(pi, minus_one), true) + assertEquals(testCosinus(threePiHalf, zero), true) + assertEquals(testCosinus(twoPi, one), true) + assertEquals(testCosinus(piQuarter, div(sqrt2)(two)), true) + assertEquals(testCosinus(piSixth, div(sqrt3)(two)), true) + assertEquals(testCosinus(piThird, inverse(two)), true) + assertEquals(testCosinus(sub(zero)(piHalf), zero), true) + assertEquals(testCosinus(sub(piHalf)(twoPi), zero), true) + } + + @Test + fun testTrigo() { + val precision = 11 + val angle = piHalf + val cos = cosinus(angle, precision) + val sin = sinus(angle, precision) + val res = add(mul(cos)(cos))(mul(sin)(sin)) + val diff = abs(sub(res)(one)) + assertEquals(lt(diff)(epsilon), true); + } + + @Benchmark + fun all() { + testTrigoSinus() + testTrigoCosinus() + testTrigo() + } +} \ No newline at end of file diff --git a/bench/bench_kotlin/src/test/kotlin/math/TestTrigoRational.kt b/bench/bench_kotlin/src/test/kotlin/math/TestTrigoRational.kt new file mode 100644 index 0000000..15b3537 --- /dev/null +++ b/bench/bench_kotlin/src/test/kotlin/math/TestTrigoRational.kt @@ -0,0 +1,97 @@ +package math + + +import math.Rational.T +import math.TrigoRational.abs +import math.TrigoRational.add +import math.TrigoRational.cosinus +import math.TrigoRational.div +import math.TrigoRational.inverse +import math.TrigoRational.lt +import math.TrigoRational.minus_one +import math.TrigoRational.mul +import math.TrigoRational.new +import math.TrigoRational.one +import math.TrigoRational.pi +import math.TrigoRational.piHalf +import math.TrigoRational.piQuarter +import math.TrigoRational.piSixth +import math.TrigoRational.piThird +import math.TrigoRational.sinus +import math.TrigoRational.sqrt2 +import math.TrigoRational.sqrt3 +import math.TrigoRational.sub +import math.TrigoRational.threePiHalf +import math.TrigoRational.two +import math.TrigoRational.twoPi +import math.TrigoRational.zero +import math.lib.Math.power +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import java.math.BigInteger.valueOf +import kotlin.test.Test +import kotlin.test.assertEquals + +@State(Scope.Benchmark) +open class TestTrigoRational { + + private val epsilon = inverse(new(power(valueOf(10), valueOf(12)))) + private val precision = 11 + + private fun testSinus(angle: T, expected: T): Boolean { + val diff = abs(sub(sinus(angle, precision))(expected)) + return lt(diff)(epsilon) + } + + @Test + fun testTrigoSinus() { + assertEquals(testSinus(zero, zero), true) + assertEquals(testSinus(piHalf, one), true) + assertEquals(testSinus(pi, zero), true) + assertEquals(testSinus(threePiHalf, minus_one), true) + assertEquals(testSinus(twoPi, zero), true) + assertEquals(testSinus(piQuarter, div(sqrt2)(two)), true) + assertEquals(testSinus(piSixth, inverse(two)), true) + assertEquals(testSinus(piThird, div(sqrt3)(two)), true) + assertEquals(testSinus(sub(zero)(piHalf), minus_one), true) + assertEquals(testSinus(sub(piHalf)(twoPi), one), true) + } + + private fun testCosinus(angle: T, expected: T): Boolean { + val diff = abs(sub(cosinus(angle, precision))(expected)) + return lt(diff)(epsilon) + } + + @Test + fun testTrigoCosinus() { + assertEquals(testCosinus(zero, one), true) + assertEquals(testCosinus(piHalf, zero), true) + assertEquals(testCosinus(pi, minus_one), true) + assertEquals(testCosinus(threePiHalf, zero), true) + assertEquals(testCosinus(twoPi, one), true) + assertEquals(testCosinus(piQuarter, div(sqrt2)(two)), true) + assertEquals(testCosinus(piSixth, div(sqrt3)(two)), true) + assertEquals(testCosinus(piThird, inverse(two)), true) + assertEquals(testCosinus(sub(zero)(piHalf), zero), true) + assertEquals(testCosinus(sub(piHalf)(twoPi), zero), true) + } + + @Test + fun testTrigo() { + val precision = 11 + val angle = piHalf + val cos = cosinus(angle, precision) + val sin = sinus(angle, precision) + val res = add(mul(cos)(cos))(mul(sin)(sin)) + val diff = abs(sub(res)(one)) + assertEquals(lt(diff)(epsilon), true); + } + + @Benchmark + fun all() { + testTrigoSinus() + testTrigoCosinus() + testTrigo() + } +} \ No newline at end of file