• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Big Division

Status
Not open for further replies.
At last, I've solved how to do division in a step-by-step manner.



The algorithm is as follows.


Given a dividend N and a divisor D

1. Get the biggest digit of D

2. Divide the biggest digit of N divided by D. If N < D, then do biggest 2 digits of N divided by D -> X1

3. Multiply X1 by D

4. Append 0's to X1 to match the length of N

5. Do N - X1

6. If X1 >= D, Go back to step 1 where N = X1


You will have X1 to Xn. Add X1 to X(n-1). Your remainder is Xn or D - Xn. Keep in mind that you may run into a single negative X. If you do, it and all X values after it are negative (this is where D - Xn comes in).

If the remainder is > 0 and is negative, you will need to subtract 1. The remainder is a decimal. You can actually keep going until you have a remainder of 0 to get the decimal answer.



Now for a couple of examples


Example 1

18225/31

D = 31
D biggest = 3
N biggest = 1
N 2nd biggest = 8

while N >= 31 Do

Iteration 1
------------------------------------------------------

N = 18225
1 >= 3? 1/3 : 18/3 = 6
6*31 = 186
length(18225) - length(186) = 2
186*10^2 = 18600
18225 - 18600 = -375

Iteration 2
------------------------------------------------------

N = 375 (remember that this is negative)
3 >= 3? 3/3 : 37/3 = 1
1*31 = 31
length(375) - length(31) = 1
31*10^1 = 310
375 - 310 = 65

Iteration 3
------------------------------------------------------

N = 65
6 >= 3? 6/3 : 65/3 = 2
2*31 = 62
length(65) - length(62) = 0
65 - 62 = 3

Iteration 4
------------------------------------------------------

N = 3, 3 < 31, stop

------------------------------------------------------


Now you have 4 numbers

X1 = 6
X2 = 1
X3 = 2
X4 = 3

Remember that X2 is negative

Get the total numbers - 2, which is (4 - 2), or 2.

6*10^2 = 600
1*10^1 = 10
2*10^0 = 2

Now, 600 - 10 - 2 = 588

X4, your remainder, is > 0 and negative, so

588 - 1 = 587

Now to get the remainder

Remainder = D - X4 = 31 - 3 = 28

Answer: 587 r28
 
In the BigInt update =D



I've wanted to update BigInt for a long time. I want to allow it to use any arbitrary base (just given base size) and then output to alphabet (this is where you pass in Base). I've also wanted to rework division to be 100% reliable. The last thing I wanted to do was move it from a linked list to an array.


Here is the current library. It currently has add, subtract, multiply, divide, and some comparators written. These are all big to small, I don't have big to big written yet. However, each operation is essentially big to big in the background (the little integer is broken into digits), so writing the big to big is going to be easy =D.

After everything is written, I'll test operation to see how much it can handle before it hits the op limit. I'll also do the packing optimization with a look-up table.

Here is the current state

