/*
 * $RCSfile: EBCOTRateAllocator.java,v $
 * $Revision: 1.1 $
 * $Date: 2005-02-11 05:02:08 $
 * $State: Exp $
 *
 * Class:                   EBCOTRateAllocator
 *
 * Description:             Generic interface for post-compression
 *                          rate allocator.
 *
 *
 *
 * COPYRIGHT:
 *
 * This software module was originally developed by Raphaël Grosbois and
 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
 * Centre France S.A) in the course of development of the JPEG2000
 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
 * software module is an implementation of a part of the JPEG 2000
 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
 * Partners) agree not to assert against ISO/IEC and users of the JPEG
 * 2000 Standard (Users) any of their rights under the copyright, not
 * including other intellectual property rights, for this software module
 * with respect to the usage by ISO/IEC and Users of this software module
 * or modifications thereof for use in hardware or software products
 * claiming conformance to the JPEG 2000 Standard. Those intending to use
 * this software module in hardware or software products are advised that
 * their use may infringe existing patents. The original developers of
 * this software module, JJ2000 Partners and ISO/IEC assume no liability
 * for use of this software module or modifications thereof. No license
 * or right to this software module is granted for non JPEG 2000 Standard
 * conforming products. JJ2000 Partners have full right to use this
 * software module for his/her own purpose, assign or donate this
 * software module to any third party and to inhibit third parties from
 * using this software module for non JPEG 2000 Standard conforming
 * products. This copyright notice must be included in all copies or
 * derivative works of this software module.
 *
 * Copyright (c) 1999/2000 JJ2000 Partners.
 * */
package jj2000.j2k.entropy.encoder;
import java.awt.Point;

import jj2000.j2k.codestream.writer.*;
import jj2000.j2k.wavelet.analysis.*;
import jj2000.j2k.entropy.encoder.*;
import jj2000.j2k.codestream.*;
import jj2000.j2k.entropy.*;
import jj2000.j2k.image.*;
import jj2000.j2k.util.*;

import java.util.Vector;
import java.io.*;

import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
/**
 * This implements the EBCOT post compression rate allocation algorithm. This
 * algorithm finds the most suitable truncation points for the set of
 * code-blocks, for each layer target bitrate. It works by first collecting
 * the rate distortion info from all code-blocks, in all tiles and all
 * components, and then running the rate-allocation on the whole image at
 * once, for each layer.
 *
 * <P>This implementation also provides some timing features. They can be
 * enabled by setting the 'DO_TIMING' constant of this class to true and
 * recompiling. The timing uses the 'System.currentTimeMillis()' Java API
 * call, which returns wall clock time, not the actual CPU time used. The
 * timing results will be printed on the message output. Since the times
 * reported are wall clock times and not CPU usage times they can not be added
 * to find the total used time (i.e. some time might be counted in several
 * places). When timing is disabled ('DO_TIMING' is false) there is no penalty
 * if the compiler performs some basic optimizations. Even if not the penalty
 * should be negligeable.
 *
 * @see PostCompRateAllocator
 *
 * @see CodedCBlkDataSrcEnc
 *
 * @see jj2000.j2k.codestream.writer.CodestreamWriter
 * */
public class EBCOTRateAllocator extends PostCompRateAllocator {

    /** Whether to collect timing information or not: false. Used as a compile
     * time directive. */
    private final static boolean DO_TIMING = false;

    /** The wall time for the initialization. */
    private long initTime;

    /** The wall time for the building of layers. */
    private long buildTime;

    /** The wall time for the writing of layers. */
    private long writeTime;

    /**
     * 5D Array containing all the coded code-blocks:
     *
     * <ul>
     * <li>1st index: tile index</li>
     * <li>2nd index: component index</li>
     * <li>3rd index: resolution level index</li>
     * <li>4th index: subband index</li>
     * <li>5th index: code-block index</li>
     * </ul>
     **/
    private CBlkRateDistStats cblks[][][][][];

    /**
     * 6D Array containing the indices of the truncation points. It actually
     * contains the index of the element in CBlkRateDistStats.truncIdxs that
     * gives the real truncation point index.
     *
     * <ul>
     * <li>1st index: tile index</li>
     * <li>2nd index: layer index</li>
     * <li>3rd index: component index</li>
     * <li>4th index: resolution level index</li>
     * <li>5th index: subband index</li>
     * <li>6th index: code-block index</li>
     * </ul>
     **/
    private int truncIdxs[][][][][][];

    /**
     * Maximum number of precincts :
     *
     * <ul>
     * <li>1st dim: tile index.</li>
     * <li>2nd dim: component index.</li>
     * <li>3nd dim: resolution level index.</li>
     * </ul>
     */
    private Point numPrec[][][];

    /** Array containing the layers information. */
    private EBCOTLayer layers[];

    /** The log of 2, natural base */
    private static final double LOG2 = Math.log(2);

    /** The normalization offset for the R-D summary table */
    private static final int RD_SUMMARY_OFF = 24;

    /** The size of the summary table */
    private static final int RD_SUMMARY_SIZE = 64;

    /** The relative precision for float data. This is the relative tolerance
     * up to which the layer slope thresholds are calculated. */
    private static final float FLOAT_REL_PRECISION = 1e-4f;

    /** The precision for float data type, in an absolute sense. Two float
     * numbers are considered "equal" if they are within this precision. */
    private static final float FLOAT_ABS_PRECISION = 1e-10f;

    /**
     * Minimum average size of a packet. If layer has less bytes than the this
     * constant multiplied by number of packets in the layer, then the layer
     * is skipped.
     * */
    private static final int MIN_AVG_PACKET_SZ = 32;

    /**
     * The R-D summary information collected from the coding of all
     * code-blocks. For each entry it contains the accumulated length of all
     * truncation points that have a slope not less than
     * '2*(k-RD_SUMMARY_OFF)', where 'k' is the entry index.
     *
     * <P>Therefore, the length at entry 'k' is the total number of bytes of
     * code-block data that would be obtained if the truncation slope was
     * chosen as '2*(k-RD_SUMMARY_OFF)', without counting the overhead
     * associated with the packet heads.
     *
     * <P>This summary is used to estimate the relation of the R-D slope to
     * coded length, and to obtain absolute minimums on the slope given a
     * length.
     **/
    private int RDSlopesRates[];

    /** Packet encoder. */
    private PktEncoder pktEnc;

    /** The layer specifications */
    private LayersInfo lyrSpec;

    /** The maximum slope accross all code-blocks and truncation points. */
    private float maxSlope;

    /** The minimum slope accross all code-blocks and truncation points. */
    private float minSlope;

