/* Functions to support link list bitsets.
   Copyright (C) 2002 Free Software Foundation, Inc.
   Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz).

   This program 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 2 of the License, 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "lbitset.h"
#include "obstack.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

/* This file implements linked-list bitsets.  These bitsets can be of
   arbitrary length and are more efficient than arrays of bits for
   large sparse sets.

   Usually if all the bits in an element are zero we remove the element
   from the list.  However, a side effect of the bit caching is that we
   do not always notice when an element becomes zero.  Hence the
   lbitset_weed function which removes zero elements.  */


/* Number of words to use for each element.  The larger the value the
   greater the size of the cache and the shorter the time to find a given bit
   but the more memory wasted for sparse bitsets and the longer the time
   to search for set bits.  */

#define LBITSET_ELT_WORDS 2

typedef bitset_word lbitset_word;

#define LBITSET_WORD_BITS BITSET_WORD_BITS

/* Number of bits stored in each element.  */
#define LBITSET_ELT_BITS \
  ((unsigned) (LBITSET_ELT_WORDS * LBITSET_WORD_BITS))

/* Lbitset element.   We use an array of bits for each element.
   These are linked together in a doubly-linked list.  */
typedef struct lbitset_elt_struct
{
  struct lbitset_elt_struct *next;	/* Next element.  */
  struct lbitset_elt_struct *prev;	/* Previous element.  */
  bitset_windex index;	/* bitno / BITSET_WORD_BITS.  */
  bitset_word words[LBITSET_ELT_WORDS];	/* Bits that are set.  */
}
lbitset_elt;


enum lbitset_find_mode
  { LBITSET_FIND, LBITSET_CREATE, LBITSET_SUBST };

static lbitset_elt lbitset_zero_elts[3]; /* Elements of all zero bits.  */

/* Obstack to allocate bitset elements from.  */
static struct obstack lbitset_obstack;
static int lbitset_obstack_init = 0;
static lbitset_elt *lbitset_free_list;	/* Free list of bitset elements.  */

extern void debug_lbitset PARAMS ((bitset));

#define LBITSET_CURRENT1(X) \
  ((lbitset_elt *) (void *) ((char *) (X) - offsetof (lbitset_elt, words)))

#define LBITSET_CURRENT(X) LBITSET_CURRENT1((X)->b.cdata)

#define LBITSET_HEAD(X) ((X)->l.head)
#define LBITSET_TAIL(X) ((X)->l.tail)

/* Allocate a lbitset element.  The bits are not cleared.  */
static inline lbitset_elt *
lbitset_elt_alloc (void)
{
  lbitset_elt *elt;

  if (lbitset_free_list != 0)
    {
      elt = lbitset_free_list;
      lbitset_free_list = elt->next;
    }
  else
    {
      if (!lbitset_obstack_init)
	{
	  lbitset_obstack_init = 1;

	  /* Let particular systems override the size of a chunk.  */

#ifndef OBSTACK_CHUNK_SIZE
#define OBSTACK_CHUNK_SIZE 0
#endif

	  /* Let them override the alloc and free routines too.  */

#ifndef OBSTACK_CHUNK_ALLOC
#define OBSTACK_CHUNK_ALLOC xmalloc
#endif

#ifndef OBSTACK_CHUNK_FREE
#define OBSTACK_CHUNK_FREE free
#endif

#if !defined(__GNUC__) || (__GNUC__ < 2)
#define __alignof__(type) 0
#endif

	  obstack_specify_allocation (&lbitset_obstack, OBSTACK_CHUNK_SIZE,
				      __alignof__ (lbitset_elt),
				      (void *(*)PARAMS ((long)))
				      OBSTACK_CHUNK_ALLOC,
				      (void (*)PARAMS ((void *)))
				      OBSTACK_CHUNK_FREE);
	}

      /* Perhaps we should add a number of new elements to the free
	 list.  */
      elt = (lbitset_elt *) obstack_alloc (&lbitset_obstack,
					   sizeof (lbitset_elt));
    }

  return elt;
}


/* Allocate a lbitset element.  The bits are cleared.  */
static inline lbitset_elt *
lbitset_elt_calloc (void)
{
  lbitset_elt *elt;

  elt = lbitset_elt_alloc ();
  memset (elt->words, 0, sizeof (elt->words));
  return elt;
}


static inline void
lbitset_elt_free (lbitset_elt *elt)
{
  elt->next = lbitset_free_list;
  lbitset_free_list = elt;
}


