/*****************************************************************************\
 *  bitstring.c - bitmap manipulation functions
 *****************************************************************************
 *  See comments about origin, limitations, and internal structure in
 *  bitstring.h.
 *
 *  Copyright (C) 2002 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Jim Garlick <garlick@llnl.gov>, Morris Jette <jette1@llnl.gov>
 *  CODE-OCEC-09-009. All rights reserved.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm 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.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#include "config.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "src/common/bitstring.h"
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"

#define BITSTR_MAGIC 		0x42434445
#define BITSTR_MAXPOS		(BITSTR_WORD_SIZE - 1)
#define BITSTR_OVERHEAD 	2
#define BITSTR_SHIFT 		6
#define BITSTR_SHIFT_WORD8	3
#define BITSTR_SHIFT_WORD64	6
#define BITSTR_MAXVAL		0xffffffffffffffff
#define BITSTR_WORD_SIZE	(sizeof(bitstr_t) * 8)

/* word of the bitstring bit is in */
#define	_bit_word(bit) 		(((bit) >> BITSTR_SHIFT) + BITSTR_OVERHEAD)

/* address of the byte containing bit */
#define _bit_byteaddr(name, bit) \
	((char *)((name) + BITSTR_OVERHEAD) + ((bit) >> BITSTR_SHIFT_WORD8))

/* mask for the bit within its word */
#ifdef SLURM_BIGENDIAN
#define	_bit_mask(bit) ((bitstr_t)1 << (BITSTR_MAXPOS - ((bit)&BITSTR_MAXPOS)))
#else
#define	_bit_mask(bit) ((bitstr_t)1 << ((bit)&BITSTR_MAXPOS))
#endif

/* mask for less significant bits within their word*/
#ifdef SLURM_BIGENDIAN
#define _bit_nmask(n) \
	((~((bitstr_t) 0)) << ((BITSTR_MAXPOS + 1) - ((n) & BITSTR_MAXPOS)))
#else
#define _bit_nmask(n) \
	(((bitstr_t) 1 << ((n) & BITSTR_MAXPOS)) - 1)
#endif

/* number of bits actually allocated to a bitstr */
#define _bitstr_bits(name) 	((name)[1])

/* magic cookie stored here */
#define _bitstr_magic(name) 	((name)[0])

/* words in a bitstring of nbits bits */
#define	_bitstr_words(nbits)	\
	((((nbits) + BITSTR_MAXPOS) >> BITSTR_SHIFT) + BITSTR_OVERHEAD)

#undef bit_test
#define bit_test(name, bit) \
	((((name)[_bit_word(bit)]) & _bit_mask(bit)) ? 1 : 0)

/* check signature */
#define _assert_bitstr_valid(name) do { \
	xassert((name) != NULL); \
	xassert(_bitstr_magic(name) == BITSTR_MAGIC); \
} while (0)

/* check bit position */
#define _assert_bit_valid(name,bit) do { \
	xassert((bit) >= 0); \
	xassert((bit) < _bitstr_bits(name)); 	\
} while (0)


/* Ensure valid bitmap size, prevent overflow in buffer size calculation */
#define _assert_valid_size(bit) do {	\
	xassert((bit) > 0);		\
	xassert((bit) <= 0x40000000); 	\
} while (0)

/*
 * Define slurm-specific aliases for use by plugins, see slurm_xlator.h
 * for details.
 */
strong_alias(bit_alloc,		slurm_bit_alloc);
strong_alias(bit_set,		slurm_bit_set);
strong_alias(bit_clear,		slurm_bit_clear);
strong_alias(bit_nclear,	slurm_bit_nclear);
strong_alias(bit_nset,		slurm_bit_nset);
strong_alias(bit_set_all,	slurm_bit_set_all);
strong_alias(bit_clear_all,	slurm_bit_clear_all);
strong_alias(bit_ffc,		slurm_bit_ffc);
strong_alias(bit_ffs,		slurm_bit_ffs);
strong_alias(bit_size,		slurm_bit_size);
strong_alias(bit_and,		slurm_bit_and);
strong_alias(bit_not,		slurm_bit_not);
strong_alias(bit_or,		slurm_bit_or);
strong_alias(bit_set_count,	slurm_bit_set_count);
strong_alias(bit_set_count_range, slurm_bit_set_count_range);
strong_alias(bit_clear_count,	slurm_bit_clear_count);
strong_alias(bit_rotate_copy,	slurm_bit_rotate_copy);
strong_alias(bit_rotate,	slurm_bit_rotate);
strong_alias(bit_fmt,		slurm_bit_fmt);
strong_alias(bit_fmt_full,	slurm_bit_fmt_full);
strong_alias(bit_unfmt,		slurm_bit_unfmt);
strong_alias(bitfmt2int,	slurm_bitfmt2int);
strong_alias(bit_fmt_hexmask,	slurm_bit_fmt_hexmask);
strong_alias(bit_fmt_hexmask_trim, slurm_bit_fmt_hexmask_trim);
strong_alias(bit_unfmt_hexmask,	slurm_bit_unfmt_hexmask);
strong_alias(bit_fls,		slurm_bit_fls);
strong_alias(bit_fls_from_bit,	slurm_bit_fls_from_bit);
strong_alias(bit_fill_gaps,	slurm_bit_fill_gaps);
strong_alias(bit_super_set,	slurm_bit_super_set);
strong_alias(bit_overlap,	slurm_bit_overlap);
strong_alias(bit_overlap_any,	slurm_bit_overlap_any);
strong_alias(bit_equal,		slurm_bit_equal);
strong_alias(bit_copy,		slurm_bit_copy);
strong_alias(bit_pick_cnt,	slurm_bit_pick_cnt);
strong_alias(bit_copybits,	slurm_bit_copybits);
strong_alias(bit_get_bit_num,	slurm_bit_get_bit_num);