    /**
     * Initializes the EBCOT rate allocator of entropy coded data. The layout
     * of layers, and their bitrate constraints, is specified by the 'lyrs'
     * parameter.
     *
     * @param src The source of entropy coded data.
     *
     * @param lyrs The layers layout specification.
     *
     * @param writer The bit stream writer.
     *
     * @see ProgressionType
     * */
    public EBCOTRateAllocator(CodedCBlkDataSrcEnc src, LayersInfo lyrs,
                              CodestreamWriter writer,
                              J2KImageWriteParamJava wp) {

        super(src,lyrs.getTotNumLayers(),writer,wp);

        int minsbi, maxsbi;
        int i;
        SubbandAn sb, sb2;
        Point ncblks = null;

        // If we do timing create necessary structures
        if (DO_TIMING) {
            // If we are timing make sure that 'finalize' gets called.
            System.runFinalizersOnExit(true);
            // The System.runFinalizersOnExit() method is deprecated in Java
            // 1.2 since it can cause a deadlock in some cases. However, here
            // we use it only for profiling purposes and is disabled in
            // production code.
            initTime = 0L;
            buildTime = 0L;
            writeTime = 0L;
        }

        // Save the layer specs
        lyrSpec = lyrs;

        //Initialize the size of the RD slope rates array
        RDSlopesRates = new int[RD_SUMMARY_SIZE];

        //Get number of tiles, components
        int nt = src.getNumTiles();
        int nc = getNumComps();

        //Allocate the coded code-blocks and truncation points indexes arrays
        cblks = new CBlkRateDistStats[nt][nc][][][];
        truncIdxs = new int[nt][numLayers][nc][][][];

        int cblkPerSubband; // Number of code-blocks per subband
	int mrl; // Number of resolution levels
        int l; // layer index
	int s; //subband index

        // Used to compute the maximum number of precincts for each resolution
        // level
        int tx0, ty0, tx1, ty1; // Current tile position in the reference grid
        int tcx0, tcy0, tcx1, tcy1; // Current tile position in the domain of
        // the image component
        int trx0, try0, trx1, try1; // Current tile position in the reduced
        // resolution image domain
        int xrsiz, yrsiz; // Component sub-sampling factors
        Point tileI = null;
        Point nTiles = null;
        int xsiz,ysiz,x0siz,y0siz;
        int xt0siz,yt0siz;
        int xtsiz,ytsiz;
    
        int cb0x = src.getCbULX();
        int cb0y = src.getCbULY();
    
        src.setTile(0,0);
        for (int t=0; t<nt; t++) { // Loop on tiles
            nTiles = src.getNumTiles(nTiles);
            tileI = src.getTile(tileI);
            x0siz = getImgULX();
            y0siz = getImgULY();
            xsiz = x0siz + getImgWidth();
            ysiz = y0siz + getImgHeight();
            xt0siz = src.getTilePartULX();
            yt0siz = src.getTilePartULY();
            xtsiz = src.getNomTileWidth();
            ytsiz = src.getNomTileHeight();

            // Tile's coordinates on the reference grid
            tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
            ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
            tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
            ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;

            for(int c=0; c<nc; c++) { // loop on components

                //Get the number of resolution levels
                sb = src.getAnSubbandTree(t,c);
                mrl = sb.resLvl+1;

                // Initialize maximum number of precincts per resolution array
                if (numPrec==null) { numPrec = new Point[nt][nc][]; }
                if (numPrec[t][c]==null) {
                    numPrec[t][c] = new Point[mrl];
                }

                // Subsampling factors
                xrsiz = src.getCompSubsX(c);
                yrsiz = src.getCompSubsY(c);

                // Tile's coordinates in the image component domain
                tcx0 = (int)Math.ceil(tx0/(double)(xrsiz));
                tcy0 = (int)Math.ceil(ty0/(double)(yrsiz));
                tcx1 = (int)Math.ceil(tx1/(double)(xrsiz));
                tcy1 = (int)Math.ceil(ty1/(double)(yrsiz));

                cblks[t][c] = new CBlkRateDistStats[mrl][][];

                for(l=0; l<numLayers; l++) {
                    truncIdxs[t][l][c] = new int[mrl][][];
                }

                for(int r=0; r<mrl; r++) { // loop on resolution levels

                    // Tile's coordinates in the reduced resolution image
                    // domain
                    trx0 = (int)Math.ceil(tcx0/(double)(1<<(mrl-1-r)));
                    try0 = (int)Math.ceil(tcy0/(double)(1<<(mrl-1-r)));
                    trx1 = (int)Math.ceil(tcx1/(double)(1<<(mrl-1-r)));
                    try1 = (int)Math.ceil(tcy1/(double)(1<<(mrl-1-r)));

                    // Calculate the maximum number of precincts for each
                    // resolution level taking into account tile specific
                    // options.
                    double twoppx = (double)wp.getPrecinctPartition().getPPX(t,c,r);
                    double twoppy = (double)wp.getPrecinctPartition().getPPY(t,c,r);
                    numPrec[t][c][r] = new Point();
                    if (trx1>trx0) {
                        numPrec[t][c][r].x = (int)Math.ceil((trx1-cb0x)/twoppx)
                            - (int)Math.floor((trx0-cb0x)/twoppx);
                    } else {
                        numPrec[t][c][r].x = 0;
                    }
                    if (try1>try0) {
                        numPrec[t][c][r].y = (int)Math.ceil((try1-cb0y)/twoppy)
                            - (int)Math.floor((try0-cb0y)/(double)twoppy);
                    } else {
                        numPrec[t][c][r].y = 0;
                    }

                    minsbi = (r==0) ? 0 : 1;
                    maxsbi = (r==0) ? 1 : 4;

                    cblks[t][c][r] = new CBlkRateDistStats[maxsbi][];
                    for(l=0; l<numLayers; l++) {
                        truncIdxs[t][l][c][r] = new int[maxsbi][];
                    }

                    for(s=minsbi; s<maxsbi; s++) { // loop on subbands
                        //Get the number of blocks in the current subband
                        sb2 = (SubbandAn)sb.getSubbandByIdx(r,s);
                        ncblks = sb2.numCb;
                        cblkPerSubband = ncblks.x*ncblks.y;
                        cblks[t][c][r][s] =
                            new CBlkRateDistStats[cblkPerSubband];

                        for(l=0; l<numLayers; l++) {
                            truncIdxs[t][l][c][r][s] = new int[cblkPerSubband];
                            for(i=0; i<cblkPerSubband; i++) {
                                truncIdxs[t][l][c][r][s][i] = -1;
                            }
                        }
                    } // End loop on subbands
                } // End lopp on resolution levels
            } // End loop on components
            if (t!=nt-1) {
                src.nextTile();
            }
        } // End loop on tiles

        //Initialize the packet encoder
        pktEnc = new PktEncoder(src,wp,numPrec);

        // The layers array has to be initialized after the constructor since
        // it is needed that the bit stream header has been entirely written
    }

    /**
     * Prints the timing information, if collected, and calls 'finalize' on
     * the super class.
     * */
    public void finalize() throws Throwable {
        if (DO_TIMING) {
            StringBuffer sb;

            sb = new StringBuffer("EBCOTRateAllocator wall clock times:\n");
            sb.append("  initialization: ");
            sb.append(initTime);
            sb.append(" ms\n");
            sb.append("  layer building: ");
            sb.append(buildTime);
            sb.append(" ms\n");
            sb.append("  final writing:  ");
            sb.append(writeTime);
            sb.append(" ms");
            FacilityManager.getMsgLogger().
                printmsg(MsgLogger.INFO,sb.toString());
        }
        super.finalize();
    }

    /**
     * Runs the rate allocation algorithm and writes the data to the bit
     * stream writer object provided to the constructor.
     * */
    public void runAndWrite() throws IOException {
        //Now, run the rate allocation
        buildAndWriteLayers();
    }

    /**
     * Initializes the layers array. This must be called after the main header
     * has been entirely written or simulated, so as to take its overhead into
     * account. This method will get all the code-blocks and then initialize
     * the target bitrates for each layer, according to the specifications.
     * */
    public void initialize() throws IOException{
        int n,i,l;
        int ho; // The header overhead (in bytes)
        float np;// The number of pixels divided by the number of bits per byte
        double ls; // Step for log-scale
        double basebytes;
        int lastbytes,newbytes,nextbytes;
        int loopnlyrs;
        int minlsz; // The minimum allowable number of bytes in a layer
        int totenclength;
        int maxpkt;
        int numTiles  = src.getNumTiles();
        int numComps  = src.getNumComps();
        int numLvls;
        int avgPktLen;

        long stime = 0L;

        // Start by getting all the code-blocks, we need this in order to have
        // an idea of the total encoded bitrate.
        getAllCodeBlocks();

        if (DO_TIMING) stime = System.currentTimeMillis();

        // Now get the total encoded length
        totenclength = RDSlopesRates[0]; // all the encoded data
        // Make a rough estimation of the packet head overhead, as 2 bytes per
        // packet in average (plus EPH / SOP) , and add that to the total
        // encoded length
	for( int t=0 ; t<numTiles ; t++ ){
            avgPktLen = 2;
            // Add SOP length if set
            if (((String)wp.getSOP().getTileDef(t)).equalsIgnoreCase("true")) {
                avgPktLen += Markers.SOP_LENGTH;
            }
            // Add EPH length if set
            if (((String)wp.getEPH().getTileDef(t)).equalsIgnoreCase("true")) {
                avgPktLen += Markers.EPH_LENGTH;
            }

	    for( int c=0 ; c<numComps ; c++ ){
		numLvls   = src.getAnSubbandTree(t,c).resLvl+1;
		if( !src.precinctPartitionUsed(c,t) ) {
		    // Precinct partition is not used so there is only
		    // one packet per resolution level/layer
		    totenclength += numLayers*avgPktLen*numLvls;
		}
		else {
		    // Precinct partition is used so for each
		    // component/tile/resolution level, we get the maximum
		    // number of packets
                    for ( int rl=0 ; rl<numLvls ; rl++ ) {
                        maxpkt = numPrec[t][c][rl].x * numPrec[t][c][rl].y;
                        totenclength += numLayers*avgPktLen*maxpkt;
                    }
		}
	    } // End loop on components
        } // End loop on tiles

        // If any layer specifies more than 'totenclength' as its target
        // length then 'totenclength' is used. This is to prevent that
        // estimated layers get excessively large target lengths due to an
        // excessively large target bitrate. At the end the last layer is set
        // to the target length corresponding to the overall target
        // bitrate. Thus, 'totenclength' can not limit the total amount of
        // encoded data, as intended.

        ho = headEnc.getLength();
        np = src.getImgWidth()*src.getImgHeight()/8f;

        // SOT marker must be taken into account
        for(int t=0; t<numTiles; t++){
            headEnc.reset();
            headEnc.encodeTilePartHeader(0,t);
            ho += headEnc.getLength();
        }

        layers = new EBCOTLayer[numLayers];
        for (n = numLayers-1; n>=0; n--) {
            layers[n] = new EBCOTLayer();
        }

        minlsz = 0; // To keep compiler happy
	for( int t=0 ; t<numTiles ; t++ ){
            for( int c=0 ; c<numComps ; c++ ){
		numLvls   = src.getAnSubbandTree(t,c).resLvl+1;

		if ( !src.precinctPartitionUsed(c,t) ) {
		    // Precinct partition is not used
		    minlsz += MIN_AVG_PACKET_SZ*numLvls;
		}
		else {
		    // Precinct partition is used
                    for ( int rl=0 ; rl<numLvls ; rl++ ) {
                        maxpkt = numPrec[t][c][rl].x * numPrec[t][c][rl].y;
                        minlsz += MIN_AVG_PACKET_SZ*maxpkt;
                    }
		}
	    } // End loop on components
        } // End loop on tiles

        // Initialize layers
        n = 0;
        i = 0;
        lastbytes = 0;

        while (n < numLayers-1) {
            // At an optimized layer
            basebytes = Math.floor(lyrSpec.getTargetBitrate(i)*np);
            if (i < lyrSpec.getNOptPoints()-1) {
                nextbytes = (int) (lyrSpec.getTargetBitrate(i+1)*np);
                // Limit target length to 'totenclength'
                if (nextbytes > totenclength)
                    nextbytes = totenclength;
            }
            else {
                nextbytes = 1;
            }
            loopnlyrs = lyrSpec.getExtraLayers(i)+1;
            ls = Math.exp(Math.log((double)nextbytes/basebytes)/loopnlyrs);
            layers[n].optimize = true;
            for (l = 0; l < loopnlyrs; l++) {
                newbytes = (int)basebytes - lastbytes - ho;
                if (newbytes < minlsz) {  // Skip layer (too small)
                    basebytes *= ls;
                    numLayers--;
                    continue;
                }
                lastbytes = (int)basebytes - ho;
                layers[n].maxBytes = lastbytes;
                basebytes *= ls;
                n++;
            }
            i++; // Goto next optimization point
        }

        // Ensure minimum size of last layer (this one determines overall
        // bitrate)
        n = numLayers-2;
        nextbytes = (int) (lyrSpec.getTotBitrate()*np) - ho;
        newbytes = nextbytes - ((n>=0) ? layers[n].maxBytes : 0);
        while (newbytes < minlsz) {
            if (numLayers == 1) {
                if (newbytes <= 0) {
                    throw new
                        IllegalArgumentException("Overall target bitrate too "+
                                                 "low, given the current "+
                                                 "bit stream header overhead");
                }
                break;
            }
            // Delete last layer
            numLayers--;
            n--;
            newbytes = nextbytes - ((n>=0) ? layers[n].maxBytes : 0);
        }
        // Set last layer to the overall target bitrate
        n++;
        layers[n].maxBytes = nextbytes;
        layers[n].optimize = true;

	// Re-initialize progression order changes if needed Default values
	Progression[] prog1,prog2;
	prog1 = (Progression[])wp.getProgressionType().getDefault();
	int nValidProg = prog1.length;
	for(int prg=0; prg<prog1.length;prg++){
	    if(prog1[prg].lye>numLayers){
		prog1[prg].lye = numLayers;
	    }
	}
	if(nValidProg==0)
	    throw new Error("Unable to initialize rate allocator: No "+
                            "default progression type has been defined.");

	// Tile specific values
	for(int t=0; t<numTiles; t++){
	    if(wp.getProgressionType().isTileSpecified(t)){
		prog1 = (Progression[])wp.getProgressionType().getTileDef(t);
		nValidProg = prog1.length;
		for(int prg=0; prg<prog1.length;prg++){
		    if(prog1[prg].lye>numLayers){
			prog1[prg].lye = numLayers;
		    }
		}
		if(nValidProg==0)
		    throw new Error("Unable to initialize rate allocator: No "+
                            "default progression type has been defined.");
	    }
	} // End loop on tiles

        if (DO_TIMING) initTime += System.currentTimeMillis()-stime;
    }

