/*  #########################################################################
These routines to apply the H-compress compression algorithm to a 2-D Fits
image were written by R. White at the STScI and were obtained from the STScI at
http://www.stsci.edu/software/hcompress.html

This source file is a concatination of the following sources files in the
original distribution 
 htrans.c 
 digitize.c 
 encode.c 
 qwrite.c 
 doencode.c 
 bit_output.c 
 qtree_encode.c

The following modifications have been made to the original code:

  - commented out redundant "include" statements
  - added the noutchar global variable 
  - changed all the 'extern' declarations to 'static', since all the routines are in
    the same source file
  - changed the first parameter in encode (and in lower level routines from a file stream
    to a char array
  - modifid the encode routine to return the size of the compressed array of bytes
  - changed calls to printf and perror to call the CFITSIO ffpmsg routine
  - modified the mywrite routine, and lower level byte writing routines,  to copy 
    the output bytes to a char array, instead of writing them to a file stream
  - replace "exit" statements with "return" statements
  - changed the function declarations to the more modern ANSI C style

 ############################################################################  */
 
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "fitsio2.h"

static long noutchar;
static long noutmax;

static int htrans(int a[],int nx,int ny);
static void digitize(int a[], int nx, int ny, int scale);
static int encode(char *outfile, long *nlen, int a[], int nx, int ny, int scale);
static void shuffle(int a[], int n, int n2, int tmp[]);

static int htrans64(LONGLONG a[],int nx,int ny);
static void digitize64(LONGLONG a[], int nx, int ny, int scale);
static int encode64(char *outfile, long *nlen, LONGLONG a[], int nx, int ny, int scale);
static void shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]);

static  void writeint(char *outfile, int a);
static  void writelonglong(char *outfile, LONGLONG a);
static  int qwrite(char *outfile, char *a, int n); 
static  int doencode(char *outfile, int a[], int nx, int ny, unsigned char nbitplanes[3]);
static  int doencode64(char *outfile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3]);
static int  mywrite(char *file, char buffer[], int n);

static int qtree_encode(char *outfile, int a[], int n, int nqx, int nqy, int nbitplanes);
static int qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes);
static void start_outputing_bits();
static void done_outputing_bits(char *outfile);
static void output_nbits(char *outfile, int bits, int n);

static void qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[], int bit);
static void qtree_onebit64(LONGLONG a[], int n, int nx, int ny, unsigned char b[], int bit);
static void qtree_reduce(unsigned char a[], int n, int nx, int ny, unsigned char b[]);
static int  bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, int bmax);
static void write_bdirect(char *outfile, int a[], int n,int nqx, int nqy, unsigned char scratch[], int bit);
static void write_bdirect64(char *outfile, LONGLONG a[], int n,int nqx, int nqy, unsigned char scratch[], int bit);

#define output_nybble(outfile,c)	output_nbits(outfile,c,4)
#define output_huffman(outfile,c)	output_nbits(outfile,code[c],ncode[c])

/* ---------------------------------------------------------------------- */
int fits_hcompress(int *a, int ny, int nx, int scale, char *output, 
                  long *nbytes, int *status)
{
  /* 
     compress the input image using the H-compress algorithm
  
   a  - input image array
   nx - size of X axis of image
   ny - size of Y axis of image
   scale - quantization scale factor. Larger values results in more (lossy) compression
           scale = 0 does lossless compression
   output - pre-allocated array to hold the output compressed stream of bytes
   nbyts  - input value = size of the output buffer;
            returned value = size of the compressed byte stream, in bytes

 NOTE: the nx and ny dimensions as defined within this code are reversed from
 the usual FITS notation.  ny is the fastest varying dimension, which is
 usually considered the X axis in the FITS image display

  */

  int stat;
  
  if (*status > 0) return(*status);

  /* H-transform */
  stat = htrans(a, nx, ny);
  if (stat) {
     *status = stat;
     return(*status);
  }

  /* digitize */
  digitize(a, nx, ny, scale);

  /* encode and write to output array */

  noutmax = *nbytes;  /* input value is the allocated size of the array */
  *nbytes = 0;  /* reset */

  stat = encode(output, nbytes, a, nx, ny, scale);

  *status = stat;
  return(*status);
}
/* ---------------------------------------------------------------------- */
int fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, char *output, 
                  long *nbytes, int *status)
{
  /* 
     compress the input image using the H-compress algorithm
  
   a  - input image array
   nx - size of X axis of image
   ny - size of Y axis of image
   scale - quantization scale factor. Larger values results in more (lossy) compression
           scale = 0 does lossless compression
   output - pre-allocated array to hold the output compressed stream of bytes
   nbyts  - size of the compressed byte stream, in bytes

 NOTE: the nx and ny dimensions as defined within this code are reversed from
 the usual FITS notation.  ny is the fastest varying dimension, which is
 usually considered the X axis in the FITS image display

  */

  int stat;
  
  if (*status > 0) return(*status);

  /* H-transform */
  stat = htrans64(a, nx, ny);
  if (stat) {
     *status = stat;
     return(*status);
  }

  /* digitize */
  digitize64(a, nx, ny, scale);

  /* encode and write to output array */
  noutmax = *nbytes;  /* input value is the allocated size of the array */
  *nbytes = 0;  /* reset */

  stat = encode64(output, nbytes, a, nx, ny, scale);

  *status = stat;
  return(*status);
}

 
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* htrans.c   H-transform of NX x NY integer image
 *
 * Programmer: R. White		Date: 11 May 1992
 */

