struct Bit_Array { // we need to keep track of our allocator separately.
  u64* data;
  s32 count;
  s32 data_count; // do we even need this??
  
  bool operator[](s64 index) {
#if ARRAY_ENABLE_BOUNDS_CHECKING
    if (index < 0 || index >= count) { debug_break(); } // index out of bounds
#endif
    return (bool)(data[index >> 6] & (1ull << (index & 63)));
  }
}; // 16 B.

// bool contains (Bit_Array* src, Bit_Array* query) {
//   Assert(src && query);
//   Assert(src->data_count == query->data_count);
//   for (s32 i = 0; i < src->data_count; i += 1) {
//     if (src->data[i] & query->data[i] != query->data[i]) {
//       return false;
//     }
//   }
  
//   return true;
// }

bool contains_single_ascii (Bit_Array* src, Bit_Array* query) {
  Assert(src && query);
  Assert(src->data_count == query->data_count);

  for (s32 i = 0; i < src->data_count; i += 1) {
    // if query u64 is non-zero, we should get a non-zero result:
    if (query->data[i] && (src->data[i] & query->data[i]) == 0) {
      return false;
    }
  }
  
  return true;
}

void bit_array_initialize (Bit_Array* ba, s64 count) {
  s64 u64_count = (count + 63) >> 6;
  
  ba->data = NewArray<u64>(u64_count);
  ba->data_count = u64_count;
  ba->count = count;
}

// Returns a bit array of the given size:
Bit_Array new_bit_array (s64 count) {
  Bit_Array ba;
  bit_array_initialize(&ba, count);
  return ba;
}

void bit_array_delete (Bit_Array* ba) {
  internal_free(ba->data);
  ba->count = 0;
  ba->data_count = 0;
}

void set_bit (Bit_Array* ba, s64 i) {
  Assert(i < ba->count);
  ba->data[i >> 6] |= (1ull << (i & 63));
}

void clear_bit (Bit_Array* ba, s64 i) {
  Assert(i < ba->count);
  ba->data[i >> 6] &= ~(1ull << (i & 63));
}

void toggle_bit (Bit_Array* ba, s64 i) {
  Assert(i < ba->count);
  ba->data[i >> 6] ^= ~(1ull << (i & 63));
}

// See "Conditionally set or clear bits without branching" at http://graphics.stanford.edu/~seander/bithacks.html
force_inline s64 set_bits (s64 w, s64 m, bool b) {
  return (w & ~m) | ((-(s64)b) & m);
}

void set_bit_to (Bit_Array* ba, s64 i, bool value) {
  Assert(i < ba->count);
  ba->data[i >> 6] = set_bits(ba->data[i >> 6], 1ull << (i & 63), value);
  Assert((*ba)[i] == value);
}

void clear_all_bits (Bit_Array* ba) {
  memset(ba->data, 0, ba->data_count * sizeof(u64));
}

void set_all_bits (Bit_Array* ba) {
  memset(ba->data, 0xFF, ba->data_count * sizeof(u64));
}

void toggle_all_bits (Bit_Array* ba) {
  for (s64 s = 0; s < ba->data_count; s += 1) {
    ba->data[s] ^= U64_MAX;
  }
}

// Using Bit_Array.jai as a reference:
/*
struct Bit_Array { // 40 B
  ArrayView<u64> slots;
  s64 count; // bit count;
  Allocator allocator;
  
  bool operator[](s64 index) {
#if ARRAY_ENABLE_BOUNDS_CHECKING
    if (index < 0 || index >= count) { debug_break(); } // index out of bounds
#endif
    return (bool)(slots[index >> 6] & (1ull << (index & 63)));
  }
};

void bit_array_initialize (Bit_Array* ba, s64 count) {
  if (ba->allocator.proc == nullptr) ba->allocator = context_allocator();
  
  s64 u64_count = (count + 63) >> 6;
  
  ba->slots = ArrayView<u64>(ba->allocator, u64_count);
  ba->count = count;
}

// Returns a bit array of the given size:
Bit_Array new_bit_array (s64 count) {
  Bit_Array ba;
  bit_array_initialize(&ba, count);
  return ba;
}

void bit_array_delete (Bit_Array* ba) {
  push_allocator(ba->allocator);
  array_free(ba->slots);
  ba->slots = {};
  ba->count = 0;
}

// #TODO: set_bit, clear_bit, toggle_bit, etc.
void set_bit (Bit_Array* ba, s64 i) {
  Assert(i < ba->count);
  ba->slots[i >> 6] |= (1ull << (i & 63));
}

void clear_bit (Bit_Array* ba, s64 i) {
  Assert(i < ba->count);
  ba->slots[i >> 6] &= ~(1ull << (i & 63));
}

void toggle_bit (Bit_Array* ba, s64 i) {
  Assert(i < ba->count);
  ba->slots[i >> 6] ^= ~(1ull << (i & 63));
}


void set_bit_to (Bit_Array* ba, s64 i, bool value) {
  Assert(i < ba->count);
  ba->slots[i >> 6] = set_bits(ba->slots[i >> 6], 1ull << (i & 63), value);
  Assert((*ba)[i] == value);
}

void clear_all_bits (Bit_Array* ba) {
  array_zero(ba->slots);
}

void set_all_bits (Bit_Array* ba) {
  memset(ba->slots.data, 0xFF, ba->slots.count * sizeof(u64));
}

void toggle_all_bits (Bit_Array* ba) {
  for_each(s, ba->slots) {
    ba->slots[s] ^= U64_MAX;
  }
}
*/