    /**
     * This method gets all the coded code-blocks from the EBCOT entropy coder
     * for every component and every tile. Each coded code-block is stored in
     * a 5D array according to the component, the resolution level, the tile,
     * the subband it belongs and its position in the subband.
     *
     * <P> For each code-block, the valid slopes are computed and converted
     * into the mantissa-exponent representation.
     * */
    private void getAllCodeBlocks() {

        int numComps, numTiles, numBytes;
        int c, r, t, s, sidx, k;
        int slope;
        SubbandAn subb;
        CBlkRateDistStats ccb = null;
        Point ncblks = null;
        int last_sidx;
        float fslope;

        long stime = 0L;

        maxSlope = 0f;
        minSlope = Float.MAX_VALUE;

        //Get the number of components and tiles
        numComps = src.getNumComps();
        numTiles = src.getNumTiles();

        SubbandAn root,sb;
        int cblkToEncode = 0;
        int nEncCblk = 0;
        ProgressWatch pw = FacilityManager.getProgressWatch();

        //Get all coded code-blocks Goto first tile
        src.setTile(0,0);
        for (t=0; t<numTiles; t++) { //loop on tiles
            nEncCblk = 0;
            cblkToEncode = 0;
            for(c=0; c<numComps; c++) {
                root = src.getAnSubbandTree(t,c);
                for(r=0; r<=root.resLvl; r++) {
                    if(r==0) {
                        sb = (SubbandAn)root.getSubbandByIdx(0,0);
                        if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
                    } else {
                        sb = (SubbandAn)root.getSubbandByIdx(r,1);
                        if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
                        sb = (SubbandAn)root.getSubbandByIdx(r,2);
                        if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
                        sb = (SubbandAn)root.getSubbandByIdx(r,3);
                        if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
                    }
                }
            }
            if(pw!=null) {
                pw.initProgressWatch(0,cblkToEncode,"Encoding tile "+t+"...");
            }

            for (c=0; c<numComps; c++) { //loop on components

                //Get next coded code-block coordinates
                while ( (ccb = src.getNextCodeBlock(c,ccb)) != null) {
                    if (DO_TIMING) stime = System.currentTimeMillis();

                    if(pw!=null) {
                        nEncCblk++;
                        pw.updateProgressWatch(nEncCblk,null);
                    }

                    subb = ccb.sb;

                    //Get the coded code-block resolution level index
                    r = subb.resLvl;

                    //Get the coded code-block subband index
                    s = subb.sbandIdx;

                    //Get the number of blocks in the current subband
                    ncblks = subb.numCb;

                    // Add code-block contribution to summary R-D table
                    // RDSlopesRates
                    last_sidx = -1;
                    for (k=ccb.nVldTrunc-1; k>=0; k--) {
                        fslope = ccb.truncSlopes[k];
                        if (fslope > maxSlope) maxSlope = fslope;
                        if (fslope < minSlope) minSlope = fslope;
                        sidx = getLimitedSIndexFromSlope(fslope);
                        for (; sidx > last_sidx; sidx--) {
                            RDSlopesRates[sidx] +=
                                ccb.truncRates[ccb.truncIdxs[k]];
                        }
                        last_sidx = getLimitedSIndexFromSlope(fslope);
                    }

                    //Fills code-blocks array
                    cblks[t][c][r][s][(ccb.m*ncblks.x)+ccb.n] = ccb;
                    ccb = null;

                    if(DO_TIMING) initTime += System.currentTimeMillis()-stime;
                }
            }

            if(pw!=null) {
                pw.terminateProgressWatch();
            }

            //Goto next tile
            if(t<numTiles-1) //not at last tile
                src.nextTile();
        }
    }

