/*
 * Copyright (c) 1999-2000 Image Power, Inc. and the University of
 *   British Columbia.
 * Copyright (c) 2001-2002 Michael David Adams.
 * All rights reserved.
 */

/* __START_OF_JASPER_LICENSE__
 *
 * JasPer Software License
 *
 * IMAGE POWER JPEG-2000 PUBLIC LICENSE
 * ************************************
 *
 * GRANT:
 *
 * Permission is hereby granted, free of charge, to any person (the "User")
 * obtaining a copy of this software and associated documentation, to deal
 * in the JasPer Software without restriction, including without limitation
 * the right to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the JasPer Software (in source and binary forms),
 * and to permit persons to whom the JasPer Software is furnished to do so,
 * provided further that the License Conditions below are met.
 *
 * License Conditions
 * ******************
 *
 * A.  Redistributions of source code must retain the above copyright notice,
 * and this list of conditions, and the following disclaimer.
 *
 * B.  Redistributions in binary form must reproduce the above copyright
 * notice, and this list of conditions, and the following disclaimer in
 * the documentation and/or other materials provided with the distribution.
 *
 * C.  Neither the name of Image Power, Inc. nor any other contributor
 * (including, but not limited to, the University of British Columbia and
 * Michael David Adams) may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * D.  User agrees that it shall not commence any action against Image Power,
 * Inc., the University of British Columbia, Michael David Adams, or any
 * other contributors (collectively "Licensors") for infringement of any
 * intellectual property rights ("IPR") held by the User in respect of any
 * technology that User owns or has a right to license or sublicense and
 * which is an element required in order to claim compliance with ISO/IEC
 * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
 * rights worldwide arising under statutory or common law, and whether
 * or not perfected, including, without limitation, all (i) patents and
 * patent applications owned or licensable by User; (ii) rights associated
 * with works of authorship including copyrights, copyright applications,
 * copyright registrations, mask work rights, mask work applications,
 * mask work registrations; (iii) rights relating to the protection of
 * trade secrets and confidential information; (iv) any right analogous
 * to those set forth in subsections (i), (ii), or (iii) and any other
 * proprietary rights relating to intangible property (other than trademark,
 * trade dress, or service mark rights); and (v) divisions, continuations,
 * renewals, reissues and extensions of the foregoing (as and to the extent
 * applicable) now existing, hereafter filed, issued or acquired.
 *
 * E.  If User commences an infringement action against any Licensor(s) then
 * such Licensor(s) shall have the right to terminate User's license and
 * all sublicenses that have been granted hereunder by User to other parties.
 *
 * F.  This software is for use only in hardware or software products that
 * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
 * or right to this Software is granted for products that do not comply
 * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
 * from the ISO.
 *
 * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
 * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
 * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
 * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
 * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
 * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
 * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
 * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
 * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
 * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
 * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
 * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
 * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
 * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
 * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
 * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
 * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
 * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
 * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
 * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
 * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
 * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
 * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
 * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
 * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
 * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
 * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
 * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
 * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
 * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
 * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
 * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
 * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
 * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
 * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
 * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
 * NOTICE SPECIFIED IN THIS SECTION.
 *
 * __END_OF_JASPER_LICENSE__
 */

/*
 * Quadrature Mirror-Image Filter Bank (QMFB) Library
 *
 * $Id$
 */

/******************************************************************************\
* Includes.
\******************************************************************************/

#include <assert.h>

#include "jasper/jas_malloc.h"
#include "jasper/jas_math.h"

#include "jpc_qmfb.h"
#include "jpc_tsfb.h"
#include "jpc_math.h"

/******************************************************************************\
*
\******************************************************************************/

static jpc_qmfb1d_t *jpc_qmfb1d_create(void);

static int jpc_ft_getnumchans(jpc_qmfb1d_t *qmfb);
static int jpc_ft_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
static int jpc_ft_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
static void jpc_ft_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
static void jpc_ft_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);

static int jpc_ns_getnumchans(jpc_qmfb1d_t *qmfb);
static int jpc_ns_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
static int jpc_ns_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
static void jpc_ns_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
static void jpc_ns_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);

/******************************************************************************\
*
\******************************************************************************/