/* Unlink element ELT from bitset BSET.  */
static inline void
lbitset_elt_unlink (bitset bset, lbitset_elt *elt)
{
  lbitset_elt *next = elt->next;
  lbitset_elt *prev = elt->prev;

  if (prev)
    prev->next = next;

  if (next)
    next->prev = prev;

  if (LBITSET_HEAD (bset) == elt)
    LBITSET_HEAD (bset) = next;
  if (LBITSET_TAIL (bset) == elt)
    LBITSET_TAIL (bset) = prev;

  /* Update cache pointer.  Since the first thing we try is to insert
     before current, make current the next entry in preference to the
     previous.  */
  if (LBITSET_CURRENT (bset) == elt)
    {
      if (next)
	{
	  bset->b.cdata = next->words;
	  bset->b.cindex = next->index;
	}
      else if (prev)
	{
	  bset->b.cdata = prev->words;
	  bset->b.cindex = prev->index;
	}
      else
	{
	  bset->b.csize = 0;
	  bset->b.cdata = 0;
	}
    }

  lbitset_elt_free (elt);
}


/* Cut the chain of bitset BSET before element ELT and free the
   elements.  */
static inline void
lbitset_prune (bitset bset, lbitset_elt *elt)
{
  lbitset_elt *next;

  if (!elt)
    return;

  if (elt->prev)
    {
      LBITSET_TAIL (bset) = elt->prev;
      bset->b.cdata = elt->prev->words;
      bset->b.cindex = elt->prev->index;
      elt->prev->next = 0;
    }
  else
    {
      LBITSET_HEAD (bset) = 0;
      LBITSET_TAIL (bset) = 0;
      bset->b.cdata = 0;
      bset->b.csize = 0;
    }

  for (; elt; elt = next)
    {
      next = elt->next;
      lbitset_elt_free (elt);
    }
}


/* Return nonzero if all bits in an element are zero.  */
static inline int
lbitset_elt_zero_p (lbitset_elt *elt)
{
  int i;

  for (i = 0; i < LBITSET_ELT_WORDS; i++)
    if (elt->words[i])
      return 0;

  return 1;
}


/* Link the bitset element into the current bitset linked list.  */
static inline void
lbitset_elt_link (bitset bset, lbitset_elt *elt)
{
  bitset_windex windex = elt->index;
  lbitset_elt *ptr;
  lbitset_elt *current;

  if (bset->b.csize)
    current = LBITSET_CURRENT (bset);
  else
    current = LBITSET_HEAD (bset);

  /* If this is the first and only element, add it in.  */
  if (LBITSET_HEAD (bset) == 0)
    {
      elt->next = elt->prev = 0;
      LBITSET_HEAD (bset) = elt;
      LBITSET_TAIL (bset) = elt;
    }

  /* If this index is less than that of the current element, it goes
     somewhere before the current element.  */
  else if (windex < bset->b.cindex)
    {
      for (ptr = current;
	   ptr->prev && ptr->prev->index > windex; ptr = ptr->prev)
	continue;

      if (ptr->prev)
	ptr->prev->next = elt;
      else
	LBITSET_HEAD (bset) = elt;

      elt->prev = ptr->prev;
      elt->next = ptr;
      ptr->prev = elt;
    }

  /* Otherwise, it must go somewhere after the current element.  */
  else
    {
      for (ptr = current;
	   ptr->next && ptr->next->index < windex; ptr = ptr->next)
	continue;

      if (ptr->next)
	ptr->next->prev = elt;
      else
	LBITSET_TAIL (bset) = elt;

      elt->next = ptr->next;
      elt->prev = ptr;
      ptr->next = elt;
    }

  /* Set up so this is the first element searched.  */
  bset->b.cindex = windex;
  bset->b.csize = LBITSET_ELT_WORDS;
  bset->b.cdata = elt->words;
}


static lbitset_elt *
lbitset_elt_find (bitset bset, bitset_windex windex,
		  enum lbitset_find_mode mode)
{
  lbitset_elt *elt;
  lbitset_elt *current;

  if (bset->b.csize)
    {
      current = LBITSET_CURRENT (bset);
      /* Check if element is the cached element.  */
      if ((windex - bset->b.cindex) < bset->b.csize)
	return current;
    }
  else
    {
      current = LBITSET_HEAD (bset);
    }

