// Copyright (C) 2016, 2017  Stefan Vargyas
// 
// This file is part of Json-Type.
// 
// Json-Type is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Json-Type 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.
// 
// You should have received a copy of the GNU General Public License
// along with Json-Type.  If not, see <http://www.gnu.org/licenses/>.

#ifndef BITSET_NEED_DECL_ONLY

#include <stdint.h>

#include "json-defs.h"

// stev: the discriminator of the union
// below is 'size' by the conditions:
//   size <= BIT_SET_VAL_BITS => use 'val'
//   size >  BIT_SET_VAL_BITS => use 'ptr'

struct bit_set_t
{
    size_t size;
    union {
        uintptr_t      val;
        const uchar_t* ptr;
    } u;
};

#endif // BITSET_NEED_DECL_ONLY

#ifndef BITSET_NEED_STRUCT_ONLY

#ifndef __BIT_SET_H
#define __BIT_SET_H

#include <limits.h>

#include "int-traits.h"

size_t bit_set_get_size(
    const struct bit_set_t* set);

void bit_set_print(
    const struct bit_set_t* set, FILE* file);

#ifdef JSON_DEBUG
void bit_set_print_debug(
    const struct bit_set_t* set, FILE* file);
#endif

size_t bit_set_get_ptr_count(
    const struct bit_set_t* set);

#define TYPEOF_IS_BIT_SET_(q, x)            \
    (                                       \
        TYPEOF_IS(x, q struct bit_set_t*)   \
    )
#define TYPEOF_IS_BIT_SET_CONST(x)          \
    (                                       \
        TYPEOF_IS_BIT_SET_(const, x) ||     \
        TYPEOF_IS_BIT_SET_(, x)             \
    )
#define TYPEOF_IS_BIT_SET(x)                \
    TYPEOF_IS_BIT_SET_(, x)

#define BIT_SET_VAL_BITS TYPE_WIDTH(uintptr_t)

#define BIT_SET_IS_VAL_(b)                  \
    (                                       \
        (b)->size <= BIT_SET_VAL_BITS       \
    )

#define BIT_SET_INIT(b, a, s, ...)          \
    do {                                    \
        STATIC(TYPEOF_IS_SIZET(s));         \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        if ((s) <= BIT_SET_VAL_BITS)        \
            (b)->u.val = SZ(0);             \
        else {                              \
            size_t __s =                    \
                BIT_SET_WIDTH_(s);          \
            uchar_t* __p =                  \
                a(__s, ## __VA_ARGS__);     \
            memset(__p, 0, __s);            \
            (b)->u.ptr = __p;               \
        }                                   \
        (b)->size = s;                      \
    } while (0)

#define BIT_SET_CONST_CAST_(p)              \
    CONST_CAST(p, uchar_t)

#define BIT_SET_DONE(b, d, ...)             \
    do {                                    \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        if (!BIT_SET_IS_VAL_(b))            \
            d(BIT_SET_CONST_CAST_(          \
                (b)->u.ptr), ##             \
                __VA_ARGS__);               \
    } while (0)

// stev: BIT_SET_NTH_(n) is well-defined <=> n < SIZE_BIT,
// for n of type size_t

#define BIT_SET_NTH_(n)                     \
    (                                       \
        SZ(1) << (n)                        \
    )

#define BIT_SET_POS_(i)                     \
    (                                       \
        (i) / CHAR_BIT                      \
    )

// stev: BIT_SET_VAL_BITS <= SIZE_BIT <=>
//       TYPE_WIDTH(uintptr_t) <= TYPE_WIDTH(size_t) <=>
//       sizeof(uintptr_t) <= sizeof(size_t)

// stev: UINTPTR_MAX <= SIZE_MAX <=>
//       TYPE_WIDTH(uintptr_t) <= TYPE_WIDTH(size_t),
// since both UINTPTR_MAX and SIZE_MAX are powers of 2

// stev: CHAR_BIT <= SIZE_BIT =>
//       BIT_SET_NTH_(i mod CHAR_BIT) is well-defined:
// indeed: (i mod CHAR_BIT) < CHAR_BIT <= SIZE_BIT

#define BIT_SET_MOD_(i)                     \
    (                                       \
        STATIC(SIZE_BIT >= CHAR_BIT),       \
        STATIC(SIZE_MAX >= UINTPTR_MAX),    \
        STATIC(SIZE_IS_POW2((size_t)        \
            CHAR_BIT)),                     \
        (i) & (CHAR_BIT - 1)                \
    )

#define BIT_SET_BIT_(i)                     \
    (                                       \
        BIT_SET_NTH_(BIT_SET_MOD_(i))       \
    )

#define BIT_SET_WIDTH_(n)                   \
    ({                                      \
        size_t __r = BIT_SET_POS_(n);       \
        BIT_SET_MOD_(n)                     \
        ? __r + 1                           \
        : __r;                              \
    })

#define BIT_SET_VAL_MASK_(b)                \
    (                                       \
        STATIC(                             \
          SIZE_BIT >= BIT_SET_VAL_BITS),    \
        ! (b)->size > BIT_SET_VAL_BITS      \
        ? BIT_SET_BIT_((b)->size) - 1       \
        /* stev: avoid undef behaviour */   \
        /* in BIT_SET_NTH_ when size_t */   \
        /* is identical with uintptr_t */   \
        : (b)->size < BIT_SET_VAL_BITS      \
        ? BIT_SET_NTH_((b)->size) - 1       \
        : UINTPTR_MAX                       \
    )

#define BIT_SET_PTR_SIZE(n)                 \
    (                                       \
        STATIC(TYPEOF_IS_SIZET(n)),         \
        (n) > BIT_SET_VAL_BITS              \
        ? BIT_SET_WIDTH_(n)                 \
        : 0                                 \
    )

#define BIT_SET_MEM_SIZE(n)                 \
    ({                                      \
        size_t __r =                        \
            sizeof(struct bit_set_t);       \
        if ((n) > BIT_SET_VAL_BITS) {       \
            size_t __w = BIT_SET_WIDTH_(n); \
            SIZE_ADD_EQ(__r, __w);          \
        }                                   \
        __r;                                \
    })

#define BIT_SET_POS_END_(b)                 \
    (                                       \
        BIT_SET_WIDTH_((b)->size)           \
    )

#define BIT_SET_ASSERT_POS_(b, i)           \
    ASSERT((i) < (b)->size)

// stev: BIT_SET_ASSERT_POS_(b, i) && BIT_SET_IS_VAL_(b) =>
//       BIT_SET_NTH_(i) is well-defined:
// indeed, from hypothesis
// => i < b->size <= BIT_SET_VAL_BITS <= SIZE_BIT
// => i < SIZE_BIT => BIT_SET_NTH_(i) is well-defined

// stev: BIT_SET_ASSERT_POS_(b, i) =>
//       b->ptr[BIT_SET_POS_(i)] is well-defined:
// indeed, BIT_SET_ASSERT_POS_(b, i) <=> i < b->size
// => BIT_SET_POS_(i) < BIT_SET_POS_END_(b)
// => BIT_SET_POS_(i) is in range of b->ptr

#define BIT_SET_TEST(b, i)                  \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET_CONST(b)); \
        STATIC(TYPEOF_IS_SIZET(i));         \
        BIT_SET_ASSERT_POS_(b, i);          \
        BIT_SET_IS_VAL_(b)                  \
        ? (b)->u.val & BIT_SET_NTH_(i)      \
        : (b)->u.ptr[BIT_SET_POS_(i)] &     \
                     BIT_SET_BIT_(i);       \
    })