/* ######################################################################### */
static int htrans(int a[],int nx,int ny)
{
int nmax, log2n, h0, hx, hy, hc, nxtop, nytop, i, j, k;
int oddx, oddy;
int shift, mask, mask2, prnd, prnd2, nrnd2;
int s10, s00;
int *tmp;

	/*
	 * log2n is log2 of max(nx,ny) rounded up to next power of 2
	 */
	nmax = (nx>ny) ? nx : ny;
	log2n = (int) (log((float) nmax)/log(2.0)+0.5);
	if ( nmax > (1<<log2n) ) {
		log2n += 1;
	}
	/*
	 * get temporary storage for shuffling elements
	 */
	tmp = (int *) malloc(((nmax+1)/2)*sizeof(int));
	if(tmp == (int *) NULL) {
	/*	fprintf(stderr, "htrans: insufficient memory\n"); 
		exit(-1);  */
	        ffpmsg("htrans: insufficient memory");
		return(DATA_COMPRESSION_ERR);
	}
	/*
	 * set up rounding and shifting masks
	 */
	shift = 0;
	mask  = -2;
	mask2 = mask << 1;
	prnd  = 1;
	prnd2 = prnd << 1;
	nrnd2 = prnd2 - 1;
	/*
	 * do log2n reductions
	 *
	 * We're indexing a as a 2-D array with dimensions (nx,ny).
	 */
	nxtop = nx;
	nytop = ny;
	for (k = 0; k<log2n; k++) {
		oddx = nxtop % 2;
		oddy = nytop % 2;
		for (i = 0; i<nxtop-oddx; i += 2) {
			s00 = i*ny;				/* s00 is index of a[i,j]	*/
			s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
			for (j = 0; j<nytop-oddy; j += 2) {
				/*
				 * Divide h0,hx,hy,hc by 2 (1 the first time through).
				 */
				h0 = (a[s10+1] + a[s10] + a[s00+1] + a[s00]) >> shift;
				hx = (a[s10+1] + a[s10] - a[s00+1] - a[s00]) >> shift;
				hy = (a[s10+1] - a[s10] + a[s00+1] - a[s00]) >> shift;
				hc = (a[s10+1] - a[s10] - a[s00+1] + a[s00]) >> shift;
				/*
				 * Throw away the 2 bottom bits of h0, bottom bit of hx,hy.
				 * To get rounding to be same for positive and negative
				 * numbers, nrnd2 = prnd2 - 1.
				 */
				a[s10+1] = hc;
				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
				s00 += 2;
				s10 += 2;
			}
			if (oddy) {
				/*
				 * do last element in row if row length is odd
				 * s00+1, s10+1 are off edge
				 */
				h0 = (a[s10] + a[s00]) << (1-shift);
				hx = (a[s10] - a[s00]) << (1-shift);
				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
				s00 += 1;
				s10 += 1;
			}
		}
		if (oddx) {
			/*
			 * do last row if column length is odd
			 * s10, s10+1 are off edge
			 */
			s00 = i*ny;
			for (j = 0; j<nytop-oddy; j += 2) {
				h0 = (a[s00+1] + a[s00]) << (1-shift);
				hy = (a[s00+1] - a[s00]) << (1-shift);
				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
				s00 += 2;
			}
			if (oddy) {
				/*
				 * do corner element if both row and column lengths are odd
				 * s00+1, s10, s10+1 are off edge
				 */
				h0 = a[s00] << (2-shift);
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
			}
		}
		/*
		 * now shuffle in each dimension to group coefficients by order
		 */
		for (i = 0; i<nxtop; i++) {
			shuffle(&a[ny*i],nytop,1,tmp);
		}
		for (j = 0; j<nytop; j++) {
			shuffle(&a[j],nxtop,ny,tmp);
		}
		/*
		 * image size reduced by 2 (round up if odd)
		 */
		nxtop = (nxtop+1)>>1;
		nytop = (nytop+1)>>1;
		/*
		 * divisor doubles after first reduction
		 */
		shift = 1;
		/*
		 * masks, rounding values double after each iteration
		 */
		mask  = mask2;
		prnd  = prnd2;
		mask2 = mask2 << 1;
		prnd2 = prnd2 << 1;
		nrnd2 = prnd2 - 1;
	}
	free(tmp);
	return(0);
}
/* ######################################################################### */

static int htrans64(LONGLONG a[],int nx,int ny)
{
int nmax, log2n, nxtop, nytop, i, j, k;
int oddx, oddy;
int shift;
int s10, s00;
LONGLONG h0, hx, hy, hc, prnd, prnd2, nrnd2, mask, mask2;
LONGLONG *tmp;

	/*
	 * log2n is log2 of max(nx,ny) rounded up to next power of 2
	 */
	nmax = (nx>ny) ? nx : ny;
	log2n = (int) (log((float) nmax)/log(2.0)+0.5);
	if ( nmax > (1<<log2n) ) {
		log2n += 1;
	}
	/*
	 * get temporary storage for shuffling elements
	 */
	tmp = (LONGLONG *) malloc(((nmax+1)/2)*sizeof(LONGLONG));
	if(tmp == (LONGLONG *) NULL) {
	/*	fprintf(stderr, "htrans: insufficient memory\n"); 
		exit(-1);  */
	        ffpmsg("htrans64: insufficient memory");
		return(DATA_COMPRESSION_ERR);
	}
	/*
	 * set up rounding and shifting masks
	 */
	shift = 0;
	mask  = (LONGLONG) -2;
	mask2 = mask << 1;
	prnd  = (LONGLONG) 1;
	prnd2 = prnd << 1;
	nrnd2 = prnd2 - 1;
	/*
	 * do log2n reductions
	 *
	 * We're indexing a as a 2-D array with dimensions (nx,ny).
	 */
	nxtop = nx;
	nytop = ny;
	for (k = 0; k<log2n; k++) {
		oddx = nxtop % 2;
		oddy = nytop % 2;
		for (i = 0; i<nxtop-oddx; i += 2) {
			s00 = i*ny;				/* s00 is index of a[i,j]	*/
			s10 = s00+ny;			/* s10 is index of a[i+1,j]	*/
			for (j = 0; j<nytop-oddy; j += 2) {
				/*
				 * Divide h0,hx,hy,hc by 2 (1 the first time through).
				 */
				h0 = (a[s10+1] + a[s10] + a[s00+1] + a[s00]) >> shift;
				hx = (a[s10+1] + a[s10] - a[s00+1] - a[s00]) >> shift;
				hy = (a[s10+1] - a[s10] + a[s00+1] - a[s00]) >> shift;
				hc = (a[s10+1] - a[s10] - a[s00+1] + a[s00]) >> shift;
				/*
				 * Throw away the 2 bottom bits of h0, bottom bit of hx,hy.
				 * To get rounding to be same for positive and negative
				 * numbers, nrnd2 = prnd2 - 1.
				 */
				a[s10+1] = hc;
				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
				s00 += 2;
				s10 += 2;
			}
			if (oddy) {
				/*
				 * do last element in row if row length is odd
				 * s00+1, s10+1 are off edge
				 */
				h0 = (a[s10] + a[s00]) << (1-shift);
				hx = (a[s10] - a[s00]) << (1-shift);
				a[s10  ] = ( (hx>=0) ? (hx+prnd)  :  hx        ) & mask ;
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
				s00 += 1;
				s10 += 1;
			}
		}
		if (oddx) {
			/*
			 * do last row if column length is odd
			 * s10, s10+1 are off edge
			 */
			s00 = i*ny;
			for (j = 0; j<nytop-oddy; j += 2) {
				h0 = (a[s00+1] + a[s00]) << (1-shift);
				hy = (a[s00+1] - a[s00]) << (1-shift);
				a[s00+1] = ( (hy>=0) ? (hy+prnd)  :  hy        ) & mask ;
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
				s00 += 2;
			}
			if (oddy) {
				/*
				 * do corner element if both row and column lengths are odd
				 * s00+1, s10, s10+1 are off edge
				 */
				h0 = a[s00] << (2-shift);
				a[s00  ] = ( (h0>=0) ? (h0+prnd2) : (h0+nrnd2) ) & mask2;
			}
		}
		/*
		 * now shuffle in each dimension to group coefficients by order
		 */
		for (i = 0; i<nxtop; i++) {
			shuffle64(&a[ny*i],nytop,1,tmp);
		}
		for (j = 0; j<nytop; j++) {
			shuffle64(&a[j],nxtop,ny,tmp);
		}
		/*
		 * image size reduced by 2 (round up if odd)
		 */
		nxtop = (nxtop+1)>>1;
		nytop = (nytop+1)>>1;
		/*
		 * divisor doubles after first reduction
		 */
		shift = 1;
		/*
		 * masks, rounding values double after each iteration
		 */
		mask  = mask2;
		prnd  = prnd2;
		mask2 = mask2 << 1;
		prnd2 = prnd2 << 1;
		nrnd2 = prnd2 - 1;
	}
	free(tmp);
	return(0);
}