jpc_qmfb1dops_t jpc_ft_ops = {
        jpc_ft_getnumchans,
        jpc_ft_getanalfilters,
        jpc_ft_getsynfilters,
        jpc_ft_analyze,
        jpc_ft_synthesize
};

jpc_qmfb1dops_t jpc_ns_ops = {
        jpc_ns_getnumchans,
        jpc_ns_getanalfilters,
        jpc_ns_getsynfilters,
        jpc_ns_analyze,
        jpc_ns_synthesize
};

/******************************************************************************\
*
\******************************************************************************/

static void jpc_qmfb1d_setup(jpc_fix_t *startptr, int startind, int endind,
  int intrastep, jpc_fix_t **lstartptr, int *lstartind, int *lendind,
  jpc_fix_t **hstartptr, int *hstartind, int *hendind)
{
        *lstartind = JPC_CEILDIVPOW2(startind, 1);
        *lendind = JPC_CEILDIVPOW2(endind, 1);
        *hstartind = JPC_FLOORDIVPOW2(startind, 1);
        *hendind = JPC_FLOORDIVPOW2(endind, 1);
        *lstartptr = startptr;
        *hstartptr = &startptr[(*lendind - *lstartind) * intrastep];
}

static void jpc_qmfb1d_split(jpc_fix_t *startptr, int startind, int endind,
  int step, jpc_fix_t *lstartptr, int lstartind, int lendind,
  jpc_fix_t *hstartptr, int hstartind, int hendind)
{
#define QMFB_SPLITBUFSIZE 4096
        jpc_fix_t splitbuf[QMFB_SPLITBUFSIZE];
        jpc_fix_t *buf = splitbuf;
        int llen;
        int hlen;
        int twostep;
        jpc_fix_t *tmpptr;
        jpc_fix_t *ptr;
        jpc_fix_t *hptr;
        jpc_fix_t *lptr;
        int n;
        int state;

        twostep = step << 1;
        llen = lendind - lstartind;
        hlen = hendind - hstartind;

        if (hstartind < lstartind) {
                /* The first sample in the input signal is to appear
                  in the highpass subband signal. */
                /* Copy the appropriate samples into the lowpass subband
                  signal, saving any samples destined for the highpass subband
                  signal as they are overwritten. */
                tmpptr = buf;
                ptr = &startptr[step];
                lptr = lstartptr;
                n = llen;
                state = 1;
                while (n-- > 0) {
                        if (state) {
                                *tmpptr = *lptr;
                                ++tmpptr;
                        }
                        *lptr = *ptr;
                        ptr += twostep;
                        lptr += step;
                        state ^= 1;
                }
                /* Copy the appropriate samples into the highpass subband
                  signal. */
                /* Handle the nonoverwritten samples. */
                hptr = &hstartptr[(hlen - 1) * step];
                ptr = &startptr[(((llen + hlen - 1) >> 1) << 1) * step];
                n = hlen - (tmpptr - buf);
                while (n-- > 0) {
                        *hptr = *ptr;
                        hptr -= step;
                        ptr -= twostep;
                }
                /* Handle the overwritten samples. */
                n = tmpptr - buf;
                while (n-- > 0) {
                        --tmpptr;
                        *hptr = *tmpptr;
                        hptr -= step;
                }
        } else {
                /* The first sample in the input signal is to appear
                  in the lowpass subband signal. */
                /* Copy the appropriate samples into the lowpass subband
                  signal, saving any samples for the highpass subband
                  signal as they are overwritten. */
                state = 0;
                ptr = startptr;
                lptr = lstartptr;
                tmpptr = buf;
                n = llen;
                while (n-- > 0) {
                        if (state) {
                                *tmpptr = *lptr;
                                ++tmpptr;
                        }
                        *lptr = *ptr;
                        ptr += twostep;
                        lptr += step;
                        state ^= 1;
                }
                /* Copy the appropriate samples into the highpass subband
                  signal. */
                /* Handle the nonoverwritten samples. */
                ptr = &startptr[((((llen + hlen) >> 1) << 1) - 1) * step];
                hptr = &hstartptr[(hlen - 1) * step];
                n = hlen - (tmpptr - buf);
                while (n-- > 0) {
                        *hptr = *ptr;
                        ptr -= twostep;
                        hptr -= step;
                }
                /* Handle the overwritten samples. */
                n = tmpptr - buf;
                while (n-- > 0) {
                        --tmpptr;
                        *hptr = *tmpptr;
                        hptr -= step;
                }
        }
}