#ifdef SLURM_BIGENDIAN
static const char* hexmask_lookup[256] = {
	"00",	"80",	"40",	"C0",	"20",	"A0",	"60",	"E0",
	"10",	"90",	"50",	"D0",	"30",	"B0",	"70",	"F0",
	"08",	"88",	"48",	"C8",	"28",	"A8",	"68",	"E8",
	"18",	"98",	"58",	"D8",	"38",	"B8",	"78",	"F8",
	"04",	"84",	"44",	"C4",	"24",	"A4",	"64",	"E4",
	"14",	"94",	"54",	"D4",	"34",	"B4",	"74",	"F4",
	"0C",	"8C",	"4C",	"CC",	"2C",	"AC",	"6C",	"EC",
	"1C",	"9C",	"5C",	"DC",	"3C",	"BC",	"7C",	"FC",
	"02",	"82",	"42",	"C2",	"22",	"A2",	"62",	"E2",
	"12",	"92",	"52",	"D2",	"32",	"B2",	"72",	"F2",
	"0A",	"8A",	"4A",	"CA",	"2A",	"AA",	"6A",	"EA",
	"1A",	"9A",	"5A",	"DA",	"3A",	"BA",	"7A",	"FA",
	"06",	"86",	"46",	"C6",	"26",	"A6",	"66",	"E6",
	"16",	"96",	"56",	"D6",	"36",	"B6",	"76",	"F6",
	"0E",	"8E",	"4E",	"CE",	"2E",	"AE",	"6E",	"EE",
	"1E",	"9E",	"5E",	"DE",	"3E",	"BE",	"7E",	"FE",
	"01",	"81",	"41",	"C1",	"21",	"A1",	"61",	"E1",
	"11",	"91",	"51",	"D1",	"31",	"B1",	"71",	"F1",
	"09",	"89",	"49",	"C9",	"29",	"A9",	"69",	"E9",
	"19",	"99",	"59",	"D9",	"39",	"B9",	"79",	"F9",
	"05",	"85",	"45",	"C5",	"25",	"A5",	"65",	"E5",
	"15",	"95",	"55",	"D5",	"35",	"B5",	"75",	"F5",
	"0D",	"8D",	"4D",	"CD",	"2D",	"AD",	"6D",	"ED",
	"1D",	"9D",	"5D",	"DD",	"3D",	"BD",	"7D",	"FD",
	"03",	"83",	"43",	"C3",	"23",	"A3",	"63",	"E3",
	"13",	"93",	"53",	"D3",	"33",	"B3",	"73",	"F3",
	"0B",	"8B",	"4B",	"CB",	"2B",	"AB",	"6B",	"EB",
	"1B",	"9B",	"5B",	"DB",	"3B",	"BB",	"7B",	"FB",
	"07",	"87",	"47",	"C7",	"27",	"A7",	"67",	"E7",
	"17",	"97",	"57",	"D7",	"37",	"B7",	"77",	"F7",
	"0F",	"8F",	"4F",	"CF",	"2F",	"AF",	"6F",	"EF",
	"1F",	"9F",	"5F",	"DF",	"3F",	"BF",	"7F",	"FF",
};
#else
static const char* hexmask_lookup[256] = {
	"00",	"01",	"02",	"03",	"04",	"05",	"06",	"07",
	"08",	"09",	"0A",	"0B",	"0C",	"0D",	"0E",	"0F",
	"10",	"11",	"12",	"13",	"14",	"15",	"16",	"17",
	"18",	"19",	"1A",	"1B",	"1C",	"1D",	"1E",	"1F",
	"20",	"21",	"22",	"23",	"24",	"25",	"26",	"27",
	"28",	"29",	"2A",	"2B",	"2C",	"2D",	"2E",	"2F",
	"30",	"31",	"32",	"33",	"34",	"35",	"36",	"37",
	"38",	"39",	"3A",	"3B",	"3C",	"3D",	"3E",	"3F",
	"40",	"41",	"42",	"43",	"44",	"45",	"46",	"47",
	"48",	"49",	"4A",	"4B",	"4C",	"4D",	"4E",	"4F",
	"50",	"51",	"52",	"53",	"54",	"55",	"56",	"57",
	"58",	"59",	"5A",	"5B",	"5C",	"5D",	"5E",	"5F",
	"60",	"61",	"62",	"63",	"64",	"65",	"66",	"67",
	"68",	"69",	"6A",	"6B",	"6C",	"6D",	"6E",	"6F",
	"70",	"71",	"72",	"73",	"74",	"75",	"76",	"77",
	"78",	"79",	"7A",	"7B",	"7C",	"7D",	"7E",	"7F",
	"80",	"81",	"82",	"83",	"84",	"85",	"86",	"87",
	"88",	"89",	"8A",	"8B",	"8C",	"8D",	"8E",	"8F",
	"90",	"91",	"92",	"93",	"94",	"95",	"96",	"97",
	"98",	"99",	"9A",	"9B",	"9C",	"9D",	"9E",	"9F",
	"A0",	"A1",	"A2",	"A3",	"A4",	"A5",	"A6",	"A7",
	"A8",	"A9",	"AA",	"AB",	"AC",	"AD",	"AE",	"AF",
	"B0",	"B1",	"B2",	"B3",	"B4",	"B5",	"B6",	"B7",
	"B8",	"B9",	"BA",	"BB",	"BC",	"BD",	"BE",	"BF",
	"C0",	"C1",	"C2",	"C3",	"C4",	"C5",	"C6",	"C7",
	"C8",	"C9",	"CA",	"CB",	"CC",	"CD",	"CE",	"CF",
	"D0",	"D1",	"D2",	"D3",	"D4",	"D5",	"D6",	"D7",
	"D8",	"D9",	"DA",	"DB",	"DC",	"DD",	"DE",	"DF",
	"E0",	"E1",	"E2",	"E3",	"E4",	"E5",	"E6",	"E7",
	"E8",	"E9",	"EA",	"EB",	"EC",	"ED",	"EE",	"EF",
	"F0",	"F1",	"F2",	"F3",	"F4",	"F5",	"F6",	"F7",
	"F8",	"F9",	"FA",	"FB",	"FC",	"FD",	"FE",	"FF",
};
#endif

/*
 * Implement a simple cache for a specific size of bitstring rather
 * than churning through xfree() + xmalloc() constantly.
 * This is intended for use with node_record_count in slurmctld, which
 * is constantly cycling through bitstrings of that size in the scheduler.
 */
static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static void *cached_bitstr = NULL;
static bitoff_t cached_bitstr_len = 0;

static void *_cache_pop(void)
{
	void *b = NULL;

	slurm_mutex_lock(&cache_mutex);
	if (cached_bitstr) {
		b = cached_bitstr;
		cached_bitstr = *(void **) b;
	}
	slurm_mutex_unlock(&cache_mutex);

	return b;
}

static void _cache_push(void *b)
{
	slurm_mutex_lock(&cache_mutex);
	*(void **) b = cached_bitstr;
	cached_bitstr = b;
	slurm_mutex_unlock(&cache_mutex);
}

extern void bit_cache_init(bitoff_t nbits)
{
/*
 * Disable cache in MEMORY_LEAK_DEBUG as otherwise valgrind may attribute
 * any bitstring leaks that cycled through the cache to the original
 * allocation location, which is potentially completely unrelated to where
 * the leak is. The only downside is that a leak of the cache itself will
 * not be caught.
 */
#ifndef MEMORY_LEAK_DEBUG
	slurm_mutex_lock(&cache_mutex);
	if (cached_bitstr_len && (cached_bitstr_len != nbits))
		fatal_abort("%s: cannot change size once set", __func__);
	cached_bitstr_len = nbits;
	slurm_mutex_unlock(&cache_mutex);
#endif
}

extern void bit_cache_fini(void)
{
	void *b = NULL;
	while ((b = _cache_pop()))
		xfree(b);
}

/*
 * Allocate a bitstring.
 *   nbits (IN)		valid bits in new bitstring, initialized to all clear
 *   RETURN		new bitstring
 */