    /**
     * This method builds all the bit stream layers and then writes them to
     * the output bit stream. Firstly it builds all the layers by computing
     * the threshold according to the layer target bit-rate, and then it
     * writes the layer bit streams according to the Progression type.
     * */
    private void buildAndWriteLayers() throws IOException {
        int nPrec = 0;
        int maxBytes, actualBytes;
        float rdThreshold;
        SubbandAn sb;
        float threshold;
        BitOutputBuffer hBuff = null;
        byte[] bBuff = null;
        int[] tileLengths; // Length of each tile
        int tmp;
        boolean sopUsed; // Should SOP markers be used ?
        boolean ephUsed; // Should EPH markers be used ?
        int nc = src.getNumComps();
        int nt = src.getNumTiles();
        int mrl;

        long stime = 0L;

        if (DO_TIMING) stime = System.currentTimeMillis();

        // Start with the maximum slope
        rdThreshold = maxSlope;

        tileLengths = new int[nt];
        actualBytes = 0;

        // +------------------------------+
        // |  First we build the layers   |
        // +------------------------------+
        // Bitstream is simulated to know tile length
        for(int l=0; l<numLayers; l++){ //loop on layers

            maxBytes = layers[l].maxBytes;
            if(layers[l].optimize) {
                rdThreshold =
                    optimizeBitstreamLayer(l,rdThreshold,maxBytes,actualBytes);
            } else {
                if( l<=0 || l>=numLayers-1 ) {
                    throw new IllegalArgumentException("The first and the"+
                                                       " last layer "+
                                                       "thresholds"+
                                                       " must be optimized");
                }
                rdThreshold = estimateLayerThreshold(maxBytes,layers[l-1]);
            }

            for(int t=0; t<nt; t++) { //loop on tiles
                if(l==0) {
                    // Tile header
                    headEnc.reset();
                    headEnc.encodeTilePartHeader(0,t);
                    tileLengths[t] += headEnc.getLength();
                }

                for(int c=0; c<nc; c++) { //loop on components

                    // set boolean sopUsed here (SOP markers)
                    sopUsed = ((String)wp.getSOP().getTileDef(t)).
                        equalsIgnoreCase("true");
                    // set boolean ephUsed here (EPH markers)
                    ephUsed = ((String)wp.getEPH().getTileDef(t)).
                        equalsIgnoreCase("true");

                    // Go to LL band
                    sb = src.getAnSubbandTree(t,c);
                    mrl = sb.resLvl+1;

                    while (sb.subb_LL!=null) {
                        sb = sb.subb_LL;
                    }

                    for(int r=0; r<mrl ; r++) { // loop on resolution levels

                        nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
                        for(int p=0; p<nPrec; p++) { // loop on precincts

                            findTruncIndices(l,c,r,t,sb,rdThreshold,p);

                            hBuff =
                                pktEnc.encodePacket(l+1,c,r,t,
                                                    cblks[t][c][r],
                                                    truncIdxs[t][l][c][r],
                                                    hBuff, bBuff,p);
                            if(pktEnc.isPacketWritable()) {
                                tmp = bsWriter.
                                    writePacketHead(hBuff.getBuffer(),
                                                    hBuff.getLength(),
                                                    true, sopUsed,ephUsed);
                                tmp += bsWriter.
                                    writePacketBody(pktEnc.getLastBodyBuf(),
                                                    pktEnc.getLastBodyLen(),
                                                    true,pktEnc.isROIinPkt(),
                                                    pktEnc.getROILen());
                                actualBytes += tmp;
                                tileLengths[t] += tmp;
                            }
                        } // End loop on precincts
                        sb = sb.parent;
                    } // End loop on resolution levels
                } // End loop on components
            } // end loop on tiles
            layers[l].rdThreshold = rdThreshold;
            layers[l].actualBytes = actualBytes;
        } // end loop on layers

        if (DO_TIMING) buildTime += System.currentTimeMillis()-stime;

        // The bit-stream was not yet generated (only simulated).

        if (DO_TIMING) stime = System.currentTimeMillis();

        // +--------------------------------------------------+
        // | Write tiles according to their Progression order |
        // +--------------------------------------------------+
        // Reset the packet encoder before writing all packets
        pktEnc.reset();
        Progression[] prog; // Progression(s) in each tile
        int cs,ce,rs,re,lye;

        int[] mrlc = new int[nc];
        for(int t=0; t<nt; t++) { //loop on tiles
            int[][] lysA; // layer index start for each component and
            // resolution level
            int[][] lys = new int[nc][];
            for(int c=0; c<nc; c++){
                mrlc[c] = src.getAnSubbandTree(t,c).resLvl;
                lys[c] = new int[mrlc[c]+1];
            }

            // Tile header
            headEnc.reset();
            headEnc.encodeTilePartHeader(tileLengths[t],t);
            bsWriter.commitBitstreamHeader(headEnc);
            prog = (Progression[])wp.getProgressionType().getTileDef(t);

            for(int prg=0; prg<prog.length;prg++){ // Loop on progression
                lye = prog[prg].lye;
                cs = prog[prg].cs;
                ce = prog[prg].ce;
                rs = prog[prg].rs;
                re = prog[prg].re;

                switch(prog[prg].type){
                case ProgressionType.RES_LY_COMP_POS_PROG:
                    writeResLyCompPos(t,rs,re,cs,ce,lys,lye);
                    break;
                case ProgressionType.LY_RES_COMP_POS_PROG:
                    writeLyResCompPos(t,rs,re,cs,ce,lys,lye);
                    break;
                case ProgressionType.POS_COMP_RES_LY_PROG:
                    writePosCompResLy(t,rs,re,cs,ce,lys,lye);
                    break;
                case ProgressionType.COMP_POS_RES_LY_PROG:
                    writeCompPosResLy(t,rs,re,cs,ce,lys,lye);
                    break;
                case ProgressionType.RES_POS_COMP_LY_PROG:
                    writeResPosCompLy(t,rs,re,cs,ce,lys,lye);
                    break;
                default:
                    throw new Error("Unsupported bit stream progression type");
                } // switch on progression

                // Update next first layer index 
                for(int c=cs; c<ce; c++)
                    for(int r=rs; r<re; r++){
                        if(r>mrlc[c]) continue;
                        lys[c][r] = lye;
                    }
            } // End loop on progression
        } // End loop on tiles

        if (DO_TIMING) writeTime += System.currentTimeMillis()-stime;
    }

    /** 
     * Write a piece of bit stream according to the
     * RES_LY_COMP_POS_PROG progression mode and between given bounds
     *
     * @param t Tile index.
     *
     * @param rs First resolution level index.
     *
     * @param re Last resolution level index.
     *
     * @param cs First component index.
     *
     * @param ce Last component index.
     *
     * @param lys First layer index for each component and resolution.
     *
     * @param lye Index of the last layer.
     * */
    public void writeResLyCompPos(int t,int rs,int re,int cs,int ce,
                                  int lys[][],int lye) throws IOException {

        boolean sopUsed; // Should SOP markers be used ?
        boolean ephUsed; // Should EPH markers be used ?
        int nc = src.getNumComps();
        int[] mrl = new int[nc];
        SubbandAn sb;
        float threshold;
        BitOutputBuffer hBuff = null;
        byte[] bBuff = null;
        int nPrec = 0;

        // Max number of resolution levels in the tile
        int maxResLvl = 0;
        for(int c=0; c<nc; c++) {
            mrl[c] = src.getAnSubbandTree(t,c).resLvl;
            if(mrl[c]>maxResLvl) maxResLvl = mrl[c];
        }

        int minlys; // minimum layer start index of each component

        for(int r=rs; r<re; r++) { //loop on resolution levels
            if(r>maxResLvl) continue;

            minlys = 100000;
            for(int c=cs; c<ce; c++) {
                if( r<lys[c].length && lys[c][r]<minlys ) {
                    minlys = lys[c][r];
                }
            }

            for(int l=minlys; l<lye; l++) { //loop on layers
                for(int c=cs; c<ce; c++) {//loop on components
                    if(r>=lys[c].length) continue;
                    if(l<lys[c][r]) continue;

                    // If no more decomposition levels for this component
                    if(r>mrl[c]) continue;

                    nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
                    for(int p=0; p<nPrec; p++) { // loop on precincts

                        // set boolean sopUsed here (SOP markers)
                        sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
                        // set boolean ephUsed here (EPH markers)
                        ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");

                        sb = src.getAnSubbandTree(t,c);
                        for(int i=mrl[c]; i>r; i--) {
                            sb = sb.subb_LL;
                        }

                        threshold = layers[l].rdThreshold;
                        findTruncIndices(l,c,r,t,sb,threshold,p);

                        hBuff = pktEnc.encodePacket(l+1,c,r,t,cblks[t][c][r],
                                                    truncIdxs[t][l][c][r],
                                                    hBuff,bBuff,p);

                        if(pktEnc.isPacketWritable()) {
                            bsWriter.writePacketHead(hBuff.getBuffer(),
                                                     hBuff.getLength(),
                                                     false,sopUsed,ephUsed);
                            bsWriter.writePacketBody(pktEnc.getLastBodyBuf(),
                                                     pktEnc.getLastBodyLen(),
                                                     false,pktEnc.isROIinPkt(),
                                                     pktEnc.getROILen());
                        }

                    } // End loop on precincts
                } // End loop on components
            } // End loop on layers
        } // End loop on resolution levels
    }

    /** 
     * Write a piece of bit stream according to the
     * LY_RES_COMP_POS_PROG progression mode and between given bounds
     *
     * @param t Tile index.
     *
     * @param rs First resolution level index.
     *
     * @param re Last resolution level index.
     *
     * @param cs First component index.
     *
     * @param ce Last component index.
     *
     * @param lys First layer index for each component and resolution.
     *
     * @param lye Index of the last layer.
     * */
    public void writeLyResCompPos(int t,int rs,int re,int cs,int ce,
                                  int[][] lys,int lye) throws IOException {

        boolean sopUsed; // Should SOP markers be used ?
        boolean ephUsed; // Should EPH markers be used ?
        int nc = src.getNumComps();
        int mrl;
        SubbandAn sb;
        float threshold;
        BitOutputBuffer hBuff = null;
        byte[] bBuff = null;
        int nPrec = 0;

        int minlys = 100000; // minimum layer start index of each component
        for(int c=cs; c<ce; c++) {
            for(int r=0; r<lys.length; r++) {
                if(lys[c]!=null && r<lys[c].length && lys[c][r]<minlys ) {
                    minlys = lys[c][r];
                }
            }
        }

        for(int l=minlys; l<lye; l++) { // loop on layers
            for(int r=rs; r<re; r++) { // loop on resolution level
                for(int c=cs; c<ce; c++) { // loop on components
                    mrl = src.getAnSubbandTree(t,c).resLvl;
                    if(r>mrl) continue;
                    if(r>=lys[c].length) continue;
                    if(l<lys[c][r]) continue;
                    nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
                    for(int p=0; p<nPrec; p++) { // loop on precincts

                        // set boolean sopUsed here (SOP markers)
                        sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
                        // set boolean ephUsed here (EPH markers)
                        ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");

                        sb = src.getAnSubbandTree(t,c);
                        for(int i=mrl; i>r; i--) {
                            sb = sb.subb_LL;
                        }

                        threshold = layers[l].rdThreshold;
                        findTruncIndices(l,c,r,t,sb,threshold,p);

                        hBuff = pktEnc.encodePacket(l+1,c,r,t,cblks[t][c][r],
                                                    truncIdxs[t][l][c][r],
                                                    hBuff,bBuff,p);

                        if(pktEnc.isPacketWritable()) {
                            bsWriter.writePacketHead(hBuff.getBuffer(),
                                                     hBuff.getLength(),
                                                     false,sopUsed,ephUsed);
                            bsWriter.writePacketBody(pktEnc.getLastBodyBuf(),
                                                     pktEnc.getLastBodyLen(),
                                                     false,pktEnc.isROIinPkt(),
                                                     pktEnc.getROILen());
                        }
                    } // end loop on precincts
                } // end loop on components
            } // end loop on resolution levels
        } // end loop on layers
    }

