Add signed initialization, bitwise ops, addition, negation

This commit is contained in:
mratsim 2018-04-25 17:50:53 +02:00
parent e2ab2dbf59
commit b34019c0b5
17 changed files with 233 additions and 14 deletions

View File

@ -33,3 +33,36 @@ func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
r_ptr[r_ptr[].len - 1] = n
else:
result.data = (type result.data)(n)
func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}=
when result.data is UintImpl:
when getSize(n) > bits:
# To avoid a costly runtime check, we refuse storing into StUint types smaller
# than the input type.
raise newException(ValueError, "Input " & $n & " (" & $T &
") cannot be stored in a multi-precision " &
$bits & "-bit integer." &
"\nUse a smaller input type instead. This is a compile-time check" &
" to avoid a costly run-time bit_length check at each StUint initialization.")
else:
let r_ptr = cast[ptr array[bits div (sizeof(T) * 8), T]](result.addr)
when system.cpuEndian == littleEndian:
# "Least significant byte are at the beginning"
if n < 0:
r_ptr[0] = -n
result = -result
else:
r_ptr[0] = n
else:
if n < 0:
r_ptr[r_ptr[].len - 1] = -n
result = -result
else:
r_ptr[r_ptr[].len - 1] = n
else:
if n < 0:
result.data = (type result.data)(-n)
result = -result
else:
result.data = (type result.data)(n)

79
src/int_public.nim Normal file
View File

@ -0,0 +1,79 @@
# Mpint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./private/datatypes, macros
export StInt, IntImpl, intImpl # TODO remove the need to export intImpl and this macro
type
Int128* = Stint[128]
Int256* = Stint[256]
template make_conv(conv_name: untyped, size: int): untyped =
func `convname`*(n: SomeInteger): Stint[size] {.inline, noInit.}=
n.stint(size)
make_conv(i128, 128)
make_conv(i256, 256)
template make_unary(op, ResultTy): untyped =
func `op`*(x: Stint): ResultTy {.noInit, inline.} =
when ResultTy is Stint:
result.data = op(x.data)
else:
op(x.data)
export op
template make_binary(op, ResultTy): untyped =
func `op`*(x, y: Stint): ResultTy {.noInit, inline.} =
when ResultTy is Stint:
result.data = op(x.data, y.data)
else:
op(x.data, y.data)
export `op`
template make_binary_inplace(op): untyped =
func `op`*(x: var Stint, y: Stint) {.inline.} =
op(x.data, y.data)
export op
import ./private/int_addsubneg
make_binary(`+`, Stint)
make_binary_inplace(`+=`)
make_unary(`-`, Stint)
# make_binary(`-`, Stint)
# make_binary_inplace(`-=`)
# import ./private/int_mul
# make_binary(`*`, Stint)
# import ./private/int_div
# make_binary(`div`, Stint)
# make_binary(`mod`, Stint)
# func divmod*(x, y: Stint): tuple[quot, rem: Stint] {.noInit, inline.} =
# (result.quot.data, result.rem.data) = divmod(x.data, y.data)
import ./private/int_comparison
make_binary(`<`, bool)
make_binary(`<=`, bool)
make_binary(`==`, bool)
func isZero*(x: Stint): bool {.inline.} = isZero x.data
import ./private/int_bitwise_ops
make_unary(`not`, Stint)
make_binary(`or`, Stint)
make_binary(`and`, Stint)
make_binary(`xor`, Stint)
# proc `shr`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} =
# result.data = x.data shr y
# proc `shl`*(x: Stint, y: SomeInteger): Stint {.noInit, inline, noSideEffect.} =
# result.data = x.data shl y

View File

@ -25,7 +25,7 @@ func countLeadingZeroBits*(n: UintImpl): int {.inline.} =
n.lo.countLeadingZeroBits + maxHalfRepr
else: hi_clz
func msb*[T: SomeInteger](n: T): T =
func msb*[T: SomeInteger](n: T): T {.inline.}=
## Returns the most significant bit of an integer.
when T is int64 or (T is int and sizeof(int) == 8):
@ -42,7 +42,7 @@ func msb*[T: SomeInteger](n: T): T =
const msb_pos = sizeof(T) * 8 - 1
result = T(cast[Uint](n) shr msb_pos)
func msb*(n: IntImpl): auto =
func msb*(n: IntImpl): auto {.inline.}=
## Returns the most significant bit of an arbitrary precision integer.
result = msb most_significant_word(n)

View File