#define BIT_SET_SET(b, i)                   \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        STATIC(TYPEOF_IS_SIZET(i));         \
        BIT_SET_ASSERT_POS_(b, i);          \
        if (BIT_SET_IS_VAL_(b))             \
            (b)->u.val |= BIT_SET_NTH_(i);  \
        else                                \
            BIT_SET_CONST_CAST_((b)->u.ptr) \
                      [BIT_SET_POS_(i)] |=  \
                       BIT_SET_BIT_(i);     \
    })

#define BIT_SET_RESET(b, i)                 \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        STATIC(TYPEOF_IS_SIZET(i));         \
        BIT_SET_ASSERT_POS_(b, i);          \
        if (BIT_SET_IS_VAL_(b))             \
            (b)->u.val &= BIT_SET_NTH_(i);  \
        else                                \
            BIT_SET_CONST_CAST_((b)->u.ptr) \
                      [BIT_SET_POS_(i)] &=  \
                      ~BIT_SET_BIT_(i);     \
    })

// stev: purposedly don't care about leaving
// trashed bits outside the range of set bits;
// mask out those trashy bits only when need,
// e.g. in IS_BIN_OP_EMPTY_ or IS_BIN_OP_EQUAL_

#define BIT_SET_BIN_OP_(b, c, o)            \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        STATIC(TYPEOF_IS_BIT_SET_CONST(c)); \
        ASSERT((b)->size == (c)->size);     \
        if (BIT_SET_IS_VAL_(b))             \
            (b)->u.val o ## = (c)->u.val;   \
        else {                              \
            uchar_t* __p =                  \
                BIT_SET_CONST_CAST_(        \
                    (b)->u.ptr);            \
            const uchar_t* __q =            \
                    (c)->u.ptr;             \
            const uchar_t* __e =            \
                __p + BIT_SET_POS_END_(b);  \
            while (__p < __e)               \
                 *__p ++ o ## = *__q ++;    \
        }                                   \
    })

#define BIT_SET_ASSIGN(b, c)                \
    BIT_SET_BIN_OP_(b, c, )

#define BIT_SET_OR(b, c)                    \
    BIT_SET_BIN_OP_(b, c, |)

#define BIT_SET_AND(b, c)                   \
    BIT_SET_BIN_OP_(b, c, &)