    /** 
     * Write a piece of bit stream according to the
     * COMP_POS_RES_LY_PROG progression mode and between given bounds
     *
     * @param t Tile index.
     *
     * @param rs First resolution level index.
     *
     * @param re Last resolution level index.
     *
     * @param cs First component index.
     *
     * @param ce Last component index.
     *
     * @param lys First layer index for each component and resolution.
     *
     * @param lye Index of the last layer.
     * */
    public void writePosCompResLy(int t,int rs,int re,int cs,int ce,
                                  int[][] lys,int lye) throws IOException {

        boolean sopUsed; // Should SOP markers be used ?
        boolean ephUsed; // Should EPH markers be used ?
        int nc = src.getNumComps();
        int mrl;
        SubbandAn sb;
        float threshold;
        BitOutputBuffer hBuff = null;
        byte[] bBuff = null;

        // Computes current tile offset in the reference grid
        Point nTiles = src.getNumTiles(null);
        Point tileI = src.getTile(null);
        int x0siz = src.getImgULX();
        int y0siz = src.getImgULY();
        int xsiz = x0siz + src.getImgWidth();
        int ysiz = y0siz + src.getImgHeight();
        int xt0siz = src.getTilePartULX();
        int yt0siz = src.getTilePartULY();
        int xtsiz = src.getNomTileWidth();
        int ytsiz = src.getNomTileHeight();
        int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
        int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
        int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
        int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;

        // Get precinct information (number,distance between two consecutive
        // precincts in the reference grid) in each component and resolution
        // level
        PrecInfo prec; // temporary variable
        int p; // Current precinct index
        int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
        int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
        int nPrec = 0; // Total number of found precincts
        int[][] nextPrec = new int [ce][]; // Next precinct index in each
        // component and resolution level
        int minlys = 100000; // minimum layer start index of each component
        int minx = tx1; // Horiz. offset of the second precinct in the
        // reference grid
        int miny = ty1; // Vert. offset of the second precinct in the
        // reference grid. 
        int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
        int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
        for(int c=cs; c<ce; c++) {
            mrl = src.getAnSubbandTree(t,c).resLvl;
            nextPrec[c] = new int[mrl+1];
            for(int r=rs; r<re; r++) {
                if(r>mrl) continue;
                if (r<lys[c].length && lys[c][r]<minlys) {
                    minlys = lys[c][r];
                }
                p = numPrec[t][c][r].y*numPrec[t][c][r].x-1;
                for(; p>=0; p--) {
                    prec = pktEnc.getPrecInfo(t,c,r,p);
                    if(prec.rgulx!=tx0) {
                        if(prec.rgulx<minx) minx = prec.rgulx;
                        if(prec.rgulx>maxx) maxx = prec.rgulx;
                    }
                    if(prec.rguly!=ty0){
                        if(prec.rguly<miny) miny = prec.rguly;
                        if(prec.rguly>maxy) maxy = prec.rguly;
                    }

                    if(nPrec==0) {
                        gcd_x = prec.rgw;
                        gcd_y = prec.rgh;
                    } else {
                        gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
                        gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
                    }
                    nPrec++;
                } // precincts
            } // resolution levels
        } // components
        if(nPrec==0) {
            throw new Error("Image cannot have no precinct");
        }

        int pyend = (maxy-miny)/gcd_y+1;
        int pxend = (maxx-minx)/gcd_x+1;
        int y = ty0;
        int x = tx0;
        for(int py=0; py<=pyend; py++) { // Vertical precincts
            for(int px=0; px<=pxend; px++) { // Horiz. precincts
                for(int c=cs; c<ce; c++) { // Components
                    mrl = src.getAnSubbandTree(t,c).resLvl;
                    for(int r=rs; r<re; r++) { // Resolution levels
                        if(r>mrl) continue;
                        if(nextPrec[c][r] >=
                           numPrec[t][c][r].x*numPrec[t][c][r].y) {
                            continue;
                        }
                        prec = pktEnc.getPrecInfo(t,c,r,nextPrec[c][r]);
                        if((prec.rgulx!=x) || (prec.rguly!=y)) {
                            continue;
                        }
                        for(int l=minlys; l<lye; l++) { // Layers
                            if(r>=lys[c].length) continue;
                            if(l<lys[c][r]) continue;

                            // set boolean sopUsed here (SOP markers)
                            sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
                            // set boolean ephUsed here (EPH markers)
                            ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");

                            sb = src.getAnSubbandTree(t,c);
                            for(int i=mrl; i>r; i--) {
                                sb = sb.subb_LL;
                            }

                            threshold = layers[l].rdThreshold;
                            findTruncIndices(l,c,r,t,sb,threshold,
                                             nextPrec[c][r]);


                            hBuff = pktEnc.encodePacket(l+1,c,r,t,
                                                        cblks[t][c][r],
                                                        truncIdxs[t][l][c][r],
                                                        hBuff,bBuff,
                                                        nextPrec[c][r]);

                            if(pktEnc.isPacketWritable()) {
                                bsWriter.writePacketHead(hBuff.getBuffer(),
                                                         hBuff.getLength(),
                                                         false,sopUsed,
                                                         ephUsed);
                                bsWriter.writePacketBody(pktEnc.
                                                         getLastBodyBuf(),
                                                         pktEnc.
                                                         getLastBodyLen(),
                                                         false,
                                                         pktEnc.isROIinPkt(),
                                                         pktEnc.getROILen());
                            }
                        } // layers
                        nextPrec[c][r]++;
                    } // Resolution levels
                } // Components
                if(px!=pxend) {
                    x = minx+px*gcd_x;
                } else {
                    x = tx0;
                }
            } // Horizontal precincts
            if(py!=pyend) {
                y = miny+py*gcd_y;
            } else {
                y = ty0;
            }
        } // Vertical precincts

        // Check that all precincts have been written
        for(int c=cs; c<ce; c++) {
            mrl = src.getAnSubbandTree(t,c).resLvl;
            for(int r=rs; r<re; r++) {
                if(r>mrl) continue;
                if(nextPrec[c][r]<numPrec[t][c][r].x*numPrec[t][c][r].y-1) {
                    throw new Error("JJ2000 bug: One precinct at least has "+
                                    "not been written for resolution level "+r
                                    +" of component "+c+" in tile "+t+".");
                }
            }
        }
    }