bitstr_t *bit_alloc(bitoff_t nbits)
{
	bitstr_t *new = NULL;

	_assert_valid_size(nbits);

	if (nbits == cached_bitstr_len)
		new = _cache_pop();

	if (new)
		memset(new, 0, _bitstr_words(nbits) * sizeof(bitstr_t));
	else
		new = xcalloc(_bitstr_words(nbits), sizeof(bitstr_t));

	_bitstr_magic(new) = BITSTR_MAGIC;
	_bitstr_bits(new) = nbits;

	return new;
}

static bitstr_t *bit_alloc_nz(bitoff_t nbits)
{
	bitstr_t *new = NULL;

	_assert_valid_size(nbits);

	if (nbits == cached_bitstr_len)
		new = _cache_pop();

	if (!new)
		new = xcalloc_nz(_bitstr_words(nbits), sizeof(bitstr_t));

	_bitstr_magic(new) = BITSTR_MAGIC;
	_bitstr_bits(new) = nbits;

	return new;
}

/*
 * Reallocate a bitstring (expand or contract size).
 *   b (IN)		pointer to old bitstring
 *   nbits (IN)		valid bits in new bitstr
 *   RETURN		new bitstring
 */
bitstr_t *slurm_bit_realloc(bitstr_t **b, bitoff_t nbits)
{
	_assert_bitstr_valid(*b);
	_assert_valid_size(nbits);

	xrecalloc(*b, _bitstr_words(nbits), sizeof(bitstr_t));

	_assert_bitstr_valid(*b);
	_bitstr_bits(*b) = nbits;

	return *b;
}

/*
 * Free a bitstr.
 *   b (IN/OUT)	bitstr to be freed
 */
void slurm_bit_free(bitstr_t **b)
{
	xassert(*b);
	xassert(_bitstr_magic(*b) == BITSTR_MAGIC);

	_bitstr_magic(*b) = 0;

	if (_bitstr_bits(*b) == cached_bitstr_len) {
		_cache_push(*b);
		*b = NULL;
	} else
		xfree(*b);
}

void bit_free_ptr(void *p)
{
	bitstr_t *b = p;
	slurm_bit_free(&b);
}

/*
 * Return the number of possible bits in a bitstring.
 *   b (IN)		bitstring to check
 *   RETURN		number of bits allocated
 */
bitoff_t
bit_size(bitstr_t *b)
{
	_assert_bitstr_valid(b);
	return _bitstr_bits(b);
}

/*
 * Is bit N of bitstring b set?
 *   b (IN)		bitstring to test
 *   bit (IN)		bit position to test
 *   RETURN		1 if bit set, 0 if clear
 */
int
slurm_bit_test(bitstr_t *b, bitoff_t bit)
{
	_assert_bitstr_valid(b);
	_assert_bit_valid(b, bit);
	return bit_test(b, bit);
}

/*
 * Set bit N of bitstring.
 *   b (IN)		target bitstring
 *   bit (IN)		bit position to set
 */
void
bit_set(bitstr_t *b, bitoff_t bit)
{
	_assert_bitstr_valid(b);
	_assert_bit_valid(b, bit);
	b[_bit_word(bit)] |= _bit_mask(bit);
}

/*
 * Clear bit N of bitstring
 *   b (IN)		target bitstring
 *   bit (IN)		bit position to clear
 */
void
bit_clear(bitstr_t *b, bitoff_t bit)
{
	_assert_bitstr_valid(b);
	_assert_bit_valid(b, bit);
	b[_bit_word(bit)] &= ~_bit_mask(bit);
}

/*
 * Set bits start ... stop in bitstring
 *   b (IN)		target bitstring
 *   start (IN)		starting (low numbered) bit position
 *   stop (IN)		ending (higher numbered) bit position
 */
void
bit_nset(bitstr_t *b, bitoff_t start, bitoff_t stop)
{
	_assert_bitstr_valid(b);
	_assert_bit_valid(b, start);
	_assert_bit_valid(b, stop);

	while (start <= stop && start % 8 > 0) 	     /* partial first byte? */
		bit_set(b, start++);
	while (stop >= start && (stop+1) % 8 > 0)    /* partial last byte? */
		bit_set(b, stop--);
	if (stop > start) {                          /* now do whole bytes */
		xassert((stop-start+1) % 8 == 0);
		memset(_bit_byteaddr(b, start), 0xff, (stop-start+1) / 8);
	}
}

/*
 * Clear bits start ... stop in bitstring
 *   b (IN)		target bitstring
 *   start (IN)		starting (low numbered) bit position
 *   stop (IN)		ending (higher numbered) bit position
 */
void
bit_nclear(bitstr_t *b, bitoff_t start, bitoff_t stop)
{
	_assert_bitstr_valid(b);
	_assert_bit_valid(b, start);
	_assert_bit_valid(b, stop);

	while (start <= stop && start % 8 > 0) 	/* partial first byte? */
		bit_clear(b, start++);
	while (stop >= start && (stop+1) % 8 > 0)/* partial last byte? */
		bit_clear(b, stop--);
	if (stop > start) {			/* now do whole bytes */
		xassert((stop-start+1) % 8 == 0);
		memset(_bit_byteaddr(b, start), 0, (stop-start+1) / 8);
	}
}

/*
 * Set all bits in bitstring
 *   b (IN)		target bitstring
 */
void
bit_set_all(bitstr_t *b)
{
	bit_nset(b, 0, bit_size(b)-1);
}

/*
 * Clear all bits in bitstring
 *   b (IN)		target bitstring
 */
void
bit_clear_all(bitstr_t *b)
{
	bit_nclear(b, 0, bit_size(b)-1);
}

/*
 * Find first bit clear in bitstring.
 *   b (IN)		bitstring to search
 *   nbits (IN)		number of bits to search
 *   RETURN      	resulting bit position (-1 if none found)
 */
bitoff_t
bit_ffc(bitstr_t *b)
{
	bitoff_t bit = 0, value = -1;

	_assert_bitstr_valid(b);

	while (bit < _bitstr_bits(b) && value == -1) {
		int32_t word = _bit_word(bit);

		if (b[word] == BITSTR_MAXVAL) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}
		while (bit < _bitstr_bits(b) && _bit_word(bit) == word) {
			if (!bit_test(b, bit)) {
				value = bit;
				break;
			}
			bit++;
		}
	}
	return value;
}

/*
 * Find first bit set in b from an offset.
 *   b (IN)		bitstring to search
 *   bit (IN)		bit to start the search at
 *   RETURN		resulting bit position (-1 if none found)
 */