static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
  int step, jpc_fix_t *lstartptr, int lstartind, int lendind,
  jpc_fix_t *hstartptr, int hstartind, int hendind)
{
#define QMFB_JOINBUFSIZE        4096
        jpc_fix_t joinbuf[QMFB_JOINBUFSIZE];
        jpc_fix_t *buf = joinbuf;
        int llen;
        int hlen;
        int twostep;
        jpc_fix_t *tmpptr;
        jpc_fix_t *ptr;
        jpc_fix_t *hptr;
        jpc_fix_t *lptr;
        int n;
        int state;

        twostep = step << 1;
        llen = lendind - lstartind;
        hlen = hendind - hstartind;

        if (hstartind < lstartind) {
                /* The first sample in the highpass subband signal is to
                  appear first in the output signal. */
                /* Copy the appropriate samples into the first phase of the
                  output signal. */
                tmpptr = buf;
                hptr = hstartptr;
                ptr = startptr;
                n = (llen + 1) >> 1;
                while (n-- > 0) {
                        *tmpptr = *ptr;
                        *ptr = *hptr;
                        ++tmpptr;
                        ptr += twostep;
                        hptr += step;
                }
                n = hlen - ((llen + 1) >> 1);
                while (n-- > 0) {
                        *ptr = *hptr;
                        ptr += twostep;
                        hptr += step;
                }
                /* Copy the appropriate samples into the second phase of
                  the output signal. */
                ptr -= (lendind > hendind) ? (step) : (step + twostep);
                state = !((llen - 1) & 1);
                lptr = &lstartptr[(llen - 1) * step];
                n = llen;
                while (n-- > 0) {
                        if (state) {
                                --tmpptr;
                                *ptr = *tmpptr;
                        } else {
                                *ptr = *lptr;
                        }
                        lptr -= step;
                        ptr -= twostep;
                        state ^= 1;
                }
        } else {
                /* The first sample in the lowpass subband signal is to
                  appear first in the output signal. */
                /* Copy the appropriate samples into the first phase of the
                  output signal (corresponding to even indexed samples). */
                lptr = &lstartptr[(llen - 1) * step];
                ptr = &startptr[((llen - 1) << 1) * step];
                n = llen >> 1;
                tmpptr = buf;
                while (n-- > 0) {
                        *tmpptr = *ptr;
                        *ptr = *lptr;
                        ++tmpptr;
                        ptr -= twostep;
                        lptr -= step;
                }
                n = llen - (llen >> 1);
                while (n-- > 0) {
                        *ptr = *lptr;
                        ptr -= twostep;
                        lptr -= step;
                }
                /* Copy the appropriate samples into the second phase of
                  the output signal (corresponding to odd indexed
                  samples). */
                ptr = &startptr[step];
                hptr = hstartptr;
                state = !(llen & 1);
                n = hlen;
                while (n-- > 0) {
                        if (state) {
                                --tmpptr;
                                *ptr = *tmpptr;
                        } else {
                                *ptr = *hptr;
                        }
                        hptr += step;
                        ptr += twostep;
                        state ^= 1;
                }
        }
}

/******************************************************************************\
* Code for 5/3 transform.
\******************************************************************************/

static int jpc_ft_getnumchans(jpc_qmfb1d_t *qmfb)
{
        return 2;
}

static int jpc_ft_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
{
        abort();
        return -1;
}