/* ######################################################################### */
static void
shuffle(int a[], int n, int n2, int tmp[])
{

/* 
int a[];	 array to shuffle					
int n;		 number of elements to shuffle	
int n2;		 second dimension					
int tmp[];	 scratch storage					
*/

int i;
int *p1, *p2, *pt;

	/*
	 * copy odd elements to tmp
	 */
	pt = tmp;
	p1 = &a[n2];
	for (i=1; i < n; i += 2) {
		*pt = *p1;
		pt += 1;
		p1 += (n2+n2);
	}
	/*
	 * compress even elements into first half of A
	 */
	p1 = &a[n2];
	p2 = &a[n2+n2];
	for (i=2; i<n; i += 2) {
		*p1 = *p2;
		p1 += n2;
		p2 += (n2+n2);
	}
	/*
	 * put odd elements into 2nd half
	 */
	pt = tmp;
	for (i = 1; i<n; i += 2) {
		*p1 = *pt;
		p1 += n2;
		pt += 1;
	}
}
/* ######################################################################### */
static void
shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[])
{

/* 
LONGLONG a[];	 array to shuffle					
int n;		 number of elements to shuffle	
int n2;		 second dimension					
LONGLONG tmp[];	 scratch storage					
*/

int i;
LONGLONG *p1, *p2, *pt;

	/*
	 * copy odd elements to tmp
	 */
	pt = tmp;
	p1 = &a[n2];
	for (i=1; i < n; i += 2) {
		*pt = *p1;
		pt += 1;
		p1 += (n2+n2);
	}
	/*
	 * compress even elements into first half of A
	 */
	p1 = &a[n2];
	p2 = &a[n2+n2];
	for (i=2; i<n; i += 2) {
		*p1 = *p2;
		p1 += n2;
		p2 += (n2+n2);
	}
	/*
	 * put odd elements into 2nd half
	 */
	pt = tmp;
	for (i = 1; i<n; i += 2) {
		*p1 = *pt;
		p1 += n2;
		pt += 1;
	}
}
/* ######################################################################### */
/* ######################################################################### */
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* digitize.c	digitize H-transform
 *
 * Programmer: R. White		Date: 11 March 1991
 */

/* ######################################################################### */
static void  
digitize(int a[], int nx, int ny, int scale)
{
int d, *p;

	/*
	 * round to multiple of scale
	 */
	if (scale <= 1) return;
	d=(scale+1)/2-1;
	for (p=a; p <= &a[nx*ny-1]; p++) *p = ((*p>0) ? (*p+d) : (*p-d))/scale;
}

/* ######################################################################### */
static void  
digitize64(LONGLONG a[], int nx, int ny, int scale)
{
LONGLONG d, *p, scale64;

	/*
	 * round to multiple of scale
	 */
	if (scale <= 1) return;
	d=(scale+1)/2-1;
	scale64 = scale;  /* use a 64-bit int for efficiency in the big loop */

	for (p=a; p <= &a[nx*ny-1]; p++) *p = ((*p>0) ? (*p+d) : (*p-d))/scale64;
}
/* ######################################################################### */
/* ######################################################################### */
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* encode.c		encode H-transform and write to outfile
 *
 * Programmer: R. White		Date: 2 February 1994
 */

static char code_magic[2] = { (char)0xDD, (char)0x99 };


