/* $Id: dwt_aux.c,v 1.6 1998/03/03 21:54:28 hjlee Exp $ */
/****************************************************************************/
/*   MPEG4 Visual Texture Coding (VTC) Mode Software                        */
/*                                                                          */
/*   This software was developed by                                         */
/*   Sarnoff Coporation                   and    Texas Instruments          */
/*   Iraj Sodagar   (iraj@sarnoff.com)           Jie Liang (liang@ti.com)   */
/*   Hung-Ju Lee    (hjlee@sarnoff.com)                                     */
/*   Paul Hatrack   (hatrack@sarnoff.com)                                   */
/*   Shipeng Li     (shipeng@sarnoff.com)                                   */
/*   Bing-Bing Chai (bchai@sarnoff.com)                                     */
/*                                                                          */
/* In the course of development of the MPEG-4 standard. This software       */
/* module is an implementation of a part of one or more MPEG-4 tools as     */
/* specified by the MPEG-4 standard.                                        */
/*                                                                          */
/* The copyright of this software belongs to ISO/IEC. ISO/IEC gives use     */
/* of the MPEG-4 standard free license to use this  software module or      */
/* modifications thereof for hardware or software products claiming         */
/* conformance to the MPEG-4 standard.                                      */
/*                                                                          */
/* Those intending to use this software module in hardware or software      */
/* products are advised that use may infringe existing  patents. The        */
/* original developers of this software module and their companies, the     */
/* subsequent editors and their companies, and ISO/IEC have no liability    */
/* and ISO/IEC have no liability for use of this software module or         */
/* modification thereof in an implementation.                               */
/*                                                                          */
/* Permission is granted to MPEG memebers to use, copy, modify,             */
/* and distribute the software modules ( or portions thereof )              */
/* for standardization activity within ISO/IEC JTC1/SC29/WG11.              */
/*                                                                          */
/* Copyright (C) 1998  Sarnoff Coporation and Texas Instruments             */ 
/****************************************************************************/

/************************************************************/
/*     Sarnoff Very Low Bit Rate Still Image Coder          */
/*     Copyright 1995, 1996, 1997, 1998 Sarnoff Corporation */
/************************************************************/

#ifndef _DWT_DBL_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <memory.h>
#include "dwt.h"
#define _DWT_INT_
#endif /* _DWT_DBL_ */

#undef DWTDATA

#ifdef _DWT_INT_
#define DWTDATA Int
#else
#define DWTDATA Double
#endif
/* local prototype */
#ifdef _DWT_INT_
static Int SADWT1dInt(Int *InBuf, UChar *InMaskBuf, Int *OutBuf, 
		      UChar *OutMaskBuf, Int Length, FILTER *Filter, 
		      Int Direction);
static Int SADWT1dOddSymInt(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			    UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			    Int Direction);
static Int DecomposeSegmentOddSymInt(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				     Int PosFlag, Int Length, FILTER *Filter);
static Int SADWT1dEvenSymInt(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			     UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			     Int Direction);
static Int DecomposeSegmentEvenSymInt(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				      Int PosFlag, Int Length, FILTER *Filter);
#else
static Int SADWT1dDbl(Double *InBuf, UChar *InMaskBuf, Double *OutBuf, 
		      UChar *OutMaskBuf, Int Length, FILTER *Filter, 
		      Int Direction);