bitoff_t bit_ffs_from_bit(bitstr_t *b, bitoff_t bit)
{
	bitoff_t value = -1;
	int32_t word;
#if (HAVE___BUILTIN_CLZLL && (defined SLURM_BIGENDIAN)) || \
    (HAVE___BUILTIN_CTZLL && (!defined SLURM_BIGENDIAN))
	bitstr_t bitstr_word;

	_assert_bitstr_valid(b);
	if ((bit % BITSTR_WORD_SIZE) && (bit < _bitstr_bits(b))) {
		bitstr_t mask = ~_bit_nmask(bit);
		bit -= (bit % BITSTR_WORD_SIZE);
		word = _bit_word(bit);
		bitstr_word = b[word] & mask;
		goto test_word;
	}

	while ((bit < _bitstr_bits(b)) && (value == -1)) {

		word = _bit_word(bit);
		bitstr_word = b[word];
test_word:
		if (bitstr_word == 0) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}
#if HAVE___BUILTIN_CLZLL && (defined SLURM_BIGENDIAN)
		value = bit + __builtin_clzll(bitstr_word);
#elif HAVE___BUILTIN_CTZLL && (!defined SLURM_BIGENDIAN)
		value = bit + __builtin_ctzll(bitstr_word);
#endif
	}

	if (value < _bitstr_bits(b))
		return value;
	else
		return -1;
#else
	_assert_bitstr_valid(b);
	if ((bit % BITSTR_WORD_SIZE) && (bit < _bitstr_bits(b)) &&
	    (b[_bit_word(bit)] == 0)) {
		bit += BITSTR_WORD_SIZE;
		bit -= (bit % BITSTR_WORD_SIZE);
	}
	while ((bit < _bitstr_bits(b)) && (value == -1)) {
		word = _bit_word(bit);
		if (b[word] == 0) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}
		while (bit < _bitstr_bits(b) && _bit_word(bit) == word) {
			if (bit_test(b, bit)) {
				value = bit;
				break;
			}
			bit++;
		}
	}
	return value;
#endif
}

/*
 * Find first bit set in b.
 *   b (IN)		bitstring to search
 *   RETURN 		resulting bit position (-1 if none found)
 */
bitoff_t bit_ffs(bitstr_t *b)
{
	return bit_ffs_from_bit(b, 0);
}

/*
 * Find last bit set in b.
 *   b (IN)		bitstring to search
 *   RETURN 		resulting bit position (-1 if none found)
 */
bitoff_t bit_fls(bitstr_t *b)
{
	/* zero origin */
	bitoff_t bit = _bitstr_bits(b) - 1;

	return bit_fls_from_bit(b, bit);
}

/*
 * Find last bit set in b from an offset.
 *   b (IN)		bitstring to search
 *   bit (IN)		bit to start the search at
 *   RETURN		resulting bit position (-1 if none found)
 */
bitoff_t bit_fls_from_bit(bitstr_t *b, bitoff_t bit)
{
	bitoff_t value = -1;
	int32_t word;

	_assert_bitstr_valid(b);
	_assert_bit_valid(b, bit);

	if (_bitstr_bits(b) == 0)	/* empty bitstring */
		return -1;

	while (bit >= 0 && 		/* test partial words */
		(_bit_word(bit) == _bit_word(bit + 1))) {
		if (bit_test(b, bit)) {
			value = bit;
			break;
		}
		bit--;
	}
	while (bit >= 0 && value == -1) {	/* test whole words */
		word = _bit_word(bit);
		if (b[word] == 0) {
			bit -= BITSTR_WORD_SIZE;
			continue;
		}
#if HAVE___BUILTIN_CTZLL && (defined SLURM_BIGENDIAN)
		value = bit - __builtin_ctzll(b[word]);
#elif HAVE___BUILTIN_CLZLL && (!defined SLURM_BIGENDIAN)
		value = bit - __builtin_clzll(b[word]);
#else
		while (bit >= 0) {
			if (bit_test(b, bit)) {
				value = bit;
				break;
			}
			bit--;
		}
#endif
	}
	return value;
}

/*
 * set all bits between the first and last bits set (i.e. fill in the gaps
 *	to make set bits contiguous)
 */
void
bit_fill_gaps(bitstr_t *b)
{
	bitoff_t first, last;

	_assert_bitstr_valid(b);

	first = bit_ffs(b);
	if (first == -1)
		return;

	last = bit_fls(b);
	bit_nset(b, first, last);

	return;
}

/*
 * return 1 if all bits set in b1 are also set in b2, 0 otherwise
 */
int
bit_super_set(bitstr_t *b1, bitstr_t *b2)
{
	bitoff_t bit;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);
	xassert(_bitstr_bits(b1) == _bitstr_bits(b2));

	for (bit = 0; bit < _bitstr_bits(b1); bit += BITSTR_WORD_SIZE) {
		if (b1[_bit_word(bit)] != (b1[_bit_word(bit)] &
		                           b2[_bit_word(bit)])) {
			bitstr_t mask;
			if ((bit + BITSTR_WORD_SIZE) <= _bitstr_bits(b1))
				return 0;
			mask = _bit_nmask(_bitstr_bits(b1));
			if ((b1[_bit_word(bit)] & mask) != (b1[_bit_word(bit)] &
							    b2[_bit_word(bit)] &
							    mask))
				return 0;
		}

	}

	return 1;
}

/*
 * return 1 if b1 and b2 are identical, 0 otherwise
 */
extern int
bit_equal(bitstr_t *b1, bitstr_t *b2)
{
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);

	if (_bitstr_bits(b1) != _bitstr_bits(b2))
		return 0;

	bit_cnt = _bitstr_bits(b1);

	for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt;
	     bit += BITSTR_WORD_SIZE) {
		if (b1[_bit_word(bit)] != b2[_bit_word(bit)])
			return 0;
	}
	if (bit < bit_cnt) {
		uint64_t mask = _bit_nmask(bit_cnt);
		if ((b1[_bit_word(bit)] ^ b2[_bit_word(bit)]) & mask)
			return 0;
	}

	return 1;
}



/*
 * b1 &= b2 as many bits as both bitstr_t have
 *   b1 (IN/OUT)	first string
 *   b2 (IN)		second bitstring
 */
void
bit_and(bitstr_t *b1, bitstr_t *b2)
{
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);

	bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2));
	for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt;
	     bit += BITSTR_WORD_SIZE)
		b1[_bit_word(bit)] &= b2[_bit_word(bit)];

	if (bit < bit_cnt) {
		uint64_t mask = ~(_bit_nmask(bit_cnt));
		b1[_bit_word(bit)] &= (b2[_bit_word(bit)] | mask);
	}
}

/*
 * b1 &= ~b2 as many bits as both bitstr_t have
 * b1 (IN/OUT)
 * b2 (IN)
 */
void bit_and_not(bitstr_t *b1, bitstr_t *b2)
{
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);

	bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2));
	for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt;
	     bit += BITSTR_WORD_SIZE)
		b1[_bit_word(bit)] &= ~b2[_bit_word(bit)];

	if (bit < bit_cnt) {
		uint64_t mask = _bit_nmask(bit_cnt);
		b1[_bit_word(bit)] &= ~(b2[_bit_word(bit)] & mask);
	}
}

/*
 * b1 = ~b1		one's complement
 *   b1 (IN/OUT)	first bitmap
 */