    /** 
     * Write a piece of bit stream according to the
     * COMP_POS_RES_LY_PROG progression mode and between given bounds
     *
     * @param t Tile index.
     *
     * @param rs First resolution level index.
     *
     * @param re Last resolution level index.
     *
     * @param cs First component index.
     *
     * @param ce Last component index.
     *
     * @param lys First layer index for each component and resolution.
     *
     * @param lye Index of the last layer.
     * */
    public void writeCompPosResLy(int t,int rs,int re,int cs,int ce,
                                  int[][] lys,int lye) throws IOException {

        boolean sopUsed; // Should SOP markers be used ?
        boolean ephUsed; // Should EPH markers be used ?
        int nc = src.getNumComps();
        int mrl;
        SubbandAn sb;
        float threshold;
        BitOutputBuffer hBuff = null;
        byte[] bBuff = null;

        // Computes current tile offset in the reference grid
        Point nTiles = src.getNumTiles(null);
        Point tileI = src.getTile(null);
        int x0siz = src.getImgULX();
        int y0siz = src.getImgULY();
        int xsiz = x0siz + src.getImgWidth();
        int ysiz = y0siz + src.getImgHeight();
        int xt0siz = src.getTilePartULX();
        int yt0siz = src.getTilePartULY();
        int xtsiz = src.getNomTileWidth();
        int ytsiz = src.getNomTileHeight();
        int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
        int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
        int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
        int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;

        // Get precinct information (number,distance between two consecutive
        // precincts in the reference grid) in each component and resolution
        // level
        PrecInfo prec; // temporary variable
        int p; // Current precinct index
        int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
        int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
        int nPrec = 0; // Total number of found precincts
        int[][] nextPrec = new int [ce][]; // Next precinct index in each
        // component and resolution level
        int minlys = 100000; // minimum layer start index of each component
        int minx = tx1; // Horiz. offset of the second precinct in the
        // reference grid
        int miny = ty1; // Vert. offset of the second precinct in the
        // reference grid. 
        int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
        int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
        for(int c=cs; c<ce; c++) {
            mrl = src.getAnSubbandTree(t,c).resLvl;
            for(int r=rs; r<re; r++) {
                if(r>mrl) continue;
                nextPrec[c] = new int[mrl+1];
                if (r<lys[c].length && lys[c][r]<minlys) {
                    minlys = lys[c][r];
                }
                p = numPrec[t][c][r].y*numPrec[t][c][r].x-1;
                for(; p>=0; p--) {
                    prec = pktEnc.getPrecInfo(t,c,r,p);
                    if(prec.rgulx!=tx0) {
                        if(prec.rgulx<minx) minx = prec.rgulx;
                        if(prec.rgulx>maxx) maxx = prec.rgulx;
                    }
                    if(prec.rguly!=ty0){
                        if(prec.rguly<miny) miny = prec.rguly;
                        if(prec.rguly>maxy) maxy = prec.rguly;
                    }

                    if(nPrec==0) {
                        gcd_x = prec.rgw;
                        gcd_y = prec.rgh;
                    } else {
                        gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
                        gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
                    }
                    nPrec++;
                } // precincts
            } // resolution levels
        } // components
        if(nPrec==0) {
            throw new Error("Image cannot have no precinct");
        }

        int pyend = (maxy-miny)/gcd_y+1;
        int pxend = (maxx-minx)/gcd_x+1;
        int y;
        int x;
        for(int c=cs; c<ce; c++) { // Loop on components
            y = ty0;
            x = tx0;
            mrl = src.getAnSubbandTree(t,c).resLvl;
            for(int py=0; py<=pyend; py++) { // Vertical precincts
                for(int px=0; px<=pxend; px++) { // Horiz. precincts
                    for(int r=rs; r<re; r++) { // Resolution levels
                        if(r>mrl) continue;
                        if(nextPrec[c][r] >=
                           numPrec[t][c][r].x*numPrec[t][c][r].y) {
                            continue;
                        }
                        prec = pktEnc.getPrecInfo(t,c,r,nextPrec[c][r]);
                        if((prec.rgulx!=x) || (prec.rguly!=y)) {
                            continue;
                        }

                        for(int l=minlys; l<lye; l++) { // Layers
                            if(r>=lys[c].length) continue;
                            if(l<lys[c][r]) continue;

                            // set boolean sopUsed here (SOP markers)
                            sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
                            // set boolean ephUsed here (EPH markers)
                            ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");

                            sb = src.getAnSubbandTree(t,c);
                            for(int i=mrl; i>r; i--) {
                                sb = sb.subb_LL;
                            }

                            threshold = layers[l].rdThreshold;
                            findTruncIndices(l,c,r,t,sb,threshold,
                                             nextPrec[c][r]);

                            hBuff = pktEnc.encodePacket(l+1,c,r,t,
                                                        cblks[t][c][r],
                                                        truncIdxs[t][l][c][r],
                                                        hBuff,bBuff,
                                                        nextPrec[c][r]);

                            if(pktEnc.isPacketWritable()) {
                                bsWriter.writePacketHead(hBuff.getBuffer(),
                                                         hBuff.getLength(),
                                                         false,sopUsed,
                                                         ephUsed);
                                bsWriter.writePacketBody(pktEnc.
                                                         getLastBodyBuf(),
                                                         pktEnc.
                                                         getLastBodyLen(),
                                                         false,
                                                         pktEnc.isROIinPkt(),
                                                         pktEnc.getROILen());
                            }

                        } // Layers
                        nextPrec[c][r]++;
                    } // Resolution levels                    
                    if(px!=pxend) {
                        x = minx+px*gcd_x;
                    } else {
                        x = tx0;
                    }
                } // Horizontal precincts
                if(py!=pyend) {
                    y = miny+py*gcd_y;
                } else {
                    y = ty0;
                }
            } // Vertical precincts
        } // components

        // Check that all precincts have been written
        for(int c=cs; c<ce; c++) {
            mrl = src.getAnSubbandTree(t,c).resLvl;
            for(int r=rs; r<re; r++) {
                if(r>mrl) continue;
                if(nextPrec[c][r]<numPrec[t][c][r].x*numPrec[t][c][r].y-1) {
                    throw new Error("JJ2000 bug: One precinct at least has "+
                                    "not been written for resolution level "+r
                                    +" of component "+c+" in tile "+t+".");
                }
            }
        }
    }

    /** 
     * Write a piece of bit stream according to the
     * RES_POS_COMP_LY_PROG progression mode and between given bounds
     *
     * @param t Tile index.
     *
     * @param rs First resolution level index.
     *
     * @param re Last resolution level index.
     *
     * @param cs First component index.
     *
     * @param ce Last component index.
     *
     * @param lys First layer index for each component and resolution.
     *
     * @param lye Last layer index.
     * */
    public void writeResPosCompLy(int t,int rs,int re,int cs,int ce,
                                  int[][] lys,int lye) throws IOException {

        boolean sopUsed; // Should SOP markers be used ?
        boolean ephUsed; // Should EPH markers be used ?
        int nc = src.getNumComps();
        int mrl;
        SubbandAn sb;
        float threshold;
        BitOutputBuffer hBuff = null;
        byte[] bBuff = null;

        // Computes current tile offset in the reference grid
        Point nTiles = src.getNumTiles(null);
        Point tileI = src.getTile(null);
        int x0siz = src.getImgULX();
        int y0siz = src.getImgULY();
        int xsiz = x0siz + src.getImgWidth();
        int ysiz = y0siz + src.getImgHeight();
        int xt0siz = src.getTilePartULX();
        int yt0siz = src.getTilePartULY();
        int xtsiz = src.getNomTileWidth();
        int ytsiz = src.getNomTileHeight();
        int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
        int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
        int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
        int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;

        // Get precinct information (number,distance between two consecutive
        // precincts in the reference grid) in each component and resolution
        // level
        PrecInfo prec; // temporary variable
        int p; // Current precinct index
        int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
        int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
        int nPrec = 0; // Total number of found precincts
        int[][] nextPrec = new int [ce][]; // Next precinct index in each
        // component and resolution level
        int minlys = 100000; // minimum layer start index of each component
        int minx = tx1; // Horiz. offset of the second precinct in the
        // reference grid
        int miny = ty1; // Vert. offset of the second precinct in the
        // reference grid. 
        int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
        int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
        for(int c=cs; c<ce; c++) {
            mrl = src.getAnSubbandTree(t,c).resLvl;
            nextPrec[c] = new int[mrl+1];
            for(int r=rs; r<re; r++) {
                if(r>mrl) continue;
                if (r<lys[c].length && lys[c][r]<minlys) {
                    minlys = lys[c][r];
                }
                p = numPrec[t][c][r].y*numPrec[t][c][r].x-1;
                for(; p>=0; p--) {
                    prec = pktEnc.getPrecInfo(t,c,r,p);
                    if(prec.rgulx!=tx0) {
                        if(prec.rgulx<minx) minx = prec.rgulx;
                        if(prec.rgulx>maxx) maxx = prec.rgulx;
                    }
                    if(prec.rguly!=ty0){
                        if(prec.rguly<miny) miny = prec.rguly;
                        if(prec.rguly>maxy) maxy = prec.rguly;
                    }

                    if(nPrec==0) {
                        gcd_x = prec.rgw;
                        gcd_y = prec.rgh;
                    } else {
                        gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
                        gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
                    }
                    nPrec++;
                } // precincts
            } // resolution levels
        } // components

        if(nPrec==0) {
            throw new Error("Image cannot have no precinct");
        }

        int pyend = (maxy-miny)/gcd_y+1;
        int pxend = (maxx-minx)/gcd_x+1;
        int x,y;
        for(int r=rs; r<re; r++) { // Resolution levels
            y = ty0;
            x = tx0;
            for(int py=0; py<=pyend; py++) { // Vertical precincts
                for(int px=0; px<=pxend; px++) { // Horiz. precincts
                    for(int c=cs; c<ce; c++) { // Components
                        mrl = src.getAnSubbandTree(t,c).resLvl;
                        if(r>mrl) continue;
                        if(nextPrec[c][r] >=
                           numPrec[t][c][r].x*numPrec[t][c][r].y) {
                            continue;
                        }
                        prec = pktEnc.getPrecInfo(t,c,r,nextPrec[c][r]);
                        if((prec.rgulx!=x) || (prec.rguly!=y)) {
                            continue;
                        }
                        for(int l=minlys; l<lye; l++) {
                            if(r>=lys[c].length) continue;
                            if(l<lys[c][r]) continue;

                            // set boolean sopUsed here (SOP markers)
                            sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
                            // set boolean ephUsed here (EPH markers)
                            ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");

                            sb = src.getAnSubbandTree(t,c);
                            for(int i=mrl; i>r; i--) {
                                sb = sb.subb_LL;
                            }

                            threshold = layers[l].rdThreshold;
                            findTruncIndices(l,c,r,t,sb,threshold,
                                             nextPrec[c][r]);

                            hBuff = pktEnc.encodePacket(l+1,c,r,t,
                                                        cblks[t][c][r],
                                                        truncIdxs[t][l][c][r],
                                                        hBuff,bBuff,
                                                        nextPrec[c][r]);

                            if(pktEnc.isPacketWritable()) {
                                bsWriter.writePacketHead(hBuff.getBuffer(),
                                                         hBuff.getLength(),
                                                         false,sopUsed,
                                                         ephUsed);
                                bsWriter.writePacketBody(pktEnc.
                                                         getLastBodyBuf(),
                                                         pktEnc.
                                                         getLastBodyLen(),
                                                         false,
                                                         pktEnc.isROIinPkt(),
                                                         pktEnc.getROILen());
                            }

                        } // layers
                        nextPrec[c][r]++;
                    } // Components
                    if(px!=pxend) {
                        x = minx+px*gcd_x;
                    } else {
                        x = tx0;
                    }
                } // Horizontal precincts
                if(py!=pyend) {
                    y = miny+py*gcd_y;
                } else {
                    y = ty0;
                }
            } // Vertical precincts
        } // Resolution levels

        // Check that all precincts have been written
        for(int c=cs; c<ce; c++) {
            mrl = src.getAnSubbandTree(t,c).resLvl;
            for(int r=rs; r<re; r++) {
                if(r>mrl) continue;
                if(nextPrec[c][r]<numPrec[t][c][r].x*numPrec[t][c][r].y-1) {
                    throw new Error("JJ2000 bug: One precinct at least has "+
                                    "not been written for resolution level "+r
                                    +" of component "+c+" in tile "+t+".");
                }
            }
        }
    }

