isefire icon

VeryLargeInteger_Final

isefire | PRO | 09/11/14 08:04:51 PM UTC | 0 ⭐ | 381 👁️ | Never ⏰ | []
Java |

20.74 KB

|

None

|

0 👍

/

0 👎

/*
 *  VeryLargeInteger - manipulate N-digit integer numbers. (N <= RAM)
 *  Copyright (C) 2014  Daniel Latham
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * VERSION: 
 * 1.1.2 Changed some method names, mod and remainder confused, clarified
 *       text in LargeIntTest, resubmitted FINAL
 * 1.1.1 - Fixed mod bug where returned could be more than dividend
 *         Turned this version in, 3rd, //NOT//FINAL
 * 1.1 - Optimized file length(Deleted extra muldiv method)
 * 1.0 - Working FAST div and mod! Documentation improved!
 * 0.7 everything working.
 * 0.6 div and mod buggy, quit for the night
 * 0.5 multiplication working
 * 0.1 add/subtract working
 *
 *
 */
public class VeryLargeInteger
{
    /**
     * number declared to hold the value of given number in user
     * code.
     */
    public String number;
    /**
     * revnumber is just the reversed version of the number string
     */
    public String revnumber;
    /**
     * Sign of number, true for positive numbers and
     * false for negative numbers.
     */
    public boolean sign; // true is pos, false is neg
    /** VeryLargeInteger constructor, So far it can add, subtract, multiply,
     * divide, compare and modulo any two VeryLargeInteger objects that 
     * have less than or equal to 2^32 digits because of all my calls to 
     * the length function of the String class
     * @param a Long number to be set as this.number
     * @param sig Sign (+, - referred to true, false) of this
     * @author Daniel_Latham
     * @author [email protected]
     */
    public VeryLargeInteger(long a, boolean sig)
    {
        number = String.valueOf(a);
        sign = sig;
        StringBuffer temp = new StringBuffer(number);
        temp = temp.reverse();
        revnumber = temp.toString();
    }
    /** VeryLargeIteger constructor
     * @param b String of number to set as this.number
     * @param sig Sign (+, - referred to true, false) of this
     */
    public VeryLargeInteger(String b, boolean sig)
    {
        number = b.toString();
        //self explanatory but makes sure this.number != "" ; EmptyStringVal
        assert (number.length() != 0) : "Empty String Value!";
        sign = sig;
        StringBuffer temp = new StringBuffer(number);
        temp = temp.reverse();
        revnumber = temp.toString();
    }
    /** Prints our the number with sign prepended
     */
    public void print()
    {
        if (number.length() == 1 && (int) number.charAt(0) - '0' == 0)
        {
            System.out.println(number);
        }
        else if (sign == true) 
        {
            String newnumber = number.replaceFirst ("^0*", "");
            System.out.println(newnumber);
        }
        else
        {
            String newnumber = number.replaceFirst ("^0*", "");
            System.out.println("-"+newnumber);
        }
    }
    /** Returns string of number with sign prepended
     * @return number
     */
    public String toString()
    {
        if (number.length() == 1 && (int) number.charAt(0) - '0' == 0) 
        {
            return(number);
        }
        else if (sign == true)
        {
            String newnumber = number.replaceFirst ("^0*", "");
            return newnumber;
        }
        else 
        {
            String newnumber = number.replaceFirst ("^0*", "");
            return("-" + newnumber);
        }
    }
    /** Checks to see if the two VLInumbers are the same
     * @param other VLInumber you want to compare
     * @return      The boolean of this.number == other.number
     */
    public boolean equalTo(VeryLargeInteger other)
    {
        boolean isequal = false;
        boolean equals = (number.length() == other.number.length());
        if (equals == true)
        {
            for(int i = number.length()-1; i >= 0; i--)
            {
                if (((int) number.charAt(i)-'0') != ((int) other.number.charAt(i)-'0'))
                {
                    isequal = false;
                    break;
                }
                if (i == 0)//exact same number because previous if block was never tripped, sans sign
                {
                    isequal = true;
                }
            }
        }
        isequal = isequal && (sign == other.sign); //same trick as seen in greaterThan method
        //thank you CS250
        return isequal;
    }
    /** Checks if this.number is larger than other.number
     * @param other VLInumber to capare against, is this.number > other.number?
     * @return      The boolean of this.number > other.number
     */
    public boolean greaterThan(VeryLargeInteger other)
    {
        boolean isgreater = (number.length() > other.number.length());//Final value to be returned
        boolean isequal = false; //if equals block is tripped, this will be set true if the two strings are actually equal
        boolean equals = (number.length() == other.number.length());
        if (equals == true)
        {
            for(int i = number.length()-1; i >= 0; i--)
            {
                if (((int) revnumber.charAt(i)-'0') > ((int) other.revnumber.charAt(i)-'0'))
                {
                    isgreater = true;
                    break;
                }
                else if (((int) other.revnumber.charAt(i)-'0') > ((int) revnumber.charAt(i)-'0'))
                {
                    isgreater = false;
                    break;
                }
                if (i == 0)//exact same number sans sign
                {
                    isequal = true;
                }
            }
        }
        isequal = isequal && (sign == other.sign);//true if signs are the same and isequal is true, not if not
        if (isequal == true)
        {
            return false;//if they are equal, they are the "same" VLI x, then x is not greater than x
        }
        if (isgreater == true)
        {
            if (sign != other.sign)
            {
                isgreater = sign;//if signs differ, then value will equal whatever this.sign  is
            }
        }
        else if (isgreater == false)
        {
            if (sign != other.sign)
            {
                isgreater = sign;//if signs differ
            }
        }
        return isgreater;
    }
 