static int jpc_ft_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
{
        jas_seq_t *lf;
        jas_seq_t *hf;

        lf = 0;
        hf = 0;

        if (len > 1 || (!len)) {
                if (!(lf = jas_seq_create(-1, 2))) {
                        goto error;
                }
                jas_seq_set(lf, -1, jpc_dbltofix(0.5));
                jas_seq_set(lf, 0, jpc_dbltofix(1.0));
                jas_seq_set(lf, 1, jpc_dbltofix(0.5));
                if (!(hf = jas_seq_create(-1, 4))) {
                        goto error;
                }
                jas_seq_set(hf, -1, jpc_dbltofix(-0.125));
                jas_seq_set(hf, 0, jpc_dbltofix(-0.25));
                jas_seq_set(hf, 1, jpc_dbltofix(0.75));
                jas_seq_set(hf, 2, jpc_dbltofix(-0.25));
                jas_seq_set(hf, 3, jpc_dbltofix(-0.125));
        } else if (len == 1) {
                if (!(lf = jas_seq_create(0, 1))) {
                        goto error;
                }
                jas_seq_set(lf, 0, jpc_dbltofix(1.0));
                if (!(hf = jas_seq_create(0, 1))) {
                        goto error;
                }
                jas_seq_set(hf, 0, jpc_dbltofix(2.0));
        } else {
                abort();
        }

        filters[0] = lf;
        filters[1] = hf;

        return 0;

error:
        if (lf) {
                jas_seq_destroy(lf);
        }
        if (hf) {
                jas_seq_destroy(hf);
        }
        return -1;
}

#define NFT_LIFT0(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pluseq) \
{ \
        jpc_fix_t *lptr = (lstartptr); \
        jpc_fix_t *hptr = (hstartptr); \
        int n = (hendind) - (hstartind); \
        if ((hstartind) < (lstartind)) { \
                pluseq(*hptr, *lptr); \
                hptr += (step); \
                --n; \
        } \
        if ((hendind) >= (lendind)) { \
                --n; \
        } \
        while (n-- > 0) { \
                pluseq(*hptr, jpc_fix_asr(jpc_fix_add(*lptr, lptr[(step)]), 1)); \
                hptr += (step); \
                lptr += (step); \
        } \
        if ((hendind) >= (lendind)) { \
                pluseq(*hptr, *lptr); \
        } \
}

#define NFT_LIFT1(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pluseq) \
{ \
        jpc_fix_t *lptr = (lstartptr); \
        jpc_fix_t *hptr = (hstartptr); \
        int n = (lendind) - (lstartind); \
        if ((hstartind) >= (lstartind)) { \
                pluseq(*lptr, *hptr); \
                lptr += (step); \
                --n; \
        } \
        if ((lendind) > (hendind)) { \
                --n; \
        } \
        while (n-- > 0) { \
                pluseq(*lptr, jpc_fix_asr(jpc_fix_add(*hptr, hptr[(step)]), 2)); \
                lptr += (step); \
                hptr += (step); \
        } \
        if ((lendind) > (hendind)) { \
                pluseq(*lptr, *hptr); \
        } \
}

#define RFT_LIFT0(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pmeqop) \
{ \
        jpc_fix_t *lptr = (lstartptr); \
        jpc_fix_t *hptr = (hstartptr); \
        int n = (hendind) - (hstartind); \
        if ((hstartind) < (lstartind)) { \
                *hptr pmeqop *lptr; \
                hptr += (step); \
                --n; \
        } \
        if ((hendind) >= (lendind)) { \
                --n; \
        } \
        while (n-- > 0) { \
                *hptr pmeqop (*lptr + lptr[(step)]) >> 1; \
                hptr += (step); \
                lptr += (step); \
        } \
        if ((hendind) >= (lendind)) { \
                *hptr pmeqop *lptr; \
        } \
}

#define RFT_LIFT1(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pmeqop) \
{ \
        jpc_fix_t *lptr = (lstartptr); \
        jpc_fix_t *hptr = (hstartptr); \
        int n = (lendind) - (lstartind); \
        if ((hstartind) >= (lstartind)) { \
                *lptr pmeqop ((*hptr << 1) + 2) >> 2; \
                lptr += (step); \
                --n; \
        } \
        if ((lendind) > (hendind)) { \
                --n; \
        } \
        while (n-- > 0) { \
                *lptr pmeqop ((*hptr + hptr[(step)]) + 2) >> 2; \
                lptr += (step); \
                hptr += (step); \
        } \
        if ((lendind) > (hendind)) { \
                *lptr pmeqop ((*hptr << 1) + 2) >> 2; \
        } \
}

