clay/docs/14_bit_manipulation.md
Claude faea55a9b9
Add comprehensive C learning materials with specialized topic tutorials
Complete learning guide covering ALL C concepts from beginner to advanced:

Main Guide (LEARNING_C_WITH_CLAY.md):
- Added Chapters 3-5: Operators, Control Flow, Loops
- Enhanced Chapters 6-13: Functions, Pointers, Structs, Arrays, Strings,
  Type Casting, Storage Classes, Recursion
- All chapters include extensive Clay library examples
- Progressive difficulty from basics to advanced topics

Specialized Tutorial Files (docs/):
- 14_bit_manipulation.md: Comprehensive bit operations guide
  * Bitwise operators, masks, flags
  * Counting bits, power of 2 operations
  * Practical applications with Clay examples

- 21_standard_library.md: C Standard Library overview
  * stdio.h, stdlib.h, string.h, math.h
  * Clay's zero-dependency approach
  * Custom implementations vs stdlib

- 22_file_io.md: Complete File I/O guide
  * Text and binary file operations
  * Configuration files for Clay apps
  * Error handling and best practices

- 23_command_line_arguments.md: CLI argument parsing
  * argc/argv basics
  * Flag parsing and subcommands
  * Clay application configuration examples

- README_C_TUTORIALS.md: Master index and learning guide
  * Complete chapter overview
  * Learning path recommendations
  * Progress tracking checklist
  * Prerequisites and setup instructions

Features:
- 24 comprehensive chapters covering all C concepts
- 100+ code examples with detailed explanations
- Real-world patterns from Clay library throughout
- Practice exercises for each chapter
- Modular organization for easy navigation
- Zero-dependency programming concepts
- Professional C development practices

Total content: ~70,000+ words of detailed tutorials
2025-11-13 20:32:16 +00:00

12 KiB

Chapter 14: Bit Manipulation in C

Complete Guide with Clay Library Examples


14.1 Binary Number System

Understanding binary is essential for bit manipulation:

// Decimal vs Binary
0 = 0000
1 = 0001
2 = 0010
3 = 0011
4 = 0100
5 = 0101
6 = 0110
7 = 0111
8 = 1000

Converting Binary to Decimal

#include <stdio.h>

int binaryToDecimal(int binary) {
    int decimal = 0, base = 1;
    while (binary > 0) {
        int lastDigit = binary % 10;
        decimal += lastDigit * base;
        base *= 2;
        binary /= 10;
    }
    return decimal;
}

int main(void) {
    printf("%d\n", binaryToDecimal(1010));  // 10
    printf("%d\n", binaryToDecimal(1111));  // 15
    return 0;
}

14.2 Bitwise Operators

AND Operator (&)

// Both bits must be 1
int a = 5;   // 0101
int b = 3;   // 0011
int c = a & b;  // 0001 = 1

// Check if number is even
int isEven(int n) {
    return (n & 1) == 0;  // Check if last bit is 0
}

Clay Example:

// Check if corner radius flag is set
typedef enum {
    CLAY_CORNER_RADIUS_TOP_LEFT = 1 << 0,      // 0001
    CLAY_CORNER_RADIUS_TOP_RIGHT = 1 << 1,     // 0010
    CLAY_CORNER_RADIUS_BOTTOM_LEFT = 1 << 2,   // 0100
    CLAY_CORNER_RADIUS_BOTTOM_RIGHT = 1 << 3   // 1000
} Clay_CornerRadiusSet;

// Check if top-left corner has radius
if (flags & CLAY_CORNER_RADIUS_TOP_LEFT) {
    // Top left corner has radius
}

OR Operator (|)

// At least one bit must be 1
int a = 5;   // 0101
int b = 3;   // 0011
int c = a | b;  // 0111 = 7

// Set specific bit
int setBit(int num, int pos) {
    return num | (1 << pos);
}

Clay Example:

// Combine multiple corner radius flags
int flags = CLAY_CORNER_RADIUS_TOP_LEFT |
            CLAY_CORNER_RADIUS_TOP_RIGHT;  // 0011 = both top corners

// Add another flag
flags |= CLAY_CORNER_RADIUS_BOTTOM_LEFT;  // Now 0111 = three corners

