nim-stint/stint/intops.nim

368 lines
8.6 KiB
Nim
Raw Normal View History

# Stint
2023-06-05 11:42:36 +00:00
# Copyright 2018-2023 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.
2023-06-13 12:03:43 +00:00
import
./private/datatypes,
./private/uint_bitwise,
./private/uint_shift,
./private/uint_addsub,
2023-06-14 02:41:01 +00:00
./private/uint_div,
2023-06-13 12:03:43 +00:00
./uintops
export StInt
2023-06-13 12:03:43 +00:00
const
2023-06-13 14:53:19 +00:00
signMask = 1.Word shl (WordBitWidth - 1)
2023-06-13 12:03:43 +00:00
clearSignMask = not signMask
2023-06-13 12:03:43 +00:00
# Signedness
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func sign*(a: StInt): int =
2023-06-14 01:06:44 +00:00
## get the sign of `a`
## either -1, 0, or 1
2023-06-15 07:31:59 +00:00
if a.impl.isZero: return 0
2023-06-13 12:03:43 +00:00
if a.limbs[^1] < signMask: 1
else: -1
func isNegative*(a: StInt): bool =
2023-06-14 00:38:23 +00:00
a.limbs[^1] >= signMask
2023-06-13 12:03:43 +00:00
2023-06-14 02:41:01 +00:00
func isPositive*(a: StInt): bool =
a.limbs[^1] < signMask
2023-06-13 12:51:49 +00:00
func clearMSB(a: var StInt) =
2023-06-13 13:39:22 +00:00
a.limbs[^1] = a.limbs[^1] and clearSignMask
2023-06-13 12:03:43 +00:00
2023-06-13 12:51:49 +00:00
func setMSB(a: var StInt) =
2023-06-13 13:39:22 +00:00
a.limbs[^1] = a.limbs[^1] or signMask
2023-06-13 12:03:43 +00:00
func negate*(a: var StInt) =
2023-06-14 01:06:44 +00:00
## two complement negation
2023-06-15 07:31:59 +00:00
a.impl.bitnot(a.impl)
a.impl.inc
2023-06-13 12:03:43 +00:00
func neg*(a: StInt): StInt =
2023-06-14 01:06:44 +00:00
## two complement negation
2023-06-15 07:31:59 +00:00
result.impl.bitnot(a.impl)
result.impl.inc
2023-06-13 12:03:43 +00:00
func abs*(a: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Returns the positive value of Stint
2023-06-13 12:03:43 +00:00
if a.isNegative:
a.neg
else:
a
func `-`*(a: StInt): StInt =
2023-06-14 01:06:44 +00:00
## two complement negation
2023-06-13 12:03:43 +00:00
a.neg
{.pop.}
# Initialization
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func setZero*(a: var StInt) =
## Set ``a`` to 0
2023-06-15 07:31:59 +00:00
a.impl.setZero
2023-06-13 12:03:43 +00:00
func setOne*(a: var StInt) =
2023-06-14 01:06:44 +00:00
## Set ``a`` to 1
2023-06-15 07:31:59 +00:00
a.impl.setOne
2023-06-13 12:03:43 +00:00
func zero*[bits: static[int]](T: typedesc[StInt[bits]]): T =
## Returns the zero of the input type
2023-06-13 12:03:43 +00:00
result.setZero
func one*[bits: static[int]](T: typedesc[StInt[bits]]): T =
## Returns the one of the input type
2023-06-13 12:03:43 +00:00
result.setOne
2023-06-13 12:03:43 +00:00
func high*[bits](_: typedesc[StInt[bits]]): StInt[bits] =
2023-06-15 07:08:27 +00:00
## Returns the highest value of Stint
2023-06-13 12:03:43 +00:00
# The highest signed int has representation
# 0b0111_1111_1111_1111 ....
# so we only have to unset the most significant bit.
for i in 0 ..< result.limbs.len:
result[i] = high(Word)
2023-06-13 12:51:49 +00:00
result.clearMSB
2023-06-13 12:03:43 +00:00
func low*[bits](_: typedesc[StInt[bits]]): StInt[bits] =
2023-06-15 07:08:27 +00:00
## Returns the lowest value of Stint
2023-06-13 12:03:43 +00:00
# The lowest signed int has representation
# 0b1000_0000_0000_0000 ....
# so we only have to set the most significant bit.
result.setZero
2023-06-13 12:51:49 +00:00
result.setMSB
2023-06-13 12:03:43 +00:00
{.pop.}
2023-06-13 12:03:43 +00:00
# Comparisons
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
2023-06-13 12:03:43 +00:00
func isZero*(a: StInt): bool =
2023-06-15 07:31:59 +00:00
a.impl.isZero
2023-06-13 12:03:43 +00:00
func `==`*(a, b: StInt): bool =
2023-06-15 07:08:27 +00:00
## Signed int `equal` comparison
2023-06-15 07:31:59 +00:00
a.impl == b.impl
2023-06-13 12:03:43 +00:00
func `<`*(a, b: StInt): bool =
2023-06-15 07:08:27 +00:00
## Signed int `less than` comparison
2023-06-13 12:03:43 +00:00
let
2023-06-14 00:38:23 +00:00
aNeg = a.isNegative
bNeg = b.isNegative
2023-06-14 00:38:23 +00:00
if aNeg xor bNeg:
return aNeg
2019-07-22 08:13:19 +00:00
2023-06-15 07:31:59 +00:00
a.impl < b.impl
2023-06-13 12:03:43 +00:00
func `<=`*(a, b: StInt): bool =
2023-06-15 07:08:27 +00:00
## Signed int `less or equal` comparison
2023-06-13 12:03:43 +00:00
not(b < a)
2023-06-13 12:03:43 +00:00
func isOdd*(a: StInt): bool =
2023-06-15 07:08:27 +00:00
## Returns true if input is odd
## false otherwise
2023-06-13 12:03:43 +00:00
bool(a[0] and 1)
2023-06-13 12:03:43 +00:00
func isEven*(a: StInt): bool =
## Returns true if input is zero
## false otherwise
2023-06-13 12:03:43 +00:00
not a.isOdd()
2023-06-13 12:03:43 +00:00
{.pop.}
2023-06-13 12:03:43 +00:00
# Bitwise operations
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
2023-06-13 12:03:43 +00:00
func `not`*(a: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Bitwise complement of signed integer a
2023-06-13 12:03:43 +00:00
## i.e. flips all bits of the input
2023-06-15 07:31:59 +00:00
result.impl.bitnot(a.impl)
2023-06-13 12:03:43 +00:00
func `or`*(a, b: StInt): StInt =
## `Bitwise or` of numbers a and b
2023-06-15 07:31:59 +00:00
result.impl.bitor(a.impl, b.impl)
2019-10-22 15:23:08 +00:00
2023-06-13 12:03:43 +00:00
func `and`*(a, b: StInt): StInt =
## `Bitwise and` of numbers a and b
2023-06-15 07:31:59 +00:00
result.impl.bitand(a.impl, b.impl)
2023-06-13 12:03:43 +00:00
func `xor`*(a, b: StInt): StInt =
## `Bitwise xor` of numbers x and y
2023-06-15 07:31:59 +00:00
result.impl.bitxor(a.impl, b.impl)
2023-06-13 12:03:43 +00:00
{.pop.} # End noInit
2023-06-13 12:03:43 +00:00
{.push raises: [], inline, gcsafe.}
2023-06-13 12:03:43 +00:00
func `shr`*(a: StInt, k: SomeInteger): StInt =
## Shift right by k bits, arithmetically
2023-06-13 15:17:43 +00:00
## value < 0 ? ~(~value >> amount) : value >> amount
if a.isNegative:
var tmp: type a
2023-06-15 07:31:59 +00:00
result.impl.bitnot(a.impl)
tmp.impl.shiftRight(result.impl, k)
result.impl.bitnot(tmp.impl)
2023-06-13 15:17:43 +00:00
else:
2023-06-15 07:31:59 +00:00
result.impl.shiftRight(a.impl, k)
2023-06-13 12:03:43 +00:00
func `shl`*(a: StInt, k: SomeInteger): StInt =
## Shift left by k bits
2023-06-15 07:31:59 +00:00
result.impl.shiftLeft(a.impl, k)
2023-06-14 00:38:23 +00:00
func setBit*(a: var StInt, k: Natural) =
2023-06-14 01:06:44 +00:00
## set bit at position `k`
## k = 0..a.bits-1
2023-06-15 07:31:59 +00:00
a.impl.setBit(k)
2023-06-14 00:38:23 +00:00
func clearBit*(a: var StInt, k: Natural) =
2023-06-14 01:06:44 +00:00
## set bit at position `k`
2023-06-14 01:31:28 +00:00
## k = 0..a.bits-1
2023-06-15 07:31:59 +00:00
a.impl.clearBit(k)
2023-06-14 00:38:23 +00:00
func getBit*(a: StInt, k: Natural): bool =
2023-06-14 01:06:44 +00:00
## set bit at position `k`
2023-06-14 01:31:28 +00:00
## k = 0..a.bits-1
2023-06-15 07:31:59 +00:00
a.impl.getBit(k)
2023-06-14 00:38:23 +00:00
2023-06-13 12:03:43 +00:00
{.pop.}
# Addsub
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func `+`*(a, b: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Addition for multi-precision signed int
2023-06-15 07:31:59 +00:00
result.impl.sum(a.impl, b.impl)
2023-06-13 12:03:43 +00:00
func `+=`*(a: var StInt, b: StInt) =
2023-06-15 07:08:27 +00:00
## In-place addition for multi-precision signed int
2023-06-15 07:31:59 +00:00
a.impl.sum(a.impl, b.impl)
2023-06-13 12:03:43 +00:00
func `-`*(a, b: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Substraction for multi-precision signed int
2023-06-15 07:31:59 +00:00
result.impl.diff(a.impl, b.impl)
2023-06-13 12:03:43 +00:00
func `-=`*(a: var StInt, b: StInt) =
2023-06-15 07:08:27 +00:00
## In-place substraction for multi-precision signed int
2023-06-15 07:31:59 +00:00
a.impl.diff(a.impl, b.impl)
2023-06-13 12:03:43 +00:00
func inc*(a: var StInt, w: Word = 1) =
2023-06-15 07:31:59 +00:00
a.impl.inc(w)
2023-06-13 12:03:43 +00:00
func `+`*(a: StInt, b: SomeUnsignedInt): StInt =
2023-06-15 07:08:27 +00:00
## Addition for multi-precision signed int
2023-06-13 12:03:43 +00:00
## with an unsigned integer
2023-06-15 07:31:59 +00:00
result.impl.sum(a.impl, Word(b))
2023-06-13 12:03:43 +00:00
func `+=`*(a: var StInt, b: SomeUnsignedInt) =
2023-06-15 07:08:27 +00:00
## In-place addition for multi-precision signed int
2023-06-13 12:03:43 +00:00
## with an unsigned integer
2023-06-15 07:31:59 +00:00
a.impl.inc(Word(b))
2023-06-13 12:03:43 +00:00
2023-06-14 01:06:44 +00:00
{.pop.}
# Exponentiation
# --------------------------------------------------------
{.push raises: [], noinit, gcsafe.}
2023-06-14 01:31:28 +00:00
func isOdd(x: Natural): bool =
bool(x and 1)
func pow*(a: StInt, e: Natural): StInt =
2023-06-14 01:06:44 +00:00
## Compute ``a`` to the power of ``e``,
## ``e`` must be non-negative
2023-06-14 01:31:28 +00:00
if a.isNegative:
let base = a.neg
2023-06-15 07:31:59 +00:00
result.impl = base.impl.pow(e)
2023-06-14 01:31:28 +00:00
if e.isOdd:
result.negate
else:
2023-06-15 07:31:59 +00:00
result.impl = a.impl.pow(e)
2023-06-14 01:31:28 +00:00
func pow*[aBits, eBits](a: StInt[aBits], e: StInt[eBits]): StInt[aBits] =
2023-06-14 01:06:44 +00:00
## Compute ``x`` to the power of ``y``,
2023-06-14 01:31:28 +00:00
## ``x`` must be non-negative
doAssert e.isNegative.not, "exponent must be non-negative"
if a.isNegative:
let base = a.neg
2023-06-15 07:31:59 +00:00
result.impl = base.impl.pow(e.impl)
2023-06-14 01:31:28 +00:00
if e.isOdd:
result.negate
else:
2023-06-15 07:31:59 +00:00
result.impl = a.impl.pow(e.impl)
2023-06-14 02:41:01 +00:00
{.pop.}
# Division & Modulo
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func `div`*(n, d: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Division operation for multi-precision signed uint
2023-06-14 02:41:01 +00:00
var tmp{.noinit.}: StInt
if n.isPositive:
if d.isPositive:
# pos / pos
2023-06-15 07:31:59 +00:00
result.impl = n.impl div d.impl
2023-06-14 02:41:01 +00:00
return
else:
# pos / neg
tmp = d.neg
2023-06-15 07:31:59 +00:00
result.impl = n.impl div tmp.impl
2023-06-14 02:41:01 +00:00
result.negate
return
let nneg = n.neg
if d.isNegative:
# neg / neg
tmp = d.neg
2023-06-15 07:31:59 +00:00
result.impl = nneg.impl div tmp.impl
2023-06-14 02:41:01 +00:00
return
# neg / pos
2023-06-15 07:31:59 +00:00
result.impl = nneg.impl div d.impl
2023-06-14 02:41:01 +00:00
result.negate
func `mod`*(x, y: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Remainder operation for multi-precision signed uint
## The behavior is similar to Nim's `mod` operator
## The sign of the remainder will follow the sign of left operand
2023-06-14 02:41:01 +00:00
let
xIn = x.abs
yIn = y.abs
2023-06-15 07:31:59 +00:00
result.impl = xIn.impl mod yIn.impl
2023-06-14 02:41:01 +00:00
if x.isNegative:
result.negate
func divmodI(x, y: StInt): tuple[quot, rem: StInt] =
## Division and remainder operations for multi-precision uint
## with StInt operands
divRem(result.quot.limbs, result.rem.limbs, x.limbs, y.limbs)
func divmod*(n, d: StInt): tuple[quot, rem: StInt] =
2023-06-15 07:08:27 +00:00
## Division and remainder operations for multi-precision signed uint
## The sign of the remainder will follow the sign of left operand
2023-06-14 02:41:01 +00:00
var tmp{.noinit.}: StInt
if n.isPositive:
if d.isPositive:
# pos / pos
return divmodI(n, d)
else:
# pos / neg
tmp = d.neg
result = divmodI(n, tmp)
result.quot.negate
return
let nneg = n.neg
if d.isNegative:
# neg / neg
tmp = d.neg
result = divmodI(nneg, tmp)
result.rem.negate
return
# neg / pos
result = divmodI(nneg, d)
result.quot.negate
result.rem.negate
{.pop.}
# Multiplication
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func `*`*(a, b: StInt): StInt =
2023-06-15 07:08:27 +00:00
## Signed integer multiplication
2023-06-14 02:41:01 +00:00
let
av = a.abs
bv = b.abs
2023-06-15 07:31:59 +00:00
result.impl = av.impl * bv.impl
2023-06-14 02:41:01 +00:00
if a.isNegative xor b.isNegative:
result.negate
2023-06-15 07:08:27 +00:00
{.pop.}