static void jpc_ft_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
{
        jpc_fix_t *startptr;
        int startind;
        int endind;
        jpc_fix_t *  lstartptr;
        int   lstartind;
        int   lendind;
        jpc_fix_t *  hstartptr;
        int   hstartind;
        int   hendind;
        int interstep;
        int intrastep;
        int numseq;

        if (flags & JPC_QMFB1D_VERT) {
                interstep = 1;
                intrastep = jas_seq2d_rowstep(x);
                numseq = jas_seq2d_width(x);
                startind = jas_seq2d_ystart(x);
                endind = jas_seq2d_yend(x);
        } else {
                interstep = jas_seq2d_rowstep(x);
                intrastep = 1;
                numseq = jas_seq2d_height(x);
                startind = jas_seq2d_xstart(x);
                endind = jas_seq2d_xend(x);
        }

        assert(startind < endind);

        startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
        if (flags & JPC_QMFB1D_RITIMODE) {
                while (numseq-- > 0) {
                        jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
                          &lstartptr, &lstartind, &lendind, &hstartptr,
                          &hstartind, &hendind);
                        if (endind - startind > 1) {
                                jpc_qmfb1d_split(startptr, startind, endind,
                                  intrastep, lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind);
                                RFT_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep, -=);
                                RFT_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep, +=);
                        } else {
                                if (lstartind == lendind) {
                                        *startptr <<= 1;
                                }
                        }
                        startptr += interstep;
                }
        } else {
                while (numseq-- > 0) {
                        jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
                          &lstartptr, &lstartind, &lendind, &hstartptr,
                          &hstartind, &hendind);
                        if (endind - startind > 1) {
                                jpc_qmfb1d_split(startptr, startind, endind,
                                  intrastep, lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind);
                                NFT_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_fix_minuseq);
                                NFT_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_fix_pluseq);
                        } else {
                                if (lstartind == lendind) {
                                        *startptr = jpc_fix_asl(*startptr, 1);
                                }
                        }
                        startptr += interstep;
                }
        }
}

static void jpc_ft_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
{
        jpc_fix_t *startptr;
        int startind;
        int endind;
        jpc_fix_t *lstartptr;
        int lstartind;
        int lendind;
        jpc_fix_t *hstartptr;
        int hstartind;
        int hendind;
        int interstep;
        int intrastep;
        int numseq;

        if (flags & JPC_QMFB1D_VERT) {
                interstep = 1;
                intrastep = jas_seq2d_rowstep(x);
                numseq = jas_seq2d_width(x);
                startind = jas_seq2d_ystart(x);
                endind = jas_seq2d_yend(x);
        } else {
                interstep = jas_seq2d_rowstep(x);
                intrastep = 1;
                numseq = jas_seq2d_height(x);
                startind = jas_seq2d_xstart(x);
                endind = jas_seq2d_xend(x);
        }

        assert(startind < endind);

        startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
        if (flags & JPC_QMFB1D_RITIMODE) {
                while (numseq-- > 0) {
                        jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
                          &lstartptr, &lstartind, &lendind, &hstartptr,
                          &hstartind, &hendind);
                        if (endind - startind > 1) {
                                RFT_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep, -=);
                                RFT_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep, +=);
                                jpc_qmfb1d_join(startptr, startind, endind,
                                  intrastep, lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind);
                        } else {
                                if (lstartind == lendind) {
                                        *startptr >>= 1;
                                }
                        }
                        startptr += interstep;
                }
        } else {
                while (numseq-- > 0) {
                        jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
                          &lstartptr, &lstartind, &lendind, &hstartptr,
                          &hstartind, &hendind);
                        if (endind - startind > 1) {
                                NFT_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_fix_minuseq);
                                NFT_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_fix_pluseq);
                                jpc_qmfb1d_join(startptr, startind, endind,
                                  intrastep, lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind);
                        } else {
                                if (lstartind == lendind) {
                                        *startptr = jpc_fix_asr(*startptr, 1);
                                }
                        }
                        startptr += interstep;
                }
        }
}

/******************************************************************************\
* Code for 9/7 transform.
\******************************************************************************/

