/* 
 * bitAnd - x&y using only ~ and | 
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1
 */
int bitAnd(int x, int y) {
  // ~(x && y) == ~x | ~y
  return ~((~x) | (~y));
}
/* 
 * getByte - Extract byte n from word x
 *   Bytes numbered from 0 (LSB) to 3 (MSB)
 *   Examples: getByte(0x12345678,1) = 0x56
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 2
 */
int getByte(int x, int n) {
  // n >> 3 == n * 8
  return x >> (n << 3) & 0xff;
}
/* 
 * logicalShift - shift x to the right by n, using a logical shift
 *   Can assume that 0 <= n <= 31
 *   Examples: logicalShift(0x87654321,4) = 0x08765432
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3 
 */
int logicalShift(int x, int n) {
  // mask inserted 1s from arithmetic shift
  // return (x >> n) & (0xffffffff >> n);
  // 32+~n == 31-n
  int mask = ((1 << (32 + ~n)) + ~0) | (1 << (32 + ~n));
  return (x >> n) & mask;
}
/*
 * bitCount - returns count of number of 1's in word
 *   Examples: bitCount(5) = 2, bitCount(7) = 3
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
 */
int bitCount(int x) {
    // https://stackoverflow.com/questions/3815165/how-to-implement-bitcount-using-only-bitwise-operators
    int mask_3333 = 0x33, mask_5555 = 0x55, mask_0F0F = 0x0f, mask_00FF = 0xff,
        mask_FFFF = 0xff, c;
    mask_3333 |= mask_3333 << 8;
    mask_3333 |= mask_3333 << 16;
    mask_5555 |= mask_5555 << 8;
    mask_5555 |= mask_5555 << 16;
    mask_0F0F |= mask_0F0F << 8;
    mask_0F0F |= mask_0F0F << 16;
    mask_00FF |= mask_00FF << 16;
    mask_FFFF |= mask_FFFF << 8;

    c = (x & mask_5555) + ((x >> 1) & mask_5555);
    c = (c & mask_3333) + ((c >> 2) & mask_3333);
    c = (c & mask_0F0F) + ((c >> 4) & mask_0F0F);
    c = (c & mask_00FF) + ((c >> 8) & mask_00FF);
    c = (c & mask_FFFF) + ((c >> 16) & mask_FFFF);

    return c;
}

/* 
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int bang(int x) {
  // the goal is to | every bit in x, the result is at the 0th bit
  x |= (x >> 16);
  x |= (x >> 8);
  x |= (x >> 4);
  x |= (x >> 2);
  x |= (x >> 1);
  return ~x & 1;
}

/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
  // 0x10000000
  return 1 << 31;
}

/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int fitsBits(int x, int n) {
    // only under two cases return 1: (n-1)th bit to 31st bit is all 0 or 1

    // solution 1
    // c = 32 - n;
    int c = 33 + ~n;
    int r = x << c >> c;
    // r should be identical to x
    return !(x ^ r);
    
    // solution 2
    // x >> n-1
    // x >>= n + ~0;
    // // now x should be either -1 or 0
    // return !(x + 1) | !x;
}
/* 
 * divpwr2 - Compute x/(2^n), for 0 <= n <= 30
 *  Round toward zero
 *   Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int divpwr2(int x, int n) {
    // if x < 0, only when the 0th to (n-1)th bit of x are all 0s will x>>n == x
    // https://stackoverflow.com/questions/5061093/dividing-by-power-of-2-using-bit-shifting
    return (x + ((x >> 31) & ((1 << n) + ~0))) >> n;
}
/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
  return ~x + 1;
}
/* 
 * isPositive - return 1 if x > 0, return 0 otherwise 
 *   Example: isPositive(-1) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 3
 */
int isPositive(int x) {
  // !(x>>31) is to determine whether x >= 0, !!x to determine whether x==0
  return !!x & !(x >> 31);
}
/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
int isLessOrEqual(int x, int y) {
    // y - x
    int tmp = y + ~x + 1;
    // avoid overflow. if y > 0 && x < 0, return 1. if y < 0 && x > 0, return 0.
    x >>= 31;
    y >>= 31;
    // return !(!!y & !x) & ((!y & !!x) | !(tmp >> 31));  // equivalent to
    return (!y | !!x) & ((!y & !!x) | !(tmp >> 31));
}
/*
 * ilog2 - return floor(log base 2 of x), where x > 0
 *   Example: ilog2(16) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 90
 *   Rating: 4
 */
