//
// $Id: BitSet.m,v 1.19 2005/05/18 06:46:52 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#import "BitSet.h"
#import "Constants.h"
#import "ContainerCoder.h"
#import <string.h>
#import <limits.h>
#import <Foundation/NSString.h>
#import <Foundation/NSData.h>
#import <Foundation/NSCoder.h>

#define OL_BITS_PER_WORD (CHAR_BIT * sizeof(uint32_t))
#define OL_BITSET_WORDS(n) ((n + OL_BITS_PER_WORD - 1) / OL_BITS_PER_WORD)

static unsigned char BIT_COUNT[256] =
{
    0, /*   0 */ 1, /*   1 */ 1, /*   2 */ 2, /*   3 */ 1, /*   4 */
    2, /*   5 */ 2, /*   6 */ 3, /*   7 */ 1, /*   8 */ 2, /*   9 */
    2, /*  10 */ 3, /*  11 */ 2, /*  12 */ 3, /*  13 */ 3, /*  14 */
    4, /*  15 */ 1, /*  16 */ 2, /*  17 */ 2, /*  18 */ 3, /*  19 */
    2, /*  20 */ 3, /*  21 */ 3, /*  22 */ 4, /*  23 */ 2, /*  24 */
    3, /*  25 */ 3, /*  26 */ 4, /*  27 */ 3, /*  28 */ 4, /*  29 */
    4, /*  30 */ 5, /*  31 */ 1, /*  32 */ 2, /*  33 */ 2, /*  34 */
    3, /*  35 */ 2, /*  36 */ 3, /*  37 */ 3, /*  38 */ 4, /*  39 */
    2, /*  40 */ 3, /*  41 */ 3, /*  42 */ 4, /*  43 */ 3, /*  44 */
    4, /*  45 */ 4, /*  46 */ 5, /*  47 */ 2, /*  48 */ 3, /*  49 */
    3, /*  50 */ 4, /*  51 */ 3, /*  52 */ 4, /*  53 */ 4, /*  54 */
    5, /*  55 */ 3, /*  56 */ 4, /*  57 */ 4, /*  58 */ 5, /*  59 */
    4, /*  60 */ 5, /*  61 */ 5, /*  62 */ 6, /*  63 */ 1, /*  64 */
    2, /*  65 */ 2, /*  66 */ 3, /*  67 */ 2, /*  68 */ 3, /*  69 */
    3, /*  70 */ 4, /*  71 */ 2, /*  72 */ 3, /*  73 */ 3, /*  74 */
    4, /*  75 */ 3, /*  76 */ 4, /*  77 */ 4, /*  78 */ 5, /*  79 */
    2, /*  80 */ 3, /*  81 */ 3, /*  82 */ 4, /*  83 */ 3, /*  84 */
    4, /*  85 */ 4, /*  86 */ 5, /*  87 */ 3, /*  88 */ 4, /*  89 */
    4, /*  90 */ 5, /*  91 */ 4, /*  92 */ 5, /*  93 */ 5, /*  94 */
    6, /*  95 */ 2, /*  96 */ 3, /*  97 */ 3, /*  98 */ 4, /*  99 */
    3, /* 100 */ 4, /* 101 */ 4, /* 102 */ 5, /* 103 */ 3, /* 104 */
    4, /* 105 */ 4, /* 106 */ 5, /* 107 */ 4, /* 108 */ 5, /* 109 */
    5, /* 110 */ 6, /* 111 */ 3, /* 112 */ 4, /* 113 */ 4, /* 114 */
    5, /* 115 */ 4, /* 116 */ 5, /* 117 */ 5, /* 118 */ 6, /* 119 */
    4, /* 120 */ 5, /* 121 */ 5, /* 122 */ 6, /* 123 */ 5, /* 124 */
    6, /* 125 */ 6, /* 126 */ 7, /* 127 */ 1, /* 128 */ 2, /* 129 */
    2, /* 130 */ 3, /* 131 */ 2, /* 132 */ 3, /* 133 */ 3, /* 134 */
    4, /* 135 */ 2, /* 136 */ 3, /* 137 */ 3, /* 138 */ 4, /* 139 */
    3, /* 140 */ 4, /* 141 */ 4, /* 142 */ 5, /* 143 */ 2, /* 144 */
    3, /* 145 */ 3, /* 146 */ 4, /* 147 */ 3, /* 148 */ 4, /* 149 */
    4, /* 150 */ 5, /* 151 */ 3, /* 152 */ 4, /* 153 */ 4, /* 154 */
    5, /* 155 */ 4, /* 156 */ 5, /* 157 */ 5, /* 158 */ 6, /* 159 */
    2, /* 160 */ 3, /* 161 */ 3, /* 162 */ 4, /* 163 */ 3, /* 164 */
    4, /* 165 */ 4, /* 166 */ 5, /* 167 */ 3, /* 168 */ 4, /* 169 */
    4, /* 170 */ 5, /* 171 */ 4, /* 172 */ 5, /* 173 */ 5, /* 174 */
    6, /* 175 */ 3, /* 176 */ 4, /* 177 */ 4, /* 178 */ 5, /* 179 */
    4, /* 180 */ 5, /* 181 */ 5, /* 182 */ 6, /* 183 */ 4, /* 184 */
    5, /* 185 */ 5, /* 186 */ 6, /* 187 */ 5, /* 188 */ 6, /* 189 */
    6, /* 190 */ 7, /* 191 */ 2, /* 192 */ 3, /* 193 */ 3, /* 194 */
    4, /* 195 */ 3, /* 196 */ 4, /* 197 */ 4, /* 198 */ 5, /* 199 */
    3, /* 200 */ 4, /* 201 */ 4, /* 202 */ 5, /* 203 */ 4, /* 204 */
    5, /* 205 */ 5, /* 206 */ 6, /* 207 */ 3, /* 208 */ 4, /* 209 */
    4, /* 210 */ 5, /* 211 */ 4, /* 212 */ 5, /* 213 */ 5, /* 214 */
    6, /* 215 */ 4, /* 216 */ 5, /* 217 */ 5, /* 218 */ 6, /* 219 */
    5, /* 220 */ 6, /* 221 */ 6, /* 222 */ 7, /* 223 */ 3, /* 224 */
    4, /* 225 */ 4, /* 226 */ 5, /* 227 */ 4, /* 228 */ 5, /* 229 */
    5, /* 230 */ 6, /* 231 */ 4, /* 232 */ 5, /* 233 */ 5, /* 234 */
    6, /* 235 */ 5, /* 236 */ 6, /* 237 */ 6, /* 238 */ 7, /* 239 */
    4, /* 240 */ 5, /* 241 */ 5, /* 242 */ 6, /* 243 */ 5, /* 244 */
    6, /* 245 */ 6, /* 246 */ 7, /* 247 */ 5, /* 248 */ 6, /* 249 */
    6, /* 250 */ 7, /* 251 */ 6, /* 252 */ 7, /* 253 */ 7, /* 254 */
    8  /* 255 */
};

