From 3b52eb7ac183fc8c14efb16c4c13df0870691e61 Mon Sep 17 00:00:00 2001
From: Iaroslav Postovalov <postovalovya@gmail.com>
Date: Sat, 12 Sep 2020 18:22:59 +0700
Subject: [PATCH] Implement bignum module and write basic benchmark

---
 examples/build.gradle.kts                     |  2 +
 .../ast/ExpressionsInterpretersBenchmark.kt   |  3 +-
 .../kmath/bignum/BigIntegerBenchmark.kt       | 61 +++++++++++++++++++
 kmath-bignum/build.gradle.kts                 | 10 +++
 .../scientifik/kmath/bignum/BigIntegerRing.kt | 23 +++++++
 settings.gradle.kts                           |  1 +
 6 files changed, 99 insertions(+), 1 deletion(-)
 create mode 100644 examples/src/main/kotlin/scientifik/kmath/bignum/BigIntegerBenchmark.kt
 create mode 100644 kmath-bignum/build.gradle.kts
 create mode 100644 kmath-bignum/src/commonMain/kotlin/scientifik/kmath/bignum/BigIntegerRing.kt

diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts
index f5a4d5831..3f3c06764 100644
--- a/examples/build.gradle.kts
+++ b/examples/build.gradle.kts
@@ -32,6 +32,8 @@ dependencies {
     implementation(project(":kmath-koma"))
     implementation(project(":kmath-viktor"))
     implementation(project(":kmath-dimensions"))
+    implementation(project(":kmath-bignum"))
+    implementation(project(":kmath-memory"))
     implementation("com.kyonifer:koma-core-ejml:0.12")
     implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6")
     implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8")
diff --git a/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt
index 17a70a4aa..d941fb2c7 100644
--- a/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt
+++ b/examples/src/main/kotlin/scientifik/kmath/ast/ExpressionsInterpretersBenchmark.kt
@@ -9,8 +9,9 @@ import scientifik.kmath.operations.RealField
 import kotlin.random.Random
 import kotlin.system.measureTimeMillis
 
-class ExpressionsInterpretersBenchmark {
+private class ExpressionsInterpretersBenchmark {
     private val algebra: Field<Double> = RealField
+
     fun functionalExpression() {
         val expr = algebra.expressionInField {
             variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0)
diff --git a/examples/src/main/kotlin/scientifik/kmath/bignum/BigIntegerBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/bignum/BigIntegerBenchmark.kt
new file mode 100644
index 000000000..069fb628e
--- /dev/null
+++ b/examples/src/main/kotlin/scientifik/kmath/bignum/BigIntegerBenchmark.kt
@@ -0,0 +1,61 @@
+package scientifik.kmath.bignum
+
+import com.ionspin.kotlin.bignum.integer.BigInteger
+import scientifik.kmath.operations.BigIntField
+import scientifik.kmath.operations.invoke
+import kotlin.concurrent.thread
+import kotlin.random.Random
+import kotlin.system.measureTimeMillis
+
+private class BigIntegerBenchmark {
+    fun java() {
+        invokeAndSum { l, l2 -> (l.toBigInteger() * l2.toBigInteger()).toLong() }
+    }
+
+    fun bignum() {
+        invokeAndSum { l, l2 -> (BigInteger.fromLong(l) * BigInteger.fromLong(l2)).longValue() }
+    }
+
+    fun bigint() {
+        invokeAndSum { l, l2 -> BigIntField { number(l) * number(l2) }.toString().toLong() }
+    }
+
+    fun long() {
+        invokeAndSum { l, l2 -> l * l2 }
+    }
+
+    private fun invokeAndSum(op: (Long, Long) -> Long) {
+        val random = Random(0)
+        var sum = 0.0
+
+        repeat(1000000) {
+            sum += op(random.nextInt().toLong(), random.nextInt().toLong())
+        }
+
+        println(sum)
+    }
+}
+
+fun main() {
+    val benchmark = BigIntegerBenchmark()
+
+    thread {
+        val java = measureTimeMillis(benchmark::java)
+        println("java=$java")
+    }
+
+    thread {
+        val bignum = measureTimeMillis(benchmark::bignum)
+        println("bignum=$bignum")
+    }
+
+    thread {
+        val bigint = measureTimeMillis(benchmark::bigint)
+        println("bigint=$bigint")
+    }
+
+    thread {
+        val long = measureTimeMillis(benchmark::long)
+        println("long=$long")
+    }
+}
diff --git a/kmath-bignum/build.gradle.kts b/kmath-bignum/build.gradle.kts
new file mode 100644
index 000000000..cb23be68b
--- /dev/null
+++ b/kmath-bignum/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins { id("scientifik.mpp") }
+
+kotlin.sourceSets {
+    commonMain {
+        dependencies {
+            api("com.ionspin.kotlin:bignum:0.2.0")
+            api(project(":kmath-core"))
+        }
+    }
+}
diff --git a/kmath-bignum/src/commonMain/kotlin/scientifik/kmath/bignum/BigIntegerRing.kt b/kmath-bignum/src/commonMain/kotlin/scientifik/kmath/bignum/BigIntegerRing.kt
new file mode 100644
index 000000000..f32ed61eb
--- /dev/null
+++ b/kmath-bignum/src/commonMain/kotlin/scientifik/kmath/bignum/BigIntegerRing.kt
@@ -0,0 +1,23 @@
+package scientifik.kmath.bignum
+
+import com.ionspin.kotlin.bignum.integer.BigInteger
+import scientifik.kmath.operations.Ring
+
+class BigIntegerRing : Ring<BigInteger> {
+    override val zero: BigInteger
+        get() = BigInteger.ZERO
+
+    override val one: BigInteger
+        get() = BigInteger.ONE
+
+    override fun number(value: Number): BigInteger = BigInteger.fromLong(value.toLong())
+    override fun add(a: BigInteger, b: BigInteger): BigInteger = a + b
+    override fun multiply(a: BigInteger, k: Number): BigInteger = a * (number(k))
+    override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a * b
+    override fun BigInteger.unaryMinus(): BigInteger = negate()
+    override fun BigInteger.minus(b: BigInteger): BigInteger = minus(b)
+    override fun BigInteger.plus(b: Number): BigInteger = plus(number(b))
+    override fun BigInteger.minus(b: Number): BigInteger = minus(number(b))
+    override fun BigInteger.div(k: Number): BigInteger = divide(number(k))
+    override fun BigInteger.times(k: Number): BigInteger = multiply(number(k))
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 487e1d87f..e902b70bb 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -47,5 +47,6 @@ include(
     ":kmath-for-real",
     ":kmath-geometry",
     ":kmath-ast",
+    ":kmath-bignum",
     ":examples"
 )