static Int SADWT1dOddSymDbl(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			    UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			    Int Direction);
static Int DecomposeSegmentOddSymDbl(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				     Int PosFlag, Int Length, FILTER *Filter);
static Int SADWT1dEvenSymDbl(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			     UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			     Int Direction);
static Int DecomposeSegmentEvenSymDbl(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				      Int PosFlag, Int Length, FILTER *Filter);
#endif
#ifdef _DWT_INT_
/* Function:   DecomposeOneLevelInt()
   Description: Pyramid Decompose one level of wavelet coefficients
   Input:
   Width  -- Width of Image; 
   Height -- Height of Image;
   level -- current decomposition level
   Filter     -- Filter Used.
   MaxCoeff, MinCoeff -- bounds of the output data;
   Input/Output:

   OutCoeff   -- Ouput wavelet coefficients
   OutMask    -- Output mask corresponding to wavelet coefficients

   Return: DWT_OK if success.
*/
Int DecomposeOneLevelInt(DATA *OutCoeff, UChar *OutMask, Int Width,
				Int Height, Int level, FILTER *Filter,
				Int MaxCoeff, Int MinCoeff)
{
  Int *InBuf, *OutBuf ;
  UChar *InMaskBuf, *OutMaskBuf;
  Int width = Width>>(level-1);
  Int height = Height>>(level-1);
  Int MaxLength = (width > height)?width:height;
  Int i,k,ret;
  Int *a;
  UChar *c,*d;
  DATA *e;

  /* double check filter type */
  if(Filter->Type != INT_TYPE) return(DWT_INTERNAL_ERROR);
  /* allocate line buffers */
  InBuf = (Int *) malloc(sizeof(Int)*MaxLength);
  InMaskBuf = (UChar *) malloc(sizeof(UChar)*MaxLength);
  OutBuf = (Int *) malloc(sizeof(Int)*MaxLength);
  OutMaskBuf = (UChar *) malloc(sizeof(UChar)*MaxLength);
  if(InBuf==NULL || InMaskBuf ==NULL || OutBuf == NULL || OutMaskBuf==NULL) 
    return(DWT_MEMORY_FAILED);
    
  /* horizontal decomposition first*/
  for(i=0,k=0;i<height;i++,k+=Width) {
    /* get a line of coefficients */
    for(a=InBuf, e=OutCoeff+k;a<InBuf+width;a++,e++) {
     
      *a = (Int) *e;
    }
    /* get a line of mask */
    memcpy(InMaskBuf, OutMask+k, sizeof(UChar)*width);
    /* Perform horizontal SA-DWT */
    ret=SADWT1dInt(InBuf, InMaskBuf, OutBuf, OutMaskBuf, width, Filter, 
		   HORIZONTAL);
    if(ret!=DWT_OK) {
      free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
      return(ret);
    }
    /* put the transformed line back */
    for(a=OutBuf,e=OutCoeff+k;a<OutBuf+width;a++,e++) {
      /* Scale and Leave 3 extra bits for precision reason */
      *a = ROUNDDIV((*a <<3), Filter->Scale);
      if(*a > MaxCoeff || *a < MinCoeff) {
	free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
	return(DWT_COEFF_OVERFLOW);
      }
      *e = (DATA) *a;
    }
    memcpy(OutMask+k, OutMaskBuf, sizeof(UChar)*width);
  }

  /* then vertical decomposition */
  for(i=0;i<width;i++) {
    /* get a column of coefficients */
    for(a=InBuf, e=OutCoeff+i, c= InMaskBuf, d= OutMask+i;
	a<InBuf+height; a++, c++, e+=Width, d+=Width) {
      *a=(Int) *e;
      *c = *d;
    }
    /* perform vertical SA-DWT */
    ret=SADWT1dInt(InBuf, InMaskBuf, OutBuf, OutMaskBuf, height, Filter, 
		   VERTICAL);
    if(ret!=DWT_OK) {
      free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
      return(ret);
    }
    /* put the transformed column back */
    for(a=OutBuf, e=OutCoeff+i, c= OutMaskBuf, d= OutMask+i;
	a<OutBuf+height; a++, c++, e+=Width, d+=Width) {
      /*Scale and leave the sqrt(2)*sqrt(2) factor in the scale */
      *a = ROUNDDIV(*a, (Filter->Scale<<2)); 
      if(*a > MaxCoeff || *a < MinCoeff) {
	free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
	return(DWT_COEFF_OVERFLOW);
      }
      *e= (DATA) *a;
     /*  printf("%d\n", *e); */
      *d = *c;
    }
  }
  free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
  return(DWT_OK);
}
#else
/* Function:   DecomposeOneLevelDbl()
   Description: Pyramid Decompose one level of wavelet coefficients
   Input:
   Width  -- Width of Image; 
   Height -- Height of Image;
   level -- current decomposition level
   Filter     -- Filter Used.
   MaxCoeff, MinCoeff -- bounds of the output data;
   Input/Output:

   OutCoeff   -- Ouput wavelet coefficients
   OutMask    -- Output mask corresponding to wavelet coefficients

   Return: DWT_OK if success.
*/
Int DecomposeOneLevelDbl(Double *OutCoeff, UChar *OutMask, Int Width,
				Int Height, Int level, FILTER *Filter)
{
  Double *InBuf, *OutBuf ;
  UChar *InMaskBuf, *OutMaskBuf;
  Int width = Width>>(level-1);
  Int height = Height>>(level-1);
  Int MaxLength = (width > height)?width:height;
  Int i,k,ret;
  Double *a;
  UChar *c,*d;
  Double *e;

  /* double check filter type */
  if(Filter->Type != DBL_TYPE) return(DWT_INTERNAL_ERROR);
  /* allocate line buffers */
  InBuf = (Double *) malloc(sizeof(Double)*MaxLength);
  InMaskBuf = (UChar *) malloc(sizeof(UChar)*MaxLength);
  OutBuf = (Double *) malloc(sizeof(Double)*MaxLength);
  OutMaskBuf = (UChar *) malloc(sizeof(UChar)*MaxLength);
  if(InBuf==NULL || InMaskBuf ==NULL || OutBuf == NULL || OutMaskBuf==NULL) 
    return(DWT_MEMORY_FAILED);
    
  /* horizontal decomposition first*/
  for(i=0,k=0;i<height;i++,k+=Width) {
    /* get a line of coefficients */
    for(a=InBuf, e=OutCoeff+k;a<InBuf+width;a++,e++) {
      *a =  *e;
    }
    /* get a line of mask */
    memcpy(InMaskBuf, OutMask+k, sizeof(UChar)*width);
    /* Perform horizontal SA-DWT */
    ret=SADWT1dDbl(InBuf, InMaskBuf, OutBuf, OutMaskBuf, width, Filter, 
		   HORIZONTAL);
    if(ret!=DWT_OK) {
      free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
      return(ret);
    }
    /* put the transformed line back */
    for(a=OutBuf,e=OutCoeff+k;a<OutBuf+width;a++,e++) {
      *e =  *a;
    }
    memcpy(OutMask+k, OutMaskBuf, sizeof(UChar)*width);
  }

  /* then vertical decomposition */
  for(i=0;i<width;i++) {
    /* get a column of coefficients */
    for(a=InBuf, e=OutCoeff+i, c= InMaskBuf, d= OutMask+i;
	a<InBuf+height; a++, c++, e+=Width, d+=Width) {
      *a = *e;
      *c = *d;
    }
    /* perform vertical SA-DWT */
    ret=SADWT1dDbl(InBuf, InMaskBuf, OutBuf, OutMaskBuf, height, Filter, 
		   VERTICAL);
    if(ret!=DWT_OK) {
      free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
      return(ret);
    }
    /* put the transformed column back */
    for(a=OutBuf, e=OutCoeff+i, c= OutMaskBuf, d= OutMask+i;
	a<OutBuf+height; a++, c++, e+=Width, d+=Width) {
      *e = *a;
      *d = *c;
    }
  }
  free(InBuf);free(OutBuf);free(InMaskBuf);free(OutMaskBuf);
  return(DWT_OK);
}
#endif
/* Function: SADWT1dInt() or SADWT1dDbl()
   Description:  1-d SA-DWT
   Input:

   InBuf -- Input 1d data buffer
   InMaskBuf -- Input 1d mask buffer
   Length -- length of the input data
   Filter -- filter used
   Direction -- vertical or horizontal decomposition (used for inversible 
   mask decomposition)

   Output:
   
   OutBuf -- Transformed 1d Data
   OutMask -- Mask for the Transformed 1d Data

   Return: return DWT_OK if successful
  
*/
#ifdef _DWT_INT_
static Int SADWT1dInt(Int *InBuf, UChar *InMaskBuf, Int *OutBuf, 
		      UChar *OutMaskBuf, Int Length, FILTER *Filter, 
		      Int Direction)
{

  switch(Filter->Class){
  case ODD_SYMMETRIC:
    return(SADWT1dOddSymInt(InBuf, InMaskBuf, OutBuf, OutMaskBuf, 
			    Length, Filter, Direction));
  case EVEN_SYMMETRIC:
    return(SADWT1dEvenSymInt(InBuf, InMaskBuf, OutBuf, OutMaskBuf, 
			     Length, Filter, Direction));
    /*  case ORTHOGONAL:
	return(SADWT1dOrthInt(InBuf, InMaskBuf, OutBuf, OutMaskBuf, 
			  Length, Filter, Direction)); */
  default:
    return(DWT_FILTER_UNSUPPORTED);
  }
}
#else
static Int SADWT1dDbl(Double *InBuf, UChar *InMaskBuf, Double *OutBuf, 
		      UChar *OutMaskBuf, Int Length, FILTER *Filter, 
		      Int Direction)
{

  switch(Filter->Class){
  case ODD_SYMMETRIC:
    return(SADWT1dOddSymDbl(InBuf, InMaskBuf, OutBuf, OutMaskBuf, 
			    Length, Filter, Direction));
  case EVEN_SYMMETRIC:
    return(SADWT1dEvenSymDbl(InBuf, InMaskBuf, OutBuf, OutMaskBuf, 
			     Length, Filter, Direction));
    /*  case ORTHOGONAL:
	return(SADWT1dOrthDbl(InBuf, InMaskBuf, OutBuf, OutMaskBuf, 
			  Length, Filter, Direction)); */
  default:
    return(DWT_FILTER_UNSUPPORTED);
  }
}
#endif