JASS:
library BigInt uses ErrorMessage
    private function Print takes string msg returns nothing
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,msg)
    endfunction

    private module Init
        static if thistype.init.exists then
            private static method onInit takes nothing returns nothing
                call init()
            endmethod
        endif
    endmodule

    private struct Number extends array
        private static constant integer SIZE = 1638
        private static integer array recycler
        
        integer digit
        integer lastDigit
        integer base
        
        static method allocate takes integer base returns thistype
            local thistype this = recycler[0]
            
            debug call ThrowError(this == 0, "Alloc", "allocate", "thistype", 0, "Overflow.")
            
            set recycler[0] = recycler[this]
            debug set recycler[this] = -1
            
            set lastDigit = this
            set this.base = base
            
            return this
        endmethod
        
        method setTo takes Number number returns nothing
            local thistype digit = this
            local thistype num = number
            local integer last = number.lastDigit
            
            loop
                set digit.digit = number.digit
            
                set number = number + 1
                set digit = digit + 1
                exitwhen integer(number) > last
            endloop
            
            set last = lastDigit
            loop
                exitwhen integer(digit) > last
                
                set digit.digit = 0
                
                set digit = digit + 1
            endloop
            
            set lastDigit = this + (num.lastDigit - num)
            set base = num.base
        endmethod
        
        method clear takes nothing returns nothing
            local thistype digit = this
            
            loop
                set digit.digit = 0
                
                set digit = digit + 1
                exitwhen integer(digit) > lastDigit
            endloop
            
            set lastDigit = this
        endmethod
        
        method clearRange takes Number start returns nothing
            local integer end = lastDigit
        
            if (integer(start) == integer(this)) then
                set lastDigit = this
            else
                set lastDigit = start - 1
            endif
        
            loop
                set start.digit = 0
                
                set start = start + 1
                exitwhen integer(start) > end
            endloop
        endmethod
        
        method deallocate takes nothing returns nothing
            debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
            
            call clear()
            
            set recycler[this] = recycler[0]
            set recycler[0] = this
        endmethod
        
        private static method init takes nothing returns nothing
            local integer i = 1
            
            loop
                exitwhen i + SIZE > 8191
                set recycler[i] = i + SIZE
                set i = i + SIZE
            endloop
            
            set recycler[0] = 1
        endmethod
    
        implement Init
    endstruct
    
    private function ToInt takes Number number returns integer
        local integer base = number.base
        local integer end = number
        local integer value = 0
        
        set number = number.lastDigit

        loop
            set value = value*base + number.digit
            
            set number = number - 1
            exitwhen integer(number) < end
        endloop
        
        return value
    endfunction
    
    private function ToString takes Number number returns string
        local integer last = number.lastDigit
        
        local string out = ""
        
        local string comma = ""
        
        //if (number.base != 10) then
            set comma = ", "
        //endif
        
        loop
            if (out == "") then
                set out = I2S(number.digit)
            else
                set out = I2S(number.digit) + comma + out
            endif
        
            set number = number + 1
            exitwhen integer(number) > last
        endloop
        
        return out
    endfunction
    
    private struct Normalize extends array
        static method forward takes Number number, Number first, Number last returns nothing
            local integer base = number.base
        
            loop
                set Number(first + 1).digit = Number(first + 1).digit + first.digit/base
                set first.digit = first.digit - first.digit/base*base
                
                set first = first + 1
                exitwhen integer(first) > integer(last)
            endloop
            loop
                exitwhen first.digit < base
                
                set Number(first + 1).digit = Number(first + 1).digit + first.digit/base
                set first.digit = first.digit - first.digit/base*base
                
                set first = first + 1
            endloop
            loop
                exitwhen first.digit != 0 or integer(first) == integer(number)
                
                set first = first - 1
            endloop
            
            set number.lastDigit = first
        endmethod
        
        static method backward takes Number number, Number first, Number last returns nothing
            local integer base = number.base
        
            loop
                if (first.digit < 0) then
                    set Number(first + 1).digit = Number(first + 1).digit - (first.digit/base + 1)
                    set first.digit = first.digit + (first.digit/base + 1)*base
                endif
                
                set first = first + 1
                exitwhen integer(first) > integer(last)
            endloop
            loop
                exitwhen first.digit != 0 or integer(first) == integer(number)
                
                set first = first - 1
            endloop
            
            set number.lastDigit = first
        endmethod
    endstruct
    
    private struct Add extends array
        static method add takes Number number, integer i returns nothing
            local Number current = number
            local integer base = number.base
            local integer digit
            
            if (i == 0) then
                return
            endif
            
            loop
                exitwhen i == 0
                
                set digit = current.digit + (i - i/base*base)
                set i = i/base
                
                set current.digit = digit - digit/base*base
                set current = current + 1
                set current.digit = current.digit + digit/base
            endloop
            
            loop
                exitwhen current.digit < base
                
                set digit = current.digit
                
                set current.digit = digit - digit/base*base
                set current = current + 1
                set current.digit = current.digit + digit/base
            endloop
            
            if (current.digit == 0) then
                set current = current - 1
            endif
            
            if (integer(current) > number.lastDigit) then
                set number.lastDigit = current
            endif
        endmethod
    endstruct
    
    private struct Subtract extends array
        static method subtract takes Number number, integer i returns nothing
            local Number current = number
            local integer base = number.base
            
            if (i == 0) then
                return
            endif
        
            loop
                exitwhen i == 0
                
                set current.digit = current.digit - (i - i/base*base)
                set i = i/base
                
                if (current.digit < 0) then
                    set Number(current + 1).digit = Number(current + 1).digit - (current.digit/base + 1)
                    set current.digit = current.digit + (current.digit/base + 1)*base
                endif
                
                set current = current + 1
            endloop
            
            loop
                exitwhen current.digit != 0 or integer(current) == integer(number)
                set current = current - 1
            endloop
            
            set number.lastDigit = current
        endmethod
    endstruct
    
    private struct Multiply extends array
        private static method multiplyInner takes Number number, integer i returns nothing
            local Number target = Number.allocate(number.base)
            local integer first = target
            local Number currentTarget
            
            local Number digit
            local integer lastDigit = number.lastDigit
            local integer base = number.base
            
            local integer m
            
            set target.lastDigit = target + (lastDigit - number)
            
            loop
                set m = i - i/base*base
                set i = i/base
                
                if (m != 0) then
                    set currentTarget = first
                    set digit = number
                    loop
                        set currentTarget.digit = currentTarget.digit + digit.digit*m
                        
                        set Number(currentTarget + 1).digit = Number(currentTarget + 1).digit + currentTarget.digit/base
                        set currentTarget.digit = currentTarget.digit - currentTarget.digit/base*base
                    
                        set digit = digit + 1
                        exitwhen integer(digit) > lastDigit
                        set currentTarget = currentTarget + 1
                    endloop
                    
                    loop
                        set currentTarget = currentTarget + 1
                        exitwhen currentTarget.digit < base
                        
                        set Number(currentTarget + 1).digit = Number(currentTarget + 1).digit + currentTarget.digit/base
                        set currentTarget.digit = currentTarget.digit - currentTarget.digit/base*base
                    endloop
                    if (currentTarget.digit == 0) then
                        set currentTarget = currentTarget - 1
                    endif
                    if (integer(currentTarget) > target.lastDigit) then
                        set target.lastDigit = currentTarget
                    endif
                endif
                
                exitwhen i == 0
                
                set first = first + 1
                if (first > target.lastDigit) then
                    set target.lastDigit = first
                endif
            endloop
            
            call number.setTo(target)
            
            call target.deallocate()
        endmethod
    
        static method multiply takes Number number, integer i returns nothing
            if (number.digit != 0 and i != 1) then
                if (i == 0) then
                    call number.clear()
                else
                    call multiplyInner(number, i)
                endif
            endif
        endmethod
    endstruct
    
    private struct Compare extends array
        static method compare takes Number number, integer i returns integer
            local integer d = 0
            local integer base = number.base
            
            loop
                exitwhen i == 0
                
                set d = d + (number.digit - (i - i/base*base))*base
                set i = i/base
                
                set number = number + 1
            endloop
            
            return d
        endmethod
        
        static method compareNumber takes Number numberLeft, Number numberRight returns integer
            local integer sizeLeft = numberLeft.lastDigit - numberLeft
            local integer compare = sizeLeft - (numberRight.lastDigit - numberRight)
            
            if (compare == 0) then
                loop
                    set compare = numberLeft.digit - numberRight.digit
                    
                    set numberLeft = numberLeft + 1
                    exitwhen integer(numberLeft) > sizeLeft or compare != 0
                    set numberRight = numberRight + 1
                endloop
            endif
        
            return compare
        endmethod
    endstruct
    
    private struct Divide extends array
        private static method calculateDigit takes Number dividend, integer divisorBig, integer base returns integer
            if (Number(dividend.lastDigit).digit < divisorBig) then
                return (Number(dividend.lastDigit).digit*base + Number(dividend.lastDigit - 1).digit)/divisorBig
            endif
            
            return Number(dividend.lastDigit).digit/divisorBig
        endmethod
        private static method getDivisorBig takes integer divisor, integer base returns integer
            loop
                exitwhen divisor < base
                set divisor = divisor/base
            endloop
            
            return divisor
        endmethod
        private static method compare takes Number numberLeft, Number numberRight returns integer
            local integer start = numberRight
            
            local integer compare
            
            set numberLeft = numberLeft.lastDigit
            set numberRight = numberRight.lastDigit
            
            loop
                set compare = numberLeft.digit - numberRight.digit
                
                set numberRight = numberRight - 1
                exitwhen integer(numberRight) < start or compare != 0
                set numberLeft = numberLeft - 1
            endloop
        
            return compare
        endmethod
        
        private static method subtract takes Number top, Number bottom, integer base, Number difference returns nothing
            local integer end
            local Number start
            local integer beginning = difference
            
            /*
            *   Calculate what region to subtract in the number with more digits
            *
            *   If the top number is the one with more digits, it will subtract 0's
            *
            *   If it is the bottom number, then negate the trailing digits
            *   The bottom number is pretty much subtracting itself from 0's that should
            *   be in the top number, but aren't to make the algorithm faster
            */
            if (integer(top) == integer(difference)) then
                set end = top.lastDigit
                set start = end - (bottom.lastDigit - bottom)
                
                set top = start
            else
                set end = bottom.lastDigit
                set start = end - (top.lastDigit - top)
                
                loop
                    exitwhen integer(difference) == integer(start)
                    
                    set difference.digit = -difference.digit
                    
                    set difference = difference + 1
                endloop
                
                set bottom = start
            endif
            
            /*
            *   Subtract the first x numbers where x is the number of digits
            *   in the smaller length number
            *
            *   Difference is always the biggest length number
            */
            set difference = start
            loop
                set difference.digit = top.digit - bottom.digit
                
                set difference = difference + 1
                exitwhen integer(difference) > end
                set top = top + 1
                set bottom = bottom + 1
            endloop
            
            /*
            *   Normalize the number (handle carries)
            */
            call Normalize.backward(beginning, beginning, Number(beginning).lastDigit)
        endmethod
    
        private static method divideInner takes Number number, integer divisor returns integer
            /*
            *   Locals
            */
            local Number dividend                   //number to be divided
            local Number subtractor                 //number to subtract from dividend
            
            local integer digit                     //specific digit in quotient, can be negative
            
            local integer array digits              //collection of all digits in quotient
            local integer digitCount                //number of digits in quotient, including remainder
            
            local integer base                      //base of the number
            
            local integer divisorBig                //largest digit in divisor
            
            local integer multiplier                //current multiplier for digits
            
            local integer remainder
            
            local integer start
            
            /*
            *   Initialization
            */
            set base = number.base
            
            set dividend = Number.allocate(base)
            set subtractor = Number.allocate(base)
            
            set divisorBig = getDivisorBig(divisor, base)
            
            set multiplier = 1
            
            set digitCount = 0
            
            call dividend.setTo(number)
            
            /*
            *   Loop
            */
            loop
                /*
                *   Get next biggest digit in quotient
                */
                set digit = calculateDigit(dividend, divisorBig, base)
                
                set digits[digitCount] = digit*multiplier
                set digitCount = digitCount + 1
                
                /*
                *   Get subtractor
                */
                call Add.add(subtractor, digit)
                call Multiply.multiply(subtractor, divisor)
                
                /*
                *   Do forward subtraction
                *   (subtract first x digits, where x is length of subtractor)
                */
                if (compare(dividend, subtractor) < 0) then
                    set multiplier = -1
                    
                    call subtract(subtractor, dividend, base, dividend)
                else
                    call subtract(dividend, subtractor, base, dividend)
                endif
                
                exitwhen Compare.compare(dividend, divisor) < 0
                
                call subtractor.clear()
            endloop
            
            /*
            *   Get remainder
            */
            set remainder = ToInt(dividend)*multiplier
            
            /*
            *   Calculate remainder
            */
            if (remainder < 0) then
                if (digitCount != 0) then
                    set digits[digitCount - 1] = digits[digitCount - 1] - 1
                endif
                
                set remainder = divisor + remainder
            endif
            
            /*
            *   Calculate quotient
            */
            call number.clearRange(number + digitCount)
            
            set start = number
            loop
                exitwhen digitCount == 0
                set digitCount = digitCount - 1
                
                set number.digit = digits[digitCount]
                set number = number + 1
            endloop
            set number = start
            
            call Normalize.backward(number, number, number.lastDigit)
            
            /*
            *   Clean
            */
            call subtractor.deallocate()
            call dividend.deallocate()
            
            return remainder
        endmethod
    
        static method divide takes Number number, integer i returns integer
            if (number.digit != 0 and i != 1 and i != 0) then
                if (Compare.compare(number, i) < 0) then
                    set i = ToInt(number)
                    
                    call number.clear()
                    
                    return i
                else
                    return divideInner(number, i)
                endif
            endif
            
            return 0
        endmethod
    endstruct
    
    private struct Mod extends array
    endstruct
    
    private struct Base extends array
    endstruct
    
    struct BigInt extends array
    endstruct
    
    private struct Tester extends array
        private static method onInit takes nothing returns nothing
            local Number number = Number.allocate(10)
            local Number number2 = Number.allocate(10)
            
            local integer n = 0
            
            local integer remainder
            local integer remainderBig
            
            call Print(ToString(number) + " == " + I2S(n))
            call Add.add(number, 34)
            set n = n + 34
            call Print(ToString(number) + " == " + I2S(n))
            call Add.add(number, 92)
            set n = n + 92
            call Print(ToString(number) + " == " + I2S(n))
            call Subtract.subtract(number, 45)
            set n = n - 45
            call Print(ToString(number) + " == " + I2S(n))
            call Multiply.multiply(number, 15)
            set n = n*15
            call Print(ToString(number) + " == " + I2S(n))
            call Multiply.multiply(number, 15)
            set n = n*15
            call Print(ToString(number) + " == " + I2S(n))
            set remainderBig = Divide.divide(number, 31)
            set remainder = n - n/31*31
            set n = n/31
            call Print(ToString(number) + "r" + I2S(remainderBig) + " == " + I2S(n) + "r" + I2S(remainder))
        endmethod
    endstruct