  if (current)
    {
      if (windex < bset->b.cindex)
	{
	  for (elt = current;
	       elt->prev && elt->index > windex; elt = elt->prev)
	    continue;
	}
      else
	{
	  for (elt = current;
	       elt->next && (elt->index + LBITSET_ELT_WORDS - 1) < windex;
	       elt = elt->next)
	    continue;
	}

      /* ELT is the nearest to the one we want.  If it's not the one
	 we want, the one we want does not exist.  */
      if (elt && (windex - elt->index) < LBITSET_ELT_WORDS)
	{
	  bset->b.cindex = elt->index;
	  bset->b.csize = LBITSET_ELT_WORDS;
	  bset->b.cdata = elt->words;
	  return elt;
	}
    }

  switch (mode)
    {
    case LBITSET_FIND:
      return 0;

    case LBITSET_CREATE:
      windex -= windex % LBITSET_ELT_WORDS;

      elt = lbitset_elt_calloc ();
      elt->index = windex;
      lbitset_elt_link (bset, elt);
      return elt;

    case LBITSET_SUBST:
      return &lbitset_zero_elts[0];

    default:
      abort ();
    }
}


/* Weed out the zero elements from the list.  */
static inline void
lbitset_weed (bitset bset)
{
  lbitset_elt *elt;
  lbitset_elt *next;

  for (elt = LBITSET_HEAD (bset); elt; elt = next)
    {
      next = elt->next;
      if (lbitset_elt_zero_p (elt))
	lbitset_elt_unlink (bset, elt);
    }
}


/* Set all bits in the bitset to zero.  */
static void
lbitset_zero (bitset bset)
{
  lbitset_elt *head;

  head = LBITSET_HEAD (bset);
  if (!head)
    return;

  /* Clear a bitset by freeing the linked list at the head element.  */
  lbitset_prune (bset, head);
}


/* Return 1 if DST == SRC.  */
static inline int
lbitset_equal_p (bitset dst, bitset src)
{
  lbitset_elt *selt;
  lbitset_elt *delt;
  int j;

  if (src == dst)
    return 1;

  lbitset_weed (src);
  lbitset_weed (dst);
  for (selt = LBITSET_HEAD (src), delt = LBITSET_HEAD (dst);
       selt && delt; selt = selt->next, delt = delt->next)
    {
      if (selt->index != delt->index)
	return 0;

      for (j = 0; j < LBITSET_ELT_WORDS; j++)
	if (delt->words[j] != selt->words[j])
	  return 0;
    }
  return !selt && !delt;
}


/* Copy bits from bitset SRC to bitset DST.  */
static inline void
lbitset_copy (bitset dst, bitset src)
{
  lbitset_elt *elt;
  lbitset_elt *head;
  lbitset_elt *prev;
  lbitset_elt *tmp;

  if (src == dst)
    return;

  lbitset_zero (dst);

  head = LBITSET_HEAD (src);
  if (!head)
    return;

  prev = 0;
  for (elt = head; elt; elt = elt->next)
    {
      tmp = lbitset_elt_alloc ();
      tmp->index = elt->index;
      tmp->prev = prev;
      tmp->next = 0;
      if (prev)
	prev->next = tmp;
      else
	LBITSET_HEAD (dst) = tmp;
      prev = tmp;

      memcpy (tmp->words, elt->words, sizeof (elt->words));
    }
  LBITSET_TAIL (dst) = tmp;

  dst->b.csize = LBITSET_ELT_WORDS;
  dst->b.cdata = LBITSET_HEAD (dst)->words;
  dst->b.cindex = LBITSET_HEAD (dst)->index;
}


/* Copy bits from bitset SRC to bitset DST.  Return non-zero if
   bitsets different.  */
static inline int
lbitset_copy_cmp (bitset dst, bitset src)
{
  if (src == dst)
    return 0;

  if (!LBITSET_HEAD (dst))
    {
      lbitset_copy (dst, src);
      return LBITSET_HEAD (src) != 0;
    }

  if (lbitset_equal_p (dst, src))
    return 0;

  lbitset_copy (dst, src);
  return 1;
}


/* Return size in bits of bitset SRC.  */
static bitset_bindex
lbitset_size (bitset src)
{
  lbitset_elt *elt;

  elt = LBITSET_TAIL (src);
  if (!elt)
    return 0;

  /* Return current size of bitset in bits.  */
  return (elt->index + LBITSET_ELT_WORDS) * BITSET_WORD_BITS;
}


