// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/12/Math.jack

/**
 * A library of commonly used mathematical functions.
 * Note: Jack compilers implement multiplication and division using OS method calls.
 */
class Math {

    static Array twoToThe;

    /** Initializes the library. */
    function void init() {
        let twoToThe = Array.new(16);
        let twoToThe[0] = 1;
        let twoToThe[1] = 2;
        let twoToThe[2] = 4;
        let twoToThe[3] = 8;
        let twoToThe[4] = 16;
        let twoToThe[5] = 32;
        let twoToThe[6] = 64;
        let twoToThe[7] = 128;
        let twoToThe[8] = 256;
        let twoToThe[9] = 512;
        let twoToThe[10] = 1024;
        let twoToThe[11] = 2048;
        let twoToThe[12] = 4096;
        let twoToThe[13] = 8192;
        let twoToThe[14] = 16384;
        let twoToThe[15] = 16384 + 16384;
        return;
    }

    function boolean bit (int x, int i) {
        if (x & twoToThe[i] = 0) {
            return false;
        } else {
            return true;
        }
    }

    /** Returns the absolute value of x. */
    function int abs(int x) {
        if (x < 0) {
            return -x;
        } else {
            return x;
        }
    }

    /** Returns the product of x and y. 
     *  When a Jack compiler detects the multiplication operator '*' in the 
     *  program's code, it handles it by invoking this method. In other words,
     *  the Jack expressions x*y and multiply(x,y) return the same value.
     */
    function int multiply(int x, int y) {
        var int sum, shiftedX, i;
        let sum = 0;
        let shiftedX = x;
        let i = 0;
        while (i < 16) {
            if(Math.bit(y, i)) {
                let sum = sum + shiftedX;
            }
            let shiftedX = shiftedX + shiftedX;
            let i = i + 1;
        }
        return sum;
    }

    /** Returns the integer part of x/y.
     *  When a Jack compiler detects the multiplication operator '/' in the 
     *  program's code, it handles it by invoking this method. In other words,
     *  the Jack expressions x/y and divide(x,y) return the same value.
     */
    function int divide(int x, int y) {
        var int q, tres;
        if(x < 0 & y < 0) {
            return Math.divide(Math.abs(x), Math.abs(y));
        }
        if(x < 0 | y < 0) {
            return -Math.divide(Math.abs(x), Math.abs(y));
        } 
        if(y > x) {
            return 0;
        }
        if(y < 0) {
            return 0;
        }
        let q = Math.divide(x, 2 * y);
        if((x - (2 * q * y)) < y) {
            return 2 * q;
        } else {
            return 2 * q + 1;
        }
    }

    /** Returns the integer part of the square root of x. */
    function int sqrt(int x) {
        var int y, j;
        let y = 0;
        let j = 7;
        while(j > -1) {
            if (~((Math.pow((y + Math.pow(2, j)), 2) > x)) & (Math.pow((y + Math.pow(2, j)), 2) > 0)) {
                let y = y + Math.pow(2, j);
            }
            let j = j - 1;
        }
        return y;
    }

    function int pow(int x, int p) {
        var int r, i;
        let i = 0;
        let r = 1;
        while (i < p) {
            let r = r * x;
            let i = i + 1;
        }
        return r;
    }

    /** Returns the greater number. */
    function int max(int a, int b) {
        if(a > b) {
            return a;
        } else {
            return b;
        }
    }

    /** Returns the smaller number. */
    function int min(int a, int b) {
        if(a > b) {
            return b;
        } else {
            return a;
        }
    } 
}
