/*

    bitmask.cpp

    variable-size bit masks

    6.6.2001

    Copyright (C) 2000-2001 by Josef Pelikan, MFF UK Prague
        http://sun3.ms.mff.cuni.cz/~pepca/

    VUCAKO Bench is Open Source software (see http://www.opensource.org/
    for description what Open Source is); you may distributed it and/or
    modify it under the terms of GNU General Public License as published
    by the Free Software Foundation; either version 2, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    A copy of the GNU General Public License is available as
    `/usr/doc/copyright/GPL' in the Debian GNU/Linux distribution or on
    the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You can
    also obtain it by writing to the Free Software Foundation, Inc., 59
    Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "pch.h"

//-----------------------------------------------------------
//  CBitMask object:

CBitMask::CBitMask ()
{
    mask = NULL;
    allocMask = min = max = 0;
}

CBitMask::CBitMask ( const CBitMask *b )
{
    mask = NULL;
    assign(b);
}

CBitMask *CBitMask::clone () const
    // clones the actual object instance
{
    return new CBitMask(this);
}

void CBitMask::assign ( const CBitMask *b )
{
    empty();
    if ( b ) {
        min = b->min;
        max = b->max;
        if ( (allocMask = b->allocMask) ) {
            mask = (BYTE*)VIRTUAL_ALLOC(allocMask);
            memcpy(mask,b->mask,allocMask);
            }
        }
}

void CBitMask::empty ()
    // (re)initializes the mask
{
    if ( mask ) {
        VIRTUAL_FREE(mask,allocMask);
        mask = NULL;
        }
    allocMask = min = max = 0;
}

CBitMask::~CBitMask ()
{
    empty();
}

//-----------------------------------------------------------
//  access:

void CBitMask::assert ( int value )
    // prepares the mask array to hold the given value
{
    int add;
    BYTE *newMask;
    if ( value < min ) {
        add = (min - value + 7) >> 3;
        newMask = (BYTE*)VIRTUAL_ALLOC(allocMask+add);
        memset(newMask,0,add);
        memcpy(newMask+add,mask,allocMask);
        VIRTUAL_FREE(mask,allocMask);
        mask = newMask;
        allocMask += add;
        min -= add << 3;
        return;
        }
    if ( value >= max ) {
        add = (value - min + 8) >> 3;
        newMask = (BYTE*)VIRTUAL_ALLOC(add);
        memcpy(newMask,mask,allocMask);
        memset(newMask+allocMask,0,add-allocMask);
        VIRTUAL_FREE(mask,allocMask);
        mask = newMask;
        allocMask = add;
        max = min + (add << 3);
        }
}

void CBitMask::set ( int value, bool flag )
    // sets one flag value
{
    if ( flag ) {
        assert(value);
        value -= min;
        mask[value>>3] |= 1 << (value & 7);
        return;
        }
    if ( value < min || value >= max ) return;
    value -= min;
    mask[value>>3] &= ~(1 << (value & 7));
}

void CBitMask::on ( int value )
{
    assert(value);
    value -= min;
    mask[value>>3] |= 1 << (value & 7);
}

void CBitMask::off ( int value )
{
    if ( value < min || value >= max ) return;
    value -= min;
    mask[value>>3] &= ~(1 << (value & 7));
}

void CBitMask::range ( int from, int len, bool flag )
    // sets the given range
{
    if ( flag ) {
        assert(from);
        assert(from+len-1);
        }
    else {
        if ( from < min ) {
            len -= min - from;
            from = min;
            }
        if ( from + len > max )
            len = max - from;
        }
    while ( len-- > 0 ) setUnsafe(from++,flag);
}

int CBitMask::card () const
    // bit-mask cardinality
{
    if ( !allocMask ) return 0;
    int count = 0;
    BYTE *pm = mask;
    for ( int i = 0; i++ < allocMask; ) {
        unsigned act = *pm++;
        while ( act ) {
            count += (act & 1);
            act >>= 1;
            }
        }
    return count;
}

int CBitMask::getFirst () const
    // returns the first bit or MaskEnd
{
    if ( !mask ) return MaskEnd;
    return getNext(min-1);
}

int CBitMask::getNext ( int i ) const
    // returns the next bit or MaskEnd
{
    int ii;
    BYTE b;
    while ( ++i < max )
        if ( (b = mask[(ii = i - min) >> 3]) ) {
            if ( b & (1<<(ii & 7)) ) return i;
            }
        else {
            i += 8 - (ii & 7);
            ii >>= 3;
            while ( i < max && !mask[++ii] ) i += 8;
            i--;
            }
    return MaskEnd;
}

int CBitMask::getLast () const
{
    if ( !mask ) return MaskEnd;
    int ii = ((max - min) >> 3) - 1;
    while ( ii >= 0 && !mask[ii] ) ii--;
    if ( ii < 0 ) return MaskEnd;
    BYTE b = mask[ii];
    ii = (ii << 3) + min + 7;
    while ( !(b & 0x80) ) {
        b += b;
        ii--;
        }
    return ii;
}

//-----------------------------------------------------------
//  set operations:

void CBitMask::or ( const CBitMask &m )
{
    int i = m.getFirst();
    if ( i == MaskEnd ) return;
    on(i);
    assert(m.max-1);
    while( (i = m.getNext(i)) != MaskEnd )
        setUnsafe(i,true);
}

void CBitMask::sub ( const CBitMask &m )
{
    int i = m.getFirst();
    while ( i != MaskEnd && i < max ) {
        if ( get(i) ) setUnsafe(i,false);
        i = m.getNext(i);
        }
}

void CBitMask::and ( const CBitMask &m )
{
    int i = getFirst();
    while ( i != MaskEnd ) {
        if ( !m.get(i) ) setUnsafe(i,false);
        i = getNext(i);
        }
}