#define BIT_SET_UN_OP_(b, o)                \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        if (BIT_SET_IS_VAL_(b))             \
            (b)->u.val = o (b)->u.val;      \
        else {                              \
            uchar_t* __p =                  \
                BIT_SET_CONST_CAST_(        \
                    (b)->u.ptr);            \
            const uchar_t* __e =            \
                __p + BIT_SET_POS_END_(b);  \
            for (; __p < __e; __p ++)       \
                 *__p = o *__p;             \
        }                                   \
    })

#define BIT_SET_NEG(b)                      \
    BIT_SET_UN_OP_(b, ~)

#define BIT_SET_CONST_OP_(b, v)             \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET(b));       \
        if (BIT_SET_IS_VAL_(b))             \
            (b)->u.val = v;                 \
        else                                \
            memset(                         \
                BIT_SET_CONST_CAST_(        \
                    (b)->u.ptr), v,         \
                BIT_SET_POS_END_(b));       \
    })

#define BIT_SET_SET_ALL(b)                  \
    BIT_SET_CONST_OP_(b, ~0)

#define BIT_SET_RESET_ALL(b)                \
    BIT_SET_CONST_OP_(b, 0)

#define BIT_SET_IS_BIN_OP_EMPTY_(b, c, o)   \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET_CONST(b)); \
        STATIC(TYPEOF_IS_BIT_SET_CONST(c)); \
        size_t __m = BIT_SET_VAL_MASK_(b);  \
        ASSERT((b)->size == (c)->size);     \
        BIT_SET_IS_VAL_(b)                  \
        ? !(((b)->u.val & __m) o            \
            ((c)->u.val & __m))             \
        : !({                               \
            const uchar_t* __p =            \
                BIT_SET_CONST_CAST_(        \
                    (b)->u.ptr);            \
            const uchar_t* __q =            \
                    (c)->u.ptr;             \
            const size_t __n =              \
                BIT_SET_POS_END_(b);        \
            ASSERT(__n > 0);                \
            const uchar_t* __l =            \
                __p + (__n - 1);            \
            /* __n > 0 => __p <= __l */     \
            while (__p < __l) {             \
                 if (*__p ++ o *__q ++)     \
                    break;                  \
            }                               \
            /* __p >= __l => __p == __l */  \
            (__p < __l) ||                  \
            ((*__p & __m) o (*__q & __m));  \
        });                                 \
    })

#define BIT_SET_IS_OR_EMPTY(b, c)           \
    BIT_SET_IS_BIN_OP_EMPTY_(b, c, |)

#define BIT_SET_IS_AND_EMPTY(b, c)          \
    BIT_SET_IS_BIN_OP_EMPTY_(b, c, &)

#define BIT_SET_IS_BIN_OP_EQUAL_(b, c, o)   \
    ({                                      \
        STATIC(TYPEOF_IS_BIT_SET_CONST(b)); \
        STATIC(TYPEOF_IS_BIT_SET_CONST(c)); \
        size_t __m = BIT_SET_VAL_MASK_(b);  \
        ASSERT((b)->size == (c)->size);     \
        BIT_SET_IS_VAL_(b)                  \
        ? ((((b)->u.val & __m) o            \
            ((c)->u.val & __m)) ==          \
            ((c)->u.val & __m))             \
        : ({                                \
            const uchar_t* __p =            \
                (b)->u.ptr;                 \
            const uchar_t* __q =            \
                (c)->u.ptr;                 \
            const size_t __n =              \
                BIT_SET_POS_END_(b);        \
            ASSERT(__n > 0);                \
            const uchar_t* __l =            \
                __p + (__n - 1);            \
            /* __n > 0 => __p <= __l */     \
            for (; __p < __l; __q ++) {     \
                 if ((*__p ++ o *__q)       \
                             != *__q)       \
                    break;                  \
            }                               \
            (__p == __l) &&                 \
            (((*__p & __m) o                \
              (*__q & __m)) ==              \
              (*__q & __m));                \
        });                                 \
    })

#define BIT_SET_IS_OR_EQUAL(b, c)           \
    BIT_SET_IS_BIN_OP_EQUAL_(b, c, |)

#define BIT_SET_IS_AND_EQUAL(b, c)          \
    BIT_SET_IS_BIN_OP_EQUAL_(b, c, &)

#define BIT_SET_DECL_AUTO(v, s)             \
    struct                                  \
    {                                       \
        struct bit_set_t set;               \
        uchar_t ptr[BIT_SET_PTR_SIZE(s)];   \
    } v ## _bs__;                           \
    const size_t v ## _sz__ = (s);          \

#define BIT_SET_ALLOC_AUTO_(s, v)           \
    ({                                      \
        ASSERT(sizeof(v.ptr) == (s));       \
        v.ptr;                              \
    })
#define BIT_SET_INIT_AUTO(v)                \
    ({                                      \
        BIT_SET_INIT(                       \
            &v ## _bs__.set,                \
            BIT_SET_ALLOC_AUTO_,            \
            v ## _sz__, v ## _bs__);        \
        &v ## _bs__.set;                    \
    })

#endif /* __BIT_SET_H */

#endif // BITSET_NEED_STRUCT_ONLY