/* Set bit BITNO in bitset DST.  */
static void
lbitset_set (bitset dst, bitset_bindex bitno)
{
  bitset_windex windex = bitno / BITSET_WORD_BITS;

  lbitset_elt_find (dst, windex, LBITSET_CREATE);

  dst->b.cdata[windex - dst->b.cindex] |=
    (bitset_word) 1 << (bitno % BITSET_WORD_BITS);
}


/* Reset bit BITNO in bitset DST.  */
static void
lbitset_reset (bitset dst, bitset_bindex bitno)
{
  bitset_windex windex = bitno / BITSET_WORD_BITS;

  if (!lbitset_elt_find (dst, windex, LBITSET_FIND))
    return;

  dst->b.cdata[windex - dst->b.cindex] &=
    ~((bitset_word) 1 << (bitno % BITSET_WORD_BITS));

  /* If all the data is zero, perhaps we should unlink it now...  */
}


/* Test bit BITNO in bitset SRC.  */
static int
lbitset_test (bitset src, bitset_bindex bitno)
{
  bitset_windex windex = bitno / BITSET_WORD_BITS;

  if (!lbitset_elt_find (src, windex, LBITSET_FIND))
    return 0;

  return (src->b.cdata[windex - src->b.cindex]
	  >> (bitno % BITSET_WORD_BITS)) & 1;
}


static void
lbitset_free (bitset bset)
{
  lbitset_zero (bset);
}


/* Find list of up to NUM bits set in BSET starting from and including
 *NEXT and store in array LIST.  Return with actual number of bits
 found and with *NEXT indicating where search stopped.  */
static bitset_bindex
lbitset_list_reverse (bitset bset, bitset_bindex *list,
		      bitset_bindex num, bitset_bindex *next)
{
  bitset_bindex rbitno;
  bitset_bindex bitno;
  unsigned int bcount;
  bitset_bindex boffset;
  bitset_windex windex;
  bitset_bindex count;
  lbitset_elt *elt;
  bitset_word word;
  bitset_bindex n_bits;

  elt = LBITSET_TAIL (bset);
  if (!elt)
    return 0;

  n_bits = (elt->index + LBITSET_ELT_WORDS) * BITSET_WORD_BITS;
  rbitno = *next;

  if (rbitno >= n_bits)
    return 0;

  bitno = n_bits - (rbitno + 1);

  windex = bitno / BITSET_WORD_BITS;

  /* Skip back to starting element.  */
  for (; elt && elt->index > windex; elt = elt->prev)
    continue;

  if (!elt)
    return 0;

  if (windex >= elt->index + LBITSET_ELT_WORDS)
    {
      /* We are trying to start in no-mans land so start
	 at end of current elt.  */
      bcount = BITSET_WORD_BITS - 1;
      windex = elt->index + LBITSET_ELT_WORDS - 1;
    }
  else
    {
      bcount = bitno % BITSET_WORD_BITS;
    }

  count = 0;
  boffset = windex * BITSET_WORD_BITS;

  /* If num is 1, we could speed things up with a binary search
     of the word of interest.  */

  while (elt)
    {
      bitset_word *srcp = elt->words;

      for (; (windex - elt->index) < LBITSET_ELT_WORDS;
	   windex--, boffset -= BITSET_WORD_BITS,
	     bcount = BITSET_WORD_BITS - 1)
	{
	  word =
	    srcp[windex - elt->index] << (BITSET_WORD_BITS - 1 - bcount);

	  for (; word; bcount--)
	    {
	      if (word & BITSET_MSB)
		{
		  list[count++] = boffset + bcount;
		  if (count >= num)
		    {
		      *next = n_bits - (boffset + bcount);
		      return count;
		    }
		}
	      word <<= 1;
	    }
	}

      elt = elt->prev;
      if (elt)
	{
	  windex = elt->index + LBITSET_ELT_WORDS - 1;
	  boffset = windex * BITSET_WORD_BITS;
	}
    }

  *next = n_bits - (boffset + 1);
  return count;
}


/* Find list of up to NUM bits set in BSET starting from and including
 *NEXT and store in array LIST.  Return with actual number of bits
 found and with *NEXT indicating where search stopped.  */