/* Function: SADWT1dOddSymInt() or SADWT1dOddSymDbl()
   Description: 1D  SA-DWT using Odd Symmetric Filter
   Input:

   InBuf -- Input 1d data buffer
   InMaskBuf -- Input 1d mask buffer
   Length -- length of the input data
   Filter -- filter used
   Direction -- vertical or horizontal decomposition (used for inversible 
   mask decomposition)

   Output:
   
   OutBuf -- Transformed 1d Data
   OutMask -- Mask for the Transformed 1d Data

   Return: return DWT_OK if successful

*/



#ifdef _DWT_INT_
static Int SADWT1dOddSymInt(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			    UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			    Int Direction)
#else
static Int SADWT1dOddSymDbl(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			    UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			    Int Direction)
#endif

{
  Int i;
  Int SegLength = 0;
  Int odd;
  Int start, end;
  UChar *a, *b, *c;
  Int ret;
  /* double check filter class and type */
  if(Filter->Class != ODD_SYMMETRIC) return(DWT_INTERNAL_ERROR);
#ifdef _DWT_INT_
  if(Filter->Type != INT_TYPE) return(DWT_INTERNAL_ERROR);
#else
  if(Filter->Type != DBL_TYPE) return(DWT_INTERNAL_ERROR);
#endif
  /* double check if Length is even */
  if(Length & 1) return(DWT_INTERNAL_ERROR);
  /* initial mask output */
  for(a=InMaskBuf, b = OutMaskBuf, c= OutMaskBuf+(Length>>1); a<InMaskBuf+Length;) {
    *b++ = *a++;
    *c++ = *a++;
  }
  /* initial OutBuf to zeros */
  memset(OutBuf, (UChar)0, sizeof(DWTDATA)*Length);

  i = 0;
  a = InMaskBuf;

  while(i<Length) {
    /* search for a segment */
    while(i<Length && (a[i])!=IN) i++;
    start = i;
    if(i >= Length) break;
    while(i<Length && (a[i])==IN) i++;
    end = i;
    
    SegLength = end-start;
    odd = start%2;

    if(SegLength==1) { /* special case for single poInt */
#ifdef _DWT_INT_
      ret=DecomposeSegmentOddSymInt(InBuf+start, OutBuf+(start>>1), 
				    OutBuf+(Length>>1)+(start>>1), odd, 
				    SegLength, Filter);
#else
      ret=DecomposeSegmentOddSymDbl(InBuf+start, OutBuf+(start>>1), 
				    OutBuf+(Length>>1)+(start>>1), odd, 
				    SegLength, Filter);
#endif
	
      

      if(ret!=DWT_OK) return(ret);
      /* swap the subsampled mask for single poInt if highpass is IN */
      if(Direction == HORIZONTAL) {
	/* After horizontal decomposition, 
	   LP mask symbols: IN, OUT0;
	   HP mask symbols: IN, OUT0, OUT1 */
	if(OutMaskBuf[start>>1]==OUT0) { 
	  OutMaskBuf[start>>1] = IN;
	  OutMaskBuf[(start>>1)+(Length>>1)]=OUT1;
	}
      }
      else { /* vertical */
	/* After vertical decomposition, 
	   LLP mask symbols: IN, OUT0;
	   LHP mask symbols: IN, OUT2, OUT3;
	   HLP mask symbols: IN, OUT0, OUT1;
	   HHP mask symbols: IN, OUT0, OUT1, OUT2, OUT3 */
	if(OutMaskBuf[start>>1] == OUT0) {
	  OutMaskBuf[(start>>1)+(Length>>1)]= OUT2 ;
	  OutMaskBuf[start>>1] = IN;
	}
	else if(OutMaskBuf[start>>1] == OUT1) {
	  OutMaskBuf[(start>>1)+(Length>>1)]= OUT3 ;
	  OutMaskBuf[start>>1] = IN;
	}
      }
    }
    else {
#ifdef _DWT_INT_
      ret=DecomposeSegmentOddSymInt(InBuf+start, OutBuf+((start+1)>>1), 
				    OutBuf+(Length>>1)+(start>>1), odd, 
				    SegLength, Filter);

#else
      ret=DecomposeSegmentOddSymDbl(InBuf+start, OutBuf+((start+1)>>1), 
				    OutBuf+(Length>>1)+(start>>1), odd, 
				    SegLength, Filter);
#endif
      if(ret!=DWT_OK) return(ret);
    }
  }
  return(DWT_OK);
}