    /** Adds this.number to other.number
     * @param other
     * @return       VLInumber
     */
    public VeryLargeInteger add(VeryLargeInteger other)
    {
        /* Check sign values, decide if we want numPlusPlus(same signs)
         * or numPlusMin(different signs).
         */
        VeryLargeInteger newvli = null;
        if ((sign == false && other.sign == false) || (sign == true && other.sign == true))
        {
            if (number.length() >= other.number.length())// size of self >= other
            {
                newvli = numPlusPlus(revnumber, other.revnumber, 0);
            }
            else if (number.length() < other.number.length())//size of self < other
            {
                newvli = numPlusPlus(other.revnumber, revnumber, 0);
            }
        }
        else if(sign == true && other.sign == false)
        {
            newvli = numPlusMin(revnumber, other.revnumber, 0);
        }
        else if(sign == false && other.sign == true)
        {
            newvli = numPlusMin(other.revnumber, revnumber, 0);
        }
        
        return newvli;
    }
    /** Subtracts other.number from this.number
     * @param other
     * @return       VLInumber
     */
    public VeryLargeInteger sub(VeryLargeInteger other)
    {
        VeryLargeInteger newvli = null;
        VeryLargeInteger a = new VeryLargeInteger(number, true);
        VeryLargeInteger b = new VeryLargeInteger(other.number, true);
        if (sign == true && other.sign == true)//both are positive
        {
            newvli = numPlusMin(revnumber, other.revnumber, 0);
        }
        else if (sign == false && other.sign == false)//both are negative
        {
            newvli = numPlusMin(other.revnumber, revnumber, -1);
        }
        else if (sign == true && other.sign == false)// -- makes plus of other.number
        {
            if (a.greaterThan(b)) //BIGGER not GREATER in this case, since both are set to positive
            {
                newvli = numPlusPlus(revnumber, other.revnumber, 0);
            }
            else
            {
                newvli = numPlusPlus(other.revnumber, revnumber, 0);
            }
        }
        else if (sign == false && other.sign == true)
        {
            if (a.greaterThan(b))
            {
                newvli = numPlusPlus(revnumber, other.revnumber, -1);
            }
            else
            {
                newvli = numPlusPlus(other.revnumber, revnumber, -1);
            }
        }
        return newvli;
    }
 