static bitset_bindex
lbitset_list (bitset bset, bitset_bindex *list,
	      bitset_bindex num, bitset_bindex *next)
{
  bitset_bindex bitno;
  bitset_windex windex;
  bitset_bindex count;
  lbitset_elt *elt;
  lbitset_elt *head;
  bitset_word word;

  head = LBITSET_HEAD (bset);
  if (!head)
    return 0;

  bitno = *next;
  count = 0;

  if (!bitno)
    {
      /* This is the most common case.  */

      /* Start with the first element.  */
      elt = head;
      windex = elt->index;
      bitno = windex * BITSET_WORD_BITS;
    }
  else
    {
      windex = bitno / BITSET_WORD_BITS;

      /* Skip to starting element.  */
      for (elt = head;
	   elt && (elt->index + LBITSET_ELT_WORDS - 1) < windex;
	   elt = elt->next)
	continue;

      if (!elt)
	return 0;

      if (windex < elt->index)
	{
	  windex = elt->index;
	  bitno = windex * BITSET_WORD_BITS;
	}
      else
	{
	  bitset_word *srcp = elt->words;

	  /* We are starting within an element.  */

	  for (; (windex - elt->index) < LBITSET_ELT_WORDS; windex++)
	    {
	      word = srcp[windex - elt->index] >> (bitno % BITSET_WORD_BITS);

	      for (; word; bitno++)
		{
		  if (word & 1)
		    {
		      list[count++] = bitno;
		      if (count >= num)
			{
			  *next = bitno + 1;
			  return count;
			}
		    }
		  word >>= 1;
		}
	      bitno = (windex + 1) * BITSET_WORD_BITS;
	    }

	  elt = elt->next;
	  if (elt)
	    {
	      windex = elt->index;
	      bitno = windex * BITSET_WORD_BITS;
	    }
	}
    }


  /* If num is 1, we could speed things up with a binary search
     of the word of interest.  */

  while (elt)
    {
      int i;
      bitset_word *srcp = elt->words;

      if ((count + LBITSET_ELT_BITS) < num)
	{
	  /* The coast is clear, plant boot!  */

#if LBITSET_ELT_WORDS == 2
	  word = srcp[0];
	  if (word)
	    {
	      if (!(word & 0xffff))
		{
		  word >>= 16;
		  bitno += 16;
		}
	      if (!(word & 0xff))
		{
		  word >>= 8;
		  bitno += 8;
		}
	      for (; word; bitno++)
		{
		  if (word & 1)
		    list[count++] = bitno;
		  word >>= 1;
		}
	    }
	  windex++;
	  bitno = windex * BITSET_WORD_BITS;

	  word = srcp[1];
	  if (word)
	    {
	      if (!(word & 0xffff))
		{
		  word >>= 16;
		  bitno += 16;
		}
	      for (; word; bitno++)
		{
		  if (word & 1)
		    list[count++] = bitno;
		  word >>= 1;
		}
	    }
	  windex++;
	  bitno = windex * BITSET_WORD_BITS;
#else
	  for (i = 0; i < LBITSET_ELT_WORDS; i++)
	    {
	      word = srcp[i];
	      if (word)
		{
		  if (!(word & 0xffff))
		    {
		      word >>= 16;
		      bitno += 16;
		    }
		  if (!(word & 0xff))
		    {
		      word >>= 8;
		      bitno += 8;
		    }
		  for (; word; bitno++)
		    {
		      if (word & 1)
			list[count++] = bitno;
		      word >>= 1;
		    }
		}
	      windex++;
	      bitno = windex * BITSET_WORD_BITS;
	    }
#endif
	}
      else
	{
	  /* Tread more carefully since we need to check
	     if array overflows.  */

	  for (i = 0; i < LBITSET_ELT_WORDS; i++)
	    {
	      for (word = srcp[i]; word; bitno++)
		{
		  if (word & 1)
		    {
		      list[count++] = bitno;
		      if (count >= num)
			{
			  *next = bitno + 1;
			  return count;
			}
		    }
		  word >>= 1;
		}
	      windex++;
	      bitno = windex * BITSET_WORD_BITS;
	    }
	}

      elt = elt->next;
      if (elt)
	{
	  windex = elt->index;
	  bitno = windex * BITSET_WORD_BITS;
	}
    }

  *next = bitno;
  return count;
}


static int
lbitset_empty_p (bitset dst)
{
  lbitset_weed (dst);
  if (LBITSET_HEAD (dst))
      return 0;
  return 1;
}