@interface OLBitSet (PrivateMethods)

- (uint32_t) bitMask: (unsigned)pos;
- (void) sanitize;
- (void) unsanitize;
- (unsigned) wordIndex: (unsigned)pos;
    
@end

@implementation OLBitSet

+ (id) bitSetWithBitSet: (OLBitSet*)bitSet
{
    OL_BEGIN_AUTO_CTOR(OLBitSet)
        initWithBitSet: bitSet
    OL_END_AUTO_CTOR;
}

+ (id) bitSetWithSetSize: (unsigned)bits
{
    OL_BEGIN_AUTO_CTOR(OLBitSet)
        initWithSetSize: bits
    OL_END_AUTO_CTOR;
}

+ (id) bitSetWithString: (const char*)str position: (unsigned)pos count: (unsigned)count
{
    OL_BEGIN_AUTO_CTOR(OLBitSet)
        initWithString: str position: pos count: count
    OL_END_AUTO_CTOR;
}

+ (id) bitSetWithValue: (uint32_t)val
{
    OL_BEGIN_AUTO_CTOR(OLBitSet)
        initWithValue: val
    OL_END_AUTO_CTOR;
}

- (id) initWithBitSet: (OLBitSet*)bitSet
{
    [self initWithSetSize: bitSet->numberOfBits allocator: bitSet->allocator];
    memcpy(words, bitSet->words, OL_BITSET_WORDS(numberOfBits) * sizeof(uint32_t));
    return self;
}

- (id) initWithCoder: (NSCoder*)decoder
{
    [super init];
    allocator = OBJ_RETAIN([OLContainerCoder decodeAllocatorWithCoder: decoder]);
    if ([decoder respondsToSelector: @selector(allowsKeyedCoding)] &&
        [decoder allowsKeyedCoding])
    {
        numberOfBits = [decoder decodeIntForKey: NUMBER_OF_BITS_KEY];
        numberOfWords = [decoder decodeIntForKey: NUMBER_OF_WORDS_KEY];
    }
    else
    {
        [decoder decodeValueOfObjCType: @encode(unsigned) at: &numberOfBits];
        [decoder decodeValueOfObjCType: @encode(unsigned) at: &numberOfWords];
    }
    words = [allocator allocateBytes: numberOfWords * sizeof(uint32_t)];
    [decoder decodeArrayOfObjCType: @encode(uint32_t) count: numberOfWords
        at: words];
    return self;
}

