496 lines
15 KiB
Julia
496 lines
15 KiB
Julia
# This file is a part of Julia, but is derived from
|
|
# https://github.com/google/double-conversion which has the following license
|
|
#
|
|
# Copyright 2006-2014, the V8 project authors. All rights reserved.
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are
|
|
# met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above
|
|
# copyright notice, this list of conditions and the following
|
|
# disclaimer in the documentation and/or other materials provided
|
|
# with the distribution.
|
|
# * Neither the name of Google Inc. nor the names of its
|
|
# contributors may be used to endorse or promote products derived
|
|
# from this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
module Bignums
|
|
|
|
import Base: ==, <
|
|
|
|
export Bignum
|
|
|
|
const kMaxSignificantBits = 3584
|
|
|
|
const Chunk = UInt32
|
|
const DoubleChunk = UInt64
|
|
|
|
const kChunkSize = sizeof(Chunk) * 8
|
|
const kDoubleChunkSize = sizeof(DoubleChunk) * 8
|
|
# With bigit size of 28 we loose some bits, but a double still fits easily
|
|
# into two chunks, and more importantly we can use the Comba multiplication.
|
|
const kBigitSize = 28
|
|
const kBigitMask = Chunk((1 << kBigitSize) - 1)
|
|
# Every instance allocates kBigitLength chunks on the stack. Bignums cannot
|
|
# grow. There are no checks if the stack-allocated space is sufficient.
|
|
const kBigitCapacity = div(kMaxSignificantBits, kBigitSize)
|
|
|
|
mutable struct Bignum
|
|
bigits::Vector{UInt32}
|
|
used_digits::Int32
|
|
exponent::Int32
|
|
function Bignum()
|
|
bigits = Vector{UInt32}(kBigitCapacity)
|
|
@inbounds for i = 1:kBigitCapacity
|
|
bigits[i] = 0
|
|
end
|
|
new(bigits,0,0)
|
|
end
|
|
end
|
|
|
|
==(a::Bignum,b::Bignum) = compare(a,b) == 0
|
|
<(a::Bignum,b::Bignum) = compare(a,b) < 0
|
|
|
|
times10!(x::Bignum) = multiplybyuint32!(x,UInt32(10))
|
|
|
|
plusequal(a,b,c) = pluscompare(a,b,c) == 0
|
|
pluslessequal(a,b,c) = pluscompare(a,b,c) <= 0
|
|
plusless(a,b,c) = pluscompare(a,b,c) < 0
|
|
lessequal(a::Bignum,b::Bignum) = compare(a,b) <= 0
|
|
less(a::Bignum,b::Bignum) = compare(a,b) < 0
|
|
|
|
bigitlength(x::Bignum) = x.used_digits + x.exponent
|
|
|
|
bitsize(value) = 8 * sizeof(value)
|
|
|
|
function zero!(x::Bignum)
|
|
for i = 1:x.used_digits
|
|
@inbounds x.bigits[i] = 0
|
|
end
|
|
x.used_digits = 0
|
|
x.exponent = 0
|
|
return
|
|
end
|
|
|
|
function clamp!(x::Bignum)
|
|
@inbounds while (x.used_digits > 0 && x.bigits[x.used_digits] == 0)
|
|
x.used_digits -= 1
|
|
end
|
|
x.used_digits == 0 && (x.exponent = 0)
|
|
return
|
|
end
|
|
|
|
isclamped(x::Bignum) = x.used_digits == 0 || x.bigits[x.used_digits] != 0
|
|
|
|
function align!(x::Bignum,other::Bignum)
|
|
@inbounds if x.exponent > other.exponent
|
|
zero_digits = x.exponent - other.exponent
|
|
for i = x.used_digits:-1:1
|
|
x.bigits[i + zero_digits] = x.bigits[i]
|
|
end
|
|
for i = 1:zero_digits
|
|
x.bigits[i] = 0
|
|
end
|
|
x.used_digits += zero_digits
|
|
x.exponent -= zero_digits
|
|
end
|
|
return
|
|
end
|
|
|
|
function bigitshiftleft!(x::Bignum,shift_amount)
|
|
carry::UInt32 = 0
|
|
@inbounds begin
|
|
for i = 1:x.used_digits
|
|
new_carry::Chunk = x.bigits[i] >> (kBigitSize - shift_amount)
|
|
x.bigits[i] = ((x.bigits[i] << shift_amount) + carry) & kBigitMask
|
|
carry = new_carry
|
|
end
|
|
if carry != 0
|
|
x.bigits[x.used_digits+1] = carry
|
|
x.used_digits += 1
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
function subtracttimes!(x::Bignum,other::Bignum,factor)
|
|
if factor < 3
|
|
for i = 1:factor
|
|
subtractbignum!(x,other)
|
|
end
|
|
return
|
|
end
|
|
borrow::Chunk = 0
|
|
exponent_diff = other.exponent - x.exponent
|
|
@inbounds begin
|
|
for i = 1:other.used_digits
|
|
product::DoubleChunk = DoubleChunk(factor) * other.bigits[i]
|
|
remove::DoubleChunk = borrow + product
|
|
difference::Chunk = (x.bigits[i+exponent_diff] - (remove & kBigitMask)) % Chunk
|
|
x.bigits[i+exponent_diff] = difference & kBigitMask
|
|
borrow = ((difference >> (kChunkSize - 1)) + (remove >> kBigitSize)) % Chunk
|
|
end
|
|
for i = (other.used_digits + exponent_diff + 1):x.used_digits
|
|
borrow == 0 && return
|
|
difference::Chunk = x.bigits[i] - borrow
|
|
x.bigits[i] = difference & kBigitMask
|
|
borrow = difference >> (kChunkSize - 1)
|
|
end
|
|
end
|
|
clamp!(x)
|
|
end
|
|
|
|
function assignuint16!(x::Bignum,value::UInt16)
|
|
zero!(x)
|
|
value == 0 && return
|
|
x.bigits[1] = value
|
|
x.used_digits = 1
|
|
return
|
|
end
|
|
|
|
const kUInt64Size = 64
|
|
function assignuint64!(x::Bignum,value::UInt64)
|
|
zero!(x)
|
|
value == 0 && return
|
|
needed_bigits = div(kUInt64Size,kBigitSize) + 1
|
|
@inbounds for i = 1:needed_bigits
|
|
x.bigits[i] = value & kBigitMask
|
|
value >>= kBigitSize
|
|
end
|
|
x.used_digits = needed_bigits
|
|
clamp!(x)
|
|
end
|
|
|
|
function assignbignum!(x::Bignum,other::Bignum)
|
|
x.exponent = other.exponent
|
|
@inbounds begin
|
|
for i = 1:other.used_digits
|
|
x.bigits[i] = other.bigits[i]
|
|
end
|
|
for i = (other.used_digits+1):x.used_digits
|
|
x.bigits[i] = 0
|
|
end
|
|
end
|
|
x.used_digits = other.used_digits
|
|
return
|
|
end
|
|
|
|
function adduint64!(x::Bignum,operand::UInt64)
|
|
operand == 0 && return
|
|
other = Bignum()
|
|
assignuint64!(other,operand)
|
|
addbignum!(x,other)
|
|
end
|
|
|
|
function addbignum!(x::Bignum,other::Bignum)
|
|
align!(x,other)
|
|
carry::Chunk = 0
|
|
bigit_pos = other.exponent - x.exponent
|
|
@inbounds for i = 1:other.used_digits
|
|
sum::Chunk = x.bigits[bigit_pos+1] + other.bigits[i] + carry
|
|
x.bigits[bigit_pos+1] = sum & kBigitMask
|
|
carry = sum >> kBigitSize
|
|
bigit_pos += 1
|
|
end
|
|
@inbounds while carry != 0
|
|
sum = x.bigits[bigit_pos+1] + carry
|
|
x.bigits[bigit_pos+1] = sum & kBigitMask
|
|
carry = sum >> kBigitSize
|
|
bigit_pos += 1
|
|
end
|
|
x.used_digits = max(bigit_pos,x.used_digits)
|
|
return
|
|
end
|
|
|
|
function subtractbignum!(x::Bignum,other::Bignum)
|
|
align!(x,other)
|
|
offset = other.exponent - x.exponent
|
|
borrow = Chunk(0)
|
|
@inbounds begin
|
|
for i = 1:other.used_digits
|
|
difference = x.bigits[i+offset] - other.bigits[i] - borrow
|
|
x.bigits[i+offset] = difference & kBigitMask
|
|
borrow = difference >> (kChunkSize - 1)
|
|
end
|
|
i = other.used_digits+1
|
|
while borrow != 0
|
|
difference = x.bigits[i+offset] - borrow
|
|
x.bigits[i+offset] = difference & kBigitMask
|
|
borrow = difference >> (kChunkSize - 1)
|
|
i += 1
|
|
end
|
|
end
|
|
clamp!(x)
|
|
end
|
|
|
|
function shiftleft!(x::Bignum,shift_amount)
|
|
x.used_digits == 0 && return
|
|
x.exponent += div(shift_amount,kBigitSize)
|
|
local_shift = shift_amount % kBigitSize
|
|
bigitshiftleft!(x,local_shift)
|
|
end
|
|
|
|
function multiplybyuint32!(x::Bignum,factor::UInt32)
|
|
factor == 1 && return
|
|
if factor == 0
|
|
zero!(x)
|
|
return
|
|
end
|
|
x.used_digits == 0 && return
|
|
carry::DoubleChunk = 0
|
|
@inbounds begin
|
|
for i = 1:x.used_digits
|
|
product::DoubleChunk = (factor % DoubleChunk) * x.bigits[i] + carry
|
|
x.bigits[i] = (product & kBigitMask) % Chunk
|
|
carry = product >> kBigitSize
|
|
end
|
|
while carry != 0
|
|
x.bigits[x.used_digits+1] = carry & kBigitMask
|
|
x.used_digits += 1
|
|
carry >>= kBigitSize
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
function multiplybyuint64!(x::Bignum,factor::UInt64)
|
|
factor == 1 && return
|
|
if factor == 0
|
|
zero!(x)
|
|
return
|
|
end
|
|
carry::UInt64 = 0
|
|
low::UInt64 = factor & 0xFFFFFFFF
|
|
high::UInt64 = factor >> 32
|
|
@inbounds begin
|
|
for i = 1:x.used_digits
|
|
product_low::UInt64 = low * x.bigits[i]
|
|
product_high::UInt64 = high * x.bigits[i]
|
|
tmp::UInt64 = (carry & kBigitMask) + product_low
|
|
x.bigits[i] = tmp & kBigitMask
|
|
carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
|
|
(product_high << (32 - kBigitSize))
|
|
end
|
|
while carry != 0
|
|
x.bigits[x.used_digits+1] = carry & kBigitMask
|
|
x.used_digits += 1
|
|
carry >>= kBigitSize
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
const kFive27 = UInt64(0x6765c793fa10079d)
|
|
const kFive1 = UInt16(5)
|
|
const kFive2 = UInt16(kFive1 * 5)
|
|
const kFive3 = UInt16(kFive2 * 5)
|
|
const kFive4 = UInt16(kFive3 * 5)
|
|
const kFive5 = UInt16(kFive4 * 5)
|
|
const kFive6 = UInt16(kFive5 * 5)
|
|
const kFive7 = UInt32(kFive6 * 5)
|
|
const kFive8 = UInt32(kFive7 * 5)
|
|
const kFive9 = UInt32(kFive8 * 5)
|
|
const kFive10 = UInt32(kFive9 * 5)
|
|
const kFive11 = UInt32(kFive10 * 5)
|
|
const kFive12 = UInt32(kFive11 * 5)
|
|
const kFive13 = UInt32(kFive12 * 5)
|
|
const kFive1_to_12 = UInt32[kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
|
|
kFive7, kFive8, kFive9, kFive10, kFive11, kFive12]
|
|
function multiplybypoweroften!(x::Bignum,exponent)
|
|
exponent == 0 && return
|
|
x.used_digits == 0 && return
|
|
remaining_exponent = exponent
|
|
while remaining_exponent >= 27
|
|
multiplybyuint64!(x,kFive27)
|
|
remaining_exponent -= 27
|
|
end
|
|
while remaining_exponent >= 13
|
|
multiplybyuint32!(x,kFive13)
|
|
remaining_exponent -= 13
|
|
end
|
|
remaining_exponent > 0 && multiplybyuint32!(x,
|
|
kFive1_to_12[remaining_exponent])
|
|
shiftleft!(x,exponent)
|
|
end
|
|
|
|
function square!(x::Bignum)
|
|
product_length = 2 * x.used_digits
|
|
(1 << (2 * (kChunkSize - kBigitSize))) <= x.used_digits && error("unimplemented")
|
|
accumulator::DoubleChunk = 0
|
|
copy_offset = x.used_digits
|
|
@inbounds begin
|
|
for i = 1:x.used_digits
|
|
x.bigits[copy_offset + i] = x.bigits[i]
|
|
end
|
|
for i = 1:x.used_digits
|
|
bigit_index1 = i-1
|
|
bigit_index2 = 0
|
|
while bigit_index1 >= 0
|
|
chunk1::Chunk = x.bigits[copy_offset + bigit_index1 + 1]
|
|
chunk2::Chunk = x.bigits[copy_offset + bigit_index2 + 1]
|
|
accumulator += (chunk1 % DoubleChunk) * chunk2
|
|
bigit_index1 -= 1
|
|
bigit_index2 += 1
|
|
end
|
|
x.bigits[i] = (accumulator % Chunk) & kBigitMask
|
|
accumulator >>= kBigitSize
|
|
end
|
|
for i = x.used_digits+1:product_length
|
|
bigit_index1 = x.used_digits - 1
|
|
bigit_index2 = i - bigit_index1 - 1
|
|
while bigit_index2 < x.used_digits
|
|
chunk1::Chunk = x.bigits[copy_offset + bigit_index1 + 1]
|
|
chunk2::Chunk = x.bigits[copy_offset + bigit_index2 + 1]
|
|
accumulator += (chunk1 % DoubleChunk) * chunk2
|
|
bigit_index1 -= 1
|
|
bigit_index2 += 1
|
|
end
|
|
x.bigits[i] = (accumulator % Chunk) & kBigitMask
|
|
accumulator >>= kBigitSize
|
|
end
|
|
end
|
|
x.used_digits = product_length
|
|
x.exponent *= 2
|
|
clamp!(x)
|
|
end
|
|
|
|
function assignpoweruint16!(x::Bignum,base::UInt16,power_exponent::Int)
|
|
if power_exponent == 0
|
|
assignuint16!(x,UInt16(1))
|
|
return
|
|
end
|
|
zero!(x)
|
|
shifts::Int = 0
|
|
while base & UInt16(1) == UInt16(0)
|
|
base >>= UInt16(1)
|
|
shifts += 1
|
|
end
|
|
bit_size::Int = 0
|
|
tmp_base::Int= base
|
|
while tmp_base != 0
|
|
tmp_base >>= 1
|
|
bit_size += 1
|
|
end
|
|
final_size = bit_size * power_exponent
|
|
mask::Int = 1
|
|
while power_exponent >= mask
|
|
mask <<= 1
|
|
end
|
|
mask >>= 2
|
|
this_value::UInt64 = base
|
|
delayed_multiplication = false
|
|
max_32bits::UInt64 = 0xFFFFFFFF
|
|
while mask != 0 && this_value <= max_32bits
|
|
this_value *= this_value
|
|
if (power_exponent & mask) != 0
|
|
base_bits_mask::UInt64 = ~(UInt64(1) << (64 - bit_size) - 1)
|
|
high_bits_zero = (this_value & base_bits_mask) == 0
|
|
if high_bits_zero
|
|
this_value *= base
|
|
else
|
|
delayed_multiplication = true
|
|
end
|
|
end
|
|
mask >>= 1
|
|
end
|
|
assignuint64!(x,this_value)
|
|
delayed_multiplication && multiplybyuint32!(x,UInt32(base))
|
|
while mask != 0
|
|
square!(x)
|
|
(power_exponent & mask) != 0 && multiplybyuint32!(x,UInt32(base))
|
|
mask >>= 1
|
|
end
|
|
shiftleft!(x,shifts * power_exponent)
|
|
end
|
|
|
|
function dividemodulointbignum!(x::Bignum,other::Bignum)
|
|
bigitlength(x) < bigitlength(other) && return UInt16(0)
|
|
align!(x,other)
|
|
result::UInt16 = 0
|
|
@inbounds begin
|
|
while bigitlength(x) > bigitlength(other)
|
|
result += x.bigits[x.used_digits] % UInt16
|
|
subtracttimes!(x,other,x.bigits[x.used_digits])
|
|
end
|
|
this_bigit::Chunk = x.bigits[x.used_digits]
|
|
other_bigit::Chunk = other.bigits[other.used_digits]
|
|
if other.used_digits == 1
|
|
quotient = reinterpret(Int32,div(this_bigit,other_bigit))
|
|
x.bigits[x.used_digits] = this_bigit - other_bigit * reinterpret(UInt32,quotient)
|
|
result += quotient % UInt16
|
|
clamp!(x)
|
|
return result
|
|
end
|
|
end
|
|
division_estimate = reinterpret(Int32,div(this_bigit,other_bigit+Chunk(1)))
|
|
result += division_estimate % UInt16
|
|
subtracttimes!(x,other,division_estimate)
|
|
other_bigit * (division_estimate+1) > this_bigit && return result
|
|
while lessequal(other, x)
|
|
subtractbignum!(x,other)
|
|
result += UInt16(1)
|
|
end
|
|
return result
|
|
end
|
|
|
|
function pluscompare(a::Bignum,b::Bignum,c::Bignum)
|
|
bigitlength(a) < bigitlength(b) && return pluscompare(b,a,c)
|
|
bigitlength(a) + 1 < bigitlength(c) && return -1
|
|
bigitlength(a) > bigitlength(c) && return 1
|
|
a.exponent >= bigitlength(b) && bigitlength(a) < bigitlength(c) && return -1
|
|
borrow::Chunk = 0
|
|
min_exponent = min(a.exponent,b.exponent,c.exponent)
|
|
for i = (bigitlength(c)-1):-1:min_exponent
|
|
chunk_a::Chunk = bigitat(a,i)
|
|
chunk_b::Chunk = bigitat(b,i)
|
|
chunk_c::Chunk = bigitat(c,i)
|
|
sum::Chunk = chunk_a + chunk_b
|
|
if sum > chunk_c + borrow
|
|
return 1
|
|
else
|
|
borrow = chunk_c + borrow - sum
|
|
borrow > 1 && return -1
|
|
borrow <<= kBigitSize
|
|
end
|
|
end
|
|
borrow == 0 && return 0
|
|
return -1
|
|
end
|
|
|
|
function compare(a::Bignum,b::Bignum)
|
|
bigit_length_a = bigitlength(a)
|
|
bigit_length_b = bigitlength(b)
|
|
bigit_length_a < bigit_length_b && return -1
|
|
bigit_length_a > bigit_length_b && return 1
|
|
for i = (bigit_length_a-1):-1:min(a.exponent,b.exponent)
|
|
bigit_a::Chunk = bigitat(a,i)
|
|
bigit_b::Chunk = bigitat(b,i)
|
|
bigit_a < bigit_b && return -1
|
|
bigit_a > bigit_b && return 1
|
|
end
|
|
return 0
|
|
end
|
|
|
|
function bigitat(x::Bignum,index)
|
|
index >= bigitlength(x) && return Chunk(0)
|
|
index < x.exponent && return Chunk(0)
|
|
@inbounds ret = x.bigits[index - x.exponent+1]::Chunk
|
|
return ret
|
|
end
|
|
|
|
end # module
|