mollusk 0e4acfb8f2 fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
2018-06-11 03:28:36 -07:00

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