XOR Operator (^)

// Bits must be different
int a = 5;   // 0101
int b = 3;   // 0011
int c = a ^ b;  // 0110 = 6

// Swap two numbers without temp variable
void swap(int *a, int *b) {
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}

NOT Operator (~)

// Inverts all bits
int a = 5;      // 00000101
int b = ~a;     // 11111010 (in 8-bit)

// Clear specific bit
int clearBit(int num, int pos) {
    return num & ~(1 << pos);
}

Left Shift (<<)

// Multiply by 2^n
int a = 5;      // 0101
int b = a << 1; // 1010 = 10 (multiply by 2)
int c = a << 2; // 10100 = 20 (multiply by 4)

// Fast power of 2
int powerOf2(int n) {
    return 1 << n;  // 2^n
}

Clay Example:

// Define bit flags with left shift
typedef enum {
    CLAY_CORNER_RADIUS_TOP_LEFT = 1 << 0,      // 1
    CLAY_CORNER_RADIUS_TOP_RIGHT = 1 << 1,     // 2
    CLAY_CORNER_RADIUS_BOTTOM_LEFT = 1 << 2,   // 4
    CLAY_CORNER_RADIUS_BOTTOM_RIGHT = 1 << 3   // 8
} Clay_CornerRadiusSet;

Right Shift (>>)

// Divide by 2^n
int a = 20;     // 10100
int b = a >> 1; // 01010 = 10 (divide by 2)
int c = a >> 2; // 00101 = 5 (divide by 4)

// Extract specific bits
int getBits(int num, int start, int count) {
    return (num >> start) & ((1 << count) - 1);
}

14.3 Common Bit Operations

Check if Bit is Set

int isBitSet(int num, int pos) {
    return (num & (1 << pos)) != 0;
}

int main(void) {
    int num = 5;  // 0101
    printf("%d\n", isBitSet(num, 0));  // 1 (bit 0 is set)
    printf("%d\n", isBitSet(num, 1));  // 0 (bit 1 is not set)
    printf("%d\n", isBitSet(num, 2));  // 1 (bit 2 is set)
    return 0;
}

Set a Bit

int setBit(int num, int pos) {
    return num | (1 << pos);
}

int main(void) {
    int num = 5;  // 0101
    num = setBit(num, 1);  // 0111 = 7
    printf("%d\n", num);
    return 0;
}

Clear a Bit

int clearBit(int num, int pos) {
    return num & ~(1 << pos);
}

int main(void) {
    int num = 7;  // 0111
    num = clearBit(num, 1);  // 0101 = 5
    printf("%d\n", num);
    return 0;
}

Toggle a Bit

int toggleBit(int num, int pos) {
    return num ^ (1 << pos);
}

int main(void) {
    int num = 5;  // 0101
    num = toggleBit(num, 1);  // 0111 = 7
    num = toggleBit(num, 1);  // 0101 = 5 (back to original)
    printf("%d\n", num);
    return 0;
}

14.4 Bit Masks

Masks select specific bits:

// Extract RGB components from 32-bit color
typedef struct {
    uint32_t color;  // 0xAARRGGBB
} Color;

uint8_t getRed(Color c) {
    return (c.color >> 16) & 0xFF;  // Mask: 0x00FF0000
}

uint8_t getGreen(Color c) {
    return (c.color >> 8) & 0xFF;   // Mask: 0x0000FF00
}

uint8_t getBlue(Color c) {
    return c.color & 0xFF;           // Mask: 0x000000FF
}

uint8_t getAlpha(Color c) {
    return (c.color >> 24) & 0xFF;  // Mask: 0xFF000000
}

uint32_t makeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
    return ((uint32_t)a << 24) |
           ((uint32_t)r << 16) |
           ((uint32_t)g << 8) |
            (uint32_t)b;
}

Clay Example (conceptual):

// Clay uses float colors, but shows similar concepts
typedef struct {
    float r, g, b, a;
} Clay_Color;

// If Clay used packed colors:
uint32_t packColor(Clay_Color c) {
    uint8_t r = (uint8_t)(c.r * 255);
    uint8_t g = (uint8_t)(c.g * 255);
    uint8_t b = (uint8_t)(c.b * 255);
    uint8_t a = (uint8_t)(c.a * 255);
    return (a << 24) | (r << 16) | (g << 8) | b;
}

