/**************************************************************************
 *
 * bitio_m.h -- Macros for bitio
 * Copyright (C) 1994  Neil Sharman and Alistair Moffat
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **************************************************************************/


#ifndef H_BITIO_M
#define H_BITIO_M

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

#ifndef BIO_ENCODE_PROLOGUE
#define BIO_ENCODE_PROLOGUE
#endif

#ifndef BIO_DECODE_PROLOGUE
#define BIO_DECODE_PROLOGUE
#endif

#ifndef BIO_ENCODE_EPILOGUE
#define BIO_ENCODE_EPILOGUE
#endif

#ifndef BIO_DECODE_EPILOGUE
#define BIO_DECODE_EPILOGUE
#endif

#ifndef DECODE_ADD
#define DECODE_ADD(b) (b) += (b) + DECODE_BIT
#endif

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

#define POSITIVE(f, x)							\
  if ((x)<=0) 								\
    fprintf(stderr,"Error: Cannot "#f" encode %d\n",(mg_u_long)x),exit(1);


#define CEILLOG_2(x,v)							\
do {									\
  register int _B_x  = (x) - 1;						\
  (v) = 0;								\
  for (; _B_x ; _B_x>>=1, ++(v));					\
} while(0)


#define FLOORLOG_2(x,v)							\
do {									\
  register int _B_x  = (x);						\
  (v) = -1;								\
  for (; _B_x ; _B_x>>=1, ++(v));					\
} while(0)


/****************************************************************************/



#define UNARY_ENCODE(x)							\
do {									\
  register mg_u_long _B_x = (x);					\
  BIO_ENCODE_PROLOGUE;							\
  POSITIVE(unary, _B_x);						\
  while(--_B_x) ENCODE_BIT(0);						\
  ENCODE_BIT(1);							\
  BIO_ENCODE_EPILOGUE;							\
} while(0)

#define UNARY_ENCODE_L(x, count)					\
do {									\
  register mg_u_long _B_x = (x);					\
  BIO_ENCODE_PROLOGUE;							\
  POSITIVE(unary, _B_x);						\
  (count) += _B_x;							\
  while(--_B_x) ENCODE_BIT(0);						\
  ENCODE_BIT(1);							\
  BIO_ENCODE_EPILOGUE;							\
} while(0)

#define UNARY_DECODE(x)							\
do {									\
  BIO_DECODE_PROLOGUE;							\
  (x) = 1;								\
  while (!DECODE_BIT) ++(x);						\
  BIO_DECODE_EPILOGUE;							\
} while(0)

#define UNARY_DECODE_L(x, count)					\
do {									\
  BIO_DECODE_PROLOGUE;							\
  (x) = 1;								\
  while (!DECODE_BIT) ++(x);						\
  (count) += (x);							\
  BIO_DECODE_EPILOGUE;							\
} while(0)


#define UNARY_LENGTH(x, count)						\
do {									\
  POSITIVE(unary, x);							\
  (count) = (x);							\
} while(0)


/****************************************************************************/


#define BINARY_ENCODE(x, b)						\
do {									\
  register mg_u_long _B_x = (x);					\
  register mg_u_long _B_b = (b);					\
  register int _B_nbits, _B_logofb, _B_thresh;				\
  BIO_ENCODE_PROLOGUE;							\
  POSITIVE(binary, _B_x);						\
  CEILLOG_2(_B_b, _B_logofb);						\
  _B_thresh = (1<<_B_logofb) - _B_b;					\
  if (--_B_x < _B_thresh)						\
    _B_nbits = _B_logofb-1;						\
  else									\
    {									\
      _B_nbits = _B_logofb;						\
      _B_x += _B_thresh;						\
    }									\
  while (--_B_nbits>=0)							\
    ENCODE_BIT((_B_x>>_B_nbits) & 0x1);					\
  BIO_ENCODE_EPILOGUE;							\
} while(0)

#define BINARY_ENCODE_L(x, b, count)					\
do {									\
  register mg_u_long _B_x = (x);					\
  register mg_u_long _B_b = (b);					\
  register int _B_nbits, _B_logofb, _B_thresh;				\
  BIO_ENCODE_PROLOGUE;							\
  POSITIVE(binary, _B_x);						\
  CEILLOG_2(_B_b, _B_logofb);						\
  _B_thresh = (1<<_B_logofb) - _B_b;					\
  if (--_B_x < _B_thresh)						\
    _B_nbits = _B_logofb-1;						\
  else									\
    {									\
      _B_nbits = _B_logofb;						\
      _B_x += _B_thresh;						\
    }									\
  (count) += _B_nbits;							\
  while (--_B_nbits>=0)							\
    ENCODE_BIT((_B_x>>_B_nbits) & 0x1);					\
  BIO_ENCODE_EPILOGUE;							\
} while(0)


#define BINARY_DECODE(x, b)						\
do {									\
  register mg_u_long _B_x = 0;					\
  register mg_u_long _B_b = (b);					\
  register int _B_i, _B_logofb, _B_thresh;				\
  BIO_DECODE_PROLOGUE;							\
  if (_B_b != 1)							\
    {									\
      CEILLOG_2(_B_b, _B_logofb);					\
      _B_thresh = (1<<_B_logofb) - _B_b;				\
      --_B_logofb;							\
      for (_B_i=0; _B_i < _B_logofb; ++_B_i)			 	\
        DECODE_ADD(_B_x);						\
      if (_B_x >= _B_thresh)						\
        {								\
          DECODE_ADD(_B_x);						\
          _B_x -= _B_thresh;						\
	}								\
      (x) = _B_x+1;							\
    }									\
  else									\
    (x) = 1;								\
  BIO_DECODE_EPILOGUE;							\
} while(0)

#define BINARY_DECODE_L(x, b, count)					\
do {									\
  register mg_u_long _B_x = 0;					\
  register mg_u_long _B_b = (b);					\
  register int _B_i, _B_logofb, _B_thresh;				\
  BIO_DECODE_PROLOGUE;							\
  if (_B_b != 1)							\
    {									\
      CEILLOG_2(_B_b, _B_logofb);					\
      _B_thresh = (1<<_B_logofb) - _B_b;				\
      --_B_logofb;							\
      (count) += _B_logofb;					       	\
      for (_B_i=0; _B_i < _B_logofb; ++_B_i)			 	\
        DECODE_ADD(_B_x);						\
      if (_B_x >= _B_thresh)						\
        {								\
          DECODE_ADD(_B_x);						\
          _B_x -= _B_thresh;						\
          ++(count);							\
	}								\
      (x) = _B_x+1;							\
    }									\
  else									\
    (x) = 1;								\
  BIO_DECODE_EPILOGUE;							\
} while(0)

#define BINARY_LENGTH(x, b, count)					\
do {									\
  register mg_u_long _B_x = (x);					\
  register mg_u_long _B_b = (b);					\
  register int _B_nbits, _B_logofb, _B_thresh;				\
  POSITIVE(binary, _B_x);						\
  CEILLOG_2(_B_b, _B_logofb);						\
  _B_thresh = (1<<_B_logofb) - _B_b;					\
  if (--_B_x < _B_thresh)						\
    _B_nbits = _B_logofb-1;						\
  else									\
    _B_nbits = _B_logofb;						\
  (count) = _B_nbits;							\
} while(0)

/****************************************************************************/




#define GAMMA_ENCODE(x)							\
do {									\
  register mg_u_long _B_xx = (x);					\
  register int _B_logofb;						\
  register int _B_nbits;						\
  BIO_ENCODE_PROLOGUE;							\
  POSITIVE(gamma, _B_xx);						\
  FLOORLOG_2(_B_xx, _B_logofb);						\
  _B_nbits = _B_logofb+1;						\
  while(_B_logofb--) ENCODE_BIT(0);					\
  while (--_B_nbits>=0)							\
    ENCODE_BIT((_B_xx>>_B_nbits) & 0x1);				\
  BIO_ENCODE_EPILOGUE;							\
} while (0)

#define GAMMA_ENCODE_L(x, count)					\
do {									\
  register mg_u_long _B_xx = (x);					\
  register int _B_logofb;						\
  register int _B_nbits;						\
  BIO_ENCODE_PROLOGUE;							\
  POSITIVE(gamma, _B_xx);						\
  FLOORLOG_2(_B_xx, _B_logofb);						\
  _B_nbits = _B_logofb+1;						\
  (count) += _B_logofb*2+1;						\
  while(_B_logofb--) ENCODE_BIT(0);					\
  while (--_B_nbits>=0)							\
    ENCODE_BIT((_B_xx>>_B_nbits) & 0x1);				\
  BIO_ENCODE_EPILOGUE;							\
} while (0)

#define GAMMA_DECODE(x)							\
do {									\
  register mg_u_long _B_xx = 1;					\
  register int _B_nbits = 0;						\
  BIO_DECODE_PROLOGUE;							\
  while(!DECODE_BIT) ++_B_nbits;					\
  while (_B_nbits-- > 0)						\
    DECODE_ADD(_B_xx);							\
  (x) = _B_xx;								\
  BIO_DECODE_EPILOGUE;							\
} while (0)

#define GAMMA_DECODE_L(x, count)					\
do {									\
  register mg_u_long _B_xx = 1;					\
  register int _B_nbits = 0;						\
  BIO_DECODE_PROLOGUE;							\
  while(!DECODE_BIT) ++_B_nbits;					\
  (count) += _B_nbits*2+1;						\
  while (_B_nbits-- > 0)						\
    DECODE_ADD(_B_xx);							\
  (x) = _B_xx;								\
  BIO_DECODE_EPILOGUE;							\
} while (0)

#define GAMMA_LENGTH(x, count)						\
do {									\
  register mg_u_long _B_xx = (x);					\
  register int _B_logofb;						\
  POSITIVE(gamma, _B_xx);						\
  FLOORLOG_2(_B_xx, _B_logofb);						\
  (count) = _B_logofb*2+1;						\
} while (0)



/****************************************************************************/


#define DELTA_ENCODE(x)							\
do {									\
  register mg_u_long _B_xxx = (x);					\
  register int _B_logx;							\
  FLOORLOG_2(_B_xxx, _B_logx);						\
  GAMMA_ENCODE(_B_logx+1);						\
  BINARY_ENCODE(_B_xxx+1, 1<<_B_logx);					\
} while (0)

#define DELTA_ENCODE_L(x, count)					\
do {									\
  register mg_u_long _B_xxx = (x);					\
  register int _B_logx;							\
  FLOORLOG_2(_B_xxx, _B_logx);						\
  GAMMA_ENCODE_L(_B_logx+1, count);					\
  BINARY_ENCODE_L(_B_xxx+1, 1<<_B_logx, count);				\
} while (0)


#define DELTA_DECODE(x)							\
do {									\
  register mg_u_long _B_xxx;					\
  register int _B_logx;							\
  GAMMA_DECODE(_B_logx); --_B_logx;					\
  BINARY_DECODE(_B_xxx, 1<<_B_logx); --_B_xxx;				\
  (x) = _B_xxx + (1<<_B_logx);						\
} while (0)

#define DELTA_DECODE_L(x, count)					\
do {									\
  register mg_u_long _B_xxx;					\
  register int _B_logx;							\
  GAMMA_DECODE_L(_B_logx, count); --_B_logx;				\
  BINARY_DECODE_L(_B_xxx, 1<<_B_logx, count); --_B_xxx;			\
  (x) = _B_xxx + (1<<_B_logx);						\
} while (0)

#define DELTA_LENGTH(x, count)						\
do {									\
  register mg_u_long _B_xxx = (x);					\
  register int _B_logx, _B_dcount;					\
  FLOORLOG_2(_B_xxx, _B_logx);						\
  GAMMA_LENGTH(_B_logx+1, count);					\
  BINARY_LENGTH(_B_xxx+1, 1<<_B_logx, _B_dcount);			\
  (count) += _B_dcount;							\
} while (0)


/****************************************************************************/




#define ELIAS_ENCODE(x, b, s)						\
do {									\
  register mg_u_long _B_xx = (x);					\
  register mg_u_long _B_b = (b);					\
  register double _B_s = (s);						\
  register int _B_lower=1, _B_upper=1;					\
  register int _B_k=0;							\
  register double _B_pow=1.0;						\
  POSITIVE(elias, _B_xx);						\
  while (_B_xx>_B_upper) 						\
    {									\
      ++_B_k;								\
      _B_lower = _B_upper+1;						\
      _B_pow *= _B_s;							\
      _B_upper += _B_b*_B_pow;						\
    }									\
  UNARY_ENCODE(_B_k+1);							\
  BINARY_ENCODE(_B_xx-_B_lower+1, _B_upper-_B_lower+1);			\
} while (0)

#define ELIAS_ENCODE_L(x, b, s, count)					\
do {									\
  register mg_u_long _B_xx = (x);					\
  register mg_u_long _B_b = (b);					\
  register double _B_s = (s);						\
  register int _B_lower=1, _B_upper=1;					\
  register int _B_k=0;							\
  register double _B_pow=1.0;						\
  POSITIVE(elias, _B_xx);						\
  while (_B_xx>_B_upper) 						\
    {									\
      ++_B_k;								\
      _B_lower = _B_upper+1;						\
      _B_pow *= _B_s;							\
      _B_upper += _B_b*_B_pow;						\
    }									\
  UNARY_ENCODE_L(_B_k+1, count);					\
  BINARY_ENCODE_L(_B_xx-_B_lower+1, _B_upper-_B_lower+1, count);	\
} while (0)

#define ELIAS_DECODE(x, b, s)						\
do {									\
  register mg_u_long _B_xx;						\
  register mg_u_long _B_b = (b);					\
  register double _B_s = (s);						\
  register int _B_lower=1, _B_upper=1;					\
  register int _B_k;							\
  register double _B_pow=1.0;						\
  UNARY_DECODE(_B_k); --_B_k;						\
  while (_B_k--)							\
    {									\
      _B_lower = _B_upper+1;						\
      _B_pow *= _B_s;							\
      _B_upper += _B_b*_B_pow;						\
    }									\
  BINARY_DECODE(_B_xx, _B_upper-_B_lower+1); --_B_xx;			\
  POSITIVE(gamma, _B_xx+_B_lower);				  	\
  (x) = _B_xx+_B_lower;							\
} while (0)

#define ELIAS_DECODE_L(x, b, s, count)					\
do {									\
  register mg_u_long _B_xx;						\
  register mg_u_long _B_b = (b);					\
  register double _B_s = (s);						\
  register int _B_lower=1, _B_upper=1;					\
  register int _B_k;							\
  register double _B_pow=1.0;						\
  UNARY_DECODE_L(_B_k, count); --_B_k;					\
  while (_B_k--)							\
    {									\
      _B_lower = _B_upper+1;						\
      _B_pow *= _B_s;							\
      _B_upper += _B_b*_B_pow;						\
    }									\
  BINARY_DECODE_L(_B_xx, _B_upper-_B_lower+1, count); --_B_xx;		\
  POSITIVE(gamma, _B_xx+_B_lower);				  	\
  (x) = _B_xx+_B_lower;							\
} while (0)

#define ELIAS_LENGTH(x, b, s, count)					\
do {									\
  register mg_u_long _B_xx = (x);					\
  register mg_u_long _B_b = (b);					\
  register double _B_s = (s);						\
  register int _B_lower=1, _B_upper=1;					\
  register int _B_k=0, _B_ecount;					\
  register double _B_pow=1.0;						\
  POSITIVE(gamma, _B_xx);						\
  while (_B_xx>_B_upper) 						\
    {									\
      ++_B_k;								\
      _B_lower = _B_upper+1;						\
      _B_pow *= _B_s;							\
      _B_upper += _B_b*_B_pow;						\
    }									\
  UNARY_LENGTH(_B_k+1, count);						\
  BINARY_LENGTH(_B_xx-_B_lower+1, _B_upper-_B_lower+1, _B_ecount);	\
  (count) += _B_ecount;							\
} while (0)


/****************************************************************************/


#define BBLOCK_ENCODE(x, b)						\
do {									\
  register mg_u_long _B_xx = (x);					\
  register mg_u_long _B_bb = (b);					\
  register int _B_xdivb = 0;						\
  POSITIVE(bblock, _B_xx);						\
  --_B_xx;								\
  while (_B_xx >= _B_bb) 						\
    {									\
      ++_B_xdivb;							\
      _B_xx -= _B_bb;							\
    }									\
  UNARY_ENCODE(_B_xdivb+1);						\
  BINARY_ENCODE(_B_xx+1, _B_bb);					\
} while (0)

#define BBLOCK_ENCODE_L(x, b, count)					\
do {									\
  register mg_u_long _B_xx = (x);					\
  register mg_u_long _B_bb = (b);					\
  register int _B_xdivb = 0;						\
  POSITIVE(bblock, _B_xx);						\
  --_B_xx;								\
  while (_B_xx >= _B_bb) 						\
    {									\
      ++_B_xdivb;							\
      _B_xx -= _B_bb;							\
    }									\
  UNARY_ENCODE_L(_B_xdivb+1, count);					\
  BINARY_ENCODE_L(_B_xx+1, _B_bb, count);				\
} while (0)

#define BBLOCK_DECODE(x, b)						\
do {									\
  register mg_u_long _B_x1, _B_xx = 0;				\
  register mg_u_long _B_bb = (b);					\
  register int _B_xdivb;						\
  UNARY_DECODE(_B_xdivb); --_B_xdivb;					\
  while (_B_xdivb--)							\
    _B_xx += _B_bb;							\
  BINARY_DECODE(_B_x1, _B_bb);						\
  (x) = _B_xx+_B_x1;							\
} while (0)

#define BBLOCK_DECODE_L(x, b, count)					\
do {									\
  register mg_u_long _B_x1, _B_xx = 0;				\
  register mg_u_long _B_bb = (b);					\
  register int _B_xdivb;						\
  UNARY_DECODE_L(_B_xdivb, count); --_B_xdivb;				\
  while (_B_xdivb--)							\
    _B_xx += _B_bb;							\
  BINARY_DECODE_L(_B_x1, _B_bb, count);					\
  (x) = _B_xx+_B_x1;							\
} while (0)

#define BBLOCK_LENGTH(x, b, count)					\
do {									\
  register mg_u_long _B_bcount, _B_xx = (x);			\
  register mg_u_long _B_bb = (b);					\
  register int _B_xdivb = 0;						\
  POSITIVE(bblock, _B_xx);						\
  --_B_xx;								\
  while (_B_xx >= _B_bb) 						\
    {									\
      ++_B_xdivb;							\
      _B_xx -= _B_bb;							\
    }									\
  UNARY_LENGTH(_B_xdivb+1, count);					\
  BINARY_LENGTH(_B_xx+1, _B_bb, _B_bcount);				\
  (count) += _B_bcount;  						\
} while (0)

#endif