endlibrary

A look at the division algorithm (I love how short it is)

JASS:
        private static method divideInner takes Number number, integer divisor returns integer
            /*
            *   Locals
            */
            local Number dividend                   //number to be divided
            local Number subtractor                 //number to subtract from dividend
            
            local integer digit                     //specific digit in quotient, can be negative
            
            local integer array digits              //collection of all digits in quotient
            local integer digitCount                //number of digits in quotient, including remainder
            
            local integer base                      //base of the number
            
            local integer divisorBig                //largest digit in divisor
            
            local integer multiplier                //current multiplier for digits
            
            local integer remainder
            
            local integer start
            
            /*
            *   Initialization
            */
            set base = number.base
            
            set dividend = Number.allocate(base)
            set subtractor = Number.allocate(base)
            
            set divisorBig = getDivisorBig(divisor, base)
            
            set multiplier = 1
            
            set digitCount = 0
            
            call dividend.setTo(number)
            
            /*
            *   Loop
            */
            loop
                /*
                *   Get next biggest digit in quotient
                */
                set digit = calculateDigit(dividend, divisorBig, base)
                
                set digits[digitCount] = digit*multiplier
                set digitCount = digitCount + 1
                
                /*
                *   Get subtractor
                */
                call Add.add(subtractor, digit)
                call Multiply.multiply(subtractor, divisor)
                
                /*
                *   Do forward subtraction
                *   (subtract first x digits, where x is length of subtractor)
                */
                if (compare(dividend, subtractor) < 0) then
                    set multiplier = -1
                    
                    call subtract(subtractor, dividend, base, dividend)
                else
                    call subtract(dividend, subtractor, base, dividend)
                endif
                
                exitwhen Compare.compare(dividend, divisor) < 0
                
                call subtractor.clear()
            endloop
            
            /*
            *   Get remainder
            */
            set remainder = ToInt(dividend)*multiplier
            
            /*
            *   Calculate remainder
            */
            if (remainder < 0) then
                if (digitCount != 0) then
                    set digits[digitCount - 1] = digits[digitCount - 1] - 1
                endif
                
                set remainder = divisor + remainder
            endif
            
            /*
            *   Calculate quotient
            */
            call number.clearRange(number + digitCount)
            
            set start = number
            loop
                exitwhen digitCount == 0
                set digitCount = digitCount - 1
                
                set number.digit = digits[digitCount]
                set number = number + 1
            endloop
            set number = start
            
            call Normalize.backward(number, number, number.lastDigit)
            
            /*
            *   Clean
            */
            call subtractor.deallocate()
            call dividend.deallocate()
            
            return remainder
        endmethod