void
bit_not(bitstr_t *b)
{
	bitoff_t bit;

	_assert_bitstr_valid(b);

	for (bit = 0; bit < _bitstr_bits(b); bit += BITSTR_WORD_SIZE)
		b[_bit_word(bit)] = ~b[_bit_word(bit)];
}

/*
 * b1 |= b2 as many bits as both bitstr_t have
 *   b1 (IN/OUT)	first bitmap
 *   b2 (IN)		second bitmap
 */
void
bit_or(bitstr_t *b1, bitstr_t *b2)
{
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);

	bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2));
	for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt;
	     bit += BITSTR_WORD_SIZE)
		b1[_bit_word(bit)] |= b2[_bit_word(bit)];

	if (bit < bit_cnt) {
		uint64_t mask = _bit_nmask(bit_cnt);
		b1[_bit_word(bit)] |= (b2[_bit_word(bit)] & mask);
	}
}

/*
 * b1 |= ~b2 as many bits as both bitstr_t have
 * b1 (IN/OUT)
 * b2 (IN)
 */
void bit_or_not(bitstr_t *b1, bitstr_t *b2)
{
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);

	bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2));
	for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt;
	     bit += BITSTR_WORD_SIZE)
		b1[_bit_word(bit)] |= ~b2[_bit_word(bit)];

	if (bit < bit_cnt) {
		uint64_t mask = ~(_bit_nmask(bit_cnt));
		b1[_bit_word(bit)] |= ~(b2[_bit_word(bit)] | mask);
	}
}

/*
 * return a copy of the supplied bitmap
 */
bitstr_t *
bit_copy(bitstr_t *b)
{
	bitstr_t *new;
	int32_t newsize_bits;
	size_t len = 0;  /* Number of bytes to memcpy() */

	_assert_bitstr_valid(b);

	newsize_bits  = bit_size(b);
	len = (_bitstr_words(newsize_bits) - BITSTR_OVERHEAD)*sizeof(bitstr_t);
	new = bit_alloc_nz(newsize_bits);
	memcpy(&new[BITSTR_OVERHEAD], &b[BITSTR_OVERHEAD], len);

	return new;
}

void
bit_copybits(bitstr_t *dest, bitstr_t *src)
{
	int32_t len;

	_assert_bitstr_valid(dest);
	_assert_bitstr_valid(src);
	xassert(bit_size(src) == bit_size(dest));

	len = (_bitstr_words(bit_size(src)) - BITSTR_OVERHEAD)*sizeof(bitstr_t);
	memcpy(&dest[BITSTR_OVERHEAD], &src[BITSTR_OVERHEAD], len);
}

#ifdef HAVE___BUILTIN_POPCOUNTLL
#define hweight __builtin_popcountll
#else
/*
 * Returns the hamming weight (i.e. the number of bits set) in a word.
 * NOTE: This routine borrowed from Linux 4.9 <tools/lib/hweight.c>.
 */
static uint64_t
hweight(uint64_t w)
{
        w -= (w >> 1) & 0x5555555555555555ul;
        w =  (w & 0x3333333333333333ul) + ((w >> 2) & 0x3333333333333333ul);
        w =  (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0ful;
        return (w * 0x0101010101010101ul) >> 56;
}
#endif

/*
 * Count the number of bits set in bitstring.
 *   b (IN)		bitstring to check
 *   RETURN		count of set bits
 */
int32_t
bit_set_count(bitstr_t *b)
{
	int32_t count = 0;
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b);

	bit_cnt = _bitstr_bits(b);
	for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt;
	     bit += BITSTR_WORD_SIZE) {
		count += hweight(b[_bit_word(bit)]);
	}
	if (bit < bit_cnt) {
		uint64_t mask = _bit_nmask(bit_cnt);
		count += hweight(b[_bit_word(bit)] & mask);
	}
	return count;
}

/*
 * Count the number of bits set in a range of bitstring.
 *   b (IN)		bitstring to check
 *   start (IN) first bit to check
 *   end (IN)	last bit to check+1
 *   RETURN		count of set bits
 */
int32_t
bit_set_count_range(bitstr_t *b, int32_t start, int32_t end)
{
	int32_t count = 0, eow;
	bitoff_t bit;

	_assert_bitstr_valid(b);
	_assert_bit_valid(b,start);

	end = MIN(end, _bitstr_bits(b));
	/* end of word */
	eow = (((start + BITSTR_MAXPOS) >> BITSTR_SHIFT) << BITSTR_SHIFT);

	bit = start;
	if ((start < eow) && (eow <= end)) {
		uint64_t mask = ~_bit_nmask(start);
		count += hweight(b[_bit_word(bit)] & mask);
		bit = eow;
	} else if (eow > start) {
		uint64_t mask = ~_bit_nmask(start);
		mask &= _bit_nmask(end);
		count += hweight(b[_bit_word(bit)] & mask);
		bit = eow;
	}
	for (; (bit + BITSTR_WORD_SIZE) <= end ; bit += BITSTR_WORD_SIZE) {
		count += hweight(b[_bit_word(bit)]);
	}
	if (bit < end) {
		uint64_t mask = _bit_nmask(end);
		count += hweight(b[_bit_word(bit)] & mask);
	}

	return count;
}

static int32_t _bit_overlap_internal(bitstr_t *b1, bitstr_t *b2, bool count_it)
{
	int32_t count = 0;
	int64_t anded;
	bitoff_t bit, bit_cnt;

	_assert_bitstr_valid(b1);
	_assert_bitstr_valid(b2);
	xassert(_bitstr_bits(b1) == _bitstr_bits(b2));

	bit_cnt = _bitstr_bits(b1);
	for (bit = 0; bit < bit_cnt; bit += BITSTR_WORD_SIZE) {
		if ((bit + BITSTR_WORD_SIZE - 1) >= bit_cnt)
			break;
		anded = b1[_bit_word(bit)] & b2[_bit_word(bit)];
		if (count_it)
			count += hweight(anded);
		else if (anded)
			return 1;
	}

	if (bit < bit_cnt) {
		uint64_t mask = _bit_nmask(bit_cnt);
		anded = b1[_bit_word(bit)] & b2[_bit_word(bit)] & mask;
		if (count_it)
			count += hweight(anded);
		else if (anded)
			return 1;
	}

	return count;
}

/*
 * return number of bits set in b1 that are also set in b2, 0 if no overlap
 */
extern int32_t bit_overlap(bitstr_t *b1, bitstr_t *b2)
{
	return _bit_overlap_internal(b1, b2, 1);
}

/*
 * return 1 if there is at least one bit set in b1 that is also set in b2, 0 if
 * no overlap
 */
extern int32_t bit_overlap_any(bitstr_t *b1, bitstr_t *b2)
{
	return _bit_overlap_internal(b1, b2, 0);
}

/*
 * Count the number of bits clear in bitstring.
 *   b (IN)		bitstring to check
 *   RETURN		count of clear bits
 */
int32_t
bit_clear_count(bitstr_t *b)
{
	_assert_bitstr_valid(b);
	return (_bitstr_bits(b) - bit_set_count(b));
}

