Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/bigints.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,14 @@ iterator `..<`*(a, b: BigInt): BigInt =
yield res
inc res

func fastLog2*(a: BigInt): int =
## Computes the logarithm in base 2 of `a`.
## If `a` is negative, returns the logarithm of `abs(a)`.
## If `a` is zero, returns -1.
if a.isZero:
return -1
bitops.fastLog2(a.limbs[^1]) + 32*(a.limbs.high)

func invmod*(a, modulus: BigInt): BigInt =
## Compute the modular inverse of `a` modulo `modulus`.
## The return value is always in the range `[1, modulus-1]`
Expand Down Expand Up @@ -1127,3 +1135,4 @@ func powmod*(base, exponent, modulus: BigInt): BigInt =
result = (result * basePow) mod modulus
basePow = (basePow * basePow) mod modulus
exponent = exponent shr 1

50 changes: 50 additions & 0 deletions tests/tbigints.nim
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,56 @@ proc main() =
doAssert "fedcba9876543210".initBigInt(base = 16) == b
doAssert "ftn5qj1r58cgg".initBigInt(base = 32) == b

block: # fastLog2
let a = one shl 31
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need some "not-power-of-two" tests too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any idea of easily checkable tests then ?
I have tested the fastLog of a+b which is not a power of 2.
I have added one test but it is not obvious what is it’s logarithm.
There are not much possible errors with this simple function and I am tired of waiting 30 seconds every time I have to test my code. It is much longer now with all the GC’s.
Maybe some factorial, since with Stirling approximation, we can guess the number of digits ahead ?

Copy link
Contributor

@konsumlamm konsumlamm Jan 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also test values with all 1's, e.g. one shl 64 n- 1 and a few negative non-powers-of-two.

As for running the tests yourself, you can just nim r tests/tbigints.nim. (In theory, the behavior should be the same for all GCs, but in practice, there are bugs, as exemplified by #88.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any idea of easily checkable tests then ?

I would do some sanity checks, e.g. log of 7, 8, 9, 24, 32, 48, etc., just to make sure it works for the basics. And then do what @konsumlamm suggests.

let b = a shl 1
let c = initBigInt(0xfedcba9876543210'u64)
let d = initBigInt("ffffffffffffffffff", base = 16)

# first numbers
doAssert fastLog2(2.initBigInt) == 1
doAssert fastLog2(3.initBigInt) == 1
doAssert fastLog2(4.initBigInt) == 2
doAssert fastLog2(5.initBigInt) == 2
doAssert fastLog2(7.initBigInt) == 2
doAssert fastLog2(8.initBigInt) == 3
doAssert fastLog2(24.initBigInt) == 4
doAssert fastLog2(32.initBigInt) == 5
doAssert fastLog2(48.initBigInt) == 5

# one limb
doAssert fastLog2(a) == 31

# two limbs and more
doAssert fastLog2(b) == 32
doAssert fastLog2(b+a) == 32
doAssert fastLog2(c+b+a) == 63

doAssert fastLog2(d) == 71
doAssert fastLog2(d + one) == 72
doAssert fastLog2(d - one) == 71
doAssert fastLog2(-d) == 71
doAssert fastLog2(-d - one) == 72
doAssert fastLog2(-d + one) == 71

# negative BigInts
doAssert fastLog2(-2.initBigInt) == 1
doAssert fastLog2(-3.initBigInt) == 1
doAssert fastLog2(-4.initBigInt) == 2
doAssert fastLog2(-5.initBigInt) == 2
doAssert fastLog2(-7.initBigInt) == 2
doAssert fastLog2(-8.initBigInt) == 3
doAssert fastLog2(-24.initBigInt) == 4
doAssert fastLog2(-32.initBigInt) == 5
doAssert fastLog2(-48.initBigInt) == 5
doAssert fastLog2(-a) == 31
doAssert fastLog2(-b) == 32

# edge cases
doAssert fastLog2(one) == 0
doAssert fastLog2(zero) == -1


block: # pow
let a = "14075287".initBigInt
doAssert pow(a, 0) == one
Expand Down