int ilog2(int x) {
    // the answer could be written as 16a+8b+4c+2d+e, abcde are computed successively
    int ans = 0;
    ans = (!!(x >> (16))) << 4;
    ans += ((!!(x >> (8 + ans))) << 3);
    ans += ((!!(x >> (4 + ans))) << 2);
    ans += ((!!(x >> (2 + ans))) << 1);
    ans += ((!!(x >> (1 + ans))) << 0);
    return ans;
}
/* 
 * float_neg - Return bit-level equivalent of expression -f for
 *   floating point argument f.
 *   Both the argument and result are passed as unsigned int's, but
 *   they are to be interpreted as the bit-level representations of
 *   single-precision floating point values.
 *   When argument is NaN, return argument.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 10
 *   Rating: 2
 */
unsigned float_neg(unsigned uf) {
  // determine whether it is NaN (exp == 0xff, frac > 0)
  if (!((uf + 0x00800000) & 0x7f800000) && (uf << 9)) return uf;
  return uf ^ 0x80000000;
}
/* 
 * float_i2f - Return bit-level equivalent of expression (float) x
 *   Result is returned as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point values.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned float_i2f(int x) {
    // sign | exp | frac: 1-bit, 8-bit, 23-bit
    unsigned sign, exp;
    // omit sign. abs is used to compute the frac. tmps are for saving ops
    unsigned abs = x, abs_copy, tmp, tmp2, tmp3, tmp4;
    // the place of the highest 1
    unsigned n = -1;
    // denormalized representation for 0
    if (!x) return 0;
    // special case
    if (x == 0x80000000) return 0xcf000000;
    sign = x & 0x80000000;
    if (sign) abs = -x;
    abs_copy = abs;
    // suppose x = 10110101, then it could represented as 1.0110101 * 2^7
    // so the bits after x's highest 1 are in frac, and the place of the highest
    // 1 is e find the highest 1
    while (abs_copy) {
        abs_copy >>= 1;
        ++n;
    }
    // the frac part. when n == 0, 0xffffffff>>(32-n) == 0xffffffff.
    if (n)
        abs &= 0xffffffff >> (32 - n);
    else
        abs = 0;
    // 0th to (n-1)-th bits are in frac
    // if the length of frac > 23, right shift; else left shift
    if (n > 23) {
        // need to consider rounding problems (round-to-even)
        // if there are 1s in the low bits (n-23) to be rounded
        tmp = abs & (0xffffffff >> (55 - n));  // the rounded part
        tmp4 = n - 23;                         // just for saving ops
        tmp2 = 1 << tmp4;
        tmp3 = tmp2 >> 1;
        // upper round
        if (tmp > tmp3)
            abs += tmp2;
        else if (tmp == tmp3) {
            // at middle
            // if the lowest keeped bit is 1, it should be rounded to even
            if (abs & tmp2) abs += tmp2;
        }
        abs >>= tmp4;
    } else {
        abs <<= (23 - n);
    }
    // if after rounding, the frac part has a 1 at 23th bit, this 1 would be
    // treated as a carry and add 1 to the exp
    if (abs & 0x800000) {
        ++n;
        abs &= 0x7fffff;
    }
    // exp == e + bias
    exp = (n + 127) << 23;
    return sign | exp | abs;
}
/* 
 * float_twice - Return bit-level equivalent of expression 2*f for
 *   floating point argument f.
 *   Both the argument and result are passed as unsigned int's, but
 *   they are to be interpreted as the bit-level representation of
 *   single-precision floating point values.
 *   When argument is NaN, return argument
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned float_twice(unsigned uf) {
  unsigned sign = uf & 0x80000000, exp = uf & 0x7f800000, frac = uf & 0x7fffff;
  // determine whether it is NaN or INF (exp == 0xff)
  if (!((uf + 0x00800000) & 0x7f800000)) return uf;
  else if (!exp) {
    // denormalized. e == 1-bias, f == frac
    return sign | (frac << 1);
    // if frac<<1 is larger than 0x7fffff, a carry bit would be added to the exp part,
    // resulting in a normalized float with a "free" 1 in frac
  } else {
    // normalized
    // if exp < 0xfe, just need to add 1 to exp. else 2*uf is INF
    if (exp == 0x7f000000) return sign | 0x7f800000;
    return uf + 0x00800000;
  }
}