/* ######################################################################### */
static int encode(char *outfile, long *nlength, int a[], int nx, int ny, int scale)
{

/* FILE *outfile;  - change outfile to a char array */  
/*
  long * nlength    returned length (in bytes) of the encoded array)
  int a[];								 input H-transform array (nx,ny)
  int nx,ny;								 size of H-transform array	
  int scale;								 scale factor for digitization
*/
int nel, nx2, ny2, i, j, k, q, vmax[3], nsign, bits_to_go;
unsigned char nbitplanes[3];
unsigned char *signbits;
int stat = 0;

        noutchar = 0;  /* initialize the number of compressed bytes that have been written */
	nel = nx*ny;
	/*
	 * write magic value
	 */
	qwrite(outfile, code_magic, sizeof(code_magic));
	writeint(outfile, nx);			/* size of image */
	writeint(outfile, ny);
	writeint(outfile, scale);		/* scale factor for digitization */
	/*
	 * write first value of A (sum of all pixels -- the only value
	 * which does not compress well)
	 */
	writelonglong(outfile, (LONGLONG) a[0]);

	a[0] = 0;
	/*
	 * allocate array for sign bits and save values, 8 per byte
	 */
	signbits = (unsigned char *) malloc((nel+7)/8);
	if (signbits == (unsigned char *) NULL) {
/*		fprintf(stderr, "encode: insufficient memory\n"); 
		exit(-1); */
		ffpmsg("encode: insufficient memory");
		return(DATA_COMPRESSION_ERR);
	}
	nsign = 0;
	bits_to_go = 8;
	signbits[0] = 0;
	for (i=0; i<nel; i++) {
		if (a[i] > 0) {
			/*
			 * positive element, put zero at end of buffer
			 */
			signbits[nsign] <<= 1;
			bits_to_go -= 1;
		} else if (a[i] < 0) {
			/*
			 * negative element, shift in a one
			 */
			signbits[nsign] <<= 1;
			signbits[nsign] |= 1;
			bits_to_go -= 1;
			/*
			 * replace a by absolute value
			 */
			a[i] = -a[i];
		}
		if (bits_to_go == 0) {
			/*
			 * filled up this byte, go to the next one
			 */
			bits_to_go = 8;
			nsign += 1;
			signbits[nsign] = 0;
		}
	}
	if (bits_to_go != 8) {
		/*
		 * some bits in last element
		 * move bits in last byte to bottom and increment nsign
		 */
		signbits[nsign] <<= bits_to_go;
		nsign += 1;
	}

	/*
	 * calculate number of bit planes for 3 quadrants
	 *
	 * quadrant 0=bottom left, 1=bottom right or top left, 2=top right, 
	 */
	for (q=0; q<3; q++) {
		vmax[q] = 0;
	}
	/*
	 * get maximum absolute value in each quadrant
	 */
	nx2 = (nx+1)/2;
	ny2 = (ny+1)/2;
	j=0;	/* column counter	*/
	k=0;	/* row counter		*/
	for (i=0; i<nel; i++) {
		q = (j>=ny2) + (k>=nx2);
		if (vmax[q] < a[i]) vmax[q] = a[i];
		if (++j >= ny) {
			j = 0;
			k += 1;
		}
	}
	/*
	 * now calculate number of bits for each quadrant
	 */
	for (q = 0; q < 3; q++) {
		nbitplanes[q] = (int) (log((float) (vmax[q]+1))/log(2.0)+0.5);
		if ( (vmax[q]+1) > (1<<nbitplanes[q]) ) {
			nbitplanes[q] += 1;
		}
	}
	/*
	 * write nbitplanes
	 */
	if (0 == qwrite(outfile, (char *) nbitplanes, sizeof(nbitplanes))) {
	        *nlength = noutchar;
		ffpmsg("encode: output buffer too small");
		return(DATA_COMPRESSION_ERR);
        }
	 
	/*
	 * write coded array
	 */
	stat = doencode(outfile, a, nx, ny, nbitplanes);
	/*
	 * write sign bits
	 */

	if (nsign > 0) {

	   if ( 0 == qwrite(outfile, (char *) signbits, nsign)) {
	        free(signbits);
	        *nlength = noutchar;
		ffpmsg("encode: output buffer too small");
		return(DATA_COMPRESSION_ERR);
          }
	} 
	
	free(signbits);
	*nlength = noutchar;

        if (noutchar >= noutmax) {
		ffpmsg("encode: output buffer too small");
		return(DATA_COMPRESSION_ERR);
        }  
	
	return(stat); 
}
/* ######################################################################### */
static int encode64(char *outfile, long *nlength, LONGLONG a[], int nx, int ny, int scale)
{

/* FILE *outfile;  - change outfile to a char array */  
/*
  long * nlength    returned length (in bytes) of the encoded array)
  LONGLONG a[];								 input H-transform array (nx,ny)
  int nx,ny;								 size of H-transform array	
  int scale;								 scale factor for digitization
*/
int nel, nx2, ny2, i, j, k, q, nsign, bits_to_go;
LONGLONG vmax[3];
unsigned char nbitplanes[3];
unsigned char *signbits;
int stat = 0;

        noutchar = 0;  /* initialize the number of compressed bytes that have been written */
	nel = nx*ny;
	/*
	 * write magic value
	 */
	qwrite(outfile, code_magic, sizeof(code_magic));
	writeint(outfile, nx);				/* size of image	*/
	writeint(outfile, ny);
	writeint(outfile, scale);			/* scale factor for digitization */
	/*
	 * write first value of A (sum of all pixels -- the only value
	 * which does not compress well)
	 */

	writelonglong(outfile, a[0]);
	a[0] = 0;
	/*
	 * allocate array for sign bits and save values, 8 per byte
	 */
	signbits = (unsigned char *) malloc((nel+7)/8);
	if (signbits == (unsigned char *) NULL) {
/*		fprintf(stderr, "encode: insufficient memory\n"); 
		exit(-1); */
		ffpmsg("encode64: insufficient memory");
		return(DATA_COMPRESSION_ERR);
	}
	nsign = 0;
	bits_to_go = 8;
	signbits[0] = 0;
	for (i=0; i<nel; i++) {
		if (a[i] > 0) {
			/*
			 * positive element, put zero at end of buffer
			 */
			signbits[nsign] <<= 1;
			bits_to_go -= 1;
		} else if (a[i] < 0) {
			/*
			 * negative element, shift in a one
			 */
			signbits[nsign] <<= 1;
			signbits[nsign] |= 1;
			bits_to_go -= 1;
			/*
			 * replace a by absolute value
			 */
			a[i] = -a[i];
		}
		if (bits_to_go == 0) {
			/*
			 * filled up this byte, go to the next one
			 */
			bits_to_go = 8;
			nsign += 1;
			signbits[nsign] = 0;
		}
	}
	if (bits_to_go != 8) {
		/*
		 * some bits in last element
		 * move bits in last byte to bottom and increment nsign
		 */
		signbits[nsign] <<= bits_to_go;
		nsign += 1;
	}
	/*
	 * calculate number of bit planes for 3 quadrants
	 *
	 * quadrant 0=bottom left, 1=bottom right or top left, 2=top right, 
	 */
	for (q=0; q<3; q++) {
		vmax[q] = 0;
	}
	/*
	 * get maximum absolute value in each quadrant
	 */
	nx2 = (nx+1)/2;
	ny2 = (ny+1)/2;

        i = 0;
        for (k=0; k<ny; k++) {
            for (j=0; j<nx; j++) {
                q = (j>=ny2) + (k>=nx2);
                vmax[q] |= a[i];
                i++;
            }
        } 

	/*
	 * now calculate number of bits for each quadrant
	 */
	 
        /* this is a more efficient way to do this, */
 
 
        for (q = 0; q < 3; q++) {
            for (nbitplanes[q] = 0; vmax[q]>0; vmax[q] = vmax[q]>>1, nbitplanes[q]++) ; 
        }


/*
	for (q = 0; q < 3; q++) {
		nbitplanes[q] = log((float) (vmax[q]+1))/log(2.0)+0.5;
		if ( (vmax[q]+1) > (((LONGLONG) 1)<<nbitplanes[q]) ) {
			nbitplanes[q] += 1;
		}
	}
*/

	/*
	 * write nbitplanes
	 */

	if (0 == qwrite(outfile, (char *) nbitplanes, sizeof(nbitplanes))) {
	        *nlength = noutchar;
		ffpmsg("encode: output buffer too small");
		return(DATA_COMPRESSION_ERR);
        }
	 
	/*
	 * write coded array
	 */
	stat = doencode64(outfile, a, nx, ny, nbitplanes);
	/*
	 * write sign bits
	 */

	if (nsign > 0) {

	   if ( 0 == qwrite(outfile, (char *) signbits, nsign)) {
	        free(signbits);
	        *nlength = noutchar;
		ffpmsg("encode: output buffer too small");
		return(DATA_COMPRESSION_ERR);
          }
	} 

	free(signbits);
	
	*nlength = noutchar;

        if (noutchar >= noutmax) {
		ffpmsg("encode64: output buffer too small");
		return(DATA_COMPRESSION_ERR);
        }
		
	return(stat); 
}
/* ######################################################################### */
/* ######################################################################### */
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* qwrite.c	Write binary data
 *
 * Programmer: R. White		Date: 11 March 1991
 */