Map is attached if you want to play with it. The tester struct is at the very bottom of the library.


You can't spam BigInts like you used to be able to. A BigInt can have up to 1638 digits regardless of base.

edit
above code is bugged, map has working code ^)^. Tried it in base 16 and had failed. Fixed it.
 

Attachments

  • BigInt.w3m
    36.3 KB · Views: 60
Last edited:
Ok, I made a new interesting discovery with division

If the first digit is negative, then the entire thing becomes positive

177/9

N = 177
1 >= 9? 1/9 : 17/9 = 1
1*9 = 9
length(177) - length(9) = 2
9*10^2 = 900
177 - 900 = -723


N = 723
7 >= 9? 7/9 : 72/9 = 8
8*9 =72
length(723) - length(72) = 1
72*10^1 = 720
723 - 720 = 3

3 < 9, stop

-1,-8 r-3

-10 - 8 - 1 = -19
9 - 3 = 6

-19r6

Actual Answer: 19r6




I don't really understand why this is the case though
 
no, I mean right now it is, but if I add a check to see if the first digit is negative, then I can just make the whole thing positive :\.

edit

whacked division

Code:
-2, -4, 5 == -235

-235/9 = -26r-1


N = -2,-4,5

|-2| >= 9? -2/9 : -24/9 = -2