    /**
     * This function implements the rate-distortion optimization algorithm.
     * It saves the state of any previously generated bit-stream layers and
     * then simulate the formation of a new layer in the bit stream as often
     * as necessary to find the smallest rate-distortion threshold such that
     * the total number of bytes required to represent the layer does not
     * exceed `maxBytes' minus `prevBytes'.  It then restores the state of any
     * previously generated bit-stream layers and returns the threshold.
     *
     * @param layerIdx The index of the current layer
     *
     * @param fmaxt The maximum admissible slope value. Normally the threshold
     * slope of the previous layer.
     *
     * @param maxBytes The maximum number of bytes that can be written. It
     * includes the length of the current layer bistream length and all the
     * previous layers bit streams.
     *
     * @param prevBytes The number of bytes of all the previous layers.
     *
     * @return The value of the slope threshold.
     * */
    private float optimizeBitstreamLayer (int layerIdx, float fmaxt,
                                          int maxBytes, int prevBytes)
        throws IOException {

        int nt;         // The total number of tiles
        int nc;          // The total number of components
        int numLvls;          // The total number of resolution levels
        int actualBytes;      // Actual number of bytes for a layer
        float fmint;          // Minimum of the current threshold interval
        float ft;             // Current threshold
        SubbandAn sb;         // Current subband
        BitOutputBuffer hBuff;// The packet head buffer
        byte[] bBuff;         // The packet body buffer
        int sidx;             // The index in the summary table
        boolean sopUsed;      // Should SOP markers be used ?
        boolean ephUsed;      // Should EPH markers be used ?
        int precinctIdx;      // Precinct index for current packet
        int nPrec; // Number of precincts in the current resolution level

        // Save the packet encoder state
        pktEnc.save();

        nt = src.getNumTiles();
        nc = src.getNumComps();
        hBuff = null;
        bBuff = null;

        // Estimate the minimum slope to start with from the summary
        // information in 'RDSlopesRates'. This is a real minimum since it
        // does not include the packet head overhead, which is always
        // non-zero.

        // Look for the summary entry that gives 'maxBytes' or more data
        for (sidx=RD_SUMMARY_SIZE-1; sidx > 0; sidx--) {
            if (RDSlopesRates[sidx] >= maxBytes) {
                break;
            }
        }
        // Get the corresponding minimum slope
        fmint = getSlopeFromSIndex(sidx);
        // Ensure that it is smaller the maximum slope
        if (fmint>=fmaxt) {
            sidx--;
            fmint = getSlopeFromSIndex(sidx);
        }
        // If we are using the last entry of the summary, then that
        // corresponds to all the data, Thus, set the minimum slope to 0.
        if (sidx<=0) fmint = 0;

        // We look for the best threshold 'ft', which is the lowest threshold
        // that generates no more than 'maxBytes' code bytes.

        // The search is done iteratively using a binary split algorithm. We
        // start with 'fmaxt' as the maximum possible threshold, and 'fmint'
        // as the minimum threshold. The threshold 'ft' is calculated as the
        // middle point of 'fmaxt'-'fmint' interval. The 'fmaxt' or 'fmint'
        // bounds are moved according to the number of bytes obtained from a
        // simulation, where 'ft' is used as the threshold.

        // We stop whenever the interval is sufficiently small, and thus
        // enough precision is achieved.

        // Initialize threshold as the middle point of the interval.
        ft = (fmaxt+fmint)/2f;
        // If 'ft' reaches 'fmint' it means that 'fmaxt' and 'fmint' are so
        // close that the average is 'fmint', due to rounding. Force it to
        // 'fmaxt' instead, since 'fmint' is normally an exclusive lower
        // bound.
        if (ft <= fmint) ft = fmaxt;

        do {
            // Get the number of bytes used by this layer, if 'ft' is the
            // threshold, by simulation.
            actualBytes = prevBytes;
            src.setTile(0,0);

            for (int t=0; t<nt; t++){
                for (int c=0; c<nc; c++) {
                    // set boolean sopUsed here (SOP markers)
                    sopUsed = ((String)wp.getSOP().getTileDef(t)).equalsIgnoreCase("true");
                    // set boolean ephUsed here (EPH markers)
                    ephUsed = ((String)wp.getEPH().getTileDef(t)).equalsIgnoreCase("true");

                    // Get LL subband
                    sb = (SubbandAn) src.getAnSubbandTree(t,c);
                    numLvls = sb.resLvl + 1;
                    sb = (SubbandAn) sb.getSubbandByIdx(0,0);
                    //loop on resolution levels
                    for(int r=0; r<numLvls; r++) {

                        nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
                        for(int p=0; p<nPrec; p++) {

                            findTruncIndices(layerIdx,c,r,t,sb,ft,p);
                            hBuff = pktEnc.encodePacket(layerIdx+1,c,r,t,
                                                        cblks[t][c][r],
                                                        truncIdxs[t][layerIdx]
                                                        [c][r],hBuff,bBuff,p);

                            if(pktEnc.isPacketWritable()) {
                                bBuff = pktEnc.getLastBodyBuf();
                                actualBytes += bsWriter.
                                    writePacketHead(hBuff.getBuffer(),
                                                    hBuff.getLength(),
                                                    true, sopUsed,ephUsed);
                                actualBytes += bsWriter.
                                    writePacketBody(bBuff,
                                                    pktEnc.getLastBodyLen(),
                                                    true,pktEnc.isROIinPkt(),
                                                    pktEnc.getROILen());
                            }
                        } // end loop on precincts
                        sb = sb.parent;
                    } // End loop on resolution levels
                } // End loop on components
            } // End loop on tiles

            // Move the interval bounds according to simulation result
            if (actualBytes>maxBytes) {
                // 'ft' is too low and generates too many bytes, make it the
                // new minimum.
                fmint = ft;
            } else {
                // 'ft' is too high and does not generate as many bytes as we
                // are allowed too, make it the new maximum.
                fmaxt = ft;
            }

            // Update 'ft' for the new iteration as the middle point of the
            // new interval.
            ft = (fmaxt+fmint)/2f;
            // If 'ft' reaches 'fmint' it means that 'fmaxt' and 'fmint' are
            // so close that the average is 'fmint', due to rounding. Force it
            // to 'fmaxt' instead, since 'fmint' is normally an exclusive
            // lower bound.
            if (ft <= fmint) ft = fmaxt;

            // Restore previous packet encoder state
            pktEnc.restore();

            // We continue to iterate, until the threshold reaches the upper
            // limit of the interval, within a FLOAT_REL_PRECISION relative
            // tolerance, or a FLOAT_ABS_PRECISION absolute tolerance. This is
            // the sign that the interval is sufficiently small.
        } while (ft < fmaxt*(1f-FLOAT_REL_PRECISION) &&
                 ft < (fmaxt-FLOAT_ABS_PRECISION));

        // If we have a threshold which is close to 0, set it to 0 so that
        // everything is taken into the layer. This is to avoid not sending
        // some least significant bit-planes in the lossless case. We use the
        // FLOAT_ABS_PRECISION value as a measure of "close" to 0.
        if (ft <= FLOAT_ABS_PRECISION) {
            ft = 0f;
        } else {
            // Otherwise make the threshold 'fmaxt', just to be sure that we
            // will not send more bytes than allowed.
            ft = fmaxt;
        }
       return ft;
    }

