Commit 3e0b94e7 authored by Daniel Colascione's avatar Daniel Colascione

Add set operations for bool-vector.

http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00404.html

* data.c (Qbool_vector_p): New symbol.
(bool_vector_spare_mask,popcount_size_t_generic)
(popcount_size_t_msc,popcount_size_t_gcc)
(popcount_size_t)
(bool_vector_binop_driver)
(count_trailing_zero_bits,size_t_to_host_endian)
(Fbool_vector_exclusive_or)
(Fbool_vector_union)
(Fbool_vector_intersection,Fbool_vector_set_difference)
(Fbool_vector_subsetp,Fbool_vector_not)
(Fbool_vector_count_matches)
(Fbool_vector_count_matches_at): New functions.
(syms_of_data): Intern new symbol, functions.
* alloc.c (bool_vector_payload_bytes): New function.
(Fmake_bool_vector): Instead of calling Fmake_vector,
which performs redundant initialization and argument checking,
just call allocate_vector ourselves.  Make sure we clear any
terminating padding to zero.
(vector_nbytes,sweep_vectors): Use bool_vector_payload_bytes
instead of open-coding the size calculation.
(vroundup_ct): New macro.
(vroundup): Assume argument >= 0; invoke vroundup_ct.
* casetab.c (shuffle,set_identity): Change lint_assume to assume.
* composite.c (composition_gstring_put_cache): Change
lint_assume to assume.
* conf_post.h (assume): New macro.
(lint_assume): Remove.
* dispnew.c (update_frame_1): Change lint_assume to assume.
* ftfont.c (ftfont_shape_by_flt): Change lint_assume
to assume.
* image.c (gif_load): Change lint_assume to assume.
* lisp.h (eassert_and_assume): New macro.
(Qbool_vector_p): Declare.
(CHECK_BOOL_VECTOR,ROUNDUP,BITS_PER_SIZE_T): New macros.
(swap16,swap32,swap64): New inline functions.
* macfont.c (macfont_shape): Change lint_assume to assume.
* ralloc.c: Rename ROUNDUP to PAGE_ROUNDUP throughout.
* xsettings.c (parse_settings): Use new swap16 and
swap32 from lisp.h instead of file-specific macros.
parent 76880d88
2013-09-15 Jan Djärv <jan.h.d@swipnet.se>
2013-09-22 Daniel Colascione <dancol@dancol.org>
* NEWS: Mention new bool-vector functionality.
aaaa2013-09-15 Jan Djärv <jan.h.d@swipnet.se>
* NEWS: Mention the macfont backend.
......
......@@ -638,6 +638,16 @@ for something (not just adding elements to it), it ought not to affect you.
* Lisp Changes in Emacs 24.4
** New bool-vector set operation functions:
*** `bool-vector-exclusive-or'
*** `bool-vector-union'
*** `bool-vector-intersection'
*** `bool-vector-set-difference'
*** `bool-vector-not'
*** `bool-vector-subset'
*** `bool-vector-count-matches'
*** `bool-vector-count-matches-at'
** Comparison functions =, <, >, <=, >= now take many arguments.
** The second argument of `eval' can now be a lexical-environment.
......
2013-09-22 Daniel Colascione <dancol@dancol.org>
* data.c (Qbool_vector_p): New symbol.
(bool_vector_spare_mask,popcount_size_t_generic)
(popcount_size_t_msc,popcount_size_t_gcc)
(popcount_size_t)
(bool_vector_binop_driver)
(count_trailing_zero_bits,size_t_to_host_endian)
(Fbool_vector_exclusive_or)
(Fbool_vector_union)
(Fbool_vector_intersection,Fbool_vector_set_difference)
(Fbool_vector_subsetp,Fbool_vector_not)
(Fbool_vector_count_matches)
(Fbool_vector_count_matches_at): New functions.
(syms_of_data): Intern new symbol, functions.
* alloc.c (bool_vector_payload_bytes): New function.
(Fmake_bool_vector): Instead of calling Fmake_vector,
which performs redundant initialization and argument checking,
just call allocate_vector ourselves. Make sure we clear any
terminating padding to zero.
(vector_nbytes,sweep_vectors): Use bool_vector_payload_bytes
instead of open-coding the size calculation.
(vroundup_ct): New macro.
(vroundup): Assume argument >= 0; invoke vroundup_ct.
* casetab.c (shuffle,set_identity): Change lint_assume to assume.
* composite.c (composition_gstring_put_cache): Change
lint_assume to assume.
* conf_post.h (assume): New macro.
(lint_assume): Remove.
* dispnew.c (update_frame_1): Change lint_assume to assume.
* ftfont.c (ftfont_shape_by_flt): Change lint_assume
to assume.
* image.c (gif_load): Change lint_assume to assume.
* lisp.h (eassert_and_assume): New macro.
(Qbool_vector_p): Declare.
(CHECK_BOOL_VECTOR,ROUNDUP,BITS_PER_SIZE_T): New macros.
(swap16,swap32,swap64): New inline functions.
* macfont.c (macfont_shape): Change lint_assume to assume.
* ralloc.c: Rename ROUNDUP to PAGE_ROUNDUP throughout.
* xsettings.c (parse_settings): Use new swap16 and
swap32 from lisp.h instead of file-specific macros.
2013-09-22 Eli Zaretskii <eliz@gnu.org>
* xdisp.c (try_window_id): Don't abort if cursor row could not be
......
......@@ -2001,6 +2001,35 @@ INIT must be an integer that represents a character. */)
return val;
}
verify (sizeof (size_t) * CHAR_BIT == BITS_PER_SIZE_T);
verify ((BITS_PER_SIZE_T & (BITS_PER_SIZE_T - 1)) == 0);
static
ptrdiff_t
bool_vector_payload_bytes (ptrdiff_t nr_bits,
ptrdiff_t* exact_needed_bytes_out)
{
ptrdiff_t exact_needed_bytes;
ptrdiff_t needed_bytes;
eassert_and_assume (nr_bits >= 0);
exact_needed_bytes = ROUNDUP ((size_t) nr_bits, CHAR_BIT) / CHAR_BIT;
needed_bytes = ROUNDUP ((size_t) nr_bits, BITS_PER_SIZE_T) / CHAR_BIT;
if (needed_bytes == 0)
{
/* Always allocate at least one machine word of payload so that
bool-vector operations in data.c don't need a special case
for empty vectors. */
needed_bytes = sizeof (size_t);
}
if (exact_needed_bytes_out != NULL)
*exact_needed_bytes_out = exact_needed_bytes;
return needed_bytes;
}
DEFUN ("make-bool-vector", Fmake_bool_vector, Smake_bool_vector, 2, 2, 0,
doc: /* Return a new bool-vector of length LENGTH, using INIT for each element.
......@@ -2009,37 +2038,43 @@ LENGTH must be a number. INIT matters only in whether it is t or nil. */)
{
register Lisp_Object val;
struct Lisp_Bool_Vector *p;
ptrdiff_t length_in_chars;
EMACS_INT length_in_elts;
int bits_per_value;
int extra_bool_elts = ((bool_header_size - header_size + word_size - 1)
/ word_size);
ptrdiff_t exact_payload_bytes;
ptrdiff_t total_payload_bytes;
ptrdiff_t needed_elements;
CHECK_NATNUM (length);
if (PTRDIFF_MAX < XFASTINT (length))
memory_full (SIZE_MAX);
bits_per_value = sizeof (EMACS_INT) * BOOL_VECTOR_BITS_PER_CHAR;
total_payload_bytes = bool_vector_payload_bytes
(XFASTINT (length), &exact_payload_bytes);
length_in_elts = (XFASTINT (length) + bits_per_value - 1) / bits_per_value;
eassert_and_assume (exact_payload_bytes <= total_payload_bytes);
eassert_and_assume (0 <= exact_payload_bytes);
val = Fmake_vector (make_number (length_in_elts + extra_bool_elts), Qnil);
needed_elements = ROUNDUP ((size_t) ((bool_header_size - header_size)
+ total_payload_bytes),
word_size) / word_size;
/* No Lisp_Object to trace in there. */
p = (struct Lisp_Bool_Vector* ) allocate_vector (needed_elements);
XSETVECTOR (val, p);
XSETPVECTYPESIZE (XVECTOR (val), PVEC_BOOL_VECTOR, 0, 0);
p = XBOOL_VECTOR (val);
p->size = XFASTINT (length);
length_in_chars = ((XFASTINT (length) + BOOL_VECTOR_BITS_PER_CHAR - 1)
/ BOOL_VECTOR_BITS_PER_CHAR);
if (length_in_chars)
if (exact_payload_bytes)
{
memset (p->data, ! NILP (init) ? -1 : 0, length_in_chars);
memset (p->data, ! NILP (init) ? -1 : 0, exact_payload_bytes);
/* Clear any extraneous bits in the last byte. */
p->data[length_in_chars - 1]
p->data[exact_payload_bytes - 1]
&= (1 << ((XFASTINT (length) - 1) % BOOL_VECTOR_BITS_PER_CHAR + 1)) - 1;
}
/* Clear padding at the end. */
memset (p->data + exact_payload_bytes,
0,
total_payload_bytes - exact_payload_bytes);
return val;
}
......@@ -2565,24 +2600,22 @@ enum
roundup_size = COMMON_MULTIPLE (word_size, USE_LSB_TAG ? GCALIGNMENT : 1)
};
/* ROUNDUP_SIZE must be a power of 2. */
verify ((roundup_size & (roundup_size - 1)) == 0);
/* Verify assumptions described above. */
verify ((VECTOR_BLOCK_SIZE % roundup_size) == 0);
verify (VECTOR_BLOCK_SIZE <= (1 << PSEUDOVECTOR_SIZE_BITS));
/* Round up X to nearest mult-of-ROUNDUP_SIZE. */
#define vroundup(x) (((x) + (roundup_size - 1)) & ~(roundup_size - 1))
/* Round up X to nearest mult-of-ROUNDUP_SIZE --- use at compile time. */
#define vroundup_ct(x) ROUNDUP((size_t)(x), roundup_size)
/* Round up X to nearest mult-of-ROUNDUP_SIZE --- use at runtime. */
#define vroundup(x) (assume((x) >= 0), vroundup_ct(x))
/* Rounding helps to maintain alignment constraints if USE_LSB_TAG. */
#define VECTOR_BLOCK_BYTES (VECTOR_BLOCK_SIZE - vroundup (sizeof (void *)))
#define VECTOR_BLOCK_BYTES (VECTOR_BLOCK_SIZE - vroundup_ct (sizeof (void *)))
/* Size of the minimal vector allocated from block. */
#define VBLOCK_BYTES_MIN vroundup (header_size + sizeof (Lisp_Object))
#define VBLOCK_BYTES_MIN vroundup_ct (header_size + sizeof (Lisp_Object))
/* Size of the largest vector allocated from block. */
......@@ -2642,7 +2675,7 @@ struct large_vector
struct large_vector *vector;
#if USE_LSB_TAG
/* We need to maintain ROUNDUP_SIZE alignment for the vector member. */
unsigned char c[vroundup (sizeof (struct large_vector *))];
unsigned char c[vroundup_ct (sizeof (struct large_vector *))];
#endif
} next;
struct Lisp_Vector v;
......@@ -2783,10 +2816,14 @@ vector_nbytes (struct Lisp_Vector *v)
if (size & PSEUDOVECTOR_FLAG)
{
if (PSEUDOVECTOR_TYPEP (&v->header, PVEC_BOOL_VECTOR))
size = (bool_header_size
+ (((struct Lisp_Bool_Vector *) v)->size
+ BOOL_VECTOR_BITS_PER_CHAR - 1)
/ BOOL_VECTOR_BITS_PER_CHAR);
{
struct Lisp_Bool_Vector *bv = (struct Lisp_Bool_Vector *) v;
ptrdiff_t payload_bytes =
bool_vector_payload_bytes (bv->size, NULL);
eassert_and_assume (payload_bytes >= 0);
size = bool_header_size + ROUNDUP (payload_bytes, word_size);
}
else
size = (header_size
+ ((size & PSEUDOVECTOR_SIZE_MASK)
......@@ -2886,17 +2923,11 @@ sweep_vectors (void)
total_vectors++;
if (vector->header.size & PSEUDOVECTOR_FLAG)
{
struct Lisp_Bool_Vector *b = (struct Lisp_Bool_Vector *) vector;
/* All non-bool pseudovectors are small enough to be allocated
from vector blocks. This code should be redesigned if some
pseudovector type grows beyond VBLOCK_BYTES_MAX. */
eassert (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_BOOL_VECTOR));
total_vector_slots
+= (bool_header_size
+ ((b->size + BOOL_VECTOR_BITS_PER_CHAR - 1)
/ BOOL_VECTOR_BITS_PER_CHAR)) / word_size;
total_vector_slots += vector_nbytes (vector) / word_size;
}
else
total_vector_slots
......
......@@ -205,7 +205,7 @@ set_identity (Lisp_Object table, Lisp_Object c, Lisp_Object elt)
from = to = XINT (c);
to++;
lint_assume (to <= MAX_CHAR + 1);
assume (to <= MAX_CHAR + 1);
for (; from < to; from++)
CHAR_TABLE_SET (table, from, make_number (from));
}
......@@ -232,7 +232,7 @@ shuffle (Lisp_Object table, Lisp_Object c, Lisp_Object elt)
from = to = XINT (c);
to++;
lint_assume (to <= MAX_CHAR + 1);
assume (to <= MAX_CHAR + 1);
for (; from < to; from++)
{
Lisp_Object tem = Faref (table, elt);
......
......@@ -674,7 +674,7 @@ composition_gstring_put_cache (Lisp_Object gstring, ptrdiff_t len)
len = j;
}
lint_assume (len <= TYPE_MAXIMUM (ptrdiff_t) - 2);
assume (len <= TYPE_MAXIMUM (ptrdiff_t) - 2);
copy = Fmake_vector (make_number (len + 2), Qnil);
LGSTRING_SET_HEADER (copy, Fcopy_sequence (header));
for (i = 0; i < len; i++)
......
......@@ -248,16 +248,24 @@ extern void _DebPrint (const char *fmt, ...);
# define FLEXIBLE_ARRAY_MEMBER 1
#endif
/* assume(cond) tells the compiler (and lint) that a certain condition
* will always hold, and that it should optimize (or check) accordingly. */
#if defined lint
# define assume(cond) ((cond) ? (void) 0 : abort ())
#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) || __GNUC__ > 4
# define assume(cond) ((x) || (__builtin_unreachable(), 0))
#elif defined __MSC_VER
# define assume(cond) __assume ((cond))
#else
# define assume(cond) (0 && (cond))
#endif
/* Use this to suppress gcc's `...may be used before initialized' warnings. */
#ifdef lint
/* Use CODE only if lint checking is in effect. */
# define IF_LINT(Code) Code
/* Assume that the expression COND is true. This differs in intent
from 'assert', as it is a message from the programmer to the compiler. */
# define lint_assume(cond) ((cond) ? (void) 0 : abort ())
#else
# define IF_LINT(Code) /* empty */
# define lint_assume(cond) ((void) (0 && (cond)))
#endif
/* conf_post.h ends here */
......@@ -54,6 +54,7 @@ Lisp_Object Qintegerp, Qwholenump, Qsymbolp, Qlistp, Qconsp;
static Lisp_Object Qnatnump;
Lisp_Object Qstringp, Qarrayp, Qsequencep, Qbufferp;
Lisp_Object Qchar_or_string_p, Qmarkerp, Qinteger_or_marker_p, Qvectorp;
Lisp_Object Qbool_vector_p;
Lisp_Object Qbuffer_or_string_p;
static Lisp_Object Qkeywordp, Qboundp;
Lisp_Object Qfboundp;
......@@ -2956,6 +2957,457 @@ lowercase l) for small endian machines. */)
return make_number (order);
}
/* Because we round up the bool vector allocate size to word_size
units, we can safely read past the "end" of the vector in the
operations below. These extra bits are always zero. Also, we
always allocate bool vectors with at least one size_t of storage so
that we don't have to special-case empty bit vectors. */
static inline
size_t
bool_vector_spare_mask (ptrdiff_t nr_bits)
{
eassert_and_assume (nr_bits > 0);
return (((size_t) 1) << (nr_bits % BITS_PER_SIZE_T)) - 1;
}
#if __MSC_VER >= 1500 && (defined _M_IX86 || defined _M_X64)
# define USE_MSC_POPCOUNT
#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define USE_GCC_POPCOUNT
#else
# define NEED_GENERIC_POPCOUNT
#endif
#ifdef USE_MSC_POPCOUNT
#define NEED_GENERIC_POPCOUNT
#endif
#ifdef NEED_GENERIC_POPCOUNT
static inline
unsigned int
popcount_size_t_generic (size_t val)
{
unsigned short j;
unsigned int count = 0;
for (j = 0; j < BITS_PER_SIZE_T; ++j)
count += !!((((size_t) 1) << j) & val);
return count;
}
#endif
#ifdef USE_MSC_POPCOUNT
static inline
unsigned int
popcount_size_t_msc (size_t val)
{
unsigned int count;
#pragma intrinsic __cpuid
/* While gcc falls back to its own generic code if the machine on
which it's running doesn't support popcount, we need to perform the
detection and fallback ourselves when compiling with Microsoft's
compiler. */
static enum {
popcount_unknown_support,
popcount_use_generic,
popcount_use_intrinsic
} popcount_state;
if (popcount_state == popcount_unknown_support)
{
int cpu_info[4];
__cpuid (cpu_info, 1);
if (cpu_info[2] & (1<<23)) /* See MSDN. */
popcount_state = popcount_use_intrinsic;
else
popcount_state = popcount_use_generic;
}
if (popcount_state == popcount_use_intrinsic)
{
# if BITS_PER_SIZE_T == 64
# pragma intrinsic __popcnt64
count = __popcnt64 (val);
# else
# pragma intrinsic __popcnt
count = __popcnt (val);
# endif
}
else
count = popcount_size_t_generic (val);
return count;
}
#endif /* USE_MSC_POPCOUNT */
#ifdef USE_GCC_POPCOUNT
static inline
unsigned int
popcount_size_t_gcc (size_t val)
{
# if BITS_PER_SIZE_T == 64
return __builtin_popcountll (val);
# else
return __builtin_popcount (val);
# endif
}
#endif /* USE_GCC_POPCOUNT */
static inline
unsigned int
popcount_size_t(size_t val)
{
#if defined USE_MSC_POPCOUNT
return popcount_size_t_msc (val);
#elif defined USE_GCC_POPCOUNT
return popcount_size_t_gcc (val);
#else
return popcount_size_t_generic (val);
#endif
}
enum bool_vector_op { bool_vector_exclusive_or,
bool_vector_union,
bool_vector_intersection,
bool_vector_set_difference,
bool_vector_subsetp };
static inline
Lisp_Object
bool_vector_binop_driver (Lisp_Object op1,
Lisp_Object op2,
Lisp_Object dest,
enum bool_vector_op op)
{
EMACS_INT nr_bits;
size_t *adata, *bdata, *cdata;
ptrdiff_t i;
size_t changed = 0;
size_t mword;
ptrdiff_t nr_words;
CHECK_BOOL_VECTOR (op1);
CHECK_BOOL_VECTOR (op2);
nr_bits = min (XBOOL_VECTOR (op1)->size,
XBOOL_VECTOR (op2)->size);
if (NILP (dest))
{
dest = Fmake_bool_vector (make_number (nr_bits), Qnil);
changed = 1;
}
else
{
CHECK_BOOL_VECTOR (dest);
nr_bits = min (nr_bits, XBOOL_VECTOR (dest)->size);
}
eassert_and_assume (nr_bits >= 0);
nr_words = ROUNDUP(nr_bits, BITS_PER_SIZE_T) / BITS_PER_SIZE_T;
adata = (size_t*) XBOOL_VECTOR (dest)->data;
bdata = (size_t*) XBOOL_VECTOR (op1)->data;
cdata = (size_t*) XBOOL_VECTOR (op2)->data;
i = 0;
do
{
if (op == bool_vector_exclusive_or)
mword = bdata[i] ^ cdata[i];
else if (op == bool_vector_union || op == bool_vector_subsetp)
mword = bdata[i] | cdata[i];
else if (op == bool_vector_intersection)
mword = bdata[i] & cdata[i];
else if (op == bool_vector_set_difference)
mword = bdata[i] &~ cdata[i];
else
abort ();
changed |= adata[i] ^ mword;
if (op != bool_vector_subsetp)
adata[i] = mword;
i += 1;
}
while (i < nr_words);
return changed ? dest : Qnil;
}
/* Compute the number of trailing zero bits in val. If val is zero,
return the number of bits in val. */
static inline
unsigned int
count_trailing_zero_bits (size_t val)
{
if (val == 0)
return CHAR_BIT * sizeof (val);
#if defined USE_GCC_POPCOUNT && BITS_PER_SIZE_T == 64
return __builtin_ctzll (val);
#elif defined USE_GCC_POPCOUNT && BITS_PER_SIZE_T == 32
return __builtin_ctz (val);
#elif __MSC_VER && BITS_PER_SIZE_T == 64
# pragma intrinsic _BitScanForward64
{
/* No support test needed: support since 386. */
unsigned long result;
_BitScanForward64 (&result, val);
return (unsigned int) result;
}
#elif __MSC_VER && BITS_PER_SIZE_T == 32
# pragma intrinsic _BitScanForward
{
/* No support test needed: support since 386. */
unsigned long result;
_BitScanForward (&result, val);
return (unsigned int) result;
}
#else
{
unsigned int count;
count = 0;
for(val = ~val; val & 1; val >>= 1)
++count;
return count;
}
#endif
}
static inline
size_t
size_t_to_host_endian (size_t val)
{
#ifdef WORDS_BIGENDIAN
# if BITS_PER_SIZE_T == 64
return swap64 (val);
# else
return swap32 (val);
# endif
#else
return val;
#endif
}
DEFUN ("bool-vector-exclusive-or", Fbool_vector_exclusive_or,
Sbool_vector_exclusive_or, 2, 3, 0,
doc: /* Compute C = A ^ B, bitwise exclusive or.
A, B, and C must be bool vectors. If C is nil, allocate a new bool
vector in which to store the result. Return the destination vector if
it changed or nil otherwise. */
)
(Lisp_Object a, Lisp_Object b, Lisp_Object c)
{
return bool_vector_binop_driver (a, b, c, bool_vector_exclusive_or);
}
DEFUN ("bool-vector-union", Fbool_vector_union,
Sbool_vector_union, 2, 3, 0,
doc: /* Compute C = A | B, bitwise or.
A, B, and C must be bool vectors. If C is nil, allocate a new bool
vector in which to store the result. Return the destination vector if
it changed or nil otherwise. */)
(Lisp_Object a, Lisp_Object b, Lisp_Object c)
{
return bool_vector_binop_driver (a, b, c, bool_vector_union);
}
DEFUN ("bool-vector-intersection", Fbool_vector_intersection,
Sbool_vector_intersection, 2, 3, 0,
doc: /* Compute C = A & B, bitwise and.
A, B, and C must be bool vectors. If C is nil, allocate a new bool
vector in which to store the result. Return the destination vector if
it changed or nil otherwise. */)
(Lisp_Object a, Lisp_Object b, Lisp_Object c)
{
return bool_vector_binop_driver (a, b, c, bool_vector_intersection);
}
DEFUN ("bool-vector-set-difference", Fbool_vector_set_difference,
Sbool_vector_set_difference, 2, 3, 0,
doc: /* Compute C = A &~ B, set difference.
A, B, and C must be bool vectors. If C is nil, allocate a new bool
vector in which to store the result. Return the destination vector if
it changed or nil otherwise. */)
(Lisp_Object a, Lisp_Object b, Lisp_Object c)
{
return bool_vector_binop_driver (a, b, c, bool_vector_set_difference);
}
DEFUN ("bool-vector-subsetp", Fbool_vector_subsetp,
Sbool_vector_subsetp, 2, 2, 0,
doc: )
(Lisp_Object a, Lisp_Object b)
{
/* Like bool_vector_union, but doesn't modify b. */
return bool_vector_binop_driver (b, a, b, bool_vector_subsetp);
}
DEFUN ("bool-vector-not", Fbool_vector_not,
Sbool_vector_not, 1, 2, 0,
doc: /* Compute B = ~A.
B must be a bool vector. A must be a bool vector or nil.
If A is nil, allocate a new bool vector in which to store the result.
Return the destination vector. */)
(Lisp_Object a, Lisp_Object b)
{
EMACS_INT nr_bits;
size_t *bdata, *adata;
ptrdiff_t i;
size_t mword;
CHECK_BOOL_VECTOR (a);
nr_bits = XBOOL_VECTOR (a)->size;
if (NILP (b))
b = Fmake_bool_vector (make_number (nr_bits), Qnil);
else
{
CHECK_BOOL_VECTOR (b);
nr_bits = min (nr_bits, XBOOL_VECTOR (b)->size);
}