// Filename: bitArray.cxx
// Created by:  drose (20Jan06)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "bitArray.h"

TypeHandle BitArray::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: BitArray::is_zero
//       Access: Published
//  Description: Returns true if the entire bitmask is zero, false
//               otherwise.
////////////////////////////////////////////////////////////////////
bool BitArray::
is_zero() const {
  if (_highest_bits) {
    // If all the infinite highest bits are set, certainly the bitmask
    // is nonzero.
    return false;
  }

  // Start from the high end, since that's more likely to be nonzero.
  Array::const_reverse_iterator ai;
  for (ai = _array.rbegin(); ai != _array.rend(); ++ai) {
    if ((*ai) != 0) {
      return false;
    }
  }
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::set_range
//       Access: Published
//  Description: Sets the indicated range of bits on.
////////////////////////////////////////////////////////////////////
void BitArray::
set_range(int low_bit, int size) {
  int w = low_bit / num_bits_per_word;
  int b = low_bit % num_bits_per_word;

  if (w >= get_num_words() && _highest_bits) {
    // All the highest bits are already on.
    return;
  }
  if (b + size <= num_bits_per_word) {
    // The whole thing fits within one word of the array.
    ensure_has_word(w);
    _array[w].set_range(b, size);
    normalize();
    return;
  }

  ensure_has_word(w);
  int num_high_bits = num_bits_per_word - b;
  _array[w].set_range(b, num_high_bits);
  size -= num_high_bits;
  ++w;

  while (size > 0) {
    if (size <= num_bits_per_word) {
      // The remainder fits within one word of the array.
      ensure_has_word(w);
      _array[w].set_range(0, size);
      normalize();
      return;
    }

    // Keep going.
    ensure_has_word(w);
    _array[w] = MaskType::all_on();
    size -= num_bits_per_word;
    ++w;

    if (w >= get_num_words() && _highest_bits) {
      // All the highest bits are already on.
      normalize();
      return;
    }
  }
  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::clear_range
//       Access: Published
//  Description: Sets the indicated range of bits off.
////////////////////////////////////////////////////////////////////
void BitArray::
clear_range(int low_bit, int size) {
  int w = low_bit / num_bits_per_word;
  int b = low_bit % num_bits_per_word;

  if (w >= get_num_words() && !_highest_bits) {
    // All the highest bits are already off.
    return;
  }
  if (b + size <= num_bits_per_word) {
    // The whole thing fits within one word of the array.
    ensure_has_word(w);
    _array[w].clear_range(b, size);
    normalize();
    return;
  }

  ensure_has_word(w);
  int num_high_bits = num_bits_per_word - b;
  _array[w].clear_range(b, num_high_bits);
  size -= num_high_bits;
  ++w;

  while (size > 0) {
    if (size <= num_bits_per_word) {
      // The remainder fits within one word of the array.
      ensure_has_word(w);
      _array[w].clear_range(0, size);
      normalize();
      return;
    }

    // Keep going.
    ensure_has_word(w);
    _array[w] = MaskType::all_off();
    size -= num_bits_per_word;
    ++w;

    if (w >= get_num_words() && !_highest_bits) {
      // All the highest bits are already off.
      normalize();
      return;
    }
  }
  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::invert_in_place
//       Access: Published
//  Description: Inverts all the bits in the BitArray.  This is
//               equivalent to array = ~array.
////////////////////////////////////////////////////////////////////
void BitArray::
invert_in_place() {
  _highest_bits = !_highest_bits;
  Array::iterator ai;
  for (ai = _array.begin(); ai != _array.end(); ++ai) {
    (*ai) = ~(*ai);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::has_bits_in_common
//       Access: Published
//  Description: Returns true if this BitArray has any "one" bits in
//               common with the other one, false otherwise.
//
//               This is equivalent to (array & other) != 0, but may
//               be faster.
////////////////////////////////////////////////////////////////////
bool BitArray::
has_bits_in_common(const BitArray &other) const {
  if (_highest_bits && other._highest_bits) {
    // Yup, in fact we have an infinite number of bits in common.
    return true;
  }

  size_t num_common_words = min(_array.size(), other._array.size());

  // Consider the words that are on top of either array.
  if (other._array.size() < _array.size() && other._highest_bits) {
    // The other array has fewer actual words, and the top n words of
    // the other array are all ones.  We have bits in common if any of
    // our top n words are nonzero.
    Array::const_iterator ai;
    for (ai = _array.begin() + other._array.size(); 
         ai != _array.end(); 
         ++ai) {
      if (!(*ai).is_zero()) {
	return true;
      }
    }
    
  } else if (_array.size() < other._array.size() && _highest_bits) {
    // This array has fewer actual words, and the top n words of this
    // array are all ones.  We have bits in common if any of the the
    // other's top n words are nonzero.
    Array::const_iterator ai;
    for (ai = other._array.begin() + _array.size(); 
         ai != other._array.end(); 
         ++ai) {
      if (!(*ai).is_zero()) {
	return true;
      }
    }
  }

  // Consider the words that both arrays have in common.
  for (size_t i = 0; i < num_common_words; ++i) {
    if (!(_array[i] & other._array[i]).is_zero()) {
      return true;
    }
  }

  // Nope, nothing.
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::output
//       Access: Published
//  Description: Writes the BitArray out as a hex number.  For a
//               BitArray, this is always the same as output_hex();
//               it's too confusing for the output format to change
//               back and forth at runtime.
////////////////////////////////////////////////////////////////////
void BitArray::
output(ostream &out) const {
  output_hex(out);
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::output_binary
//       Access: Published
//  Description: Writes the BitArray out as a binary number, with
//               spaces every four bits.
////////////////////////////////////////////////////////////////////
void BitArray::
output_binary(ostream &out, int spaces_every) const {
  if (_highest_bits) {
    out << "...1 ";
  }
  int num_bits = max(get_num_bits(), spaces_every);
  for (int i = num_bits - 1; i >= 0; i--) {
    if (spaces_every != 0 && ((i % spaces_every) == spaces_every - 1)) {
      out << ' ';
    }
    out << (get_bit(i) ? '1' : '0');
  }
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::output_hex
//       Access: Published
//  Description: Writes the BitArray out as a hexadecimal number, with
//               spaces every four digits.
////////////////////////////////////////////////////////////////////
void BitArray::
output_hex(ostream &out, int spaces_every) const {
  int num_bits = get_num_bits();
  int num_digits = max((num_bits + 3) / 4, spaces_every);

  if (_highest_bits) {
    out << "...f ";
  }

  for (int i = num_digits - 1; i >= 0; i--) {
    WordType digit = extract(i * 4, 4);
    if (spaces_every != 0 && ((i % spaces_every) == spaces_every - 1)) {
      out << ' ';
    }
    if (digit > 9) {
      out << (char)(digit - 10 + 'a');
    } else {
      out << (char)(digit + '0');
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::write
//       Access: Published
//  Description: Writes the BitArray out as a binary or a hex number,
//               according to the number of bits.
////////////////////////////////////////////////////////////////////
void BitArray::
write(ostream &out, int indent_level) const {
  indent(out, indent_level) << *this << "\n";
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::compare_to
//       Access: Published
//  Description: Returns a number less than zero if this BitArray sorts
//               before the indicated other BitArray, greater than zero
//               if it sorts after, or 0 if they are equivalent.  This
//               is based on the same ordering defined by operator <.
////////////////////////////////////////////////////////////////////
int BitArray::
compare_to(const BitArray &other) const {
  if (_highest_bits != other._highest_bits) {
    return _highest_bits ? 1 : -1;
  }

  int num_words = max(get_num_words(), other.get_num_words());

  // Compare from highest-order to lowest-order word.
  for (int i = num_words - 1; i >= 0; --i) {
    int compare = get_word(i).compare_to(other.get_word(i));
    if (compare != 0) {
      return compare;
    }
  }

  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::operator &=
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
void BitArray::
operator &= (const BitArray &other) {
  size_t num_common_words = min(_array.size(), other._array.size());

  // Consider the words that are on top of either array.
  if (other._array.size() < _array.size() && !other._highest_bits) {
    // The other array has fewer actual words, and the top n words of
    // the other array are all zeroes.  "mask off" the top n words of
    // this array.
    _array.erase(_array.begin() + other._array.size(), _array.end());

  } else if (_array.size() < other._array.size() && _highest_bits) {
    // This array has fewer actual words, and the top n words of this
    // array are all ones.  "mask on" the top n words of the other
    // array.
    Array::const_iterator ai;
    for (ai = other._array.begin() + _array.size(); 
         ai != other._array.end(); 
         ++ai) {
      _array.push_back(*ai);
    }
  }

  // Consider the words that both arrays have in common.
  for (size_t i = 0; i < num_common_words; ++i) {
    _array[i] &= other._array[i];
  }

  _highest_bits &= other._highest_bits;
  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::operator |=
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
void BitArray::
operator |= (const BitArray &other) {
  size_t num_common_words = min(_array.size(), other._array.size());

  // Consider the words that are on top of either array.
  if (other._array.size() < _array.size() && other._highest_bits) {
    // The other array has fewer actual words, and the top n words of
    // the other array are all ones.  The top n words of this array
    // become ones too (which means we can drop them out).
    _array.erase(_array.begin() + other._array.size(), _array.end());

  } else if (_array.size() < other._array.size() && !_highest_bits) {
    // This array has fewer actual words, and the top n words of this
    // array are all zeros.  Copy in the top n words of the other
    // array.
    Array::const_iterator ai;
    for (ai = other._array.begin() + _array.size(); 
         ai != other._array.end(); 
         ++ai) {
      _array.push_back(*ai);
    }
  }

  // Consider the words that both arrays have in common.
  for (size_t i = 0; i < num_common_words; ++i) {
    _array[i] |= other._array[i];
  }

  _highest_bits |= other._highest_bits;
  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::operator ^=
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
void BitArray::
operator ^= (const BitArray &other) {
  size_t num_common_words = min(_array.size(), other._array.size());

  // Consider the words that are on top of either array.
  if (other._array.size() < _array.size() && other._highest_bits) {
    // The other array has fewer actual words, and the top n words of
    // the other array are all ones.  The top n words of this array
    // get inverted.
    Array::iterator ai;
    for (ai = _array.begin() + other._array.size(); 
         ai != _array.end(); 
         ++ai) {
      (*ai).invert_in_place();
    }

  } else if (_array.size() < other._array.size()) {
    if (!_highest_bits) {
      // This array has fewer actual words, and the top n words of this
      // array are all zeros.  Copy in the top n words of the other
      // array.
      Array::const_iterator ai;
      for (ai = other._array.begin() + _array.size(); 
           ai != other._array.end(); 
           ++ai) {
        _array.push_back(*ai);
      }
    } else {
      // This array has fewer actual words, and the top n words of this
      // array are all ones.  Copy in the top n words of the other
      // array, inverted.
      Array::const_iterator ai;
      for (ai = other._array.begin() + _array.size(); 
           ai != other._array.end(); 
           ++ai) {
        _array.push_back(~(*ai));
      }
    }
  }

  // Consider the words that both arrays have in common.
  for (size_t i = 0; i < num_common_words; ++i) {
    _array[i] ^= other._array[i];
  }

  _highest_bits ^= other._highest_bits;
  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::operator <<=
//       Access: Published
//  Description: Logical left shift.  The rightmost bits are filled in
//               with zeroes.  Since this is an infinite bit array,
//               none of the bits on the left are lost.
////////////////////////////////////////////////////////////////////
void BitArray::
operator <<= (int shift) {
  if (shift == 0 || _array.empty()) {
    return;
  }
  if (shift < 0) {
    operator >>= (-shift);
    return;
  }

  int w = shift / num_bits_per_word;
  int b = shift % num_bits_per_word;

  if (b == 0) {
    // Easy case--word-at-a-time.
    Array new_array;
    new_array.reserve(_array.size() + w);
    for (int i = 0; i < w; ++i) {
      new_array.push_back(MaskType::all_off());
    }
    Array::const_iterator ai;
    for (ai = _array.begin(); ai != _array.end(); ++ai) {
      new_array.push_back(*ai);
    }
    _array.swap(new_array);

  } else {
    // Harder case--we have to shuffle bits between words.
    Array new_array;
    new_array.reserve(_array.size() + w + 1);
    for (int i = 0; i < w; ++i) {
      new_array.push_back(MaskType::all_off());
    }

    int downshift_count = num_bits_per_word - b;
    MaskType lower_mask = MaskType::lower_on(downshift_count);
    MaskType upper_mask = ~lower_mask;

    Array::const_iterator ai = _array.begin();
    nassertv(ai != _array.end());
    MaskType next_bits = ((*ai) & upper_mask) >> downshift_count;
    new_array.push_back(((*ai) & lower_mask) << b);
    ++ai;
    while (ai != _array.end()) {
      new_array.push_back((((*ai) & lower_mask) << b) | next_bits);
      next_bits = ((*ai) & upper_mask) >> downshift_count;
      ++ai;
    }

    // Finally, the top n bits.
    if (_highest_bits) {
      next_bits |= ~MaskType::lower_on(b);
    }
    new_array.push_back(next_bits);
    _array.swap(new_array);
  }

  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::operator >>=
//       Access: Published
//  Description: Logical right shift.  The rightmost bits are lost.
//               Since this is an infinite bit array, there is no
//               question of sign extension; there is no need to
//               synthesize bits on the left.
////////////////////////////////////////////////////////////////////
void BitArray::
operator >>= (int shift) {
  if (shift == 0 || _array.empty()) {
    return;
  }
  if (shift < 0) {
    operator <<= (-shift);
    return;
  }

  int w = shift / num_bits_per_word;
  int b = shift % num_bits_per_word;

  if (w >= (int)_array.size()) {
    // Trivial case--shift to nothing.
    _array.clear();
    return;
  }

  if (b == 0) {
    // Easy case--word-at-a-time.
    Array new_array;
    new_array.reserve(_array.size() - w);
    Array::const_iterator ai;
    for (ai = _array.begin() + w; ai != _array.end(); ++ai) {
      new_array.push_back(*ai);
    }
    _array.swap(new_array);

  } else {
    // Harder case--we have to shuffle bits between words.
    Array new_array;
    new_array.reserve(_array.size() - w);

    int upshift_count = num_bits_per_word - b;
    MaskType lower_mask = MaskType::lower_on(b);
    MaskType upper_mask = ~lower_mask;

    Array::const_iterator ai = _array.begin() + w;
    nassertv(ai < _array.end());
    MaskType next_bits = ((*ai) & upper_mask) >> b;
    
    ++ai;
    while (ai != _array.end()) {
      new_array.push_back((((*ai) & lower_mask) << upshift_count) | next_bits);
      next_bits = ((*ai) & upper_mask) >> b;
      ++ai;
    }

    // Finally, the top n bits.
    if (_highest_bits) {
      next_bits |= ~MaskType::lower_on(upshift_count);
    }
    new_array.push_back(next_bits);
    _array.swap(new_array);
  }

  normalize();
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::generate_hash
//       Access: Public
//  Description: Adds the bitmask to the indicated hash generator.
////////////////////////////////////////////////////////////////////
void BitArray::
generate_hash(ChecksumHashGenerator &hashgen) const {
  hashgen.add_int(_highest_bits);
  Array::const_iterator ai;
  for (ai = _array.begin(); ai != _array.end(); ++ai) {
    hashgen.add_int((*ai).get_word());
  }
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::ensure_has_word
//       Access: Private
//  Description: Ensures that at least word n has been allocated into
//               the array.
////////////////////////////////////////////////////////////////////
void BitArray::
ensure_has_word(int n) {
  if (_highest_bits) {
    while (n >= (int)_array.size()) {
      _array.push_back(MaskType::all_on());
    }
  } else {
    while (n >= (int)_array.size()) {
      _array.push_back(MaskType::all_off());
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: BitArray::normalize
//       Access: Private
//  Description: Ensures that the array is the smallest array that
//               represents this same value, by removing the topmost
//               words that are all bits off (or on).
////////////////////////////////////////////////////////////////////
void BitArray::
normalize() {
  if (_highest_bits) {
    while (!_array.empty() && _array.back() == MaskType::all_on()) {
      _array.pop_back();
    }
  } else {
    while (!_array.empty() && _array.back().is_zero()) {
      _array.pop_back();
    }
  }
}