@ -109,8 +109,6 @@ macro getSize*(x: typed): untyped =
type
# ### Private ### #
# If this is not in the same type section
# the compiler has trouble
BaseUint* = UintImpl or SomeUnsignedInt
UintImpl*[Baseuint] = object
@ -120,6 +118,7 @@ type
hi*, lo*: BaseUint
IntImpl*[Baseuint] = object
# Ints are implemented in terms of uints
when system.cpuEndian == littleEndian:
lo*, hi*: BaseUint
else:

View File

@ -40,3 +40,13 @@ func one*[T: BaseUint](_: typedesc[T]): T {.inline.}=
r_ptr[0] = 1
else:
r_ptr[r_ptr[].len - 1] = 1
func one*[T: IntImpl](_: typedesc[T]): T {.inline.}=
when T is SomeInteger:
result = T(1)
else:
let r_ptr = cast[ptr array[getSize(result) div 8, byte]](result.addr)
when system.cpuEndian == bigEndian:
r_ptr[0] = 1
else:
r_ptr[r_ptr[].len - 1] = 1

View File

@ -0,0 +1,26 @@
# Mpint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./int_bitwise_ops, ./initialization
func `+=`*(x: var IntImpl, y: IntImpl) {.inline.}=
## In-place addition for multi-precision signed int
type SubTy = type x.lo
x.lo += y.lo
x.hi += (x.lo < y.lo).toSubtype(SubTy) + y.hi
func `+`*(x, y: UintImpl): UintImpl {.noInit, inline.}=
# Addition for multi-precision signed int
result = x
result += y
func `-`*[T: IntImpl](x: T): T {.noInit, inline.}=
result = not x
result += one(T)

View File

@ -0,0 +1,31 @@
# Mpint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./as_words
func `not`*(x: IntImpl): IntImpl {.noInit, inline.}=
## Bitwise complement of unsigned integer x
m_asWordsZip(result, x, ignoreEndianness = true):
result = not x
func `or`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
## `Bitwise or` of numbers x and y
m_asWordsZip(result, x, y, ignoreEndianness = true):
result = x or y
func `and`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
## `Bitwise and` of numbers x and y
m_asWordsZip(result, x, y, ignoreEndianness = true):
result = x and y
func `xor`*(x, y: IntImpl): IntImpl {.noInit, inline.}=
## `Bitwise xor` of numbers x and y
m_asWordsZip(result, x, y, ignoreEndianness = true):
result = x xor y

View File

@ -115,7 +115,7 @@ func extPrecMul*[T](result: var UintImpl[UintImpl[T]], x, y: UintImpl[T]) =
if result.lo.hi < z1.lo:
result.hi += one(UintImpl[T])
func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline.}=
func `*`*[T](x, y: UintImpl[T]): UintImpl[T] {.inline, noInit.}=
## Multiplication for multi-precision unsigned uint
#
# For our representation, it is similar to school grade multiplication

View File

@ -7,5 +7,5 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./uint_public, ./uint_init
export uint_public, uint_init
import ./uint_public, ./int_public, ./init
export uint_public, int_public, init

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/mpint, unittest, quicktest
import ../src/stint, unittest, quicktest
const itercount = 1000

View File

@ -10,7 +10,7 @@
# Requires "https://github.com/status-im/nim-ttmath#master"
# Note that currently importing both Stint and TTMath will crash the compiler for unknown reason
import ../src/mpint, unittest, quicktest, ttmath
import ../src/stint, unittest, quicktest, ttmath
const itercount = 1000

View File

@ -0,0 +1,41 @@
# Mpint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/stint, unittest
suite "Signed int - Testing comparison operators":
let
a = 10'i16.stint(16)
b = 15'i16.stint(16)
c = 150'u16
test "< operator":
check:
a < b
not (a + b < b)
not (a + a + a < b + b)
test "<= operator":
check:
a <= b
not (a + b <= b)
a + a + a <= b + b
test "> operator":
check:
b > a
not (b > a + b)
not (b + b > a + a + a)
test ">= operator":
check:
b >= a
not (b >= a + b)
b + b >= a + a + a

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/mpint, unittest
import ../src/stint, unittest
suite "Testing addition implementation":
test "In-place addition gives expected result":

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/mpint, unittest
import ../src/stint, unittest
suite "Testing bitwise operations":
let a = 100'i16.stuint(16)

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/mpint, unittest
import ../src/stint, unittest
suite "Testing comparison operators":
let

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/mpint, unittest
import ../src/stint, unittest
suite "Testing byte representation":
test "Byte representation conforms to the platform endianness":

View File

@ -7,7 +7,7 @@
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../src/mpint, unittest
import ../src/stint, unittest
suite "Testing multiplication implementation":
test "Multiplication with result fitting in low half":