- (id) initWithSetSize: (unsigned)bits
{
    return [self initWithSetSize: bits allocator: [OLAllocator defaultAllocator]];
}

- (id) initWithSetSize: (unsigned)bits allocator: (OLAllocator*)alloc
{
    unsigned numberOfBytes = OL_BITSET_WORDS(bits) * sizeof(uint32_t);

    [super init];
    allocator = OBJ_RETAIN(alloc);
    numberOfBits = bits;
    numberOfWords = OL_BITSET_WORDS(numberOfBits);
    words = [allocator allocateBytes: numberOfBytes];
    memset(words, 0, numberOfBytes);
    return self;
}

- (id) initWithString: (const char*)str position: (unsigned)pos count: (unsigned)count
{
    return [self initWithString: str position: pos count: count
        allocator: [OLAllocator defaultAllocator]];
}

- (id) initWithString: (const char*)str position: (unsigned)pos count: (unsigned)count allocator: (OLAllocator*)alloc
{
    unsigned i = 0;
    const char* start = str + pos;

    [self initWithSetSize: count allocator: alloc];
    while (i < count)
    {
        if (start[i] == '1')
            [self set: i];
        else if (start[i] != '0')
            break;
        i++;
    }
    return self;
}

- (id) initWithValue: (uint32_t)val
{
    return [self initWithValue: val allocator: [OLAllocator defaultAllocator]];
}

- (id) initWithValue: (uint32_t)val allocator: (OLAllocator*)alloc
{
    [super init];
    allocator = OBJ_RETAIN(alloc);
    words = [allocator allocateBytes: sizeof(uint32_t)];
    numberOfBits = OL_BITS_PER_WORD;
    numberOfWords = 1;
    *words = val;
    return self;
}

- (void) dealloc
{
    [allocator deallocateBytes: words];
    OBJ_RELEASE(allocator);
    [super dealloc];
}

- (BOOL) any
{
    unsigned i;

    for (i = 0; i < numberOfWords; i++)
    {
        if (words[i] != 0UL)
            return YES;
    }
    return NO;
}

- (OLBitSet*) bitSetFlipped
{
    OLBitSet* other = [self copyWithAllocator: allocator];

    [other flip];
    return OBJ_COND_AUTORELEASE(other);
}

- (OLBitSet*) bitSetShiftedLeft: (unsigned)count
{
    OLBitSet* other = [self copyWithAllocator: allocator];

    [other shiftLeft: count];
    return OBJ_COND_AUTORELEASE(other);
}

- (OLBitSet*) bitSetShiftedRight: (unsigned)count
{
    OLBitSet* other = [self copyWithAllocator: allocator];

    [other shiftRight: count];
    return OBJ_COND_AUTORELEASE(other);
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    return [[alloc allocateObject: [OLBitSet class]] initWithBitSet: self];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLBitSet allocWithZone: zone] initWithBitSet: self];
}

- (unsigned) count
{
    unsigned result = 0;
    unsigned char* current = (unsigned char*)words;
    unsigned char* end = (unsigned char*)(words + numberOfWords);

    while (current < end)
    {
        result += BIT_COUNT[*current];
        current++;
    }
    return result;
}

- (void) encodeWithCoder: (NSCoder*)encoder
{
    [OLContainerCoder encodeAllocator: allocator withCoder: encoder];
    if ([encoder respondsToSelector: @selector(allowsKeyedCoding)] &&
        [encoder allowsKeyedCoding])
    {
        [encoder encodeInt: numberOfBits forKey: NUMBER_OF_BITS_KEY];
        [encoder encodeInt: numberOfWords forKey: NUMBER_OF_WORDS_KEY];
    }
    else
    {
        [encoder encodeValueOfObjCType: @encode(unsigned) at: &numberOfBits];
        [encoder encodeValueOfObjCType: @encode(unsigned) at: &numberOfWords];
    }
    [encoder encodeArrayOfObjCType: @encode(uint32_t) count: numberOfWords
        at: words];
}

- (void) flip
{
    unsigned i;

    for (i = 0; i < numberOfWords; i++)
        words[i] = ~words[i];
    [self sanitize];
}

- (void) flip: (unsigned)pos
{
    words[[self wordIndex: pos]] ^= [self bitMask: pos];
}

- (OLAllocator*) allocator
{
    return allocator;
}