/* ######################################################################### */
static void
writeint(char *outfile, int a)
{
int i;
unsigned char b[4];

	/* Write integer A one byte at a time to outfile.
	 *
	 * This is portable from Vax to Sun since it eliminates the
	 * need for byte-swapping.
	 */
	for (i=3; i>=0; i--) {
		b[i] = a & 0x000000ff;
		a >>= 8;
	}
	for (i=0; i<4; i++) qwrite(outfile, (char *) &b[i],1);
}

/* ######################################################################### */
static void
writelonglong(char *outfile, LONGLONG a)
{
int i;
unsigned char b[8];

	/* Write integer A one byte at a time to outfile.
	 *
	 * This is portable from Vax to Sun since it eliminates the
	 * need for byte-swapping.
	 */
	for (i=7; i>=0; i--) {
		b[i] = (unsigned char) (a & 0x000000ff);
		a >>= 8;
	}
	for (i=0; i<8; i++) qwrite(outfile, (char *) &b[i],1);
}

/* ######################################################################### */
static int qwrite(char *outfile, char *a, int n)
{
int nwrite;

	nwrite = mywrite(outfile, a, n);	
	return(nwrite);      /* added by WDP to prevent compiler warning */
}

/* ######################################################################### */
static int
mywrite(char *file, char buffer[], int n)
{
    /*
     * write n bytes from buffer into file
     * returns number of bytes read (=n) if successful, <=0 if not
     */

     if (noutchar + n > noutmax) return(0);  /* buffer overflow */
     
     memcpy(&file[noutchar], buffer, n);
     noutchar += n;

     return(n);
}
/* ######################################################################### */
/* ######################################################################### */
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* doencode.c	Encode 2-D array and write stream of characters on outfile
 *
 * This version assumes that A is positive.
 *
 * Programmer: R. White		Date: 7 May 1991
 */

/* ######################################################################### */
static int
doencode(char *outfile, int a[], int nx, int ny, unsigned char nbitplanes[3])
{
/* char *outfile;						 output data stream
int a[];							 Array of values to encode			
int nx,ny;							 Array dimensions [nx][ny]			
unsigned char nbitplanes[3];		 Number of bit planes in quadrants	
*/

int nx2, ny2, stat = 0;

	nx2 = (nx+1)/2;
	ny2 = (ny+1)/2;
	/*
	 * Initialize bit output
	 */
	start_outputing_bits();
	/*
	 * write out the bit planes for each quadrant
	 */
	stat = qtree_encode(outfile, &a[0],          ny, nx2,  ny2,  nbitplanes[0]);

        if (!stat)
		stat = qtree_encode(outfile, &a[ny2],        ny, nx2,  ny/2, nbitplanes[1]);

        if (!stat)
		stat = qtree_encode(outfile, &a[ny*nx2],     ny, nx/2, ny2,  nbitplanes[1]);

        if (!stat)
		stat = qtree_encode(outfile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]);
	/*
	 * Add zero as an EOF symbol
	 */
	output_nybble(outfile, 0);
	done_outputing_bits(outfile);
	
	return(stat);
}
/* ######################################################################### */
static int
doencode64(char *outfile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3])
{
/* char *outfile;						 output data stream
LONGLONG a[];							 Array of values to encode			
int nx,ny;							 Array dimensions [nx][ny]			
unsigned char nbitplanes[3];		 Number of bit planes in quadrants	
*/

int nx2, ny2, stat = 0;

	nx2 = (nx+1)/2;
	ny2 = (ny+1)/2;
	/*
	 * Initialize bit output
	 */
	start_outputing_bits();
	/*
	 * write out the bit planes for each quadrant
	 */
	stat = qtree_encode64(outfile, &a[0],          ny, nx2,  ny2,  nbitplanes[0]);

        if (!stat)
		stat = qtree_encode64(outfile, &a[ny2],        ny, nx2,  ny/2, nbitplanes[1]);

        if (!stat)
		stat = qtree_encode64(outfile, &a[ny*nx2],     ny, nx/2, ny2,  nbitplanes[1]);

        if (!stat)
		stat = qtree_encode64(outfile, &a[ny*nx2+ny2], ny, nx/2, ny/2, nbitplanes[2]);
	/*
	 * Add zero as an EOF symbol
	 */
	output_nybble(outfile, 0);
	done_outputing_bits(outfile);
	
	return(stat);
}
/* ######################################################################### */
/* ######################################################################### */
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* BIT OUTPUT ROUTINES */


