/*
 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;
}