- (BOOL) isEqual: (id)object
{
    OLBitSet* right;
    unsigned i;

    if (![object isKindOfClass: [OLBitSet class]])
        return NO;
    right = (OLBitSet*)object;
    if (numberOfBits != right->numberOfBits)
        return NO;
    for (i = 0; i < numberOfWords; i++)
    {
        if (words[i] != right->words[i])
            return NO;
    }
    return YES;
}

- (void) logicalAnd: (OLBitSet*)right
{
    unsigned count = MIN(numberOfWords, right->numberOfWords);
    unsigned i;

    [right unsanitize];
    for (i = 0; i < count; i++)
        words[i] &= right->words[i];
    [right sanitize];
}

- (void) logicalOr: (OLBitSet*)right
{
    unsigned count = MIN(numberOfWords, right->numberOfWords);
    unsigned i;

    for (i = 0; i < count; i++)
        words[i] |= right->words[i];
    [self sanitize];
}

- (void) logicalXor: (OLBitSet*)right
{
    unsigned count = MIN(numberOfWords, right->numberOfWords);
    unsigned i;

    for (i = 0; i < count; i++)
        words[i] ^= right->words[i];
    [self sanitize];
}

- (BOOL) none
{
    return [self any] ? NO : YES;
}

- (void) reset
{
    memset(words, 0, numberOfWords * sizeof(uint32_t));
}

- (void) reset: (unsigned)pos
{
    words[[self wordIndex: pos]] &= ~[self bitMask: pos];
}

- (void) set
{
    memset(words, 255, numberOfWords * sizeof(uint32_t));
    [self sanitize];
}

- (void) set: (unsigned)pos
{
    words[[self wordIndex: pos]] |= [self bitMask: pos];
}

- (void) shiftLeft: (unsigned)count
{
    unsigned wshift;
    unsigned offset;
    unsigned i;
    unsigned subOffset;

    if (count != 0)
    {
        wshift = count / OL_BITS_PER_WORD;
        offset = count % OL_BITS_PER_WORD;
        if (offset == 0)
        {
            for (i = numberOfWords - 1; i >= wshift; i--)
                words[i] = words[i - wshift];
        }
        else
        {
            subOffset = OL_BITS_PER_WORD - offset;
            for (i = numberOfWords - 1; i > wshift; i--)
            {
                words[i] = (words[i - wshift] << offset) |
                           (words[i - wshift - 1] >> subOffset);
            }
            words[wshift] = words[0] << offset;
        }
        for (i = 0; i < wshift; i++)
            words[i] = 0UL;
    }
    [self sanitize];
}

- (void) shiftRight: (unsigned)count
{
    unsigned wshift;
    unsigned offset;
    unsigned i;
    unsigned subOffset;
    unsigned limit;

    if (count != 0)
    {
        wshift = count / OL_BITS_PER_WORD;
        offset = count % OL_BITS_PER_WORD;
        limit = numberOfWords - wshift - 1;
        if (offset == 0)
        {
            for (i = 0; i <= limit; i++)
                words[i] = words[i + wshift];
        }
        else
        {
            subOffset = OL_BITS_PER_WORD - offset;
            for (i = 0; i < limit; i++)
            {
                words[i] = (words[i + wshift] >> offset) |
                           (words[i + wshift + 1] << subOffset);
            }
            words[limit] = words[numberOfWords - 1] >> offset;
        }
        for (i = limit + 1; i < numberOfWords; i++)
            words[i] = 0UL;
    }
    [self sanitize];
}

- (unsigned) size
{
    return numberOfBits;
}

- (BOOL) test: (unsigned)pos
{
    return (words[[self wordIndex: pos]] & [self bitMask: pos]) ? YES : NO;
}

- (char*) toString
{
    char* result = [allocator allocateBytes: numberOfBits + 1];
    unsigned i;

    for (i = 0; i < numberOfBits; i++)
        result[i] = [self test: i] ? '1' : '0';
    result[i] = 0;
    return result;
}

@end

@implementation OLBitSet (PrivateMethods)

- (uint32_t) bitMask: (unsigned)pos
{
    return 1UL << (pos % OL_BITS_PER_WORD);
}

- (void) sanitize
{
    words[numberOfWords - 1] &= ~((~0UL) << (numberOfBits % OL_BITS_PER_WORD));
}

- (void) unsanitize
{
    words[numberOfWords - 1] |= (~0UL) << (numberOfBits % OL_BITS_PER_WORD);
}

- (unsigned) wordIndex: (unsigned)pos
{
    return pos / OL_BITS_PER_WORD;
}

@end
