// --------------------------------------------------------------------
//    CSC 148S, 1998, Assignment 0: Number Systems 
//    NIB (NumberInBase) Class 
//    name:            >> Insert your name here <<
//    student:         >> Insert your student number here <<
//    lecturer:        >> Insert your lecturer's name here <<
//    lecture time:    >> Insert your lecture time here <<
//    tutor:           >> Insert your tutor's name here <<
// --------------------------------------------------------------------


// Author of the starter code: Paul Gries.
// With revisions by: Jeremy Sills
// Written: 17 December 1997
// Last revised: 2 January 1998
//

import java.io.*;
import java.awt.*;

// Store a non-negative number in a base, and provide methods for
// dividing, subtracting, and converting to another number.
// ----------------------------------------------------------------------
class NIB {

    // The most digits I can store.
    static int MAX_DIGITS = 20;

    // My base.
    int base;

    // My digits in base.  The least significant digit is digits[0].
    int[] digits = new int[MAX_DIGITS];

    // The number of digits in my digits array.
    int numDigits;

  
    // Create me with my digits extracted from 'num', in base 'b'.
    // Pre: all characters of num are in base b.
    // ------------------------------------------------------------------
    public NIB (String num, int b) {

        base = b;

        // Deal with leading zeros.
        int j = 0;
        while (j < num.length() && num.substring(j,j+1).equals("0")) {
            j++;
        }

        numDigits = num.length()-j;
        
        // Think of location [0] in the array storing the rightmost digit 
        // of the number.
        // Ie. The least significant digit is stored in digits[0].

        for (; j < num.length(); j++) {
            digits[num.length()-j-1] =
                Integer.parseInt(num.substring(j,j+1), b);
        }
    }


    // Return the number of digits I have.
    // ------------------------------------------------------------------
    private int getNumDigits () {
        return numDigits;
    }


    // Set the number of digits I have to 'num'.
    // ------------------------------------------------------------------
    private void setNumDigits (int num) {
        numDigits = num;
    }


    // Return my base.
    // ------------------------------------------------------------------
    private int getBase () {
        return base;
    }


    // Return a NumberInBase (NIB) representing my digits from
    // the most significant digit to digit 'finish'.
    // ------------------------------------------------------------------
    private NIB copyFrom (int finish) {
        
        String a = new String(toString());
        String part = new String (a.substring(0,finish));
        NIB res = new NIB(part,getBase());
        
        return res;
    }

    // Change my digit at position 'where' to 'digit'.
    // Update my number of digits if needed.
    // ------------------------------------------------------------------
    private void putDigit (int digit, int where) {

        digits[where] = digit;
        if (digit > 0 && where + 1 > getNumDigits()) {
            setNumDigits(where + 1);
        }

    }


    // Copy the digits of the NIB 'num' to my digits starting at my
    // position 'where'. 
    // Ie. The NIB 'num' is shifted 'where' digits to the left and copied
    // to me.
    // Update my number of digits if needed.
    // ------------------------------------------------------------------
    private void copyTo (NIB num, int where) {

        int count = 0;
        while (count < num.getNumDigits()) {       
            digits[where+count] = num.digits[count];
            count ++;
        }

        if (where+count < getNumDigits()) {
            setNumDigits(where+count);
        }

    }


    // Return the largest multiple of 'divisor' that divides into me.
    // ------------------------------------------------------------------
    private int findFactor (NIB divisor) {

        int factor = (int) Integer.parseInt(toString(),getBase()) /
            Integer.parseInt(divisor.toString(),divisor.getBase()); 
        return factor;
    }


    // Return the product of me and 'num'.
    // ------------------------------------------------------------------
    private NIB product (int num) {

        int prod = Integer.parseInt(toString(),getBase()) * num;
        return new NIB(Integer.toString(prod,getBase()),getBase());

    }


    // Divide me by 'num' in my base, returning the new value.  (Using integer
    // division; i.e., ignore the remainder.)
    // This method mimics long division as illustrated in the assignment
    // handout.
    // ------------------------------------------------------------------
    private NIB divide (NIB num) {

        // Where the result is stored.
        NIB res = new NIB("", getBase());

        // Make sure the new base is in my base.
        NIB divisor = new
              NIB(Integer.toString(Integer.parseInt(num.toString(),
            num.getBase()),getBase()),getBase());        

        NIB dividend = new NIB(toString(),getBase());

        // Figure out where to start dividing.
        // Ie. Where the first digit is stored in the result.

        int count = dividend.getNumDigits() - divisor.getNumDigits();

        while (count > -1) {
        
            // Extract the next group of digits from the dividend. 
            NIB partDividend =
               dividend.copyFrom(dividend.getNumDigits() - count); 
            // Find the next digit in the quotient.
            int factor = partDividend.findFactor(divisor);
            // And update the result.
            res.putDigit (factor, count);

            if (factor > 0) {
                // Update the dividend.
                NIB prod = divisor.product(factor);
                NIB sub = partDividend.subtract(prod);
                dividend.copyTo(sub, count);
            }

            count --;
        }

        return res;
    }


    // Divide me by 'num' in my base, returning only the remainder.  (Using
    // integer division.)
    // This method mimics long division as illustrated in the assignment
    // handout.
    // ------------------------------------------------------------------
    private int remainder (NIB num) {

        NIB divisor = new
            NIB(Integer.toString(Integer.parseInt(num.toString(),
            num.getBase()),getBase()),getBase());        
        NIB dividend = new NIB(toString(),getBase());
        int count = dividend.getNumDigits() - divisor.getNumDigits();

        while (count > -1) {
        
            // Extract the next group of digits from the dividend. 
            NIB partDividend =
                dividend.copyFrom(dividend.getNumDigits() - count); 
            // Find the next digit in the quotient.
            int factor = partDividend.findFactor(divisor);
            // Update the dividend.
            NIB prod = divisor.product(factor);
            NIB sub = partDividend.subtract(prod);
            dividend.copyTo(sub, count);

            count --;
        }

        return Integer.parseInt(dividend.toString(),dividend.getBase());
    }


    // Subtract 'num' from me in my base, returning the new value.
    // Precondition: my number >= 'num'.
    // ------------------------------------------------------------------
    // STUDENTS WILL WRITE THIS METHOD.
    private NIB subtract (NIB num) {

    }


    // Return a new NumberInBase in base 'b' whose value is equivalent to
    // mine.
    // ------------------------------------------------------------------
    // STUDENTS WILL WRITE THIS METHOD.
    public NIB convertTo (int b) {
  
    }


    // Return my value as a String.
    // ------------------------------------------------------------------
    public String toString () {

        // If the length of the number is 0, the result is 0.
        if (getNumDigits() == 0) 
            return new String("0");
        else {
            StringBuffer s = new StringBuffer(getNumDigits());
            for (int i=getNumDigits()-1; i >= 0; i--) {
                char c = Character.forDigit(digits[i], base);
                s.append(Character.toUpperCase(c));
            }

            return new String(s);
        }
    }
}