/* 
   Function:  DecomposeSegmentOddSymInt() or  DecomposeSegmentOddSymDbl()
   Description: SA-Decompose a 1D segment based on its InitPos and Length using
                Odd-Symmetric Filters
   Input:

   In -- Input data segment;
   PosFlag -- Start position of this segment (ODD or EVEN);
   Length -- Length of this Segment;
   Filter -- Filter used;
   
   Output:

   OutL -- Low pass data;
   OutH -- High pass data;

   Return: Return DWT_OK if Successful
*/
#ifdef _DWT_INT_
static Int DecomposeSegmentOddSymInt(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				     Int PosFlag, Int Length, FILTER *Filter)
#else
static Int DecomposeSegmentOddSymDbl(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				     Int PosFlag, Int Length, FILTER *Filter)
#endif

{
  /* filter coefficients */
#ifdef _DWT_INT_
  Short *LPCoeff = Filter->LPCoeff, *HPCoeff = Filter->HPCoeff; 
  Short *fi;
#else
  Double *LPCoeff = Filter->LPCoeff, *HPCoeff = Filter->HPCoeff; 
  Double *fi;
#endif
  Int ltaps = Filter->LPLength, htaps = Filter->HPLength; /* filter lengths*/
  Int loffset = ltaps/2, hoffset = htaps/2;  /* Filter offset */
  Int border = (ltaps>htaps)?ltaps:htaps; /*the larger of ltaps and htaps */
  Int m, n;
  DWTDATA *f,*pixel, val, *buf, *a;
  Int r = Length-1;
  /* printf("Length=%d\n", Length); */
  if(Length == 1) {
    *OutL = *In * Filter->Scale;
    return (DWT_OK);
  }

  /* allocate proper buffer */
  buf = (DWTDATA *) malloc((Length+2*border)*sizeof(DWTDATA));
  if(buf==NULL)  return(DWT_MEMORY_FAILED);
  /* now symmetric extend this segment */
  a = buf+border;
  for(m=0;m< Length; m++) {
    a[m] = In[m];
    /* printf("%f ", a[m]); */
  }
  /* printf("\n"); */
  /* symmetric extension */
  for (m=1 ; m<=border; m++)
  {
    a[-m] =  a[m];  /* to allow shorter seg */
    a[r+m] = a[r-m]; 
  }

  f = buf + border + Length;
  /* always subsample even positions in a line for LP coefficents */
  if (PosFlag==POS_ODD) a = buf + border + 1;
  else a = buf + border;

  for (; a<f; a +=2)
  {
    /* filter the pixel with lowpass filter */
    for( fi=LPCoeff, pixel=a-loffset, val=0, n=0; n<ltaps; 
	 n++, fi++, pixel++)
      val += (*fi * *pixel);

    *OutL++ = val;
  }
  /* always  subsample odd positions in a line for HP coefficients */
  if (PosFlag==POS_ODD) a = buf + border;
  else a = buf + border+1;

  for (; a<f; a +=2)
    {
    /* filter the pixel with highpass filter */
      for(fi=HPCoeff, pixel=a-hoffset, val=0, n=0; n<htaps; 
	  n++, fi++, pixel++)
	val += (*fi *  *pixel);
      *OutH++ = val;
  }
  free(buf);
  return(DWT_OK);
}