static void
lbitset_ones (bitset dst)
{
  bitset_windex i;
  bitset_windex windex;
  lbitset_elt *elt;

  /* This is a decidedly unfriendly operation for a linked list
      bitset!  It makes a sparse bitset become dense.  An alternative
      is to have a flag that indicates that the bitset stores the
      complement of what it indicates.  */
  elt = LBITSET_TAIL (dst);
  /* Ignore empty set.  */
  if (!elt)
    return;

  windex = elt->index;
  for (i = 0; i < windex; i += LBITSET_ELT_WORDS)
    {
      /* Create new elements if they cannot be found.  */
      elt = lbitset_elt_find (dst, i, LBITSET_CREATE);
      memset (elt->words, -1, sizeof (elt->words));
    }
}


static void
lbitset_not (bitset dst, bitset src)
{
  lbitset_elt *elt;
  lbitset_elt *selt;
  lbitset_elt *delt;
  bitset_windex i;
  unsigned int j;
  bitset_windex windex;

  /* This is another unfriendly operation for a linked list
     bitset!  */
  elt = LBITSET_TAIL (dst);
  /* Ignore empty set.  */
  if (!elt)
    return;

  windex = elt->index;
  for (i = 0; i < windex; i += LBITSET_ELT_WORDS)
    {
      /* Create new elements for dst if they cannot be found
	 or substitute zero elements if src elements not found.  */
      selt = lbitset_elt_find (src, i, LBITSET_SUBST);
      delt = lbitset_elt_find (dst, i, LBITSET_CREATE);

      for (j = 0; j < LBITSET_ELT_WORDS; j++)
	delt->words[j] = ~selt->words[j];
    }
  lbitset_weed (dst);
  return;
}


/* Return 1 if DST == DST | SRC.  */
static int
lbitset_subset_p (bitset dst, bitset src)
{
  lbitset_elt *selt;
  lbitset_elt *delt;
  unsigned int j;

  for (selt = LBITSET_HEAD (src), delt = LBITSET_HEAD (dst);
       selt || delt; selt = selt->next, delt = delt->next)
    {
      if (!selt)
	selt = &lbitset_zero_elts[0];
      else if (!delt)
	delt = &lbitset_zero_elts[0];
      else if (selt->index != delt->index)
	{
	  if (selt->index < delt->index)
	    {
	      lbitset_zero_elts[2].next = delt;
	      delt = &lbitset_zero_elts[2];
	    }
	  else
	    {
	      lbitset_zero_elts[1].next = selt;
	      selt = &lbitset_zero_elts[1];
	    }
	}

      for (j = 0; j < LBITSET_ELT_WORDS; j++)
	if (delt->words[j] != (selt->words[j] | delt->words[j]))
	  return 0;
    }
  return 1;
}


/* Return 1 if DST & SRC == 0.  */
static int
lbitset_disjoint_p (bitset dst, bitset src)
{
  lbitset_elt *selt;
  lbitset_elt *delt;
  unsigned int j;

  for (selt = LBITSET_HEAD (src), delt = LBITSET_HEAD (dst);
       selt && delt; selt = selt->next, delt = delt->next)
    {
      if (selt->index != delt->index)
	{
	  if (selt->index < delt->index)
	    {
	      lbitset_zero_elts[2].next = delt;
	      delt = &lbitset_zero_elts[2];
	    }
	  else
	    {
	      lbitset_zero_elts[1].next = selt;
	      selt = &lbitset_zero_elts[1];
	    }
	  /* Since the elements are different, there is no
	     intersection of these elements.  */
	  continue;
	}

      for (j = 0; j < LBITSET_ELT_WORDS; j++)
	if (selt->words[j] & delt->words[j])
	  return 0;
    }
  return 1;
}