/*
 * rotate b1 by n bits returning a rotated copy
 *   b1 (IN)		bitmap to rotate
 *   n  (IN)		rotation distance (+ = rotate left, - = rotate right)
 *   nbits (IN)		size of the new copy (in which the rotation occurs)
 *				note: nbits must be >= bit_size(b1)
 *   RETURN		new rotated bitmap
 */
bitstr_t *
bit_rotate_copy(bitstr_t *b1, int32_t n, bitoff_t nbits)
{
	bitoff_t bit, dst;
	bitstr_t *new;
	bitoff_t bitsize;
	bitoff_t deltasize, wrapbits;

	_assert_bitstr_valid(b1);
	bitsize = bit_size(b1);
	xassert(nbits >= bitsize);
	deltasize = nbits - bitsize;

	/* normalize n to a single positive rotation */
	n = n % nbits;
	if (n < 0) {
		n += nbits;
	}

	wrapbits = 0;	/* number of bits that will wrap around */
	if (n > deltasize) {
		wrapbits = n - deltasize;
	}

	new = bit_alloc(nbits);

	/* bits shifting up */
	for (bit = 0; bit < (bitsize-wrapbits); bit++) {
		if (bit_test(b1, bit))
			bit_set(new, bit+n);
	}
	/* continue bit into wrap-around bits, if any */
	for (dst = 0; bit < bitsize; bit++, dst++) {
		if (bit_test(b1, bit))
			bit_set(new, dst);
	}
	return(new);
}

/*
 * rotate b1 by n bits
 *   b1 (IN/OUT)	bitmap to rotate
 *   n  (IN)		rotation distance (+ = rotate left, - = rotate right)
 */
void
bit_rotate(bitstr_t *b1, int32_t n)
{
	uint32_t bitsize;
	bitstr_t *new;

	if (n == 0)
		return;

	_assert_bitstr_valid(b1);
	bitsize = bit_size(b1);

	new = bit_rotate_copy(b1, n, bitsize);
	bit_copybits(b1, new);
	FREE_NULL_BITMAP(new);
}

/*
 * find first set n
 * return position of nth set bit in bitstr word
 * minimal validity checking: if n > set_count, return 63
 * if n == 0, return 0
 */
static bitoff_t _ffsn(bitstr_t b, bitoff_t n)
{
	uint64_t mask = 0xFFFFFFFFu;
	uint32_t size = 32u;
	bitstr_t base = 0u;

	while (size > 0) {
		if (n > hweight(b & mask)) {
			base += size;
			size >>= 1;
			mask |= mask << size;
		} else {
			size >>= 1;
			mask >>= size;
		}
	}
	return base;
}

/*
 * return the bit position of the nth set bit
 * if n > bit_set_count(b), return the position of the last set bit
 * return -1 if b is empty or n == 0
 */
bitoff_t bit_nth_set(bitstr_t *b, bitoff_t n)
{
	bitoff_t bit, bit_cnt, last_bit;
	bitstr_t mask = -1;
	bitstr_t last_mask = -1;
	uint32_t count, last_count, last_word;

	_assert_bitstr_valid(b);
	if (n <= 0)
		return -1;
	bit_cnt = _bitstr_bits(b);
	last_word = _bit_word(bit_cnt);

	last_bit = -1;
	for (bit = 0; bit < bit_cnt; bit += BITSTR_WORD_SIZE){
		if (_bit_word(bit) == last_word)
			mask = _bit_nmask(bit_cnt);
		count = hweight(b[_bit_word(bit)] & mask);
		if (count > 0){
			last_bit = bit;
			last_count = count;
		}
		if (n <= count)
			break;
		n -= count;
	}

	/* b has no set bits */
	if (last_bit < 0)
		return -1;
	if (_bit_word(last_bit) == last_word)
		last_mask = _bit_nmask(bit_cnt);

	/*
	 * if last_bit != bit, n > set_count.
	 * find position of last bit in last non-empty word
	 */
	return last_bit + _ffsn(b[_bit_word(last_bit)] & last_mask,
	                        (last_bit == bit) ? n : last_count);
}

/*
 * Clear all bits after the first n set bits
 */
void bit_pick_firstn(bitstr_t *b, bitoff_t n)
{
	_assert_bitstr_valid(b);
	bitoff_t nth_bit = bit_nth_set(b, n);
	/*
	 * b is empty or nth bit is at the last position.
	 * Nothing to do in either case
	 */
	if (((nth_bit + 1) == _bitstr_bits(b)) || (nth_bit < 0))
		return;
	bit_nclear(b, nth_bit + 1, _bitstr_bits(b) - 1);
}

/*
 * build a bitmap containing the first nbits of b which are set
 */
bitstr_t *
bit_pick_cnt(bitstr_t *b, bitoff_t nbits)
{
	bitoff_t bit = 0, new_bits, count = 0;
	bitstr_t *new;

	_assert_bitstr_valid(b);

	if (_bitstr_bits(b) < nbits)
		return NULL;

	new = bit_alloc(bit_size(b));

	while ((bit < _bitstr_bits(b)) && (count < nbits)) {
		int32_t word = _bit_word(bit);

		if (b[word] == 0) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}

		new_bits = hweight(b[word]);
		if (((count + new_bits) <= nbits) &&
		    ((bit + BITSTR_WORD_SIZE - 1) < _bitstr_bits(b))) {
			new[word] = b[word];
			count += new_bits;
			bit += BITSTR_WORD_SIZE;
			continue;
		}
		while ((bit < _bitstr_bits(b)) && (count < nbits)) {
			if (bit_test(b, bit)) {
				bit_set(new, bit);
				count++;
			}
			bit++;
		}
	}
	if (count < nbits) {
		FREE_NULL_BITMAP(new);
	}

	return new;
}

/*
 * Convert to range string format, e.g. 0-5,42
 */
char *bit_fmt(char *str, int32_t len, bitstr_t *b)
{
	int32_t word;
	char *comma = "";
	bitoff_t bit;

	_assert_bitstr_valid(b);
	xassert(len > 0);
	*str = '\0';
	for (bit = 0; bit < _bitstr_bits(b); ) {
		word = _bit_word(bit);
		if (b[word] == 0) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}

		if (bit_test(b, bit)) {
			int32_t ret, size;
			bitoff_t start = bit;

			while (bit+1 < _bitstr_bits(b) && bit_test(b, bit+1)) {
				bit++;
			}
			size = strlen(str);
			if (bit == start) {	/* add single bit position */
				ret = snprintf(str + size, len - size,
				               "%s%"BITSTR_FMT"", comma, start);
			} else { 		/* add bit position range */
				ret = snprintf(str + size, len - size,
				               "%s%"BITSTR_FMT"-%"BITSTR_FMT"",
					       comma, start, bit);
			}
			comma = ",";

			xassert(ret != -1);
			if (ret == -1)
				error("failed to write to string -- this should never happen");
		}
		bit++;
	}
	return str;
}

