/**
 * Copyright (c) 2004-2005, Regents of the University of California
 * 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 the University of California, Los Angeles 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.
 */

package cck.util;

/**
 * The <code>Arithmetic</code> class implements a set of useful methods that are used by the simulator and
 * assembler for converting java types to various data types used by the machine.
 *
 * @author Ben L. Titzer
 */
public class Arithmetic {
    public static short word(byte b1, byte b2) {
        return (short) ((b1 & 0xff) | (b2 << 8));
    }

    public static char uword(byte b1, byte b2) {
        return (char) ((b1 & 0xff) | ((b2 & 0xff) << 8));
    }

    public static int signExtend(int value, int signbit) {
        if (getBit(value, signbit)) {
            return value | 0xffffffff << signbit;
        }
        return value;
    }

    public static char ubyte(byte b1) {
        return (char) (b1 & 0xff);
    }

    public static byte low(short val) {
        return (byte) val;
    }

    public static byte high(short val) {
        return (byte) (val >> 8);
    }

    public static byte low(int val) {
        return (byte) val;
    }

    public static byte high(int val) {
        return (byte) ((val & 0xff00) >> 8);
    }

    public static char ulow(char val) {
        return (char) (val & 0xff);
    }

    public static char uhigh(char val) {
        return (char) (val >> 8);
    }

    public static char ulow(short val) {
        return (char) (val & 0xff);
    }

    public static char uhigh(short val) {
        return (char) ((val & 0xff00) >> 8);
    }

    public static boolean getBit(byte val, int bit) {
        return (val & (1 << bit)) != 0;
    }

    public static boolean getBit(long val, int bit) {
        return (val & (((long) 1) << bit)) != 0;
    }

    public static boolean getBit(int val, int bit) {
        return (val & (1 << bit)) != 0;
    }

    public static byte setBit(byte val, int bit) {
        return (byte) (val | (1 << bit));
    }

    public static byte setBit(byte val, int bit, boolean on) {
        if (on) return setBit(val, bit);
        else return clearBit(val, bit);
    }

    public static int setBit(int val, int bit, boolean on) {
        int mask = (1 << bit);
        return on ? (val | mask) : (val & ~mask);
    }

    public static long setBit(long val, int bit, boolean on) {
        long mask = (((long) 1) << bit);
        return on ? (val | mask) : (val & ~mask);
    }

    public static byte clearBit(byte val, int bit) {
        return (byte) (val & ~(1 << bit));
    }

    public static int lowestBit(long value) {
        int low = 0;

        if (((int) value) == 0) {
            low += 32;
            value = value >> 32;
        }
        if ((value & 0xFFFF) == 0) {
            low += 16;
            value = value >> 16;
        }
        if ((value & 0xFF) == 0) {
            low += 8;
            value = value >> 8;
        }
        if ((value & 0xF) == 0) {
            low += 4;
            value = value >> 4;
        }
        if ((value & 0x3) == 0) {
            low += 2;
            value = value >> 2;
        }
        if ((value & 0x1) == 0) {
            low += 1;
            value = value >> 1;
        }

        return (value == 0) ? -1 : low;
    }

    public static int highestBit(int value) {
        int high = 31;

        if ((value & 0xFFFF0000) == 0) {
            high -= 16;
            value = value << 16;
        }
        if ((value & 0xFF000000) == 0) {
            high -= 8;
            value = value << 8;
        }
        if ((value & 0xF0000000) == 0) {
            high -= 4;
            value = value << 4;
        }
        if ((value & 0xC0000000) == 0) {
            high -= 2;
            value = value << 2;
        }
        if ((value & 0x80000000) == 0) {
            high -= 1;
            value = value << 1;
        }

        return (value == 0) ? -1 : high;
    }

    public static int log(int value) {
        if ( value == 0 ) return 0;
        return 1 + highestBit(value - 1);
    }