static int
lbitset_op3_cmp (bitset dst, bitset src1, bitset src2, enum bitset_ops op)
{
  lbitset_elt *selt1 = LBITSET_HEAD (src1);
  lbitset_elt *selt2 = LBITSET_HEAD (src2);
  lbitset_elt *delt = LBITSET_HEAD (dst);
  bitset_windex windex1;
  bitset_windex windex2;
  bitset_windex windex;
  lbitset_elt *stmp1;
  lbitset_elt *stmp2;
  lbitset_elt *dtmp;
  bitset_word *srcp1;
  bitset_word *srcp2;
  bitset_word *dstp;
  int changed = 0;
  unsigned int i;

  LBITSET_HEAD (dst) = 0;
  dst->b.csize = 0;

  windex1 = (selt1) ? selt1->index : BITSET_WINDEX_MAX;
  windex2 = (selt2) ? selt2->index : BITSET_WINDEX_MAX;

  while (selt1 || selt2)
    {
      /* Figure out whether we need to substitute zero elements for
	 missing links.  */
      if (windex1 == windex2)
	{
	  windex = windex1;
	  stmp1 = selt1;
	  stmp2 = selt2;
	  selt1 = selt1->next;
	  windex1 = (selt1) ? selt1->index : BITSET_WINDEX_MAX;
	  selt2 = selt2->next;
	  windex2 = (selt2) ? selt2->index : BITSET_WINDEX_MAX;
	}
      else if (windex1 < windex2)
	{
	  windex = windex1;
	  stmp1 = selt1;
	  stmp2 = &lbitset_zero_elts[0];
	  selt1 = selt1->next;
	  windex1 = (selt1) ? selt1->index : BITSET_WINDEX_MAX;
	}
      else
	{
	  windex = windex2;
	  stmp1 = &lbitset_zero_elts[0];
	  stmp2 = selt2;
	  selt2 = selt2->next;
	  windex2 = (selt2) ? selt2->index : BITSET_WINDEX_MAX;
	}

      /* Find the appropriate element from DST.  Begin by discarding
	 elements that we've skipped.  */
      while (delt && delt->index < windex)
	{
	  changed = 1;
	  dtmp = delt;
	  delt = delt->next;
	  lbitset_elt_free (dtmp);
	}
      if (delt && delt->index == windex)
	{
	  dtmp = delt;
	  delt = delt->next;
	}
      else
	dtmp = lbitset_elt_calloc ();

      /* Do the operation, and if any bits are set, link it into the
	 linked list.  */
      srcp1 = stmp1->words;
      srcp2 = stmp2->words;
      dstp = dtmp->words;
      switch (op)
	{
	case BITSET_OP_OR:
	  for (i = 0; i < LBITSET_ELT_WORDS; i++, dstp++)
	    {
	      bitset_word tmp = *srcp1++ | *srcp2++;

	      if (*dstp != tmp)
		{
		  changed = 1;
		  *dstp = tmp;
		}
	    }
	  break;

	case BITSET_OP_AND:
	  for (i = 0; i < LBITSET_ELT_WORDS; i++, dstp++)
	    {
	      bitset_word tmp = *srcp1++ & *srcp2++;

	      if (*dstp != tmp)
		{
		  changed = 1;
		  *dstp = tmp;
		}
	    }
	  break;

	case BITSET_OP_XOR:
	  for (i = 0; i < LBITSET_ELT_WORDS; i++, dstp++)
	    {
	      bitset_word tmp = *srcp1++ ^ *srcp2++;

	      if (*dstp != tmp)
		{
		  changed = 1;
		  *dstp = tmp;
		}
	    }
	  break;

	case BITSET_OP_ANDN:
	  for (i = 0; i < LBITSET_ELT_WORDS; i++, dstp++)
	    {
	      bitset_word tmp = *srcp1++ & ~(*srcp2++);

	      if (*dstp != tmp)
		{
		  changed = 1;
		  *dstp = tmp;
		}
	    }
	  break;

	default:
	  abort ();
	}

      if (!lbitset_elt_zero_p (dtmp))
	{
	  dtmp->index = windex;
	  /* Perhaps this could be optimised...  */
	  lbitset_elt_link (dst, dtmp);
	}
      else
	{
	  lbitset_elt_free (dtmp);
	}
    }

  /* If we have elements of DST left over, free them all.  */
  if (delt)
    {
      changed = 1;
      lbitset_prune (dst, delt);
    }

  return changed;
}


static int
lbitset_and_cmp (bitset dst, bitset src1, bitset src2)
{
  lbitset_elt *selt1 = LBITSET_HEAD (src1);
  lbitset_elt *selt2 = LBITSET_HEAD (src2);
  int changed;

  if (!selt2)
    {
      lbitset_weed (dst);
      changed = !LBITSET_HEAD (dst);
      lbitset_zero (dst);
      return changed;
    }
  else if (!selt1)
    {
      lbitset_weed (dst);
      changed = !LBITSET_HEAD (dst);
      lbitset_zero (dst);
      return changed;
    }
  return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_AND);
}


static void
lbitset_and (bitset dst, bitset src1, bitset src2)
{
  lbitset_and_cmp (dst, src1, src2);
}