/*
 * Convert to range string format, e.g. 0-5,42 with no length restriction
 * Call xfree() on return value to avoid memory leak
 */
char *bit_fmt_full(bitstr_t *b)
{
	int32_t word;
	bitoff_t start, bit;
	char *str = NULL, *pos = NULL, *comma = "";
	_assert_bitstr_valid(b);

	for (bit = 0; bit < _bitstr_bits(b); ) {
		word = _bit_word(bit);
		if (b[word] == 0) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}

		if (bit_test(b, bit)) {
			start = bit;
			while (bit+1 < _bitstr_bits(b) && bit_test(b, bit+1)) {
				bit++;
			}
			if (bit == start)	/* add single bit position */
				xstrfmtcatat(str, &pos, "%s%"BITSTR_FMT"",
					     comma, start);
			else 			/* add bit position range */
				xstrfmtcatat(str, &pos,
					     "%s%"BITSTR_FMT"-%"BITSTR_FMT,
					     comma, start, bit);
			comma = ",";
		}
		bit++;
	}

	return str;
}

/*
 * Convert to range string format, e.g. 0-5,42 with no length restriction
 * offset IN - location of bit zero
 * len IN - number of bits to test
 * Call xfree() on return value to avoid memory leak
 */
char *bit_fmt_range(bitstr_t *b, int offset, int len)
{
	int32_t word;
	bitoff_t start, fini_bit, bit;
	char *str = NULL, *pos = NULL, *comma = "";
	_assert_bitstr_valid(b);

	fini_bit = MIN(_bitstr_bits(b), offset + len);
	for (bit = offset; bit < fini_bit; ) {
		word = _bit_word(bit);
		if (b[word] == 0) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}

		if (bit_test(b, bit)) {
			start = bit;
			while ((bit + 1 < fini_bit) && bit_test(b, bit + 1)) {
				bit++;
			}
			if (bit == start) {	/* add single bit position */
				xstrfmtcatat(str, &pos, "%s%"BITSTR_FMT"",
					     comma, (start - offset));
			} else {		/* add bit position range */
				xstrfmtcatat(str, &pos,
					     "%s%"BITSTR_FMT"-%"BITSTR_FMT,
					     comma, (start - offset),
					     (bit - offset));
			}
			comma = ",";
		}
		bit++;
	}

	return str;
}

/*
 * Convert range string format, e.g. "0-5,42" to bitmap
 * Ret 0 on success, -1 on error
 */
extern int bit_unfmt(bitstr_t *b, const char *str)
{
	int32_t *intvec;
	int rc = 0;

	_assert_bitstr_valid(b);
	if (!str || str[0] == '\0')	/* no bits set */
		return rc;
	intvec = bitfmt2int(str);
	if (intvec == NULL)
		return -1;
	rc = inx2bitstr(b, intvec);
	xfree(intvec);
	return rc;
}

/*
 * bitfmt2int - convert a string describing bitmap (output from bit_fmt,
 *	e.g. "0-30,45,50-60") into an array of integer (start/end) pairs
 *	terminated by -1 (e.g. "0, 30, 45, 45, 50, 60, -1")
 *	Also supports the "1-17:4" step format ("1, 5, 9, 13, 17, -1").
 * input: bitmap string as produced by bitstring.c : bitfmt
 * output: an array of integers
 * NOTE: the caller must xfree the returned memory
 */
extern int32_t *bitfmt2int(const char *bit_str_ptr)
{
	int32_t *bit_int_ptr, i, bit_inx, size, sum, start_val;
	char *tmp = NULL;
	int32_t start_task_id = -1;
	int32_t end_task_id = -1;
	int32_t step = -1;

	if (bit_str_ptr == NULL)
		return NULL;
	if (!(xstrchr(bit_str_ptr, ':'))) {
		size = strlen(bit_str_ptr) + 1;
		/* more than enough space */
		bit_int_ptr = xmalloc(sizeof(int32_t) * (size * 2 + 1));
		bit_inx = sum = 0;
		start_val = -1;
		for (i = 0; i < size; i++) {
			if (bit_str_ptr[i] >= '0' &&
			    bit_str_ptr[i] <= '9') {
				sum = (sum * 10) + (bit_str_ptr[i] - '0');
			} else if (bit_str_ptr[i] == '-') {
				start_val = sum;
				sum = 0;
			} else if (bit_str_ptr[i] == ',' ||
				   bit_str_ptr[i] == '\0') {
				if (i == 0)
					break;
				if (start_val == -1)
					start_val = sum;
				bit_int_ptr[bit_inx++] = start_val;
				bit_int_ptr[bit_inx++] = sum;
				start_val = -1;
				sum = 0;
			}
		}
		xassert(bit_inx < (size * 2 + 1));
	} else {	/* handle step format */
		start_task_id = strtol(bit_str_ptr, &tmp, 10);
		if (*tmp != '-')
			return NULL;
		end_task_id = strtol(tmp + 1, &tmp, 10);
		if (*tmp != ':')
			return NULL;
		step = strtol(tmp + 1, &tmp, 10);
		if (*tmp != '\0')
			return NULL;
		if (end_task_id < start_task_id || step <= 0)
			return NULL;

		size = ((end_task_id - start_task_id) / step) + 1;
		bit_int_ptr = xmalloc(sizeof(int32_t) * (size * 2 + 1));
		bit_inx = 0;
		for(i = start_task_id; i < end_task_id; i += step) {
			bit_int_ptr[bit_inx++] = i;     /* start of pair */
			bit_int_ptr[bit_inx++] = i;     /* end of pair */
		}
	}
	bit_int_ptr[bit_inx] = -1;
	return bit_int_ptr;
}

int inx2bitstr(bitstr_t *b, int32_t *inx)
{
	int32_t *p, bit_cnt;
	int rc = 0;

	xassert(b);
	xassert(inx);

	bit_cnt = _bitstr_bits(b);
	if (bit_cnt > 0)
		bit_nclear(b, 0, bit_cnt - 1);
	for (p = inx; *p != -1; p += 2) {
		if ((*p < 0) || (*p >= bit_cnt) ||
		    (*(p + 1) < 0) || (*(p + 1) >= bit_cnt)) {
			rc = -1;
			break;
		}
		bit_nset(b, *p, *(p + 1));
	}
	return rc;
}

/*
 * convert a bitstring to inx format
 * returns an xmalloc()'d array of int32_t that must be xfree()'d
 */