14.5 Bit Fields in Structs

Pack multiple values efficiently:

struct Flags {
    unsigned int isVisible : 1;   // 1 bit
    unsigned int isEnabled : 1;   // 1 bit
    unsigned int priority : 3;    // 3 bits (0-7)
    unsigned int id : 11;         // 11 bits
};  // Total: 16 bits = 2 bytes

int main(void) {
    struct Flags f = {0};
    f.isVisible = 1;
    f.isEnabled = 1;
    f.priority = 5;
    f.id = 1024;

    printf("Size: %zu bytes\n", sizeof(f));  // 2 or 4 (compiler dependent)
    printf("Priority: %u\n", f.priority);
    return 0;
}

Clay Example (conceptual):

// Clay could use bit fields for layout flags
typedef struct {
    uint32_t layoutDirection : 1;     // 0 = horizontal, 1 = vertical
    uint32_t wrapChildren : 1;        // Wrap to next line
    uint32_t alignmentX : 2;          // 0-3 for alignment options
    uint32_t alignmentY : 2;
    uint32_t reserved : 26;           // Future use
} Clay_LayoutFlags;

14.6 Counting Set Bits

Method 1: Loop

int countSetBits(int n) {
    int count = 0;
    while (n > 0) {
        count += n & 1;  // Add last bit
        n >>= 1;         // Shift right
    }
    return count;
}

Method 2: Brian Kernighan's Algorithm

int countSetBits(int n) {
    int count = 0;
    while (n > 0) {
        n &= (n - 1);  // Clear rightmost set bit
        count++;
    }
    return count;
}

// Example: n = 12 (1100)
// 1100 & 1011 = 1000 (count = 1)
// 1000 & 0111 = 0000 (count = 2)

Method 3: Lookup Table

// Precomputed table for 4-bit numbers
int table[16] = {
    0, 1, 1, 2, 1, 2, 2, 3,
    1, 2, 2, 3, 2, 3, 3, 4
};

int countSetBits(int n) {
    int count = 0;
    while (n > 0) {
        count += table[n & 0xF];  // Count 4 bits at a time
        n >>= 4;
    }
    return count;
}

14.7 Power of Two

Check if Power of 2

int isPowerOfTwo(int n) {
    return n > 0 && (n & (n - 1)) == 0;
}

// Examples:
// 4 (100) & 3 (011) = 0 → true
// 5 (101) & 4 (100) = 4 → false
// 8 (1000) & 7 (0111) = 0 → true

Next Power of 2

int nextPowerOfTwo(int n) {
    if (n <= 0) return 1;
    n--;
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;
    return n + 1;
}

int main(void) {
    printf("%d\n", nextPowerOfTwo(5));   // 8
    printf("%d\n", nextPowerOfTwo(17));  // 32
    return 0;
}

14.8 Swap Values

Without Temporary Variable

void swap(int *a, int *b) {
    if (a != b) {  // Must check for same address
        *a ^= *b;
        *b ^= *a;
        *a ^= *b;
    }
}

Swap Nibbles (4-bit)

int swapNibbles(int n) {
    return ((n & 0x0F) << 4) | ((n & 0xF0) >> 4);
}

// Example: 0x5A → 0xA5

14.9 Reverse Bits

uint32_t reverseBits(uint32_t n) {
    uint32_t result = 0;
    for (int i = 0; i < 32; i++) {
        result <<= 1;           // Shift result left
        result |= (n & 1);      // Add rightmost bit of n
        n >>= 1;                // Shift n right
    }
    return result;
}

14.10 Parity

Check if number of set bits is even or odd:

int getParity(int n) {
    int parity = 0;
    while (n > 0) {
        parity ^= (n & 1);
        n >>= 1;
    }
    return parity;  // 0 = even, 1 = odd
}

14.11 Gray Code

Convert between binary and Gray code:

// Binary to Gray
int binaryToGray(int n) {
    return n ^ (n >> 1);
}

// Gray to Binary
int grayToBinary(int n) {
    int binary = 0;
    while (n > 0) {
        binary ^= n;
        n >>= 1;
    }
    return binary;
}