/* Function: SADWT1dEvenSymInt() or SADWT1dEvenSymDbl()
   Description: 1D  SA-DWT using Even Symmetric Filter
   Input:

   InBuf -- Input 1d data buffer
   InMaskBuf -- Input 1d mask buffer
   Length -- length of the input data
   Filter -- filter used
   Direction -- vertical or horizontal decomposition (used for inversible 
   mask decomposition)

   Output:
   
   OutBuf -- Transformed 1d Data
   OutMask -- Mask for the Transformed 1d Data

   Return: return DWT_OK if successful

*/
#ifdef _DWT_INT_
static Int SADWT1dEvenSymInt(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			     UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			     Int Direction)
#else
static Int SADWT1dEvenSymDbl(DWTDATA *InBuf, UChar *InMaskBuf, DWTDATA *OutBuf, 
			     UChar *OutMaskBuf, Int Length, FILTER *Filter, 
			     Int Direction)
#endif

{
  Int i;
  Int SegLength = 0;
  Int odd;
  Int start, end;
  UChar *a, *b, *c;
  Int ret;
  /* double check filter class and type */
  if(Filter->Class != EVEN_SYMMETRIC) return(DWT_INTERNAL_ERROR);
#ifdef _DWT_INT_
  if(Filter->Type != INT_TYPE)  return(DWT_INTERNAL_ERROR);
#else
  if(Filter->Type != DBL_TYPE)  return(DWT_INTERNAL_ERROR);
#endif
  /* double check if Length is even */
  if(Length & 1) return(DWT_INTERNAL_ERROR);
  /* initial mask output */
  for(a=InMaskBuf, b = OutMaskBuf, c= OutMaskBuf+(Length>>1); a<InMaskBuf+Length;) {
    *b++ = *a++;
    *c++ = *a++;
  }
  /* initial OutBuf to zeros */
  memset(OutBuf, (UChar)0, sizeof(DWTDATA)*Length);
  
  i = 0;
  a = InMaskBuf;
  while(i<Length) {
    /* search for a segment */
    while(i<Length && (a[i])!=IN) i++;
    start = i;
    if(i >= Length) break;
    while(i<Length && (a[i])==IN) i++;
    end = i;
    SegLength = end-start;
    odd = start%2;
    if(SegLength==1) { /* special case for single poInt */
#ifdef _DWT_INT_
      ret=DecomposeSegmentEvenSymInt(InBuf+start, OutBuf+(start>>1), 
				     OutBuf+(Length>>1)+(start>>1), odd, 
				     SegLength, Filter);
#else
      ret=DecomposeSegmentEvenSymDbl(InBuf+start, OutBuf+(start>>1), 
				     OutBuf+(Length>>1)+(start>>1), odd, 
				     SegLength, Filter);
#endif

      if(ret!=DWT_OK) return(ret);
 
    }
    else {
#ifdef _DWT_INT_
      ret=DecomposeSegmentEvenSymInt(InBuf+start, OutBuf+(start>>1), 
				     OutBuf+(Length>>1)+((start+1)>>1), odd, 
				     SegLength, Filter);
#else
      ret=DecomposeSegmentEvenSymDbl(InBuf+start, OutBuf+(start>>1), 
				     OutBuf+(Length>>1)+((start+1)>>1), odd, 
				     SegLength, Filter);
#endif
	
      if(ret!=DWT_OK) return(ret);
    }
    /* swap the subsampled mask for the start of segment if it is odd*/
    if(odd) {
      if(Direction == HORIZONTAL) {
	/* After horizontal decomposition, 
	   LP mask symbols: IN, OUT0;
	   HP mask symbols: IN, OUT0, OUT1 */
	if(OutMaskBuf[start>>1]==OUT0) { 
	  OutMaskBuf[start>>1] = IN;
	  OutMaskBuf[(start>>1)+(Length>>1)]=OUT1;
	}
      }
      else { /* vertical */
	/* After vertical decomposition, 
	   LLP mask symbols: IN, OUT0;
	   LHP mask symbols: IN, OUT2, OUT3;
	   HLP mask symbols: IN, OUT0, OUT1;
	   HHP mask symbols: IN, OUT0, OUT1, OUT2, OUT3 */
	if(OutMaskBuf[start>>1] == OUT0) {
	  OutMaskBuf[(start>>1)+(Length>>1)]= OUT2 ;
	  OutMaskBuf[start>>1] = IN;
	}
	else if(OutMaskBuf[start>>1] == OUT1) {
	  OutMaskBuf[(start>>1)+(Length>>1)]= OUT3 ;
	  OutMaskBuf[start>>1] = IN;
	}
      }
    }
  }
  return(DWT_OK);
}