    /**
     * The <code>getBitField()</code> method reads a bit field from a value where the bits of the field
     * are not consecutive or in order. This method accepts an array of integers that denotes the permutation
     * of the bit field within the given value from highest order bits to lowest order bits
     *
     * @param value       the integer value to extract the bit field from
     * @param permutation the permutation of the bits for which to extract the bit field from highest order bit to
     *                    lowest order bit
     * @return the integer value resulting from concatentating the bits: <code>value[permutation[0]]</code>,
     *         <code>value[permutation[1]]</code>, etc into a single integer value
     */
    public static int getBitField(int value, int[] permutation) {
        int result = 0;
        for (int cntr = 0; cntr < permutation.length; cntr++) {
            result = result << 1;
            if (getBit(value, permutation[cntr])) result |= 1;
        }
        return result;
    }

    public static int getBitField(int value, int lowbit, int length) {
        return value >> lowbit & (0xffffffff >>> (32 - length));
    }

    // bit patterns for reversing the order of bits of a 4 bit quantity.
    private static final int[] reverseKey = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};

    public static byte reverseBits(byte value) {
        return (byte) (reverseKey[value & 0x0f] << 4 | reverseKey[(value >> 4) & 0x0f]);
    }

    // key for the number of bits set to one in a 4 bit quantity
    private static final int[] bitcountKey = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};

    public static int bitCount(byte value) {
        return bitcountKey[value & 0x0f] + bitcountKey[(value >> 4) & 0x0f];
    }

    public static int getSingleBitMask(int bit) {
        return 1 << bit;
    }

    public static int getSingleInverseBitMask(int bit) {
        return ~(1 << bit);
    }

    public static int getBitRangeMask(int low, int high) {
        return (0xffffffff >>> (31 - (high - low))) << low;
    }

    public static int getBitMask(int width) {
        return (0xffffffff >>> (31 - (width - 1)));
    }

    public static long getLongBitRangeMask(int low, int high) {
        return (-1L >>> (63 - (high - low))) << low;
    }

    public static int getInverseBitRangeMask(int low, int high) {
        return ~getBitRangeMask(low, high);
    }

    public static long[] modulus(long val, int[] denom) {
        long[] result = new long[denom.length + 1];
        for (int cntr = denom.length - 1; cntr >= 0; cntr--) {
            int radix = denom[cntr];
            result[cntr + 1] = (val % radix);
            val = val / radix;
        }
        result[0] = val;
        return result;
    }

    public static long mult(long[] vals, int[] denom) {
        long accum = 0;
        int radix = 1;
        assert (vals.length - 1 == denom.length);
        for (int cntr = 0; cntr < vals.length - 1; cntr++) {
            accum += vals[cntr] * radix;
            radix = radix * denom[cntr];
        }
        accum += vals[vals.length - 1] * radix;
        return accum;
    }

    public static void inc(long[] vals, int[] denom, int pos) {
        assert (vals.length - 1 == denom.length);

        for (int cntr = pos; cntr < vals.length; cntr++) {
            vals[cntr]++;
            // is there a carry out?
            if (vals[cntr] < denom[cntr]) break;
        }
    }

    public static int max(int m1, int m2) {
        return m1 > m2 ? m1 : m2;
    }

    public static int min(int m1, int m2) {
        return m1 < m2 ? m1 : m2;
    }

    public static byte packBits(int b7, int b6, int b5, int b4, int b3, int b2, int b1, int b0) {
        return (byte)((b0 > 0 ? (1 << 0) : 0) |
                        (b1 > 0 ? (1 << 1) : 0) |
                        (b2 > 0 ? (1 << 2) : 0) |
                        (b3 > 0 ? (1 << 3) : 0) |
                        (b4 > 0 ? (1 << 4) : 0) |
                        (b5 > 0 ? (1 << 5) : 0) |
                        (b6 > 0 ? (1 << 6) : 0) |
                        (b7 > 0 ? (1 << 7) : 0));
                
    }

    public static int roundup(int val, int den) {
        return (val + den - 1) / 8;
    }
}
