/*
 Bitwise and: &
int x = 0xABCD;   // …0000 1010 1011 1100 1101
int y = 0xFF;     // …0000 0000 0000 1111 1111
int z = x & y;    // …0000 0000 0000 1100 1101 == 0xCD

Bitwise or: |
int x = 0x60;    // …0110 0000
int y = 0x2B;    // …0011 1011
int z = x | y;   // …0111 1011 == 0x7B

Left shift: <<
int z = 0xF << 4; //shift 0xF 4 bits to the left. 0xF0

Arithmetic Right shift: >>. (fill with sign bit)
int z = 0xAB >> 4;        //shift 0xAB 4 bits right. 0xA.
int z2 = 0x81234567 >> 4; //shift a negative number right.
                                                  //0xF8123456

Logical Right shift: >> (fill with 0) on unsigned ints
int z2 = (unsigned int)0x81234567 >> 4; //shift a negative number right.
                          //0x08123456

 */
#include <stdio.h>
#include <stdlib.h>

const int BIN_LENGTH = 32;

//using bits for multiple booleans
//each flag is represented by a single bit in a different position
//this allows us to combine them without changing their values
const int IS_VALID = 1;   //1 0x1
const int IS_READABLE = 1 << 1;   //2  0x2
const int IS_WRITABLE = 1 << 2;  //4  0x4
const int IS_SOMETHING_ELSE = 1 << 3;  //8  0x8

//global variable
int count = 0;

char *printBinaryString(char *result, unsigned int value){
        unsigned int mask = 1;
        for(int i = 0; i < BIN_LENGTH; i++){
                result[BIN_LENGTH-1-i] = (value & mask)?'1':'0';
                mask = mask << 1;
        }
        //add null terminal
        result[BIN_LENGTH] = '\0';
        return result;
}

void printValue(char *label, unsigned int value){
        //print label in 20 spaces
        printf("%d: %s\n", ++count, label);

        //print the decimal representation of value
        printf("\tDEC: %d\n", value);

        //print the Hexadecimal representation of value
        //Note %08X: pad up to 8 hex digits with 0s
        //  need to print the 0x
        printf("\tHEX: 0x%08X\n", value);

        //print the binary representation of value
        //first create it as a string
        char bStr[BIN_LENGTH+1];
        printf("\tBIN: %s\n\n", printBinaryString(bStr,value));

}

int main(void) {
        //a hex value
        int hex = 0xDEADBEEF;

        printValue("32 bit int", hex);


        //least significant 2 bytes (4 hex digits)
        //0xFFFF is 16 0's followed by 16 1's
        //&  is the bitwise and, so the result is each binary digit of hex
        //anded with each binary digit of 0xFFFF
        printValue("2 least sig. bytes", hex & 0xFFFF);

        //most significant 4 bytes
        //shift the value to the right, sign extended (fill in with the most significant bit)
        //this preserves negative numbers
        printValue("arith. shift right", hex >> 16);

        //try logical shift - no sign extensions (fill in with 0's)
        printValue("logical shift right", (unsigned int)hex >> 16);


        //The following are the same:
        // 256
        // 2 to the 8th
        // 1 << 8
        // 1 0000 0000  in binary

        //a note about div and mod, applying them to a negative value will produce
        //  a negative result
        //using div
        printValue("AB76 div 256", 0xAB76 / 256);

        //using mod
        printValue("AB76 mod 256", 0xAB76 % 256);

        //we can combine flags with bitwise or (or addition) to remember them in one value.
        int flags = IS_VALID | IS_WRITABLE;
        printValue("Flags variable", flags);

        //we can then check to see if a particular flag is set using bitwise and
        if((flags & IS_WRITABLE) != 0) {
                //here is the math:
                //writable  0100
                //flags     0101
                //        & 0100
                printf("WRITABLE flag set.\n");
        }
        else {
                printf("WRITABLE flag NOT set.\n");

        }

        //Read a number in hex
        unsigned int in;
        printf("Enter a number: ");
        scanf("%x", &in);
        printValue("Input as hex:", in);

        return EXIT_SUCCESS;
}