    /** Multiplies this.number by other.number
     * @param other 
     * @return          VLInumber
     */
    public VeryLargeInteger mul(VeryLargeInteger other)
    {
        VeryLargeInteger newvli = null;
        if ((number.length() == 1 && ((int) revnumber.charAt(0) - '0') == 0) || //0 * num or num * 0 returns 0
            (other.number.length() == 1 && ((int) other.revnumber.charAt(0) - '0') == 0))
        {
            newvli = new VeryLargeInteger(0L, true);
        }
        else
        {
            newvli = emult(number, other.number);
        }
        boolean newsign = (other.sign == sign); //false if dif signs, so neg is dif
        newvli = new VeryLargeInteger(newvli.number, newsign);
        return newvli;
    }
    /** Divides this.number by other.number, deals with sign rules
     * @param other
     * @throws Exception
     * @return        VLInumber
     */
    public VeryLargeInteger div(VeryLargeInteger other)
    throws Exception
    {
        VeryLargeInteger newvli = null;
        VeryLargeInteger[] vliarray = null;
        VeryLargeInteger a = new VeryLargeInteger(number, true);
        VeryLargeInteger b = new VeryLargeInteger(other.number, true);
         //If other == 0, exit()
        if (other.number.length() == 1 && (((int) other.number.charAt(0) - '0') == 0))
        {
            System.out.println("You can't divide by 0!");
            System.exit(0);
        }
         //If self == 0, return 0
        if (number.length() == 1 && ((int) revnumber.charAt(0) - '0') == 0)
        {
            newvli = new VeryLargeInteger(0L, true);
        }
        //If same number, return 1
        else if (this.equalTo(other) == true) 
        {
            newvli = new VeryLargeInteger(1L, true);
        }
        //If trying to divide this int by a larger int
        else if (b.greaterThan(a))
        {
            newvli = new VeryLargeInteger(0L, true);
        }
        //Else dividing by same signs, normal
        else if (other.sign == sign)
        {
            newvli = moddiv(number, other.number)[0];
            newvli = new VeryLargeInteger(newvli.number, true);
        }
        //Else dividing by dif signs, negative
        else if (other.sign != sign)
        {
            newvli = moddiv(number, other.number)[0];
            newvli = new VeryLargeInteger(newvli.number, false);
        }
        
        return newvli;
    }
    /** Calaculates this.number / other.number returning the remainder 
     * and deals with weird sign rules
     * @param other
     * @throws Exception
     * @return        VLInumber
     */
    public VeryLargeInteger mod(VeryLargeInteger other)
    throws Exception
    {
        VeryLargeInteger newvli = null;
        VeryLargeInteger a = new VeryLargeInteger(number, true);
        VeryLargeInteger b = new VeryLargeInteger(other.number, true);
        //If trying to mod by 0, exit()
        if (other.number.length() == 1 && ((int) other.revnumber.charAt(0) - '0') == 0)
        {
            System.out.println("You can't divide by 0!");
            System.exit(0);
        }
        //if self == 0, return 0
        if (number.length() == 1 && (((int) revnumber.charAt(0) - '0') == 0))
        {
            newvli = new VeryLargeInteger(0L, true);
        }
        //if same number, return 0
        else if (a.equalTo(b) == true)
        {
            newvli = new VeryLargeInteger(0L, true);
        }
        //if this < that, return this
        else if (b.greaterThan(a))
        {
            newvli = this;
        }
        //if same signs
        else if (other.sign == sign && sign == true)
        {
            newvli = moddiv(number, other.number)[2];
            newvli = new VeryLargeInteger(newvli.number, true);
        }
        //dif signs
        else if (sign == false && other.sign == true)
        {
            newvli = moddiv(number, other.number)[2];
            newvli = new VeryLargeInteger(newvli.number, false);
        }
        //same signs, weird for mod
        else if (other.sign == sign && sign == false)
        {
            newvli = moddiv(number, other.number)[2];
            newvli = new VeryLargeInteger(newvli.number, false);
        }
        //everything else
        else
        {
            newvli = moddiv(number, other.number)[2];
            newvli = new VeryLargeInteger(newvli.number, true);
        }
        
        return newvli;
    }
    /** Helper method Adds two positive or two negative numbers
     * @param bigger The larger of the two VLInumber by number of digits
     * @param smaller The smaller of the two VLInumbers by number of digits
     * @param forcedsign If -1, then the computed new VLInumber will have
     *                   a negative sign
     * @throws ArrayOutOfBoundsException
     * @return           VLInumber that is the addition of the two numbers, 
     *                   forcing a negative sign if needed
     */
    private VeryLargeInteger numPlusPlus(String bigger, String smaller, int forcedsign)
    {
        VeryLargeInteger newvli = null;
        String newnumber = ""; // Must reverse this at end
        boolean rollover = false;
        
        for (int i = 0; i < bigger.length(); i++)
        {
            int k; //placeholder value
            try
            {
                k = ((int) bigger.charAt(i)-'0') + ((int) smaller.charAt(i)-'0'); //throws AIOOB E
                //System.out.println((int) '9' - '0'); //DEBUG
                if (rollover == true) { k+=1; rollover = false; }//if rollover, add 1 to placeholder
                if (k >= 10) { k -=10; rollover = true; }
                newnumber+=k;
            }
            catch (Exception e) //Expected ArrayIndexOOB exception
            {
                k = ((int) bigger.charAt(i)-'0');
                if (rollover == true)
                {
                    k+=1;
                    if (k >= 10) { k -=10; rollover = true; }
                    else rollover = false;
                }
                newnumber+=k; //add placeholder to stack, start again
            }
            if (i == bigger.length()-1 && rollover == true) { newnumber+=1; rollover = false; }
        }
        StringBuffer vjk = new StringBuffer(newnumber); //Convert to StringBuffer object to reverse string easily
        vjk.reverse();
        newnumber = vjk.toString(); //convert back to string, initialize newvli;
        if (forcedsign == -1)
        {
            newvli = new VeryLargeInteger(newnumber, false); //forces a negative sign
        }
        else
        {
            newvli = new VeryLargeInteger(newnumber, sign);
        }
        return newvli;
    }
    /** Helper method Adds a two different signed VLInumbers or subtracts two same signed
     * VLInumbers
     * @param bigger Bigger number as in number of digits
     * @param smaller Smaller number as in number of digits
     * @throws ArrayOutOfBoundsException
     * @return          String value of new number to numPlusMin, which then
     *                  computes the sign
     */
    private String subtralpha(String bigger, String smaller)
    { //I'm so sorry
        String newnumber = "";
        for(int i = 0; i < bigger.length(); i++)
        {
            int k; //placeholder value
            int tempnum; //don't remember why I put this here
            try
            {
                k = ((int) bigger.charAt(i)-'0') - ((int) smaller.charAt(i)-'0');
                // DEBUG System.out.println(k);
            }
            catch (Exception e)
            {
                k = ((int) bigger.charAt(i)-'0');
            }
            if (k < 0)
            {
                k = ((int) bigger.charAt(i)-'0');
                int j = 1;
                while(k < ((int) smaller.charAt(i)-'0'))
                {
                    if (((int) bigger.charAt(i+j)-'0') >= 1) //Borrow from number
                    {
                        int tempint = ((int) bigger.charAt(i+j)-'0');
                        tempint -= 1;
                        StringBuffer temp = new StringBuffer(bigger);
                        /* Took a while to understand
                         * StringBuffer.replace uses inclusive/exclusive/int
                         * I kept doing incl/incl/int and couldn't understand what
                         * I was doing wrong
                         */
                        temp.replace(i+j,i+j+1,Integer.toString(tempint)); 
                        bigger = temp.toString();
                        k+=10;
                    }
                    else /*If can't borrow from next number, next number must
                        * be zero, so changed zero to 9, and iterate again
                        */
                    {
                        StringBuffer temp = new StringBuffer(bigger);
                        temp.replace(i+j,i+j+1, "9");
                        bigger = temp.toString();
                    }
                    j++;//capable of iterating infinitely if needed
                }
                k = k - ((int) smaller.charAt(i)-'0');
            }
            // DEBUG System.out.println(k);
            newnumber += k;
            // DEBUG System.out.println(newnumber);
        }
        //System.out.println(newnumber);
        return newnumber;
    }
    /** Helper method Tests which number has more digits/is larger and then calls subtralpha 
     * on the two numbers, only then computing the sign of the new VLInumber
     * @param pos Positive number
     * @param neg Negative number
     * @param forcedsign If -1 then forces result to a negative
     * @return            VLInumber to add/sub
     */
    private VeryLargeInteger numPlusMin(String pos, String neg, int forcedsign)
    {
        VeryLargeInteger newvli = null;
        boolean longer = (pos.length() > neg.length());
        boolean equals = (pos.length() == neg.length());
        if (equals == true)
        {
            //find which one, pos or neg, is actually the greater number
            //through iteration, high to low values
            for(int i = pos.length()-1; i >= 0; i--)
            {
                if (((int) pos.charAt(i)-'0') > ((int) neg.charAt(i)-'0'))
                {
                    longer = true; //see above
                    break;
                }
                else if (((int) neg.charAt(i)-'0') > ((int) pos.charAt(i)-'0'))
                {
                    longer = false;
                    break;
                }
                if (i == 0)//exact same number wow you win wowow
                {
                    newvli = new VeryLargeInteger(0L, true);
                    return newvli;
                }
            }
        }
 
        String newnumber = "";
        if (longer == true)//pos is bigger
        {
            newnumber = subtralpha(pos, neg);
        }
        if(longer == false)//neg is bigger
        {
            newnumber = subtralpha(neg, pos);
        }
        StringBuffer newbuffer = new StringBuffer(newnumber);
        newbuffer.reverse();
        newnumber = newbuffer.toString();
        newnumber = newnumber.replaceFirst ("^0*", "");
        
        newvli = new VeryLargeInteger(newnumber, longer);
        return newvli;
    }
    /** Helper method that multiplies two numbers using a string stack for the current computation
     * and a VLInumber for the total
     * @param self
     * @param other
     * @return       VLInumber
     */
    private VeryLargeInteger emult(String self, String other)
    {
        int place = -1;
        VeryLargeInteger total = new VeryLargeInteger(0L, true); //add newvli to total every iteration
        for (int i = self.length()-1; i >= 0; i--)//iterate over number on "bottom"
        {
            VeryLargeInteger newvli = null; //newvli to hold value temporarily of stack
            String stack = ""; //stack of integers for each line computed, just like on paper
            place += 1; //perm holder for number of zeros to prepend
            int k = place; //set to place because need to add place number of zeroes each time
            int rollover = 0;
            for (int j = other.length()-1; j >= 0; j--)//iterate over number on "top"
                                    //multiplying by digit on "bottom", adding to stack
            {
                while (k > 0)//iterate through all prepended zeros and add them to stack
                {
                    stack += "0";
                    k -= 1;
                }
                int temp = ((int) self.charAt(i)-'0') * ((int) other.charAt(j)-'0');
                if (temp >= 10) //2 or more digits
                {
                    if (j == 0) //if last digit on "top", add the rollover number if there is one, split and append to stack
                    {
                        temp += rollover;
                        rollover -= rollover;
                        String tempsplit = Integer.toString(temp);
                        stack += (int) tempsplit.charAt(1) - '0';
                        stack += (int) tempsplit.charAt(0) - '0';
                    }
                    else //split, add to rollover, append
                    {
                        if (rollover > 0) //if rollover already init, add to temp
                        {
                            temp += rollover;
                            rollover -= rollover;
                        }
                        String tempsplit = Integer.toString(temp);
                        rollover = (int) tempsplit.charAt(0) - '0';
                        stack += (int) tempsplit.charAt(1) - '0';
                    }
                }
                else
                {
                    stack+=temp;
                }
                // DEBUG System.out.println("Stack " +i+" "+ stack);
                
            }
            StringBuffer bufftemp = new StringBuffer(stack);
            bufftemp = bufftemp.reverse();
            stack = bufftemp.toString();
            newvli = new VeryLargeInteger(stack, true);
            total = total.add(newvli);
            
        }
        return total;
    }
    /** Final quick-and-dirty helper method for the mod(remainder, not modular arithmetic) and div by power-of-2 multiplication
     * @param self Dividend
     * @param other Divisor
     * @throws Exception
     * @return VeryLargeInteger Array
     * 
     */
    private VeryLargeInteger[] moddiv(String self, String other)//returns array of VLIs instead of boolean deciding
    throws Exception
    {
        VeryLargeInteger selfvli = new VeryLargeInteger(self, true);
        VeryLargeInteger othervli = new VeryLargeInteger(other, true);
        VeryLargeInteger totalvli = new VeryLargeInteger(other, true);//total
        VeryLargeInteger countvli = new VeryLargeInteger(1L, true);//div
        VeryLargeInteger newadd = new VeryLargeInteger(0L, true);//placeholder
        VeryLargeInteger lastadd = new VeryLargeInteger(totalvli.number, true); //placeholder
        VeryLargeInteger lastcount = new VeryLargeInteger(countvli.number, true);//placeholder
        VeryLargeInteger remvli = new VeryLargeInteger(0L, true);//remainder
        final VeryLargeInteger MULT_BY_2 = new VeryLargeInteger(2L, true);
        while (!totalvli.equalTo(selfvli) && selfvli.greaterThan(totalvli))//powers of 2 until total >= self
        {
            lastcount = new VeryLargeInteger(countvli.number, true);
            lastadd = new VeryLargeInteger(totalvli.number, true);
            newadd = totalvli.mul(MULT_BY_2);
            totalvli = new VeryLargeInteger(newadd.number, true);
            countvli = countvli.mul(MULT_BY_2);
        }
        if (totalvli.equalTo(selfvli))//total == self, return {x, y, 0}
        {
            VeryLargeInteger[] final1 = {countvli, totalvli, remvli};
            return final1;
        }
        else
        {
            countvli = lastcount;
            totalvli = lastadd;
            remvli = selfvli.sub(totalvli);
            if (othervli.greaterThan(remvli)) //if this, then remainder != 0
            {
                VeryLargeInteger[] final1 = {countvli, totalvli, remvli};
                return final1;
            }
            //recursion for all smaller values :)
            //Base cases are above if blocks
            VeryLargeInteger[] newbarray = this.moddiv(remvli.number, othervli.number);
            remvli = newbarray[2]; //remainder
            totalvli = totalvli.add(newbarray[1]);
            countvli = countvli.add(newbarray[0]);
            VeryLargeInteger[] final1 = {countvli, totalvli, remvli};
            return final1;//this VLInumber is actually returned
        }
    }
}

Comments