static int jpc_ns_getnumchans(jpc_qmfb1d_t *qmfb)
{
        return 2;
}

static int jpc_ns_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
{
        abort();
        return -1;
}

static int jpc_ns_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
{
        jas_seq_t *lf;
        jas_seq_t *hf;

        lf = 0;
        hf = 0;

        if (len > 1 || (!len)) {
                if (!(lf = jas_seq_create(-3, 4))) {
                        goto error;
                }
                jas_seq_set(lf, -3, jpc_dbltofix(-0.09127176311424948));
                jas_seq_set(lf, -2, jpc_dbltofix(-0.05754352622849957));
                jas_seq_set(lf, -1, jpc_dbltofix(0.5912717631142470));
                jas_seq_set(lf, 0, jpc_dbltofix(1.115087052456994));
                jas_seq_set(lf, 1, jpc_dbltofix(0.5912717631142470));
                jas_seq_set(lf, 2, jpc_dbltofix(-0.05754352622849957));
                jas_seq_set(lf, 3, jpc_dbltofix(-0.09127176311424948));
                if (!(hf = jas_seq_create(-3, 6))) {
                        goto error;
                }
                jas_seq_set(hf, -3, jpc_dbltofix(-0.02674875741080976 * 2.0));
                jas_seq_set(hf, -2, jpc_dbltofix(-0.01686411844287495 * 2.0));
                jas_seq_set(hf, -1, jpc_dbltofix(0.07822326652898785 * 2.0));
                jas_seq_set(hf, 0, jpc_dbltofix(0.2668641184428723 * 2.0));
                jas_seq_set(hf, 1, jpc_dbltofix(-0.6029490182363579 * 2.0));
                jas_seq_set(hf, 2, jpc_dbltofix(0.2668641184428723 * 2.0));
                jas_seq_set(hf, 3, jpc_dbltofix(0.07822326652898785 * 2.0));
                jas_seq_set(hf, 4, jpc_dbltofix(-0.01686411844287495 * 2.0));
                jas_seq_set(hf, 5, jpc_dbltofix(-0.02674875741080976 * 2.0));
        } else if (len == 1) {
                if (!(lf = jas_seq_create(0, 1))) {
                        goto error;
                }
                jas_seq_set(lf, 0, jpc_dbltofix(1.0));
                if (!(hf = jas_seq_create(0, 1))) {
                        goto error;
                }
                jas_seq_set(hf, 0, jpc_dbltofix(2.0));
        } else {
                abort();
        }

        filters[0] = lf;
        filters[1] = hf;

        return 0;

error:
        if (lf) {
                jas_seq_destroy(lf);
        }
        if (hf) {
                jas_seq_destroy(hf);
        }
        return -1;
}

#define NNS_LIFT0(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, alpha) \
{ \
        jpc_fix_t *lptr = (lstartptr); \
        jpc_fix_t *hptr = (hstartptr); \
        int n = (hendind) - (hstartind); \
        jpc_fix_t twoalpha = jpc_fix_mulbyint(alpha, 2); \
        if ((hstartind) < (lstartind)) { \
                jpc_fix_pluseq(*hptr, jpc_fix_mul(*lptr, (twoalpha))); \
                hptr += (step); \
                --n; \
        } \
        if ((hendind) >= (lendind)) { \
                --n; \
        } \
        while (n-- > 0) { \
                jpc_fix_pluseq(*hptr, jpc_fix_mul(jpc_fix_add(*lptr, lptr[(step)]), (alpha))); \
                hptr += (step); \
                lptr += (step); \
        } \
        if ((hendind) >= (lendind)) { \
                jpc_fix_pluseq(*hptr, jpc_fix_mul(*lptr, (twoalpha))); \
        } \
}