14.12 Position of Rightmost Set Bit

int rightmostSetBit(int n) {
    return n & ~(n - 1);
}

// Example: 12 (1100)
// Returns: 4 (0100) - position of rightmost 1

14.13 Toggle All Bits After Rightmost Set Bit

int toggleAfterRightmost(int n) {
    return n ^ (n - 1);
}

// Example: 12 (1100)
// Returns: 15 (1111)

14.14 Extract and Set Bit Ranges

// Extract bits from position p to p+n
int extractBits(int num, int p, int n) {
    return (num >> p) & ((1 << n) - 1);
}

// Set bits from position p to p+n
int setBits(int num, int p, int n, int value) {
    int mask = ((1 << n) - 1) << p;
    return (num & ~mask) | ((value << p) & mask);
}

14.15 Practical Applications in Clay

Bit Flags for UI State

typedef enum {
    CLAY_ELEMENT_VISIBLE    = 1 << 0,  // 0x01
    CLAY_ELEMENT_ENABLED    = 1 << 1,  // 0x02
    CLAY_ELEMENT_FOCUSED    = 1 << 2,  // 0x04
    CLAY_ELEMENT_HOVERED    = 1 << 3,  // 0x08
    CLAY_ELEMENT_PRESSED    = 1 << 4,  // 0x10
    CLAY_ELEMENT_SELECTED   = 1 << 5,  // 0x20
    CLAY_ELEMENT_DISABLED   = 1 << 6,  // 0x40
    CLAY_ELEMENT_HIDDEN     = 1 << 7   // 0x80
} Clay_ElementFlags;

typedef struct {
    uint32_t flags;
} Clay_Element;

// Check state
int isVisible(Clay_Element *el) {
    return (el->flags & CLAY_ELEMENT_VISIBLE) != 0;
}

// Set state
void setVisible(Clay_Element *el, int visible) {
    if (visible) {
        el->flags |= CLAY_ELEMENT_VISIBLE;  // Set bit
    } else {
        el->flags &= ~CLAY_ELEMENT_VISIBLE; // Clear bit
    }
}

// Toggle state
void toggleVisible(Clay_Element *el) {
    el->flags ^= CLAY_ELEMENT_VISIBLE;
}

// Multiple states at once
void setInteractive(Clay_Element *el) {
    el->flags |= (CLAY_ELEMENT_VISIBLE |
                  CLAY_ELEMENT_ENABLED);
}

Optimized Hash Functions

// Clay uses hashing for element lookup
uint32_t hashString(const char *str, size_t length) {
    uint32_t hash = 0;
    for (size_t i = 0; i < length; i++) {
        hash = (hash << 5) - hash + str[i];  // hash * 31 + c
        // Using bit shift instead of multiply
    }
    return hash;
}

Memory Alignment

// Align size to power of 2
size_t alignSize(size_t size, size_t alignment) {
    return (size + alignment - 1) & ~(alignment - 1);
}

// Example: align to 16 bytes
// size = 25
// (25 + 15) & ~15 = 40 & 0xFFFFFFF0 = 32

14.16 Performance Tips

Use bit operations for:

  • Powers of 2 multiplication/division
  • Flag checks (faster than booleans)
  • Color packing/unpacking
  • Hash functions
  • Alignment calculations

Avoid for:

  • Regular arithmetic (compiler optimizes)
  • Readability-critical code
  • Floating-point operations

14.17 Key Concepts Learned

  • Bitwise operators: &, |, ^, ~, <<, >>
  • Bit manipulation: set, clear, toggle, check
  • Bit masks and extraction
  • Bit fields in structs
  • Counting set bits
  • Power of 2 operations
  • Practical applications in Clay
  • Performance considerations

Practice Exercises

  1. Write a function to count trailing zeros in a number
  2. Implement bitwise rotation (rotate left/right)
  3. Find the only non-duplicate number in an array (all others appear twice)
  4. Swap all odd and even bits in a number
  5. Add two numbers without using + operator
  6. Implement a bit vector data structure
  7. Create a compact permission system using bit flags
  8. Write efficient popcount for 64-bit numbers