gmp: Introduce BigInt and Ratio
authorPatrickBlanchette <PatrickBlanchette@users.noreply.github.com>
Tue, 20 Jun 2017 14:24:08 +0000 (10:24 -0400)
committerPatrickBlanchette <PatrickBlanchette@users.noreply.github.com>
Thu, 6 Jul 2017 18:31:38 +0000 (14:31 -0400)
Signed-off-by: PatrickBlanchette <PatrickBlanchette@users.noreply.github.com>

lib/gmp/gmp.nit [new file with mode: 0644]

diff --git a/lib/gmp/gmp.nit b/lib/gmp/gmp.nit
new file mode 100644 (file)
index 0000000..66bd703
--- /dev/null
@@ -0,0 +1,399 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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
+#
+#     http://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.
+
+# Multi precision integer and rational number using gmp lib
+module gmp
+
+private import native_gmp
+
+redef class Numeric
+
+    # The BigInt equivalent of `self`
+    fun to_bi: BigInt do return self.to_i.to_bi
+
+    # The Ratio equivalent of `self`
+    fun to_r: Ratio do return self.to_f.to_r
+
+end
+
+redef class Text
+
+    # Is `self` a well-formed BigInt (i.e. parsable via `to_bi`)
+    #
+    #     assert "123".is_bi
+    #     assert "-123".is_bi
+    #     assert not "0b1011".is_bi
+    #     assert not "123u8".is_bi
+    #     assert not "Not a BigInt".is_bi
+    fun is_bi: Bool do
+        var pre = prefix("-")
+        if pre != null then
+            return pre.text_after.is_dec
+        else
+            return is_dec
+        end
+    end
+
+    # Is `self` a well-formed Ratio (i.e. parsable via `to_r`)
+    #
+    #     assert "123".is_r
+    #     assert "-123".is_r
+    #     assert "1/2".is_r
+    #     assert "-1/2".is_r
+    #     assert not "-1/-2".is_r
+    #     assert not "0b1011".is_r
+    #     assert not "123u8".is_r
+    #     assert not "Not an Ratio".is_r
+    fun is_r: Bool do
+        var frac = split_once_on('/')
+        if frac.length == 2 then
+            return frac[0].is_bi and frac[1].is_dec
+        else
+            return is_bi
+        end
+    end
+
+    # If `self` contains a BigInt, return the corresponding BigInt
+    #
+    #     assert("123".to_bi == 123.to_bi)
+    #     assert("-123".to_bi == -123.to_bi)
+    fun to_bi: BigInt do
+        assert is_bi
+        var tmp = new NativeMPZ
+        tmp.set_str(self.to_cstring, 10i32)
+        return new BigInt(tmp)
+    end
+
+    # If `self` contains a Ratio, return the corresponding Ratio
+    #
+    #     assert("123".to_r == 123.to_r)
+    #     assert("-123".to_r == -123.to_r)
+    #     assert("1/2".to_r == 0.5.to_r)
+    #     assert("-1/2".to_r == -0.5.to_r)
+    fun to_r: Ratio do
+        assert is_r
+        var tmp = new NativeMPQ
+        tmp.set_str self.to_cstring
+        return new Ratio(tmp)
+    end
+end
+
+redef class Float
+    redef fun to_bi do
+        var tmp = new NativeMPZ
+        tmp.set_d self
+        return new BigInt(tmp)
+    end
+
+    redef fun to_r do
+        var tmp = new NativeMPQ
+        tmp.set_d self
+        return new Ratio(tmp)
+    end
+end
+
+redef class Int
+    redef fun to_bi do
+        var tmp = new NativeMPZ
+        tmp.set_si self
+        return new BigInt(tmp)
+    end
+
+    redef fun to_r do
+        var tmp = new NativeMPQ
+        tmp.set_si(self, 1)
+        return new Ratio(tmp)
+    end
+end
+
+# Multi precision Integer numbers.
+class BigInt
+    super Discrete
+    super Numeric
+    super FinalizableOnce
+
+    redef type OTHER: BigInt
+
+    private var val: NativeMPZ
+
+    redef fun successor(i) do return self + i.to_bi
+    redef fun predecessor(i) do return self - i.to_bi
+
+    redef fun hash do return self.to_i
+
+    redef fun <=>(i) do
+        var res = val.cmp(i.val)
+        if (res) < 0 then
+            return -1
+        else if (res) > 0 then
+            return 1
+        else
+            return 0
+        end
+    end
+
+    redef fun ==(i) do return i isa BigInt and (self <=> i) == 0
+    redef fun <=(i) do return (self <=> i) <= 0
+    redef fun <(i) do return (self <=> i) < 0
+    redef fun >=(i) do return (self <=> i) >= 0
+    redef fun >(i) do return (self <=> i) > 0
+
+
+    #     assert(2.to_bi + 2.to_bi == 4.to_bi)
+    redef fun +(i) do
+        var res = new NativeMPZ
+        val.add(res, i.val)
+        return new BigInt(res)
+    end
+
+    #     assert(-(2.to_bi) == (-2).to_bi)
+    redef fun - do
+        var res = new NativeMPZ
+        val.neg res
+        return new BigInt(res)
+    end
+
+    #     assert(2.to_bi - 2.to_bi == 0.to_bi)
+    redef fun -(i) do
+        var res = new NativeMPZ
+        val.sub(res, i.val)
+        return new BigInt(res)
+    end
+
+    #     assert(2.to_bi * 2.to_bi == 4.to_bi)
+    redef fun *(i) do
+        var res = new NativeMPZ
+        val.mul(res, i.val)
+        return new BigInt(res)
+    end
+
+    #     assert(3.to_bi / 2.to_bi == 1.to_bi)
+    redef fun /(i) do
+        var res = new NativeMPZ
+        val.tdiv_q(res, i.val)
+        return new BigInt(res)
+    end
+
+    # Modulo of `self` with `i`.
+    #
+    # Finds the remainder of the division of `self` by `i`.
+    #
+    #     assert(5.to_bi % 2.to_bi == 1.to_bi)
+    fun %(i: BigInt): BigInt do
+        var res = new NativeMPZ
+        val.mod(res, i.val)
+        return new BigInt(res)
+    end
+
+    # Returns `self` raised to the power of `e`.
+    #
+    #     assert(3.to_bi ** 2 == 9.to_bi)
+    fun **(e: Int): BigInt do
+        var res = new NativeMPZ
+        var pow = new UInt64
+        pow.set_si e
+        val.pow_ui(res, pow)
+        pow.free
+        return new BigInt(res)
+    end
+
+    # The absolute value of `self`.
+    #
+    #     assert((-3).to_bi.abs == 3.to_bi)
+    fun abs: BigInt do
+        var res = new NativeMPZ
+        val.abs res
+        return new BigInt(res)
+    end
+
+    # Returns the greatest common divisor of `self` and `i`
+    #
+    #     assert(15.to_bi.gcd(10.to_bi) == 5.to_bi)
+    fun gcd(i: BigInt): BigInt do
+        var res = new NativeMPZ
+        val.gcd(res, i.val)
+        return new BigInt(res)
+    end
+
+    # Determine if `self` is a prime number.
+    # Return 2 if `self` is prime, return 1 if `self` is probably prime and
+    # return 0 if `self` is definitely not a prime number.
+    #
+    # This function begins by trying some divisions with small number to find if
+    # there is other factors then `self` and one. After that, it uses the
+    # Miller-Rabin probabilistic primality tests. The probability of a non-prime
+    # being identified as probably prime with that test is less than
+    # `4^(-reps)`. It is recommended to use a `reps` value between 15 and 50.
+    #
+    #     assert((0x10001).to_bi.probab_prime(15) == 2)
+    fun probab_prime(reps: Int): Int do
+        return val.probab_prime_p(reps.to_i32)
+    end
+
+    # Return the next prime number greater than `self`.
+    # This fonction uses a probabilistic algorithm.
+    #
+    #     assert(11.to_bi.next_prime == 13.to_bi)
+    fun next_prime: BigInt do
+        var res = new NativeMPZ
+        val.nextprime res
+        return new BigInt(res)
+    end
+
+    #     assert(11.to_bi.zero == 0.to_bi)
+    redef fun zero do return new BigInt(new NativeMPZ)
+
+    #     assert(11.to_bi.value_of(4) == 4.to_bi)
+    redef fun value_of(i) do return i.to_bi
+
+    #     assert(11.to_bi.to_i == 11)
+    redef fun to_i do return val.get_si
+
+    #     assert(11.to_bi.to_f == 11.0)
+    redef fun to_f do return val.get_d
+
+    #     assert(11.to_bi.to_s == "11")
+    redef fun to_s do
+        var cstr = val.get_str(10.to_i32)
+        var str = cstr.to_s
+        cstr.free
+        return str
+    end
+
+    redef fun to_bi do return self
+
+    #     assert(123.to_bi.to_r == 123.to_r)
+    redef fun to_r do
+        var tmp = new NativeMPQ
+        tmp.set_z val
+        return new Ratio(tmp)
+    end
+
+    #     assert(3.to_bi.distance(6.to_bi) == -3)
+    redef fun distance(i) do return (self - i).to_i
+
+    redef fun finalize_once do val.finalize
+end
+
+# Multi precision Rational numbers.
+#
+#     assert((0.2 + 0.1) == 0.30000000000000004)
+#     assert(("1/5".to_r + "1/10".to_r) == "3/10".to_r)
+class Ratio
+    super Numeric
+    super FinalizableOnce
+
+    redef type OTHER: Ratio
+
+    private var val: NativeMPQ
+
+    redef fun hash do return self.to_i
+
+    redef fun <=>(r) do
+        var res = val.cmp(r.val)
+        if (res) < 0 then
+            return -1
+        else if (res) > 0 then
+            return 1
+        else
+            return 0
+        end
+    end
+
+    redef fun ==(r) do return r isa Ratio and (self <=> r) == 0
+    redef fun <=(r) do return (self <=> r) <= 0
+    redef fun <(r) do return (self <=> r) < 0
+    redef fun >=(r) do return (self <=> r) >= 0
+    redef fun >(r) do return (self <=> r) > 0
+
+    #     assert("3/2".to_r + "5/2".to_r == 4.to_r)
+    redef fun +(r) do
+        var res = new NativeMPQ
+        val.add(res, r.val)
+        return new Ratio(res)
+    end
+
+    #     assert( -("1/2".to_r) == ("-1/2").to_r)
+    redef fun - do
+        var res = new NativeMPQ
+        val.neg res
+        return new Ratio(res)
+    end
+
+    #     assert("5/2".to_r - "3/2".to_r == 1.to_r)
+    redef fun -(r) do
+        var res = new NativeMPQ
+        val.sub(res, r.val)
+        return new Ratio(res)
+    end
+
+    #     assert("3/2".to_r * 2.to_r == 3.to_r)
+    redef fun *(r) do
+        var res = new NativeMPQ
+        val.mul(res, r.val)
+        return new Ratio(res)
+    end
+
+    #     assert(3.to_r / 2.to_r == "3/2".to_r)
+    redef fun /(r) do
+        var res = new NativeMPQ
+        val.div(res, r.val)
+        return new Ratio(res)
+    end
+
+    # The absolute value of `self`.
+    #
+    #     assert((-3.to_r).abs == 3.to_r)
+    #     assert(3.to_r.abs == 3.to_r)
+    fun abs: Ratio do
+        var res = new NativeMPQ
+        val.abs res
+        return new Ratio(res)
+    end
+
+    #     assert((3.to_r).zero == 0.to_r)
+    redef fun zero do return new Ratio(new NativeMPQ)
+
+    #     assert((3.to_r).value_of(2) == 2.to_r)
+    redef fun value_of(n) do return n.to_r
+
+    #     assert("7/2".to_r.to_i == 3)
+    redef fun to_i do
+        var res = new NativeMPZ
+        val.numref.tdiv_q(res, val.denref)
+        return res.get_si
+    end
+
+    #     assert(3.to_r.to_f == 3.0)
+    redef fun to_f do return val.get_d
+
+    #     assert(3.to_r.to_s == "3")
+    redef fun to_s do
+        var cstr = val.get_str(10i32)
+        var str = cstr.to_s
+        cstr.free
+        return str
+    end
+
+    #     assert("7/2".to_r.to_bi == 3.to_bi)
+    redef fun to_bi do
+        var res = new NativeMPZ
+        val.numref.tdiv_q(res, val.denref)
+        return new BigInt(res)
+    end
+
+    redef fun to_r do return self
+
+    redef fun finalize_once do val.finalize
+end