#define NNS_LIFT1(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, alpha) \
{ \
        jpc_fix_t *lptr = (lstartptr); \
        jpc_fix_t *hptr = (hstartptr); \
        int n = (lendind) - (lstartind); \
        int twoalpha = jpc_fix_mulbyint(alpha, 2); \
        if ((hstartind) >= (lstartind)) { \
                jpc_fix_pluseq(*lptr, jpc_fix_mul(*hptr, (twoalpha))); \
                lptr += (step); \
                --n; \
        } \
        if ((lendind) > (hendind)) { \
                --n; \
        } \
        while (n-- > 0) { \
                jpc_fix_pluseq(*lptr, jpc_fix_mul(jpc_fix_add(*hptr, hptr[(step)]), (alpha))); \
                lptr += (step); \
                hptr += (step); \
        } \
        if ((lendind) > (hendind)) { \
                jpc_fix_pluseq(*lptr, jpc_fix_mul(*hptr, (twoalpha))); \
        } \
}

#define NNS_SCALE(startptr, startind, endind, step, alpha) \
{ \
        jpc_fix_t *ptr = (startptr); \
        int n = (endind) - (startind); \
        while (n-- > 0) { \
                jpc_fix_muleq(*ptr, alpha); \
                ptr += (step); \
        } \
}

static void jpc_ns_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
{
        jpc_fix_t *startptr;
        int startind;
        int endind;
        jpc_fix_t *lstartptr;
        int lstartind;
        int lendind;
        jpc_fix_t *hstartptr;
        int hstartind;
        int hendind;
        int interstep;
        int intrastep;
        int numseq;

        if (flags & JPC_QMFB1D_VERT) {
                interstep = 1;
                intrastep = jas_seq2d_rowstep(x);
                numseq = jas_seq2d_width(x);
                startind = jas_seq2d_ystart(x);
                endind = jas_seq2d_yend(x);
        } else {
                interstep = jas_seq2d_rowstep(x);
                intrastep = 1;
                numseq = jas_seq2d_height(x);
                startind = jas_seq2d_xstart(x);
                endind = jas_seq2d_xend(x);
        }

        assert(startind < endind);

        startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
        if (!(flags & JPC_QMFB1D_RITIMODE)) {
                while (numseq-- > 0) {
                        jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
                          &lstartptr, &lstartind, &lendind, &hstartptr,
                          &hstartind, &hendind);
                        if (endind - startind > 1) {
                                jpc_qmfb1d_split(startptr, startind, endind,
                                  intrastep, lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind);
                                NNS_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(-1.586134342));
                                NNS_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(-0.052980118));
                                NNS_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(0.882911075));
                                NNS_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(0.443506852));
                                NNS_SCALE(lstartptr, lstartind, lendind,
                                  intrastep, jpc_dbltofix(1.0/1.23017410558578));
                                NNS_SCALE(hstartptr, hstartind, hendind,
                                  intrastep, jpc_dbltofix(1.0/1.62578613134411));
                        } else {
#if 0
                                if (lstartind == lendind) {
                                        *startptr = jpc_fix_asl(*startptr, 1);
                                }
#endif
                        }
                        startptr += interstep;
                }
        } else {
                /* The reversible integer-to-integer mode is not valid for this
                  transform. */
                abort();
        }
}

static void jpc_ns_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
{
        jpc_fix_t *startptr;
        int startind;
        int endind;
        jpc_fix_t *lstartptr;
        int lstartind;
        int lendind;
        jpc_fix_t *hstartptr;
        int hstartind;
        int hendind;
        int interstep;
        int intrastep;
        int numseq;

        if (flags & JPC_QMFB1D_VERT) {
                interstep = 1;
                intrastep = jas_seq2d_rowstep(x);
                numseq = jas_seq2d_width(x);
                startind = jas_seq2d_ystart(x);
                endind = jas_seq2d_yend(x);
        } else {
                interstep = jas_seq2d_rowstep(x);
                intrastep = 1;
                numseq = jas_seq2d_height(x);
                startind = jas_seq2d_xstart(x);
                endind = jas_seq2d_xend(x);
        }

        assert(startind < endind);

        startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
        if (!(flags & JPC_QMFB1D_RITIMODE)) {
                while (numseq-- > 0) {
                        jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
                          &lstartptr, &lstartind, &lendind, &hstartptr,
                          &hstartind, &hendind);
                        if (endind - startind > 1) {
                                NNS_SCALE(lstartptr, lstartind, lendind,
                                  intrastep, jpc_dbltofix(1.23017410558578));
                                NNS_SCALE(hstartptr, hstartind, hendind,
                                  intrastep, jpc_dbltofix(1.62578613134411));
                                NNS_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(-0.443506852));
                                NNS_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(-0.882911075));
                                NNS_LIFT1(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(0.052980118));
                                NNS_LIFT0(lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind, intrastep,
                                  jpc_dbltofix(1.586134342));
                                jpc_qmfb1d_join(startptr, startind, endind,
                                  intrastep, lstartptr, lstartind, lendind,
                                  hstartptr, hstartind, hendind);
                        } else {
#if 0
                                if (lstartind == lendind) {
                                        *startptr = jpc_fix_asr(*startptr, 1);
                                }
#endif
                        }
                        startptr += interstep;
                }
        } else {
                /* The reversible integer-to-integer mode is not valid
           for this transform. */
                abort();
        }
}