static int
lbitset_andn_cmp (bitset dst, bitset src1, bitset src2)
{
  lbitset_elt *selt1 = LBITSET_HEAD (src1);
  lbitset_elt *selt2 = LBITSET_HEAD (src2);
  int changed;

  if (!selt2)
    {
      return lbitset_copy_cmp (dst, src1);
    }
  else if (!selt1)
    {
      lbitset_weed (dst);
      changed = !LBITSET_HEAD (dst);
      lbitset_zero (dst);
      return changed;
    }
  return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_ANDN);
}


static void
lbitset_andn (bitset dst, bitset src1, bitset src2)
{
  lbitset_andn_cmp (dst, src1, src2);
}


static int
lbitset_or_cmp (bitset dst, bitset src1, bitset src2)
{
  lbitset_elt *selt1 = LBITSET_HEAD (src1);
  lbitset_elt *selt2 = LBITSET_HEAD (src2);

  if (!selt2)
    {
      return lbitset_copy_cmp (dst, src1);
    }
  else if (!selt1)
    {
      return lbitset_copy_cmp (dst, src2);
    }
  return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_OR);
}


static void
lbitset_or (bitset dst, bitset src1, bitset src2)
{
  lbitset_or_cmp (dst, src1, src2);
}


static int
lbitset_xor_cmp (bitset dst, bitset src1, bitset src2)
{
  lbitset_elt *selt1 = LBITSET_HEAD (src1);
  lbitset_elt *selt2 = LBITSET_HEAD (src2);

  if (!selt2)
    {
      return lbitset_copy_cmp (dst, src1);
    }
  else if (!selt1)
    {
      return lbitset_copy_cmp (dst, src2);
    }
  return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_XOR);
}


static void
lbitset_xor (bitset dst, bitset src1, bitset src2)
{
  lbitset_xor_cmp (dst, src1, src2);
}



/* Vector of operations for linked-list bitsets.  */
struct bitset_vtable lbitset_vtable = {
  lbitset_set,
  lbitset_reset,
  bitset_toggle_,
  lbitset_test,
  lbitset_size,
  bitset_count_,
  lbitset_empty_p,
  lbitset_ones,
  lbitset_zero,
  lbitset_copy,
  lbitset_disjoint_p,
  lbitset_equal_p,
  lbitset_not,
  lbitset_subset_p,
  lbitset_and,
  lbitset_and_cmp,
  lbitset_andn,
  lbitset_andn_cmp,
  lbitset_or,
  lbitset_or_cmp,
  lbitset_xor,
  lbitset_xor_cmp,
  bitset_and_or_,
  bitset_and_or_cmp_,
  bitset_andn_or_,
  bitset_andn_or_cmp_,
  bitset_or_and_,
  bitset_or_and_cmp_,
  lbitset_list,
  lbitset_list_reverse,
  lbitset_free,
  BITSET_LIST
};


/* Return size of initial structure.  */
size_t
lbitset_bytes (bitset_bindex n_bits ATTRIBUTE_UNUSED)
{
  return sizeof (struct lbitset_struct);
}


/* Initialize a bitset.  */
bitset
lbitset_init (bitset bset, bitset_bindex n_bits ATTRIBUTE_UNUSED)
{
  bset->b.vtable = &lbitset_vtable;
  return bset;
}


void
lbitset_release_memory (void)
{
  lbitset_free_list = 0;
  if (lbitset_obstack_init)
    {
      lbitset_obstack_init = 0;
      obstack_free (&lbitset_obstack, NULL);
    }
}


/* Function to be called from debugger to debug lbitset.  */
void
debug_lbitset (bitset bset)
{
  lbitset_elt *elt;
  unsigned int i;

  if (!bset)
    return;

  for (elt = LBITSET_HEAD (bset); elt; elt = elt->next)
    {
      fprintf (stderr, "Elt %lu\n", (unsigned long) elt->index);
      for (i = 0; i < LBITSET_ELT_WORDS; i++)
	{
	  unsigned int j;
	  bitset_word word;

	  word = elt->words[i];

	  fprintf (stderr, "  Word %u:", i);
	  for (j = 0; j < LBITSET_WORD_BITS; j++)
	    if ((word & ((bitset_word) 1 << j)))
	      fprintf (stderr, " %u", j);
	  fprintf (stderr, "\n");
	}
    }
}