int32_t *bitstr2inx(bitstr_t *b)
{
	bitoff_t start, bit, pos = 0;
	int32_t *bit_inx;

	if (!b) {
		bit_inx = xmalloc(sizeof(int32_t));
		bit_inx[0] = -1;
		return bit_inx;
	}

	/* worst case: every other bit set, resulting in an array of length
	 * bitstr_bits(b) + 1 (if an odd number of elements)
	 * + 1 (for trailing -1) */
	bit_inx = xmalloc_nz(sizeof(int32_t) * (_bitstr_bits(b) + 2));

	for (bit = 0; bit < _bitstr_bits(b); ) {
		/* skip past empty words */
		if (!b[_bit_word(bit)]) {
			bit += BITSTR_WORD_SIZE;
			continue;
		}

		if (bit_test(b, bit)) {
			start = bit;
			while (bit + 1 < _bitstr_bits(b)
			       && bit_test(b, bit + 1))
				bit++;
			bit_inx[pos++] = start;
			bit_inx[pos++] = bit;
		}
		bit++;
	}
	/* terminate array with -1 */
	bit_inx[pos] = -1;

	return bit_inx;
}

/* If trim_output is true, strip off leading zeros from result. */
static char *_bit_fmt_hexmask(bitstr_t *bitmap, bool trim_output)
{
	char *retstr, *ptr;
	char current;
	bitoff_t i, j, word;
	bitoff_t bitsize;

	if (trim_output)
		bitsize = bit_fls(bitmap) + 1;
	else
		bitsize = bit_size(bitmap);

	if (!bitsize)
		return xstrdup("0x0");

	/* 4 bits per ASCII '0'-'F' */
	bitoff_t charsize = (bitsize + 3) / 4;

	retstr = xmalloc(charsize + 3);

	retstr[0] = '0';
	retstr[1] = 'x';
	retstr[charsize + 2] = '\0';
	ptr = &retstr[charsize + 1];

	for (i = 0; i < bitsize;) {
		if (i + BITSTR_WORD_SIZE <= bitsize) {
			word = _bit_word(i);
			for (j = 0; j < sizeof(bitstr_t); j++) {
				uint8_t idx = ((uint8_t *)(&(bitmap[word])))[j];
				*ptr = hexmask_lookup[idx][1];
				ptr--;
				*ptr = hexmask_lookup[idx][0];
				ptr--;
			}
			i += BITSTR_WORD_SIZE;
		} else {
			current = 0;
			if (bit_test(bitmap, i))
				current |= 0x1;
			i++;
			if ((i < bitsize) && bit_test(bitmap, i))
				current |= 0x2;
			i++;
			if ((i < bitsize) && bit_test(bitmap, i))
				current |= 0x4;
			i++;
			if ((i < bitsize) && bit_test(bitmap, i))
				current |= 0x8;
			i++;

			if (current <= 9) {
				current += '0';
			} else {
				current += 'A' - 10;
			}
			*ptr-- = current;
		}
	}

	return retstr;
}

/* bit_fmt_hexmask
 *
 * Given a bitstr_t, allocate and return a string in the form of:
 *                         "0x0123ABC\0"
 *                            ^     ^
 *                            |     |
 *                           MSB   LSB
 *   bitmap (IN)  bitmap to format
 *   RETURN       formatted string
 */
char *bit_fmt_hexmask(bitstr_t *bitmap)
{
	return _bit_fmt_hexmask(bitmap, false);
}

/* bit_fmt_hexmask_trim
 *
 * Same as bit_fmt_hexmask() except leading zeros are stripped from the output.
 */
char *bit_fmt_hexmask_trim(bitstr_t *bitmap)
{
	return _bit_fmt_hexmask(bitmap, true);
}

/* bit_unfmt_hexmask
 *
 * Given a hex mask string "0x0123ABC\0", convert to a bitstr_t *
 *                            ^     ^
 *                            |     |
 *                           MSB   LSB
 *   bitmap (OUT)  bitmap to update
 *   str (IN)      hex mask string to unformat
 *   RET: 0 on success, -1 on error
 */
int bit_unfmt_hexmask(bitstr_t * bitmap, const char* str)
{
	int32_t bit_index = 0, len;
	int rc = 0;
	const char *curpos;
	bitstr_t current;
	bitoff_t bitsize;

	if (!bitmap || !str)
		return -1;

	len = strlen(str);
	bitsize = bit_size(bitmap);
	curpos = str + len - 1;

	bit_nclear(bitmap, 0, bitsize - 1);
	if (xstrncmp(str, "0x", 2) == 0) {	/* Bypass 0x */
		str += 2;
	}

	while (curpos >= str) {
		current = (bitoff_t) *curpos;
		if (isxdigit(current)) {	/* valid hex digit */
			if (isdigit(current)) {
				current -= '0';
			} else {
				current = toupper(current);
				current -= 'A' - 10;
			}
		} else {			/* not a valid hex digit */
		    	current = 0;
			rc = -1;
			break;
		}

		if (bit_index + 3 < bitsize && !(bit_index % 4)) {
#ifdef SLURM_BIGENDIAN
			current = (((current & 1) << 3) |
				   ((current & 2) << 1) |
				   ((current & 4) >> 1) |
				   ((current & 8) >> 3));
			current = (current << ((BITSTR_MAXPOS -
					        (bit_index & BITSTR_MAXPOS)) -
					       3));
			bitmap[_bit_word(bit_index)] |= current;
#else
			bitmap[_bit_word(bit_index)] |=
				(current & 0xf) << (bit_index & BITSTR_MAXPOS);
#endif
			curpos--;
			bit_index += 4;
			continue;
		}
		if (current & 1) {
			if (bit_index < bitsize)
				bit_set(bitmap, bit_index);
			else {
				rc = -1;
				break;
			}
		}
		if (current & 2) {
			if ((bit_index + 1) < bitsize)
				bit_set(bitmap, bit_index + 1);
			else {
				rc = -1;
				break;
			}
		}
		if (current & 4) {
			if ((bit_index + 2) < bitsize)
				bit_set(bitmap, bit_index + 2);
			else {
				rc = -1;
				break;
			}
		}
		if (current & 8) {
			if ((bit_index + 3) < bitsize)
				bit_set(bitmap, bit_index + 3);
			else {
				rc = -1;
				break;
			}
		}
		curpos--;
		bit_index += 4;
	}
	return rc;
}

/* Find the bit set at pos (0 - bitstr_bits) in bitstr b.
 *   b (IN)             bitstring to search
 *   pos (IN)           bit to search for
 *   RETURN             number bit is set in bitstring (-1 on error)
 */

bitoff_t
bit_get_bit_num(bitstr_t *b, int32_t pos)
{
	bitoff_t bit;
	int32_t cnt = 0;
	bitoff_t bit_cnt;

	_assert_bitstr_valid(b);
	bit_cnt = _bitstr_bits(b);
	xassert(pos <= bit_cnt);

	for (bit = 0; bit < bit_cnt; bit++) {
		if (bit_test(b, bit)) {	/* we got one */
			if (cnt == pos)
				break;
			cnt++;
		}
	}

	if (bit >= bit_cnt)
		bit = -1;

	return bit;
}

void bit_consolidate(bitstr_t *b)
{
	int set_count = bit_set_count(b);

	if (set_count && (set_count < bit_size(b))) {
		bit_nclear(b, set_count, bit_size(b) - 1);
		bit_nset(b, 0, set_count - 1);
	}
}