/******************************************************************************\
*
\******************************************************************************/

jpc_qmfb1d_t *jpc_qmfb1d_make(int qmfbid)
{
        jpc_qmfb1d_t *qmfb;
        if (!(qmfb = jpc_qmfb1d_create())) {
                return 0;
        }
        switch (qmfbid) {
        case JPC_QMFB1D_FT:
                qmfb->ops = &jpc_ft_ops;
                break;
        case JPC_QMFB1D_NS:
                qmfb->ops = &jpc_ns_ops;
                break;
        default:
                jpc_qmfb1d_destroy(qmfb);
                return 0;
                break;
        }
        return qmfb;
}

static jpc_qmfb1d_t *jpc_qmfb1d_create()
{
        jpc_qmfb1d_t *qmfb;
        if (!(qmfb = jas_malloc(sizeof(jpc_qmfb1d_t)))) {
                return 0;
        }
        qmfb->ops = 0;
        return qmfb;
}

jpc_qmfb1d_t *jpc_qmfb1d_copy(jpc_qmfb1d_t *qmfb)
{
        jpc_qmfb1d_t *newqmfb;

        if (!(newqmfb = jpc_qmfb1d_create())) {
                return 0;
        }
        newqmfb->ops = qmfb->ops;
        return newqmfb;
}

void jpc_qmfb1d_destroy(jpc_qmfb1d_t *qmfb)
{
        jas_free(qmfb);
}

/******************************************************************************\
*
\******************************************************************************/

void jpc_qmfb1d_getbands(jpc_qmfb1d_t *qmfb, int flags, uint_fast32_t xstart,
  uint_fast32_t ystart, uint_fast32_t xend, uint_fast32_t yend, int maxbands,
  int *numbandsptr, jpc_qmfb1dband_t *bands)
{
        int start;
        int end;
        if (flags & JPC_QMFB1D_VERT) {
                start = ystart;
                end = yend;
        } else {
                start = xstart;
                end = xend;
        }
        assert(jpc_qmfb1d_getnumchans(qmfb) == 2);
        assert(start <= end);
        bands[0].start = JPC_CEILDIVPOW2(start, 1);
        bands[0].end = JPC_CEILDIVPOW2(end, 1);
        bands[0].locstart = start;
        bands[0].locend = start + bands[0].end - bands[0].start;
        bands[1].start = JPC_FLOORDIVPOW2(start, 1);
        bands[1].end = JPC_FLOORDIVPOW2(end, 1);
        bands[1].locstart = bands[0].locend;
        bands[1].locend = bands[1].locstart + bands[1].end - bands[1].start;
        assert(bands[1].locend == end);
        *numbandsptr = 2;
}

/******************************************************************************\
*
\******************************************************************************/

int jpc_qmfb1d_getnumchans(jpc_qmfb1d_t *qmfb)
{
        return (*qmfb->ops->getnumchans)(qmfb);
}

int jpc_qmfb1d_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
{
        return (*qmfb->ops->getanalfilters)(qmfb, len, filters);
}

int jpc_qmfb1d_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
{
        return (*qmfb->ops->getsynfilters)(qmfb, len, filters);
}

void jpc_qmfb1d_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
{
        (*qmfb->ops->analyze)(qmfb, flags, x);
}

void jpc_qmfb1d_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
{
        (*qmfb->ops->synthesize)(qmfb, flags, x);
}