static LONGLONG bitcount;

/* THE BIT BUFFER */

static int buffer2;			/* Bits buffered for output	*/
static int bits_to_go2;			/* Number of bits free in buffer */


/* ######################################################################### */
/* INITIALIZE FOR BIT OUTPUT */

static void
start_outputing_bits()
{
	buffer2 = 0;			/* Buffer is empty to start	*/
	bits_to_go2 = 8;		/* with				*/
	bitcount = 0;
}

/* ######################################################################### */
/* OUTPUT N BITS (N must be <= 8) */

static void
output_nbits(char *outfile, int bits, int n)
{
	/*
	 * insert bits at end of buffer
	 */
	buffer2 <<= n;
	buffer2 |= ( bits & ((1<<n)-1) );
	bits_to_go2 -= n;
	if (bits_to_go2 <= 0) {
		/*
		 * buffer2 full, put out top 8 bits
		 */
/*		putc((buffer2>>(-bits_to_go2)) & 0xff,outfile); */

	        outfile[noutchar] = ((buffer2>>(-bits_to_go2)) & 0xff);

		if (noutchar < noutmax) noutchar++;
		
		bits_to_go2 += 8;
	}
	bitcount += n;
}


/* ######################################################################### */
/* FLUSH OUT THE LAST BITS */

static void
done_outputing_bits(char *outfile)
{
	if(bits_to_go2 < 8) {
/*		putc(buffer2<<bits_to_go2,outfile); */

	        outfile[noutchar] = (buffer2<<bits_to_go2);
		if (noutchar < noutmax) noutchar++;

		/* count the garbage bits too */
		bitcount += bits_to_go2;
	}
}
/* ######################################################################### */
/* ######################################################################### */
/* Copyright (c) 1993 Association of Universities for Research 
 * in Astronomy. All rights reserved. Produced under National   
 * Aeronautics and Space Administration Contract No. NAS5-26555.
 */
/* qtree_encode.c	Encode values in quadrant of 2-D array using binary
 *					quadtree coding for each bit plane.  Assumes array is
 *					positive.
 *
 * Programmer: R. White		Date: 15 May 1991
 */

/*
 * Huffman code values and number of bits in each code
 */
static int code[16] =
	{
	0x3e, 0x00, 0x01, 0x08, 0x02, 0x09, 0x1a, 0x1b,
	0x03, 0x1c, 0x0a, 0x1d, 0x0b, 0x1e, 0x3f, 0x0c
	};
static int ncode[16] =
	{
	6,    3,    3,    4,    3,    4,    5,    5,
	3,    5,    4,    5,    4,    5,    6,    4
	};

/*
 * variables for bit output to buffer when Huffman coding
 */
static int bitbuffer, bits_to_go3;

/*
 * macros to write out 4-bit nybble, Huffman code for this value
 */