    /**
     * This function attempts to estimate a rate-distortion slope threshold
     * which will achieve a target number of code bytes close the
     * `targetBytes' value.
     *
     * @param targetBytes The target number of bytes for the current layer
     *
     * @param lastLayer The previous layer information.
     *
     * @return The value of the slope threshold for the estimated layer
     * */
    private float estimateLayerThreshold(int targetBytes,
                                         EBCOTLayer lastLayer) {
        float log_sl1;  // The log of the first slope used for interpolation
        float log_sl2;  // The log of the second slope used for interpolation
        float log_len1; // The log of the first length used for interpolation
        float log_len2; // The log of the second length used for interpolation
        float log_isl;  // The log of the interpolated slope
        float log_ilen; // Log of the interpolated length
        float log_ab;   // Log of actual bytes in last layer
        int sidx;       // Index into the summary R-D info array
        float log_off;  // The log of the offset proportion
        int tlen;       // The corrected target layer length
        float lthresh;  // The threshold of the last layer
        float eth;      // The estimated threshold

        // In order to estimate the threshold we base ourselves in the summary
        // R-D info in RDSlopesRates. In order to use it we must compensate
        // for the overhead of the packet heads. The proportion of overhead is
        // estimated using the last layer simulation results.

        // NOTE: the model used in this method is that the slope varies
        // linearly with the log of the rate (i.e. length).

        // NOTE: the model used in this method is that the distortion is
        // proprotional to a power of the rate. Thus, the slope is also
        // proportional to another power of the rate. This translates as the
        // log of the slope varies linearly with the log of the rate, which is
        // what we use.

        // 1) Find the offset of the length predicted from the summary R-D
        // information, to the actual length by using the last layer.

        // We ensure that the threshold we use for estimation actually
        // includes some data.
        lthresh = lastLayer.rdThreshold;
        if (lthresh > maxSlope) lthresh = maxSlope;
        // If the slope of the last layer is too small then we just include
        // all the rest (not possible to do better).
        if (lthresh < FLOAT_ABS_PRECISION) return 0f;
        sidx = getLimitedSIndexFromSlope(lthresh);
        // If the index is outside of the summary info array use the last two,
        // or first two, indexes, as appropriate
        if (sidx >= RD_SUMMARY_SIZE-1) sidx = RD_SUMMARY_SIZE-2;

        // Get the logs of the lengths and the slopes

        if (RDSlopesRates[sidx+1] == 0) {
            // Pathological case, we can not use log of 0. Add
            // RDSlopesRates[sidx]+1 bytes to the rates (just a crude simple
            // solution to this rare case)
            log_len1 = (float)Math.log((RDSlopesRates[sidx]<<1)+1);
            log_len2 = (float)Math.log(RDSlopesRates[sidx]+1);
            log_ab = (float)Math.log(lastLayer.actualBytes+
                                     RDSlopesRates[sidx]+1);
        }
        else {
            log_len1 = (float)Math.log(RDSlopesRates[sidx]);
            log_len2 = (float)Math.log(RDSlopesRates[sidx+1]);
            log_ab = (float)Math.log(lastLayer.actualBytes);
        }

        log_sl1 = (float)Math.log(getSlopeFromSIndex(sidx));
        log_sl2 = (float)Math.log(getSlopeFromSIndex(sidx+1));

        log_isl = (float)Math.log(lthresh);

        log_ilen = log_len1 +
            (log_isl-log_sl1)*(log_len1-log_len2)/(log_sl1-log_sl2);

        log_off = log_ab - log_ilen;

        // Do not use negative offsets (i.e. offset proportion larger than 1)
        // since that is probably a sign that our model is off. To be
        // conservative use an offset of 0 (i.e. offset proportiojn 1).
        if (log_off < 0) log_off = 0f;

        // 2) Correct the target layer length by the offset.

        tlen = (int)(targetBytes/(float)Math.exp(log_off));

        // 3) Find, from the summary R-D info, the thresholds that generate
        // lengths just above and below our corrected target layer length.

        // Look for the index in the summary info array that gives the largest
        // length smaller than the target length
        for (sidx = RD_SUMMARY_SIZE-1; sidx>=0 ; sidx--) {
            if (RDSlopesRates[sidx] >= tlen) break;
        }
        sidx++;
        // Correct if out of the array
        if (sidx >= RD_SUMMARY_SIZE) sidx = RD_SUMMARY_SIZE-1;
        if (sidx <= 0) sidx = 1;

        // Get the log of the lengths and the slopes that are just above and
        // below the target length.

        if (RDSlopesRates[sidx] == 0) {
            // Pathological case, we can not use log of 0. Add
            // RDSlopesRates[sidx-1]+1 bytes to the rates (just a crude simple
            // solution to this rare case)
            log_len1 = (float)Math.log(RDSlopesRates[sidx-1]+1);
            log_len2 = (float)Math.log((RDSlopesRates[sidx-1]<<1)+1);
            log_ilen = (float)Math.log(tlen+RDSlopesRates[sidx-1]+1);
        }
        else {
            // Normal case, we can safely take the logs.
            log_len1 = (float)Math.log(RDSlopesRates[sidx]);
            log_len2 = (float)Math.log(RDSlopesRates[sidx-1]);
            log_ilen = (float)Math.log(tlen);
        }

        log_sl1 = (float)Math.log(getSlopeFromSIndex(sidx));
        log_sl2 = (float)Math.log(getSlopeFromSIndex(sidx-1));

        // 4) Interpolate the two thresholds to find the target threshold.

        log_isl = log_sl1 +
            (log_ilen-log_len1)*(log_sl1-log_sl2)/(log_len1-log_len2);

        eth = (float)Math.exp(log_isl);

        // Correct out of bounds results
        if (eth > lthresh) eth = lthresh;
        if (eth < FLOAT_ABS_PRECISION) eth = 0f;

        // Return the estimated threshold
        return eth;
    }

    /**
     * This function finds the new truncation points indices for a packet. It
     * does so by including the data from the code-blocks in the component,
     * resolution level and tile, associated with a R-D slope which is larger
     * than or equal to 'fthresh'.
     *
     * @param layerIdx The index of the current layer
     *
     * @param compIdx The index of the current component
     *
     * @param lvlIdx The index of the current resolution level
     *
     * @param tileIdx The index of the current tile
     *
     * @param subb The LL subband in the resolution level lvlIdx, which is
     * parent of all the subbands in the packet. Except for resolution level 0
     * this subband is always a node.
     *
     * @param fthresh The value of the rate-distortion threshold
     * */
    private void findTruncIndices(int layerIdx, int compIdx, int lvlIdx,
                                  int tileIdx, SubbandAn subb,
                                  float fthresh, int precinctIdx) {
        int minsbi, maxsbi, b, bIdx, n;
        Point ncblks = null;
        SubbandAn sb;
        CBlkRateDistStats cur_cblk;
        PrecInfo prec = pktEnc.getPrecInfo(tileIdx,compIdx,lvlIdx,precinctIdx);
        Point cbCoord;

        sb = subb;
        while(sb.subb_HH != null) {
            sb = sb.subb_HH;
        }
        minsbi = (lvlIdx==0) ? 0 : 1;
        maxsbi = (lvlIdx==0) ? 1 : 4;

        int yend,xend;

        sb = (SubbandAn)subb.getSubbandByIdx(lvlIdx, minsbi);
        for(int s=minsbi; s<maxsbi; s++) { //loop on subbands
            yend = (prec.cblk[s]!=null) ? prec.cblk[s].length : 0;
            for(int y=0; y<yend; y++) {
                xend = (prec.cblk[s][y]!=null) ? prec.cblk[s][y].length : 0;
                for (int x=0; x<xend; x++) {
                    cbCoord = prec.cblk[s][y][x].idx;
                    b = cbCoord.x+cbCoord.y*sb.numCb.x;
                    //Get the current code-block
                    cur_cblk = cblks[tileIdx][compIdx][lvlIdx][s][b];
                    for(n=0; n<cur_cblk.nVldTrunc; n++) {
                        if(cur_cblk.truncSlopes[n] < fthresh) {
                            break;
                        } else {
                            continue;
                        }
                    }
                    // Store the index in the code-block truncIdxs that gives
                    // the real truncation index.
                    truncIdxs[tileIdx][layerIdx][compIdx][lvlIdx][s][b] = n-1;

                } // End loop on horizontal code-blocks
            } // End loop on vertical code-blocks
            sb = (SubbandAn)sb.nextSubband();
        } // End loop on subbands
    }


    /**
     * Returns the index of a slope for the summary table, limiting to the
     * admissible values. The index is calculated as RD_SUMMARY_OFF plus the
     * maximum exponent, base 2, that yields a value not larger than the slope
     * itself.
     *
     * <P>If the value to return is lower than 0, 0 is returned. If it is
     * larger than the maximum table index, then the maximum is returned.
     *
     * @param slope The slope value
     *
     * @return The index for the summary table of the slope.
     * */
    private static int getLimitedSIndexFromSlope(float slope) {
        int idx;

        idx = (int)Math.floor(Math.log(slope)/LOG2) + RD_SUMMARY_OFF;

        if (idx < 0) {
            return 0;
        }
        else if (idx >= RD_SUMMARY_SIZE) {
            return RD_SUMMARY_SIZE-1;
        }
        else {
            return idx;
        }
    }

    /**
     * Returns the minimum slope value associated with a summary table
     * index. This minimum slope is just 2^(index-RD_SUMMARY_OFF).
     *
     * @param index The summary index value.
     *
     * @return The minimum slope value associated with a summary table index.
     * */
    private static float getSlopeFromSIndex(int index) {
        return (float)Math.pow(2,(index-RD_SUMMARY_OFF));
    }
}