/* 
   Function:  DecomposeSegmentEvenSymInt() or DecomposeSegmentEvenSymDbl()
   Description: SA-Decompose a 1D segment based on its InitPos and Length using
                Even-Symmetric Filters
   Input:

   In -- Input data segment;
   PosFlag -- Start position of this segment (ODD or EVEN);
   Length -- Length of this Segment;
   Filter -- Filter used;
   
   Output:

   OutL -- Low pass data;
   OutH -- High pass data;

   Return: Return DWT_OK if Successful
*/
#ifdef _DWT_INT_
static Int DecomposeSegmentEvenSymInt(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				      Int PosFlag, Int Length, FILTER *Filter)
#else
static Int DecomposeSegmentEvenSymDbl(DWTDATA *In, DWTDATA *OutL, DWTDATA *OutH, 
				      Int PosFlag, Int Length, FILTER *Filter)
#endif

{
  /* filter coefficients */
#ifdef _DWT_INT_
  Short *LPCoeff = Filter->LPCoeff, *HPCoeff = Filter->HPCoeff; 
  Short *fi;
#else
  Double *LPCoeff = Filter->LPCoeff, *HPCoeff = Filter->HPCoeff; 
  Double *fi; 
#endif
  Int ltaps = Filter->LPLength, htaps = Filter->HPLength; /* filter lengths*/
  Int loffset = ltaps/2-1, hoffset = htaps/2-1;  /* Filter offset */
  Int border = (ltaps>htaps)?ltaps:htaps; /*the larger of ltaps and htaps */
  Int m, n;
  DWTDATA *f,*pixel, val, *buf, *a;
  Int r = Length-1;

  if(Length == 1) {
    *OutL = *In * Filter->Scale;
    return (DWT_OK);
  }
  /* allocate proper buffer */
  buf = (DWTDATA *) malloc((Length+2*border)*sizeof(DWTDATA));
  if(buf==NULL)  return(DWT_MEMORY_FAILED);
  /* now symmetric extend this segment */
  a = buf+border;
  for(m=0;m< Length; m++) {
    a[m] = In[m];
  }
  /* symmetric extension */
  for (m=1 ; m<=border; m++)
  {
    a[-m] =  a[m-1];  /* to allow shorter seg */
    a[r+m] = a[r-m+1]; 
  }

  f = buf + border + Length;
  /* always subsample even positions in a line for LP coefficents */
  if (PosFlag==POS_ODD) a = buf + border - 1;
  else a = buf + border;

  for (; a<f; a +=2)
  {
    /* filter the pixel with lowpass filter */
    for( fi=LPCoeff, pixel=a-loffset, val=0, n=0; n<ltaps; 
	 n++, fi++, pixel++)
      val += (*fi * *pixel);

    *OutL++ = val;
  }
  /* always  subsample even positions in a line for HP coefficients */
  if (PosFlag==POS_ODD) a = buf + border +1;
  else a = buf + border;

  for (; a<f; a +=2)
    {
    /* filter the pixel with highpass filter */
      for(fi=HPCoeff, pixel=a-hoffset, val=0, n=0; n<htaps; 
	  n++, fi++, pixel++)
	val += (*fi *  *pixel);
      *OutH++ = val;
  }
  free(buf);
  return(DWT_OK);
}

#ifdef _DWT_INT_
#undef _DWT_INT_
#define _DWT_DBL_
#include "dwt_aux.c"
#endif