-2*9 = -18

	-2, -4, 5
-	-1, -8, 0
	-1, 4, 5

	-6, 5

|-1| >= 9? -1/9 : -6/9 : -55/9 = -6

9*-6 = -54

	   -6, 5
-	   -5, -4
	   -1, 9
	       -1

-26 r-1

So when normalizing a number, go from left to right. Remove all zeroes and push numbers to the right if abs(resulting number) < base.


If the remainder doesn't match the sign, add/subtract 1 (opposite of sign, approach 0) and then do -9 + remainder (to go negative) or 9 + remainder (to go positive)


To completely normalize a number that has negative digits, make all digits in that number negative or positive.

First, normalize from left to right (try to combine digits). If the biggest number is negative, make the rest of the numbers negative. If the biggest number is positive, make the rest of the numbers positive.


-1, 9, 9, 9, 9,9 9, -1, 9, 9, 3 == ??

-1*10 + 9 = -1

-1, 9, 9, 9,9 9, -1, 9, 9, 3

go through

-1, -1, 9, 9, 3

-1*10 + -1 = -11, stop

-1*10 + 9 = -1, go through

-1, 0, 0, -1, 3

-1*10 + 3 = -7

-1, 0, 0, 0, -7

so -1, 9, 9, 9, 9,9 9, -1, 9, 9, 3 == -1, 0, 0, 0, -7, or -10007 (the negative sign goes out in front and all of the digits become positive)
 
Last edited:
Status
Not open for further replies.
Top