/* ######################################################################### */
static int
qtree_encode(char *outfile, int a[], int n, int nqx, int nqy, int nbitplanes)
{

/*
int a[];
int n;								 physical dimension of row in a		
int nqx;							 length of row			
int nqy;							 length of column (<=n)				
int nbitplanes;						 number of bit planes to output	
*/
	
int log2n, i, k, bit, b, bmax, nqmax, nqx2, nqy2, nx, ny;
unsigned char *scratch, *buffer;

	/*
	 * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
	 */
	nqmax = (nqx>nqy) ? nqx : nqy;
	log2n = (int) (log((float) nqmax)/log(2.0)+0.5);
	if (nqmax > (1<<log2n)) {
		log2n += 1;
	}
	/*
	 * initialize buffer point, max buffer size
	 */
	nqx2 = (nqx+1)/2;
	nqy2 = (nqy+1)/2;
	bmax = (nqx2*nqy2+1)/2;
	/*
	 * We're indexing A as a 2-D array with dimensions (nqx,nqy).
	 * Scratch is 2-D with dimensions (nqx/2,nqy/2) rounded up.
	 * Buffer is used to store string of codes for output.
	 */
	scratch = (unsigned char *) malloc(2*bmax);
	buffer = (unsigned char *) malloc(bmax);
	if ((scratch == (unsigned char *) NULL) ||
		(buffer  == (unsigned char *) NULL)) {
/*		fprintf(stderr, "qtree_encode: insufficient memory\n");
		exit(-1);  */
		
		ffpmsg("qtree_encode: insufficient memory");
		return(DATA_COMPRESSION_ERR);
	}
	/*
	 * now encode each bit plane, starting with the top
	 */
	for (bit=nbitplanes-1; bit >= 0; bit--) {
		/*
		 * initial bit buffer
		 */
		b = 0;
		bitbuffer = 0;
		bits_to_go3 = 0;
		/*
		 * on first pass copy A to scratch array
		 */
		qtree_onebit(a,n,nqx,nqy,scratch,bit);
		nx = (nqx+1)>>1;
		ny = (nqy+1)>>1;
		/*
		 * copy non-zero values to output buffer, which will be written
		 * in reverse order
		 */
		if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
			/*
			 * quadtree is expanding data,
			 * change warning code and just fill buffer with bit-map
			 */
			write_bdirect(outfile,a,n,nqx,nqy,scratch,bit);
			goto bitplane_done;
		}
		/*
		 * do log2n reductions
		 */
		for (k = 1; k<log2n; k++) {
			qtree_reduce(scratch,ny,nx,ny,scratch);
			nx = (nx+1)>>1;
			ny = (ny+1)>>1;
			if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
				write_bdirect(outfile,a,n,nqx,nqy,scratch,bit);
				goto bitplane_done;
			}
		}
		/*
		 * OK, we've got the code in buffer
		 * Write quadtree warning code, then write buffer in reverse order
		 */
		output_nybble(outfile,0xF);
		if (b==0) {
			if (bits_to_go3>0) {
				/*
				 * put out the last few bits
				 */
				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
					bits_to_go3);
			} else {
				/*
				 * have to write a zero nybble if there are no 1's in array
				 */
				output_huffman(outfile,0);
			}
		} else {
			if (bits_to_go3>0) {
				/*
				 * put out the last few bits
				 */
				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
					bits_to_go3);
			}
			for (i=b-1; i>=0; i--) {
				output_nbits(outfile,buffer[i],8);
			}
		}
		bitplane_done: ;
	}
	free(buffer);
	free(scratch);
	return(0);
}
/* ######################################################################### */
static int
qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes)
{

/*
LONGLONG a[];
int n;								 physical dimension of row in a		
int nqx;							 length of row			
int nqy;							 length of column (<=n)				
int nbitplanes;						 number of bit planes to output	
*/
	
int log2n, i, k, bit, b, nqmax, nqx2, nqy2, nx, ny;
int bmax;  /* this potentially needs to be made a 64-bit int to support large arrays */
unsigned char *scratch, *buffer;

	/*
	 * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
	 */
	nqmax = (nqx>nqy) ? nqx : nqy;
	log2n = (int) (log((float) nqmax)/log(2.0)+0.5);
	if (nqmax > (1<<log2n)) {
		log2n += 1;
	}
	/*
	 * initialize buffer point, max buffer size
	 */
	nqx2 = (nqx+1)/2;
	nqy2 = (nqy+1)/2;
	bmax = (( nqx2)* ( nqy2)+1)/2;
	/*
	 * We're indexing A as a 2-D array with dimensions (nqx,nqy).
	 * Scratch is 2-D with dimensions (nqx/2,nqy/2) rounded up.
	 * Buffer is used to store string of codes for output.
	 */
	scratch = (unsigned char *) malloc(2*bmax);
	buffer = (unsigned char *) malloc(bmax);
	if ((scratch == (unsigned char *) NULL) ||
		(buffer  == (unsigned char *) NULL)) {
/*		fprintf(stderr, "qtree_encode: insufficient memory\n");
		exit(-1);  */
		
		ffpmsg("qtree_encode64: insufficient memory");
		return(DATA_COMPRESSION_ERR);
	}
	/*
	 * now encode each bit plane, starting with the top
	 */
	for (bit=nbitplanes-1; bit >= 0; bit--) {
		/*
		 * initial bit buffer
		 */
		b = 0;
		bitbuffer = 0;
		bits_to_go3 = 0;
		/*
		 * on first pass copy A to scratch array
		 */
		qtree_onebit64(a,n,nqx,nqy,scratch,bit);
		nx = (nqx+1)>>1;
		ny = (nqy+1)>>1;
		/*
		 * copy non-zero values to output buffer, which will be written
		 * in reverse order
		 */
		if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
			/*
			 * quadtree is expanding data,
			 * change warning code and just fill buffer with bit-map
			 */
			write_bdirect64(outfile,a,n,nqx,nqy,scratch,bit);
			goto bitplane_done;
		}
		/*
		 * do log2n reductions
		 */
		for (k = 1; k<log2n; k++) {
			qtree_reduce(scratch,ny,nx,ny,scratch);
			nx = (nx+1)>>1;
			ny = (ny+1)>>1;
			if (bufcopy(scratch,nx*ny,buffer,&b,bmax)) {
				write_bdirect64(outfile,a,n,nqx,nqy,scratch,bit);
				goto bitplane_done;
			}
		}
		/*
		 * OK, we've got the code in buffer
		 * Write quadtree warning code, then write buffer in reverse order
		 */
		output_nybble(outfile,0xF);
		if (b==0) {
			if (bits_to_go3>0) {
				/*
				 * put out the last few bits
				 */
				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
					bits_to_go3);
			} else {
				/*
				 * have to write a zero nybble if there are no 1's in array
				 */
				output_huffman(outfile,0);
			}
		} else {
			if (bits_to_go3>0) {
				/*
				 * put out the last few bits
				 */
				output_nbits(outfile, bitbuffer & ((1<<bits_to_go3)-1),
					bits_to_go3);
			}
			for (i=b-1; i>=0; i--) {
				output_nbits(outfile,buffer[i],8);
			}
		}
		bitplane_done: ;
	}
	free(buffer);
	free(scratch);
	return(0);
}

/* ######################################################################### */
/*
 * copy non-zero codes from array to buffer
 */
static int
bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, int bmax)
{
int i;

	for (i = 0; i < n; i++) {
		if (a[i] != 0) {
			/*
			 * add Huffman code for a[i] to buffer
			 */
			bitbuffer |= code[a[i]] << bits_to_go3;
			bits_to_go3 += ncode[a[i]];
			if (bits_to_go3 >= 8) {
				buffer[*b] = bitbuffer & 0xFF;
				*b += 1;
				/*
				 * return warning code if we fill buffer
				 */
				if (*b >= bmax) return(1);
				bitbuffer >>= 8;
				bits_to_go3 -= 8;
			}
		}
	}
	return(0);
}

/* ######################################################################### */
/*
 * Do first quadtree reduction step on bit BIT of array A.
 * Results put into B.
 * 
 */
static void
qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[], int bit)
{
int i, j, k;
int b0, b1, b2, b3;
int s10, s00;

	/*
	 * use selected bit to get amount to shift
	 */
	b0 = 1<<bit;
	b1 = b0<<1;
	b2 = b0<<2;
	b3 = b0<<3;
	k = 0;							/* k is index of b[i/2,j/2]	*/
	for (i = 0; i<nx-1; i += 2) {
		s00 = n*i;					/* s00 is index of a[i,j]	*/
		s10 = s00+n;				/* s10 is index of a[i+1,j]	*/
		for (j = 0; j<ny-1; j += 2) {
			b[k] = ( ( a[s10+1]     & b0)
				   | ((a[s10  ]<<1) & b1)
				   | ((a[s00+1]<<2) & b2)
				   | ((a[s00  ]<<3) & b3) ) >> bit;
			k += 1;
			s00 += 2;
			s10 += 2;
		}
		if (j < ny) {
			/*
			 * row size is odd, do last element in row
			 * s00+1,s10+1 are off edge
			 */
			b[k] = ( ((a[s10  ]<<1) & b1)
				   | ((a[s00  ]<<3) & b3) ) >> bit;
			k += 1;
		}
	}
	if (i < nx) {
		/*
		 * column size is odd, do last row
		 * s10,s10+1 are off edge
		 */
		s00 = n*i;
		for (j = 0; j<ny-1; j += 2) {
			b[k] = ( ((a[s00+1]<<2) & b2)
				   | ((a[s00  ]<<3) & b3) ) >> bit;
			k += 1;
			s00 += 2;
		}
		if (j < ny) {
			/*
			 * both row and column size are odd, do corner element
			 * s00+1, s10, s10+1 are off edge
			 */
			b[k] = ( ((a[s00  ]<<3) & b3) ) >> bit;
			k += 1;
		}
	}
}
/* ######################################################################### */
/*
 * Do first quadtree reduction step on bit BIT of array A.
 * Results put into B.
 * 
 */
static void
qtree_onebit64(LONGLONG a[], int n, int nx, int ny, unsigned char b[], int bit)
{
int i, j, k;
LONGLONG b0, b1, b2, b3;
int s10, s00;

	/*
	 * use selected bit to get amount to shift
	 */
	b0 = ((LONGLONG) 1)<<bit;
	b1 = b0<<1;
	b2 = b0<<2;
	b3 = b0<<3;
	k = 0;							/* k is index of b[i/2,j/2]	*/
	for (i = 0; i<nx-1; i += 2) {
		s00 = n*i;					/* s00 is index of a[i,j]	*/
		s10 = s00+n;				/* s10 is index of a[i+1,j]	*/
		for (j = 0; j<ny-1; j += 2) {
			b[k] = (unsigned char) (( ( a[s10+1]     & b0)
				   | ((a[s10  ]<<1) & b1)
				   | ((a[s00+1]<<2) & b2)
				   | ((a[s00  ]<<3) & b3) ) >> bit);
			k += 1;
			s00 += 2;
			s10 += 2;
		}
		if (j < ny) {
			/*
			 * row size is odd, do last element in row
			 * s00+1,s10+1 are off edge
			 */
			b[k] = (unsigned char) (( ((a[s10  ]<<1) & b1)
				   | ((a[s00  ]<<3) & b3) ) >> bit);
			k += 1;
		}
	}
	if (i < nx) {
		/*
		 * column size is odd, do last row
		 * s10,s10+1 are off edge
		 */
		s00 = n*i;
		for (j = 0; j<ny-1; j += 2) {
			b[k] = (unsigned char) (( ((a[s00+1]<<2) & b2)
				   | ((a[s00  ]<<3) & b3) ) >> bit);
			k += 1;
			s00 += 2;
		}
		if (j < ny) {
			/*
			 * both row and column size are odd, do corner element
			 * s00+1, s10, s10+1 are off edge
			 */
			b[k] = (unsigned char) (( ((a[s00  ]<<3) & b3) ) >> bit);
			k += 1;
		}
	}
}

/* ######################################################################### */
/*
 * do one quadtree reduction step on array a
 * results put into b (which may be the same as a)
 */
static void
qtree_reduce(unsigned char a[], int n, int nx, int ny, unsigned char b[])
{
int i, j, k;
int s10, s00;

	k = 0;							/* k is index of b[i/2,j/2]	*/
	for (i = 0; i<nx-1; i += 2) {
		s00 = n*i;					/* s00 is index of a[i,j]	*/
		s10 = s00+n;				/* s10 is index of a[i+1,j]	*/
		for (j = 0; j<ny-1; j += 2) {
			b[k] =	(a[s10+1] != 0)
				| ( (a[s10  ] != 0) << 1)
				| ( (a[s00+1] != 0) << 2)
				| ( (a[s00  ] != 0) << 3);
			k += 1;
			s00 += 2;
			s10 += 2;
		}
		if (j < ny) {
			/*
			 * row size is odd, do last element in row
			 * s00+1,s10+1 are off edge
			 */
			b[k] =  ( (a[s10  ] != 0) << 1)
				  | ( (a[s00  ] != 0) << 3);
			k += 1;
		}
	}
	if (i < nx) {
		/*
		 * column size is odd, do last row
		 * s10,s10+1 are off edge
		 */
		s00 = n*i;
		for (j = 0; j<ny-1; j += 2) {
			b[k] =  ( (a[s00+1] != 0) << 2)
				  | ( (a[s00  ] != 0) << 3);
			k += 1;
			s00 += 2;
		}
		if (j < ny) {
			/*
			 * both row and column size are odd, do corner element
			 * s00+1, s10, s10+1 are off edge
			 */
			b[k] = ( (a[s00  ] != 0) << 3);
			k += 1;
		}
	}
}

/* ######################################################################### */
static void
write_bdirect(char *outfile, int a[], int n,int nqx, int nqy, unsigned char scratch[], int bit)
{
int i;

	/*
	 * Write the direct bitmap warning code
	 */
	output_nybble(outfile,0x0);
	/*
	 * Copy A to scratch array (again!), packing 4 bits/nybble
	 */
	qtree_onebit(a,n,nqx,nqy,scratch,bit);
	/*
	 * write to outfile
	 */
	for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) {
		output_nybble(outfile,scratch[i]);
	}
}
/* ######################################################################### */
static void
write_bdirect64(char *outfile, LONGLONG a[], int n,int nqx, int nqy, unsigned char scratch[], int bit)
{
int i;

	/*
	 * Write the direct bitmap warning code
	 */
	output_nybble(outfile,0x0);
	/*
	 * Copy A to scratch array (again!), packing 4 bits/nybble
	 */
	qtree_onebit64(a,n,nqx,nqy,scratch,bit);
	/*
	 * write to outfile
	 */
	for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) {
		output_nybble(outfile,scratch[i]);
	}
}
