/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "Grib.hpp"
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#ifndef sun
#include <sys/dir.h>
#endif
#include <unistd.h>
#include <string.h>

#ifndef Box_H
#include "Box.hpp"
#endif

#ifndef Projection_H
#include "Projection.hpp"
#endif

#include "min_max_macros.h"

#define BUF_IMA			7680
#define ORIGIN_CENTER 		46


#ifdef ultrix
	extern "C" { char* strdup(char*); }
#endif

long
ExtractBitField ( short b1, short nb, unsigned char* b )
{
	short bit, nbyte, nbit;
	long ivb, ivalue;

	ivalue = 0;
	for ( bit = 0; bit < nb; bit++ )
	{
		nbyte = ( b1 + bit ) / 8;
		nbit  = 7 - ( b1 + bit ) % 8;
		ivb   = b [ nbyte ];
		ivb   = ivb >> nbit;
		ivb   = ivb & 1;
		ivalue = ivalue << 1;
		ivalue = ivalue | ivb;

	}

	return ( ivalue );
}


void
InsertBitField (long cv, short b1, short nb, unsigned char* b )
{
	short bit, nbyte, nbit;
	long ivb, ivalue;

	ivalue = cv;
	for ( bit = nb; bit >= 0; bit-- )
	{
		nbyte = ( b1 + bit - 1) / 8;
		nbit  = 7 - ( b1 + bit - 1) % 8;
		ivb = ivalue & 1L;
		ivalue >>= 1;
		ivb <<= nbit;
		b [ nbyte ] |= (unsigned char)ivb;
	}

}

int
getint (unsigned char* data, short size)
{
	int	i,sign,value;

	if (size == 1) return (int)data[0];

	sign = 1;
	value = data[0];
	if (value>127)
	{
		value -= 128;
		sign = -1;
	}

	for (i = 1; i < size; i++)
	{
		value <<= 8;
		value |= data[i];

	}

	return value*sign;
}

void
putint (unsigned char* data, short size, int val)
{
	int i,value;

	value = (int)fabs((double)val);
	for (i = size-1; i > 0; i--)
	{
		data[i] = value & 0x000000ff;
		value >>= 8;
	}

	if (val < 0) data[0] = value+128;
	else	     data[0] = value;
}


GribFile :: GribFile (const char* d, const char* n)
{
	GFdir = strdup (d);
	GFname = strdup (n);
	GFfp = NULL;
	GFnopens = 0;
	GFpos = 0L;
}

GribFile :: ~GribFile ()
{
	delete GFdir;
	delete GFname;
}

void
GribFile :: Init (const char* d, const char* n)
{
	GFdir = strdup (d);
	GFname = strdup (n);
	GFfp = NULL;
	GFnopens = 0;
	GFpos = 0L;
}

short
GribFile :: Open ()
{
	char* dn;

	if (GFnopens == 0)
	{
		dn = new char [ strlen(GFdir)+strlen(GFname)+2];
		sprintf (dn,"%s/%s",GFdir,GFname);
		if ((GFfp = fopen (dn, "rb+")) == NULL) return FALSE;
		GFpos = 0L;
		delete dn;
	}
	GFnopens++;
	return TRUE;
}

short
GribFile :: IsGrib ()
{
	char	*g,*grib;
	short	i;

	grib = strdup("GRIB");
	g = strdup("");

	i=0;
	while (i<4)
	{

		if (Read (g,1) == FALSE) return FALSE;

		if (*g==grib[i]) i++;
		else
		{
			i=0;
			if (*g==grib[i]) i++;
		}
	}
	delete grib;
	delete g;

	return TRUE;
}

short
GribFile :: Create ()
{
	char* dn;
	dn = new char [ strlen(GFdir)+strlen(GFname)+2];
	sprintf (dn,"%s/%s",GFdir,GFname);
	GFnopens = 1;
	if ((GFfp = fopen (dn, "w+b")) == NULL) return FALSE;
	GFpos = 0L;
	delete dn;
	return TRUE;
}

void
GribFile :: Close ()
{
	GFnopens--;
	if (GFnopens == 0)
	{
		fclose (GFfp);
		GFfp = NULL;
	}
}

short
GribFile :: Read (char* buf, int num)
{
	if (fread(buf,1,num,GFfp) != num) return FALSE;
	GFpos += (long)num;

	return TRUE;
}

short
GribFile :: Write (char* buf, int num)
{
	int k;
	if ((k = fwrite(buf,1,num,GFfp)) != num)
	{
		printf("Write falhou : k = %d, num = %d\n", k, num);
		return FALSE;
	}
	GFpos += (long)num;

	return TRUE;
}


short
GribFile :: Skip (long offset)
{
	if (fseek (GFfp, offset, SEEK_CUR) == -1) return FALSE;
	GFpos += offset;
	return TRUE;
}

short
GribFile :: Seek (short ind)
{
	unsigned char	a;
	short		i;
	int		size;
	SECTION0	sec0;
	SECTION1	sec1;
	SECTION2	sec2;
	SECTION3	sec3;
	SECTION4	sec4;

	Open();
	Rewind();
	
	for (i = 0; i < ind; i++)
	{
		if (IsGrib() == FALSE) return FALSE;
		Skip(-4);
		Read ((char*)&sec0,SEC0_FSIZE);
		if (sec0.ed_number == 1) Skip(getint(sec0.length,sizeof(sec0.length))-SEC0_FSIZE);
		else 	// old format
		{
			// Skip Section 1
			Skip(-4);
			Read ((char*)&sec1, SEC1_FSIZE);
			size = getint(sec1.length,sizeof(sec1.length));
			Skip(size-SEC1_FSIZE);

			// Skip Section 2
			a = sec1.flag;
			a >>= 7;
			if ( a==1 )
			{
				Read ((char*)&sec2, SEC2_FSIZE);
				size = getint (sec2.length,sizeof(sec2.length));
				Skip(size-SEC2_FSIZE);
			}

			// Skip Section 3
			a = sec1.flag;
			a >>= 6;
			a &= 1;
			if ( a==1 )
			{
				Read ((char*)&sec3, SEC3_FSIZE);
				size = getint (sec3.length,sizeof(sec3.length));
				Skip(size-SEC3_FSIZE);
			}

			// Skip Section 4
			Read ((char*)&sec4, SEC4_FSIZE);
			size = getint (sec4.length,sizeof(sec4.length));
			Skip(size-SEC4_FSIZE);

			// Skip Section 5
			Skip(SEC5_FSIZE);
		}
	}

	if (IsGrib() == FALSE) return FALSE;

	Close ();
	return TRUE;
}

void
GribFile :: Rewind ()
{
	rewind (GFfp);
	GFpos = 0L;
}

short
GribFile  :: Eof (short& ind)
{
	unsigned char	a;
        int		size;
	SECTION0	sec0;
	SECTION1	sec1;
	SECTION2	sec2;
	SECTION3	sec3;
	SECTION4	sec4;

	Open();
	Rewind();
        short i = 0;
        while (IsGrib())
        {   
		Read ((char*)&sec0,SEC0_FSIZE);
		if (sec0.ed_number == 1) 
		Skip(getint(sec0.length,sizeof(sec0.length))-SEC0_FSIZE);
		else 	// old format
		{	
			// Skip Section 1
			Skip(-4);
			Read ((char*)&sec1, SEC1_FSIZE);
			size = getint(sec1.length,sizeof(sec1.length));
			Skip(size-SEC1_FSIZE);

			// Skip Section 2
			a = sec1.flag;
			a >>= 7;
			if ( a==1 )
			{
				Read ((char*)&sec2, SEC2_FSIZE);
				size = getint (sec2.length,sizeof(sec2.length));
				Skip(size-SEC2_FSIZE);
			}

			// Skip Section 3
			a = sec1.flag;
			a >>= 6;
			a &= 1;
			if ( a==1 )
			{
				Read ((char*)&sec3, SEC3_FSIZE);
				size = getint (sec3.length,sizeof(sec3.length));
				Skip(size-SEC3_FSIZE);
			}

			// Skip Section 4
			Read ((char*)&sec4, SEC4_FSIZE);
			size = getint (sec4.length,sizeof(sec4.length));
			Skip(size-SEC4_FSIZE);

			// Skip Section 5
			Skip(SEC5_FSIZE);

		}
		i++;
      
        }
        ind = i; 
	Close();
	return TRUE;
 }
 
//////////////////////////////////////////////////////////////////////////

GribData :: ~GribData ()
{
	delete GDsec2;
	delete GDsec3;
	if (GDtranslated)
	{
		delete GDlookup;
		delete GDbuf;
	}
}


GribData :: GribData ()
{
	GDfile   = NULL;
	GDbuf    = NULL;
	GDlookup = NULL;
	GDtranslated = FALSE;
	GDsec2   = NULL;
	GDsec3   = NULL;
}

GribData ::  GribData ( GribFile *gf, GribData* gd)
{
	GDfile  = gf;
	gf->Eof(GDindex);
	GDpos_inicio_dados = gf->Pos() + getint(gd->GDsec0.length, sizeof(GDsec0.length)) - getint(gd->GDsec4.length, sizeof(GDsec4.length)) -
SEC5_FSIZE;

	GDlookup           = NULL;
	GDtranslated       = FALSE;
	GDbuf              = NULL;
	GDnewnx            = gd->GDnewnx;
	GDnx               = gd->GDnx;
	GDny               = gd->GDny;
	GDresx             = gd->GDresx;
	GDresy             = gd->GDresy;
	GDzscale           = gd->GDzscale;
	GDfmin             = gd->GDfmin;
	GDibsiz            = gd->GDibsiz;
	GDiskip2           = gd->GDiskip2;

	short size;

// Initialize Section 0

	memcpy(&GDsec0,&gd->GDsec0,SEC0_FSIZE);
	GDsec0.ed_number = 1;

// Initialize Section 1

	size = getint(gd->GDsec1.length,sizeof(gd->GDsec1.length));
	if (size > SEC1_FSIZE)
	{
		GDsec1.documentation_file = new unsigned char[size-SEC1_FSIZE];
		memcpy (&GDsec1, (void*)&gd->GDsec1, SEC1_FSIZE);
		memcpy (GDsec1.documentation_file, gd->GDsec1.documentation_file, (int)size-SEC1_FSIZE);
	}
	else memcpy (&GDsec1, &gd->GDsec1, (int)size);

	// Initialize Section 2
	if (gd->GDsec2 == NULL)
		GDsec2 = NULL;
	else
	{	
		GDsec2 = new SECTION2;
		memcpy (GDsec2, gd->GDsec2, SEC2_FSIZE);
		size =  getint(gd->GDsec2->length ,sizeof(gd->GDsec2->length)) - SEC2_FSIZE;
		if (size == 0)
			GDsec2->data=NULL;
		else
		{
			GDsec2->data = new unsigned char[size];
       			memcpy (GDsec2->data, gd->GDsec2->data, size);
		}
	}

	// Initialize Section 3
	if (gd->GDsec3 == NULL)
		GDsec3 = NULL;
	else
	{	
		GDsec3 = new SECTION3;
		memcpy (GDsec3, gd->GDsec3, SEC3_FSIZE);
		size = getint(GDsec3->length,sizeof(GDsec3->length));
		GDsec3->bitmap = new unsigned char[size-SEC3_FSIZE];
		memcpy (GDsec3->bitmap, gd->GDsec3->bitmap, (int)size-SEC3_FSIZE);
	}

	// Initialize Section 4
   	 memcpy(&GDsec4, &gd->GDsec4, SEC4_FSIZE); 

	// Initialize Section 5
	 memcpy(&GDsec5, &gd->GDsec5, SEC5_FSIZE); 
}

void
GribData :: Init (GribFile* gf, int i)
{
	unsigned char a;
	int 	size;

	GDfile = gf;
	GDindex = i;

// Read SECTION 0

	GDfile->Skip(-4);
	GDfile->Read ((char*)&GDsec0,SEC0_FSIZE);
	if (GDsec0.ed_number == 0) GDfile->Skip(-4);

// Read SECTION 1

	GDfile->Read ((char*)&GDsec1, 3);
	size = getint(GDsec1.length,sizeof(GDsec1.length));

	GDfile->Skip(-3);
	if (size > 40)
	{
		GDsec1.documentation_file = new unsigned char[size-40];
		GDfile->Read ((char*)&GDsec1, 40);
		GDfile->Read ((char*)GDsec1.documentation_file,size-40);
	}
	else GDfile->Read ((char*)&GDsec1, size);

// Read SECTION 2

	a = GDsec1.flag;
	a >>= 7;
	if ( a==1 )
	{
		GDsec2 = new SECTION2;

		GDfile->Read ((char*)GDsec2, SEC2_FSIZE);
		size = getint (GDsec2->length,sizeof(GDsec2->length));
		switch(getint(&GDsec2->data_type,sizeof(GDsec2->data_type)))
		{
			case PROJSATELLITE	:
			{
				GDsec2->data = new unsigned char[size-SEC2_FSIZE];
				GDfile->Read ((char*)GDsec2->data,size-SEC2_FSIZE);
				GDnx = getint(&GDsec2->data[SAT_NX],SAT_NX_SIZE);
				GDny = getint(&GDsec2->data[SAT_NY],SAT_NY_SIZE);
				float nr = AltitudeSat ();
				GDresx = atan(tan(2. * asin(1./nr)/ApparentDiaX()) * (nr-1.)) * EARTH_RADIUS;
				GDresy = atan(tan(2. * asin(1./nr)/ApparentDiaY()) * (nr-1.)) * EARTH_RADIUS;
				break;
			}
			case PROJCYLINDRICAL :
			{
				GDsec2->data = new unsigned char[size-SEC2_FSIZE];
				GDfile->Read ((char*)GDsec2->data,size-SEC2_FSIZE);
				GDnx = getint(&GDsec2->data[CYL_NI],CYL_NI_SIZE);
				GDny = getint(&GDsec2->data[CYL_NJ],CYL_NJ_SIZE);
				GDresx  = fabs((float)getint(&GDsec2->data[CYL_DI],CYL_DI_SIZE)*0.001*CDR*EARTH_RADIUS);
				GDresy = fabs((float)getint(&GDsec2->data[CYL_DJ],CYL_DJ_SIZE)*0.001*CDR*EARTH_RADIUS);
				break;
			}
			case PROJGAUSS:
			{
				GDsec2->data = new unsigned char[size-SEC2_FSIZE];
				GDfile->Read ((char*)GDsec2->data,size-SEC2_FSIZE);
				GDnx = getint(&GDsec2->data[GSS_NI],GSS_NI_SIZE);
				GDny = getint(&GDsec2->data[GSS_NJ],GSS_NJ_SIZE);
				GDresx  = fabs((float)getint(&GDsec2->data[GSS_DI],GSS_DI_SIZE)*0.001*CDR*EARTH_RADIUS);
				GDresy = fabs(90./(float)getint(&GDsec2->data[GSS_N],GSS_N_SIZE)*CDR*EARTH_RADIUS);
				break;
			}
			case GENERALPROJ:
			{
				GDsec2->data = new unsigned char[size-SEC2_FSIZE];
				GDfile->Read ((char*)GDsec2->data,size-SEC2_FSIZE);
				GDnx = getint(&GDsec2->data[GP_NI],GP_NI_SIZE);
				GDny = getint(&GDsec2->data[GP_NJ],GP_NJ_SIZE);
				GDresx  = fabs((float)getint(&GDsec2->data[GP_DI],GP_DI_SIZE)*0.001);
				GDresy = fabs((float)getint(&GDsec2->data[GP_DJ],GP_DJ_SIZE)*0.001);
				break;
			}
			case PROJPOLY:
			{
				GDsec2->data = new unsigned char[size-SEC2_FSIZE];
				GDfile->Read ((char*)GDsec2->data,size-SEC2_FSIZE);
				GDnx = getint(&GDsec2->data[POL_NX],POL_NX_SIZE);
				GDny = getint(&GDsec2->data[POL_NY],POL_NY_SIZE);
				GDresx = getint(&GDsec2->data[POL_RX],POL_RX_SIZE)*0.01;
				GDresy = getint(&GDsec2->data[POL_RY],POL_RY_SIZE)*0.01;
				break;
			}
			default :
				GDsec2->data = new unsigned char[size-SEC2_FSIZE];
				GDfile->Read ((char*)GDsec2->data,size-SEC2_FSIZE);
				printf ("\nGribData::Init -> Projecao indefinida");
				break;
		}
	}

// Read SECTION3

	a = GDsec1.flag;
	a >>= 6;
	a &= 1;
	if ( a==1 )
	{
		GDsec3 = new SECTION3;
		GDfile->Read ((char*)GDsec3, SEC3_FSIZE);
		size = getint(GDsec3->length,sizeof(GDsec3->length));
		GDsec3->bitmap = new unsigned char[size-SEC3_FSIZE];
		GDfile->Read ((char*)GDsec3->bitmap, size-SEC3_FSIZE);
	}

// Read SECTION4

	GDfile->Read ((char*)&GDsec4, SEC4_FSIZE);

	long iscale, jscale;
	jscale = ExtractBitField ( 0, 16, GDsec4.scale );
	iscale = jscale;
	if ( iscale >= 32768 ) iscale = ( 32768 - iscale );

	long iexp, imant;
	iexp = ExtractBitField ( 0, 8, GDsec4.reference );
	imant = ExtractBitField ( 0, 24, &GDsec4.reference[1] );

	if ( jscale == 65535  &&  iexp == 255  &&  imant == 16777215 )
	{
		GDfmin = 0.0;
		GDzscale = 0.0;
	}
	else
	{
		GDzscale = pow ( (double)2.0, (double)iscale );
		short isign = 1;
		if ( iexp >= 128 )
		{
			iexp -= 128;
			isign = -1;
		}
		GDfmin = isign * pow ( (double)2.0, (double)(-24.0) ) * imant * pow ( (double)16.0, (double)(iexp - 64.0) );
	}
	GDibsiz = (short) ExtractBitField ( 0, 8, &GDsec4.bits_number );

	GDpos_inicio_dados = GDfile->Pos ();
}

void
GribData :: Init (GribFile* gf, GribData* gd, short ind, SProjection* pout, SProjection*, Box& bout, float resx, float resy)
{
	unsigned char	a;

	float	y1, y2, x1, x2, incx, incy;

	long	size, grib_size, size_bits;
	
//Initialize parameters

	y2  = bout.lowerLeftY();
	x1 = bout.lowerLeftX();
	y1  = bout.upperRightY();
	x2 = bout.upperRightX();

	GDny = (int)fabs((double)((y2-y1)/resy)) + 1;
	GDnx = (int)fabs((double)((x2-x1)/resx)) + 1;
	incy = (y2 - y1)/(float)(GDny-1);
	incx = (x2 - x1)/(float)(GDnx-1);
	GDresy = fabs(incy);
	GDresx = fabs(incx);
	GDindex = ind;
	GDfile = gf;

// Initialize Section 0

	memcpy(GDsec0.coded,"GRIB",4);
	grib_size = 8;
	GDsec0.ed_number = 1;

// Initialize Section 1

	size = getint(gd->GDsec1.length,sizeof(gd->GDsec1.length));
	grib_size += size;
	if (size > SEC1_FSIZE)
	{
		GDsec1.documentation_file = new unsigned char[size-SEC1_FSIZE];
		memcpy (&GDsec1, (void*)&gd->GDsec1, SEC1_FSIZE);
		memcpy (GDsec1.documentation_file, gd->GDsec1.documentation_file, (int)size-SEC1_FSIZE);
	}
	else memcpy (&GDsec1, &gd->GDsec1, (int)size);

// Initialize Section 2
	
	GDsec2 = new SECTION2;
	switch (pout->GribCode())
	{
		case PROJCYLINDRICAL:
		{
		GDsec2->data = new unsigned char[SEC2_CYL_SIZE];
	grib_size += SEC2_CYL_SIZE+SEC2_FSIZE;
		putint (GDsec2->length, sizeof(GDsec2->length), SEC2_CYL_SIZE+SEC2_FSIZE);
		putint (&GDsec2->unused_bits, sizeof(GDsec2->unused_bits), 0);
		putint (&GDsec2->zero, sizeof(GDsec2->zero), 0);	
		putint (&GDsec2->data_type, sizeof(GDsec2->data_type), PROJCYLINDRICAL);
		putint (&GDsec2->data[CYL_NI], CYL_NI_SIZE, GDnx);
		putint (&GDsec2->data[CYL_NJ], CYL_NJ_SIZE, GDny);
		putint (&GDsec2->data[CYL_LA1], CYL_LA1_SIZE, (int)(y1/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[CYL_LO1], CYL_LO1_SIZE, (int)(x1/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[CYL_FLAG], CYL_FLAG_SIZE, 128);
		putint (&GDsec2->data[CYL_LA2], CYL_LA2_SIZE, (int)(y2/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[CYL_LO2], CYL_LO2_SIZE, (int)(x2/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[CYL_DI], CYL_DI_SIZE, (int)(incx/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[CYL_DJ], CYL_DJ_SIZE, (int)(incy/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[CYL_SCAN], CYL_SCAN_SIZE, 0);
		memset (&GDsec2->data[CYL_RESERVED], CYL_RESERVED_SIZE, 0);
		break;
		}
		case PROJSATELLITE:
		{
		GDsec2->data = new unsigned char[SEC2_SAT_SIZE];
	grib_size += SEC2_SAT_SIZE+SEC2_FSIZE;
		putint (GDsec2->length, sizeof(GDsec2->length), SEC2_SAT_SIZE+SEC2_FSIZE);
		putint (&GDsec2->unused_bits, sizeof(GDsec2->unused_bits), 0);
		putint (&GDsec2->zero, sizeof(GDsec2->zero), 0);	
		putint (&GDsec2->data_type, sizeof(GDsec2->data_type), PROJSATELLITE);
		putint (&GDsec2->data[SAT_NX], SAT_NX_SIZE, GDnx);
		putint (&GDsec2->data[SAT_NY], SAT_NY_SIZE, GDny);
		putint (&GDsec2->data[SAT_LAP], SAT_LAP_SIZE, (int)(pout->SubSatelliteLat()*CRD*1000.));
		putint (&GDsec2->data[SAT_LOP], SAT_LOP_SIZE, (int)(pout->SubSatelliteLng()*CRD*1000.));
		putint (&GDsec2->data[SAT_FLAG], SAT_FLAG_SIZE, 128);
		putint (&GDsec2->data[SAT_DX], SAT_DX_SIZE, (int)(2.*asin(pout->EarthRadius()/pout->Radius())/pout->SensorResolutionX()));
		putint (&GDsec2->data[SAT_DY], SAT_DY_SIZE, (int)(2.*asin(pout->EarthRadius()/pout->Radius())/pout->SensorResolutionY()));
		putint (&GDsec2->data[SAT_XP], SAT_XP_SIZE, (int)pout->SubSatelliteX());
		putint (&GDsec2->data[SAT_YP], SAT_YP_SIZE, (int)pout->SubSatelliteY());
		float yaw = pout->Yaw();
		yaw = yaw > 0. ? yaw - PI : yaw + PI;
		putint (&GDsec2->data[SAT_ORIENTATION_GRID], SAT_ORIENTATION_GRID_SIZE, (int)(yaw*CRD*1000.));
		putint (&GDsec2->data[SAT_NR],SAT_NR_SIZE, (int)(pout->Radius()/pout->EarthRadius()*1000000.));
		putint (&GDsec2->data[SAT_X0],SAT_X0_SIZE, (int)(x1/incx));
		putint (&GDsec2->data[SAT_Y0],SAT_Y0_SIZE, (int)(y1/incy));
		memset (&GDsec2->data[SAT_RESERVED], SAT_RESERVED_SIZE, 0);
		break;
		}
		case PROJGAUSS:
		{
		GDsec2->data = new unsigned char[SEC2_GSS_SIZE];
	grib_size += SEC2_GSS_SIZE+SEC2_FSIZE;
		putint (GDsec2->length, sizeof(GDsec2->length), SEC2_GSS_SIZE+SEC2_FSIZE);
		putint (&GDsec2->unused_bits, sizeof(GDsec2->unused_bits), 0);
		putint (&GDsec2->zero, sizeof(GDsec2->zero), 0);	
		putint (&GDsec2->data_type, sizeof(GDsec2->data_type), PROJGAUSS);
		putint (&GDsec2->data[GSS_NI], GSS_NI_SIZE, GDnx);
		putint (&GDsec2->data[GSS_NJ], GSS_NJ_SIZE, GDny);
		putint (&GDsec2->data[GSS_LA1], GSS_LA1_SIZE, (int)(y1/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[GSS_LO1], GSS_LO1_SIZE, (int)(x1/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[GSS_FLAG], GSS_FLAG_SIZE, 128);
		putint (&GDsec2->data[GSS_LA2], GSS_LA2_SIZE, (int)(y2/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[GSS_LO2], GSS_LO2_SIZE, (int)(x2/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[GSS_DI], GSS_DI_SIZE, (int)(incx/EARTH_RADIUS*CRD*1000.));
		putint (&GDsec2->data[GSS_N], GSS_N_SIZE, (int)fabs(90./incy));
		putint (&GDsec2->data[GSS_SCAN], GSS_SCAN_SIZE, 0);
		memset (&GDsec2->data[GSS_RESERVED], GSS_RESERVED_SIZE, 0);
		break;
		}
		case GENERALPROJ:
		{
		GDsec2->data = new unsigned char[SEC2_GP_SIZE];
	grib_size += SEC2_GP_SIZE+SEC2_FSIZE;
		putint (GDsec2->length, sizeof(GDsec2->length), SEC2_GP_SIZE+SEC2_FSIZE);
		putint (&GDsec2->unused_bits, sizeof(GDsec2->unused_bits), 0);
		putint (&GDsec2->zero, sizeof(GDsec2->zero), 0);	
		putint (&GDsec2->data_type, sizeof(GDsec2->data_type), GENERALPROJ);
		putint (&GDsec2->data[GP_NI], GP_NI_SIZE, GDnx);
		putint (&GDsec2->data[GP_NJ], GP_NJ_SIZE, GDny);
		putint (&GDsec2->data[GP_I1], GP_I1_SIZE, (int)(x1*100.));
		putint (&GDsec2->data[GP_J1], GP_J1_SIZE, (int)(y1*100.));
		putint (&GDsec2->data[GP_FLAG], GP_FLAG_SIZE, 128);
		putint (&GDsec2->data[GP_I2], GP_I2_SIZE, (int)(x2*100.));
		putint (&GDsec2->data[GP_J2], GP_J2_SIZE, (int)(y2*100.));
		putint (&GDsec2->data[GP_DI], GP_DI_SIZE, (int)(incx*1000.));
		putint (&GDsec2->data[GP_DJ], GP_DJ_SIZE, (int)(incy*1000.));
		putint (&GDsec2->data[GP_SCAN], GP_SCAN_SIZE, 0);
		putint (&GDsec2->data[GP_PROJ], GP_PROJ_SIZE, pout->GribInternalCode());
		int hemis = (int)pout->Hemisphere();
		putint (&GDsec2->data[GP_HEMIS], GP_HEMIS_SIZE, hemis == -1 ? 2 : hemis);
		putint (&GDsec2->data[GP_LAT0], GP_LAT0_SIZE, (int)(pout->OriginLatitude()*CRD*1000.));
		putint (&GDsec2->data[GP_LON0], GP_LON0_SIZE, (int)(pout->OriginLongitude()*CRD*1000.));
		putint (&GDsec2->data[GP_LAT1], GP_LAT1_SIZE, (int)(pout->StandardLatitudeOne()*CRD*1000.));
		putint (&GDsec2->data[GP_LAT2], GP_LAT2_SIZE, (int)(pout->StandardLatitudeTwo()*CRD*1000.));
		putint (&GDsec2->data[GP_RD], GP_RD_SIZE,(int)(pout-> EarthRadius()));
		putint (&GDsec2->data[GP_FLT], GP_FLT_SIZE, (int)(pout->EarthFlattening()));
		putint (&GDsec2->data[GP_DX], GP_DX_SIZE, (int)(pout->EllipsoidTranslationX()));;
		putint (&GDsec2->data[GP_DY], GP_DY_SIZE, (int)(pout->EllipsoidTranslationY()));;
		putint (&GDsec2->data[GP_DZ], GP_DZ_SIZE, (int)(pout->EllipsoidTranslationZ()));
		break;
		}
	}

// Initialize Section 3

	a = gd->GDsec1.flag;
	a >>= 6;
	a &= 1;
	if ( a==1 )
	{
		GDsec3 = new SECTION3;
		memcpy (&GDsec3, &gd->GDsec3, SEC3_FSIZE);
		size = getint(GDsec3->length,sizeof(GDsec3->length));
		grib_size += size;
		GDsec3->bitmap = new unsigned char[size-SEC3_FSIZE];
		memcpy (&GDsec3->bitmap, &gd->GDsec3->bitmap, (int)size-SEC3_FSIZE);
	}

// Initialize Section 4

	size_bits = size = (long)GDnx * (long)GDny * (long)getint (&gd->GDsec4.bits_number, sizeof(gd->GDsec4.bits_number));
	if (size % 8L) size = (int)(size/8L) + 1L;	// size in bytes
	else	   size = size/8L;

	size += SEC4_FSIZE;
	if (size % 2L) size++;	// even size in bytes
	
	grib_size += size;

	putint (GDsec4.length, sizeof(GDsec4.length), (int)size);
	putint (&GDsec4.unused_bits, sizeof (GDsec4.unused_bits), (int)(size*8L-(size_bits+8*SEC4_FSIZE)));
	memcpy (&GDsec4.scale, gd->GDsec4.scale, sizeof(GDsec4.scale));
	memcpy (&GDsec4.reference, gd->GDsec4.reference, sizeof(GDsec4.reference));
	memcpy (&GDsec4.bits_number, &gd->GDsec4.bits_number, sizeof(GDsec4.bits_number));

	GDzscale = gd->GDzscale;
	GDfmin   = gd->GDfmin;
	GDibsiz  = gd->GDibsiz;
	GDiskip2 = gd->GDiskip2;
	putint (GDsec0.length, sizeof(GDsec0.length), (int)grib_size+SEC5_FSIZE);
}

void
GribData :: Init (GribFile* gf, ImageDoc& id, short index)
{

	long	grib_size, size, size_bits;

// Initialize grib data variables

	GDfile = gf;
	GDindex = index;
	GDny = id.ny;
	GDnx = id.nx;
	GDresy = id.ry;
	GDresx = id.rx;

// Initialize Section 0

	memcpy(GDsec0.coded,"GRIB",4);
	grib_size = 8;
	GDsec0.ed_number = 1;

// Initialize Section 1

	size = SEC1_FSIZE + SEC1_LND_SIZE;
	putint(GDsec1.length, sizeof(GDsec1.length), (int)size);
	grib_size += size;

	putint(&GDsec1.version_number, sizeof(GDsec1.version_number), 0);
	putint(&GDsec1.id_centre, sizeof(GDsec1.id_centre), ORIGIN_CENTER);
	putint(&GDsec1.id_process, sizeof(GDsec1.id_process), 0);
	putint(&GDsec1.grid_def, sizeof(GDsec1.grid_def), 255);
	putint(&GDsec1.flag, sizeof(GDsec1.flag), 128);
	putint(&GDsec1.parameter, sizeof(GDsec1.parameter), 127);
	putint(&GDsec1.type_level, sizeof(GDsec1.type_level), id.satellite);
	putint(GDsec1.channel, sizeof(GDsec1.channel), id.band);
	putint(&GDsec1.year, sizeof(GDsec1.year), (id.year%100));
	putint(&GDsec1.month, sizeof(GDsec1.month), id.month);
	putint(&GDsec1.day, sizeof(GDsec1.day), id.day);
	putint(&GDsec1.hour, sizeof(GDsec1.hour), id.hour);
	putint(&GDsec1.minutes, sizeof(GDsec1.minutes), id.minutes);
	putint(&GDsec1.time_range_unit, sizeof(GDsec1.time_range_unit), 0);
	putint(&GDsec1.p1, sizeof(GDsec1.p1), 0);
	putint(&GDsec1.p2, sizeof(GDsec1.p2), 0);
	putint(&GDsec1.time_range_ind, sizeof(GDsec1.time_range_ind), 0);
	putint(GDsec1.number_fields, sizeof(GDsec1.number_fields), 0);
	putint(&GDsec1.missing_fields, sizeof(GDsec1.missing_fields), 0);
	putint(&GDsec1.century, sizeof(GDsec1.century), ((int)(id.year/100.+1.)));

	GDsec1.documentation_file = new unsigned char[size-SEC1_FSIZE];
	putint(&GDsec1.documentation_file[SEC1_LND_PATH], SEC1_LND_PATH_SIZE, id.path);
	putint(&GDsec1.documentation_file[SEC1_LND_ROW], SEC1_LND_ROW_SIZE, id.row);
	putint(&GDsec1.documentation_file[SEC1_LND_PLEVEL], SEC1_LND_PLEVEL_SIZE, id.plevel);
	putint(&GDsec1.documentation_file[SEC1_LND_RESERVED], SEC1_LND_RESERVED_SIZE, 0);

// Initialize Section 2
	
	GDsec2 = new SECTION2;
	GDsec2->data = new unsigned char[SEC2_POL_SIZE];
	grib_size += SEC2_POL_SIZE+SEC2_FSIZE;
	putint (GDsec2->length, sizeof(GDsec2->length), SEC2_POL_SIZE+SEC2_FSIZE);
	putint (&GDsec2->unused_bits, sizeof(GDsec2->unused_bits), 0);
	putint (&GDsec2->zero, sizeof(GDsec2->zero), 0);	
	putint (&GDsec2->data_type, sizeof(GDsec2->data_type), PROJPOLY);
	putint (&GDsec2->data[POL_NX], POL_NX_SIZE, id.nx);
	putint (&GDsec2->data[POL_NY], POL_NY_SIZE, id.ny);
	putint (&GDsec2->data[POL_LAP], POL_LAP_SIZE, (int)(id.lap*CRD*1000.));
	putint (&GDsec2->data[POL_LOP], POL_LOP_SIZE, (int)(id.lop*CRD*1000.));
	putint (&GDsec2->data[POL_FLAG], POL_FLAG_SIZE, 128);
	putint (&GDsec2->data[POL_RX], POL_RX_SIZE, (int)(id.rx*100.));
	putint (&GDsec2->data[POL_RY], POL_RY_SIZE, (int)(id.ry*100.));
	putint (&GDsec2->data[POL_XP], POL_XP_SIZE, (int)id.xp);
	putint (&GDsec2->data[POL_YP], POL_YP_SIZE, (int)id.yp);
	putint (&GDsec2->data[POL_ORIENTATION_GRID], POL_ORIENTATION_GRID_SIZE, (int)(id.yaw*CRD*1000.));
	putint (&GDsec2->data[POL_NR],POL_NR_SIZE, (int)(id.nr/EARTH_RADIUS*1000000.));
	putint (&GDsec2->data[POL_X0],POL_X0_SIZE, (int)id.x0);
	putint (&GDsec2->data[POL_Y0],POL_Y0_SIZE, (int)id.y0);
	putint (&GDsec2->data[POL_DEGREE],POL_DEGREE_SIZE, 0);
	putint (&GDsec2->data[POL_NCP],POL_NCP_SIZE, 0);
	memset (&GDsec2->data[POL_RESERVED], POL_RESERVED_SIZE, 0);

// Initialize Section 3 (empty)

// Initialize Section 4

	size_bits = size = (long)id.nx * (long)id.ny;
	size += SEC4_FSIZE;
	if (size % 2L) size++;	// even size in bytes
	grib_size += size;
	putint (GDsec4.length, sizeof(GDsec4.length), (int)size);
	putint (&GDsec4.unused_bits, sizeof (GDsec4.unused_bits), (int)(size*8L-(size_bits*8L+8*SEC4_FSIZE)));
	putint (GDsec4.scale, sizeof(GDsec4.scale), 0);
	putint (GDsec4.reference, sizeof(GDsec4.reference), 0);
	putint (&GDsec4.bits_number, sizeof(GDsec4.bits_number), 8);
	GDzscale = GDfmin = 0.;
	GDibsiz  = 8;
	GDiskip2 = 0;
	putint (GDsec0.length, sizeof(GDsec0.length), (int)grib_size+SEC5_FSIZE);

	return;
}

void
GribData :: Init (GribFile* gf, GridDoc& id, short index, SProjection* sp)
{

	long	grib_size, size, size_bits;
	float	y1, y2, x1, x2, incx, incy;

// Initialize grib data variables

	GDfile = gf;
	GDindex = index;
	GDny = id.ny;
	GDnx = id.nx;

	x1 = id.xmin;
	x2 = id.xmax;
	y1  = id.ymax;		// max is at the upper edge
	y2  = id.ymin;		// min is at the bottom edge

	incy = (y2 - y1)/(float)(GDny-1);
	incx = (x2 - x1)/(float)(GDnx-1);

	GDresy = fabs(incy);
	GDresx = fabs(incx);

// Initialize Section 0

	memcpy(GDsec0.coded,"GRIB",4);
	grib_size = 8;
	GDsec0.ed_number = 1;

// Initialize Section 1

	size = SEC1_FSIZE + SEC1_LND_SIZE;
	putint(GDsec1.length, sizeof(GDsec1.length), (int)size);
	grib_size += size;

	putint(&GDsec1.version_number, sizeof(GDsec1.version_number), 0);
	putint(&GDsec1.id_centre, sizeof(GDsec1.id_centre), ORIGIN_CENTER);
	putint(&GDsec1.id_process, sizeof(GDsec1.id_process), 0);
	putint(&GDsec1.grid_def, sizeof(GDsec1.grid_def), 255);
	putint(&GDsec1.flag, sizeof(GDsec1.flag), 128);
	putint(&GDsec1.parameter, sizeof(GDsec1.parameter), 127);
//	putint(&GDsec1.type_level, sizeof(GDsec1.type_level), id.satellite);
//	putint(GDsec1.channel, sizeof(GDsec1.channel), id.band);
	putint(&GDsec1.year, sizeof(GDsec1.year), (id.year%100));
	putint(&GDsec1.month, sizeof(GDsec1.month), id.month);
	putint(&GDsec1.day, sizeof(GDsec1.day), id.day);
	putint(&GDsec1.hour, sizeof(GDsec1.hour), id.hour);
	putint(&GDsec1.minutes, sizeof(GDsec1.minutes), id.minutes);
	putint(&GDsec1.time_range_unit, sizeof(GDsec1.time_range_unit), 0);
	putint(&GDsec1.p1, sizeof(GDsec1.p1), 0);
	putint(&GDsec1.p2, sizeof(GDsec1.p2), 0);
	putint(&GDsec1.time_range_ind, sizeof(GDsec1.time_range_ind), 0);
	putint(GDsec1.number_fields, sizeof(GDsec1.number_fields), 0);
	putint(&GDsec1.missing_fields, sizeof(GDsec1.missing_fields), 0);
	putint(&GDsec1.century, sizeof(GDsec1.century), ((int)(id.year/100.+1.)));

	GDsec1.documentation_file = new unsigned char[size-SEC1_FSIZE];
//	putint(&GDsec1.documentation_file[SEC1_LND_PATH], SEC1_LND_PATH_SIZE, id.path);
	putint(&GDsec1.documentation_file[SEC1_LND_ROW], SEC1_LND_ROW_SIZE, id.row);
	putint(&GDsec1.documentation_file[SEC1_LND_PLEVEL], SEC1_LND_PLEVEL_SIZE, id.plevel);
	putint(&GDsec1.documentation_file[SEC1_LND_RESERVED], SEC1_LND_RESERVED_SIZE, 0);

// Initialize Section 2
	
	GDsec2 = new SECTION2;
	GDsec2->data = new unsigned char[SEC2_GP_SIZE];
	grib_size += SEC2_GP_SIZE+SEC2_FSIZE;
	putint (GDsec2->length, sizeof(GDsec2->length), SEC2_GP_SIZE+SEC2_FSIZE);
	putint (&GDsec2->unused_bits, sizeof(GDsec2->unused_bits), 0);
	putint (&GDsec2->zero, sizeof(GDsec2->zero), 0);	
	putint (&GDsec2->data_type, sizeof(GDsec2->data_type), GENERALPROJ);
	putint (&GDsec2->data[GP_NI], GP_NI_SIZE, GDnx);
	putint (&GDsec2->data[GP_NJ], GP_NJ_SIZE, GDny);
	putint (&GDsec2->data[GP_I1], GP_I1_SIZE, (int)(x1*100.));
	putint (&GDsec2->data[GP_J1], GP_J1_SIZE, (int)(y1*100.));
	putint (&GDsec2->data[GP_FLAG], GP_FLAG_SIZE, 128);
	putint (&GDsec2->data[GP_I2], GP_I2_SIZE, (int)(x2*100.));
	putint (&GDsec2->data[GP_J2], GP_J2_SIZE, (int)(y2*100.));
	putint (&GDsec2->data[GP_DI], GP_DI_SIZE, (int)(incx*1000.));
	putint (&GDsec2->data[GP_DJ], GP_DJ_SIZE, (int)(incy*1000.));
	putint (&GDsec2->data[GP_SCAN], GP_SCAN_SIZE, 0);
	putint (&GDsec2->data[GP_PROJ], GP_PROJ_SIZE, sp->GribInternalCode());
	int hemis = (int)sp->Hemisphere();
	putint (&GDsec2->data[GP_HEMIS], GP_HEMIS_SIZE, hemis == -1 ? 2 : hemis);
	putint (&GDsec2->data[GP_LAT0], GP_LAT0_SIZE, (int)(sp->OriginLatitude()*CRD*1000.));
	putint (&GDsec2->data[GP_LON0], GP_LON0_SIZE, (int)(sp->OriginLongitude()*CRD*1000.));
	putint (&GDsec2->data[GP_LAT1], GP_LAT1_SIZE, (int)(sp->StandardLatitudeOne()*CRD*1000.));
	putint (&GDsec2->data[GP_LAT2], GP_LAT2_SIZE, (int)(sp->StandardLatitudeTwo()*CRD*1000.));
	putint (&GDsec2->data[GP_RD], GP_RD_SIZE,(int)(sp-> EarthRadius()));
	putint (&GDsec2->data[GP_FLT], GP_FLT_SIZE, (int)(sp->EarthFlattening()));
	putint (&GDsec2->data[GP_DX], GP_DX_SIZE, (int)(sp->EllipsoidTranslationX()));;
	putint (&GDsec2->data[GP_DY], GP_DY_SIZE, (int)(sp->EllipsoidTranslationY()));;
	putint (&GDsec2->data[GP_DZ], GP_DZ_SIZE, (int)(sp->EllipsoidTranslationZ()));

// Initialize Section 3 (empty)

// Initialize Section 4

	size_bits = size = (long)id.nx * (long)id.ny;
	size += SEC4_FSIZE;
	if (size % 2L) size++;	// even size in bytes
	grib_size += size;
	putint (GDsec4.length, sizeof(GDsec4.length), (int)size);
	putint (&GDsec4.unused_bits, sizeof (GDsec4.unused_bits), (int)(size*8L-(size_bits*8L+8*SEC4_FSIZE)));
	putint (GDsec4.scale, sizeof(GDsec4.scale), 0);
	putint (GDsec4.reference, sizeof(GDsec4.reference), 0);
	putint (&GDsec4.bits_number, sizeof(GDsec4.bits_number), 8);
	GDzscale = GDfmin = 0.;
	GDibsiz  = 8;
	GDiskip2 = 0;
	putint (GDsec0.length, sizeof(GDsec0.length), (int)grib_size+SEC5_FSIZE);

	return;
}

short
GribData :: SkipData ()
{
	long offset;
	offset = (long)(getint (GDsec4.length,sizeof(GDsec4.length)) - SEC4_FSIZE);
	if (offset < 0) offset = 0;
	return GDfile->Skip (offset);
}

short
GribData :: Get (short linha, unsigned char* buf)
{
	long	point_arq;
	long	nbytes = GDibsiz/8;

	point_arq = GDpos_inicio_dados + (long)linha*(long)GDnx * nbytes;

	if(!GDfile->Skip (point_arq - GDfile->Pos())) return FALSE;
	return (GDfile->Read ((char*)buf, (int)(GDnx*nbytes)));
}

short
GribData :: Get (short linha, float* buff)
{
	long	pointa, pointb1, pointb2;
	unsigned char*	bufc;
	int	nnx;

	bufc = new unsigned char[GDnx*GDibsiz/8+2];

// Point to byte, related to beginning of Grib Data, containing first bit of desired element

	pointb1 = linha*GDnx*GDibsiz;
	pointa = GDpos_inicio_dados + pointb1/8;

// Evaluate number of bytes to be read

	pointb2 = (linha+1)*GDnx*GDibsiz - 1;
	nnx = (int) ((pointb2 / 8 + 1) - (pointb1 / 8));

	if(!GDfile->Skip (pointa - GDfile->Pos())) return FALSE;
	if(GDfile->Read ((char*)bufc, nnx) == FALSE) return FALSE;

	short	nbyte, nbit;

	GDiskip2 = 0;		//DEVE SER AVALIADO ALHURES

// Point to first bit of data in buffer bufc

	pointb1 = pointb1 % 8;

	unsigned long	fff;
	short	nelem;		// index for each element in buff

	if (GDtranslated)
	{
		for (nelem = 0; nelem < GDnx ; nelem++)
		{
			nbyte = (short) (pointb1 / 8);
			nbit  = (short) (pointb1 % 8);
			fff =  (float)ExtractBitField (nbit, GDibsiz, &bufc[nbyte]);
			GDbuf [nelem] = GDfmin + fff* GDzscale;
			pointb1 += ( GDibsiz + GDiskip2 );

		}
		for (nelem = 0; nelem < GDnewnx ; nelem++)
			if (GDlookup[nelem]==-1)
				buff[nelem] = MAXFLOAT;
			else
				buff[nelem] = GDbuf[GDlookup[nelem]];
	}
	else
	{
		for (nelem = 0; nelem < GDnx ; nelem++)
		{
			nbyte = (short) (pointb1 / 8);
			nbit  = (short) (pointb1 % 8);
			fff =  ExtractBitField (nbit, GDibsiz, &bufc[nbyte]);
			buff [nelem] = GDfmin + (double)fff* GDzscale;
			pointb1 += ( GDibsiz + GDiskip2 );

		}
	}
	return TRUE;
}

short
GribData :: Get(short line, unsigned char* buf, short* window)
{
	long 	initialfp;	// file pointer to the first pixel to be read
	long	nbytes = GDibsiz/8;

// Compute file pointer to the first pixel to be read

	initialfp = GDpos_inicio_dados + (window[2]+line) * (long)GDnx * nbytes + (long)window[0] * nbytes;
	if(!GDfile->Skip (initialfp - GDfile->Pos())) return FALSE;

// Read line data on gribdata
	return (GDfile->Read ((char*)buf, (int)(window[1]*nbytes)));
}

short
GribData :: Get (short* ret_env, short nedge, float fatx, float faty, unsigned char** bufi)
{
	unsigned char	buf[BUF_IMA];	// image buffer

	short	i,j,xi,xf,yi,yf,lin,	// auxiliary variables
		ii,jj,lin_f,nc,nl,yy,
		ncj,			/* numero de colunas da jan. do retang. envolv. */
		retae[4],		/* retangulo envolvente da imagem (real)	*/
		janret[4],		/* retangulo envolvente da imagem (existente) 	*/
		tam_bec,		// maximum column number of image
		coli;			/* linha e coluna inicial da imagem amostrada	*/

	float	xx;			// auxiliary variable

	memcpy (retae,ret_env,sizeof(retae));
	tam_bec = retae[1] + nedge + nedge;

	/* adiciona ao retangulo envolvente nedge linhas e colunas anterior e posterior a imagem. Caso nao seja possivel, repete pontos existentes */
	if (retae[0] > GDnx-1)		// retangulo contido apos a imagem 
	{
		janret[0] = janret[1] = 0;
		xi = xf = 0;
	}
	else
	{
		if ((retae[0]-nedge) >= 0) 	 // ponto inicial contido na imagem
		{
			retae[0] -= nedge;
			retae[1] += nedge;
			janret[0] = retae[0];
			xi = 0;
		}
		else		   	// ponto inicial antes/inicio da imagem
		{
			janret[0] = max(0,retae[0]);
			xi = nedge;	// vai repetir 1. ponto
		}

		if ((retae[0]+retae[1]-1+nedge) <= (GDnx-1))	/* ponto final antes do final da imagem */
		{
			retae[1] += nedge;
			if (xi == 0) janret[1] = retae[1];
			else	     janret[1] = max(0,retae[0]+retae[1]-janret[0]);
			xf = 0;
		}
		else
		{
			janret[1] = min(GDnx-janret[0],retae[0]+retae[1]-1);
			xf = nedge;	// vai repetir ultimo ponto
		}
	}

	if (retae[2] > (GDny-1))	// retangulo contido apos a imagem
	{
		janret[2] = janret[3] = 0;
		yi = yf = 0;
	}
	else
	{
		if ((retae[2]-nedge) >= 0)	// linha inicial contido na imagem
		{
			retae[2] -= nedge;
			retae[3] += nedge;
			janret[2] = retae[2];
			yi = 0;
		}
		else			// linha inicial antes da imagem
		{
			janret[2] = max(0,retae[2]);
			yi = nedge;		// vai repetir 1. linha
		}

		if ((retae[2]+retae[3]-1+nedge) <= (GDny-1))  // linha final antes do final da imagem
		{
			retae[3] += nedge;
			if (yi == 0) janret[3] = retae[3];
			else	     janret[3] = max(0,retae[2]+retae[3]-janret[2]);
			yf = 0;
		}
		else
		{
			janret[3] = min(GDny-janret[2],retae[2]+retae[3]-1);
			yf = nedge;		// vai repetir ultima linha
		}
	}
	 
	/* calcula numero de linhas e colunas somado 0.5 para eliminar truncamento */
	nc = (int)((float)retae[1]/fatx + 0.5);
	ncj= (int)((float)janret[1]/fatx + 0.5);		
	nl = (int)((float)retae[3]/faty + 0.5);

	/* abre imagem */
	if (GDfile->Open() == FALSE) return FALSE;

	/* adquire imagem do retangulo envolvente */
	lin=-1;

	/* calcula pixel inicial da imagem, levando em consideracao a amostragem
	   do inicio da imagem (nao considerando a parte em preto) */
	jj = (short)((janret[0] - retae[0] + xi)/fatx);
	if(((short)(janret[0] - retae[0] + xi)%(int)fatx) != 0) jj++;
	coli = (short)(fatx - (jj%(int)fatx)-1);

	lin_f = janret[2] + janret[3] - 1;

	/* calcula linha inicial da imagem, levando em consideracao a amostragem
	   do inicio da imagem */
	ii = (int)((janret[2] - retae[2] + yi)/faty);
	if(((int)(janret[2] - retae[2] + yi) % (int)faty) != 0) ii++;

	/* verifica se eh fim da imagem */
	if( ii < 0 )
	{
		/* preenche as linhas finais da imagem de saida com 0 */
		for(i = 0; i < nl; i++)
			for (j = 0; j < tam_bec; j++) bufi[i][j] = 0;
	}
	else
	{	
		/* inicializa as linhas (negativas) com 0) */
		for(i = 0; i < ii; i++)
			for (j = 0; j < tam_bec; j++) bufi[i][j] = 0;
	
		for (i = ii; i < nl; i++)
		{
			for (j = 0; j < tam_bec; j++) bufi[i][j] = 0;
		   	if((retae[2]+i*faty) <= lin_f)
			{
				xx = coli;
				lin++;
				yy = (short)((float)lin * faty);
				j = Get(yy,buf,janret);    /* nao testo erro propositalmente */
				for (j = 0; j < ncj-coli; j++)					/* SSI01*/
				{
					bufi[i][jj+j] = buf[(int)xx];				/* SSI01 */
					xx += fatx;						/* SSI01 */
				}
				for (j = 0; j < xi; j++)
					bufi[i][jj-j-1] = bufi[i][jj];
				for (j = 0; j < xf; j++)
					bufi[i][xi+nc+j]   = bufi[i][xi+nc-1];				
			}
		}
	}

	/* verifica se carregou as linhas da borda da imagem */
	for (j = 0; j < yi; j++)
		for (i = 0; i < tam_bec; i++) bufi[j][i] = bufi[yi][i];

	for (j = 0; j < yf; j++)
		for (i = 0; i < tam_bec; i++) bufi[yi+nl+j][i] = bufi[yi+nl-1][i];
	
	/* fecha imagem */
	GDfile->Close();
	return TRUE;
}

short
GribData :: Put (short linha, unsigned char* buf)
{
	long	point_arq;
	long	nbytes = GDibsiz/8;

	point_arq = GDpos_inicio_dados + (long)linha*(long)GDnx * nbytes;

	if(!GDfile->Skip (point_arq - GDfile->Pos())) return FALSE;
	if(GDfile->Write ((char*)buf, (int)(GDnx*nbytes)) == FALSE) return FALSE;
	return TRUE;
}

short
GribData :: Put (short linha, float* buff)
{
	long	pointa, pointb1, pointb2;
	unsigned char*	bufc;
	int	nnx;

	bufc = new unsigned char[GDnx*GDibsiz/8+2];

// Point to byte, related to beginning of Grib Data, containing first bit of desired element

	pointb1 = ((long)linha)*GDnx*GDibsiz;
	pointa = GDpos_inicio_dados + pointb1/8;

// Evaluate number of bytes to be read

	pointb2 = ((long)(linha+1))*GDnx*GDibsiz - 1;
	nnx = (int)((pointb2 / 8 + 1) - (pointb1 / 8));

	if(!GDfile->Skip (pointa - GDfile->Pos())) return FALSE;

	short	nbyte, nbit;

	GDiskip2 = 0;		//DEVE SER AVALIADO ALHURES

// Point to first bit of data in buffer bufc

	pointb1 = pointb1 % 8;

	unsigned long	fff;		// coded value to be inserted
	short	nelem;		// index for each element in buff

	for (nelem = 0; nelem < GDnx ; nelem++)
	{
		nbyte = (short) (pointb1 / 8);
		nbit  = (short) (pointb1 % 8);
		fff = ((double)buff[nelem] - GDfmin) / GDzscale;
		InsertBitField (fff, nbit, GDibsiz, &bufc[nbyte]);
		pointb1 += ( GDibsiz + GDiskip2 );

	}
	if(GDfile->Write ((char*)bufc, nnx) == FALSE) return FALSE;

	return TRUE;
}

short
GribData :: Put(short line, unsigned char* buf, short* window)
{
	long 	initialfp;	// file pointer to the first pixel to be write
	long	nbytes = GDibsiz/8;

// Compute file pointer to the first pixel to be write

	initialfp = GDpos_inicio_dados + (window[2]+line) * (long)GDnx * nbytes + (long)window[0] * nbytes;
	if(!GDfile->Skip (initialfp - GDfile->Pos())) return FALSE;

// Write line data on gribdata
	if(GDfile->Write ((char*)buf, (int)(window[1]*nbytes)) == FALSE) return FALSE;
	return TRUE;
}

short
GribData :: Put(short lb, short cb, unsigned char** bufs, short nlin, short ncol)
{
	short	i,janela[4];

	/* adquire janela de escrita */
	janela[0] = cb;
	janela[2] = lb;
	if ((cb+ncol) <= GDnx) janela[1] = ncol;
	else		       janela[1] = GDnx - cb;
	if ((lb+nlin) <= GDny) janela[3] = nlin;
	else		       janela[3] = GDny - lb;

	/* escreve bloco na imagem */
	for (i = 0; i < janela[3]; i++)
	{
		if (Put (i,bufs[i],janela) == FALSE) return FALSE;
	}

	return TRUE;
}

short
GribData :: Put ()
{

	unsigned char 	a;
	short 		status=TRUE;
	int 		size;

// Write SECTION 0
	if (!GDfile->Write ((char*)&GDsec0,SEC0_FSIZE)) return FALSE;

// Write SECTION 1

	size = getint(GDsec1.length,sizeof(GDsec1.length));
	if (size > SEC1_FSIZE)
	{
		if (!GDfile->Write ((char*)&GDsec1, SEC1_FSIZE)) return FALSE;
		if (!GDfile->Write ((char*)GDsec1.documentation_file,size-SEC1_FSIZE)) return FALSE;
	}
	else if (!GDfile->Write ((char*)&GDsec1, size)) return FALSE;

// Write SECTION 2

	a = GDsec1.flag;
	a >>= 7;
	if ( a==1 )
	{
		if (!GDfile->Write ((char*)GDsec2, SEC2_FSIZE)) return FALSE;
		size = getint (GDsec2->length,sizeof(GDsec2->length));
		if (!GDfile->Write ((char*)GDsec2->data, size-SEC2_FSIZE)) return FALSE;
	}


// Write SECTION3

	a = GDsec1.flag;
	a >>= 6;
	a &= 1;
	if ( a==1 )
	{
		if (!GDfile->Write ((char*)GDsec3, SEC3_FSIZE)) return FALSE;
		size = getint(GDsec3->length,sizeof(GDsec3->length));
		if (!GDfile->Write ((char*)GDsec3->bitmap, size-SEC3_FSIZE)) return FALSE;
	}

// Write SECTION4

	if (!GDfile->Write ((char*)&GDsec4, SEC4_FSIZE)) return FALSE;
	size = getint(GDsec4.length,sizeof(GDsec4.length))-SEC4_FSIZE;

	GDpos_inicio_dados = GDfile->Pos ();
	unsigned char *buf;
	buf = new unsigned char[1024];
	memset(buf,0,1024);
	for (;size > 1024; size-=1024)
		if ((status=GDfile->Write ((char*)buf, 1024)) == FALSE) break;

	if (status) status = GDfile->Write ((char*)buf, size);
	delete buf;
	if (!status) return FALSE;

// Write SECTION 5

	memcpy (GDsec5.end,"7777",4);
	if (!GDfile->Write ((char*)&GDsec5, SEC5_FSIZE)) return FALSE;
	GDfile->Flush();

	return TRUE;
}

short
GribData :: PutSection2 ()
{

	unsigned char 	a;
	int 		size;

	GDfile->Seek (GDindex);

// Skip SECTION 0
	if (GDsec0.ed_number == 1) GDfile->Skip(4);

// Skip SECTION 1
	size = getint(GDsec1.length,sizeof(GDsec1.length));
	GDfile->Skip(size);


// Write SECTION 2

	a = GDsec1.flag;
	a >>= 7;
	if ( a==1 )
	{
		if (!GDfile->Write ((char*)GDsec2, SEC2_FSIZE)) return FALSE;
		size = getint (GDsec2->length,sizeof(GDsec2->length));
		if (!GDfile->Write ((char*)GDsec2->data, size-SEC2_FSIZE)) return FALSE;
	}
	return TRUE;
}

void
GribData :: SetGribDataLength()
{
	int	size;
	int	size_sec4;

	size_sec4 = (int)GDnx * GDny * GDibsiz/8 + SEC4_FSIZE;
	if( size_sec4 % 2 ){
		Set_Section4_unused_bits( (short) 8 );
		size_sec4++;
	}
	Set_Section4_length( size_sec4 );
	size  = SEC0_FSIZE;
	size += Get_Section1_length();
	size += ( GDsec1.flag & 128 ) ? ( Get_Section2_length() ) : ( 0 );
	size += ( GDsec1.flag &  64 ) ? ( Get_Section3_length() ) : ( 0 );
	size += Get_Section4_length();
	size += SEC5_FSIZE;
	Set_Section0_length( size );
}

short
GribData :: SetTranslation (Box& b, short& nx)
{
	Box	box;
	Point	p1,p2;
	float	x1, x2;
	float	xx1,xx2;
	float	incx;
	int	i,j;
	float	x;

	if (GDtranslated) return TRUE;

	if (ProjectionCode () == PROJCYLINDRICAL)
	{
		Rectangle (box);
		p1 = box.lowerLeft ();
		p2 = box.upperRight ();

		// converts values from meters to radians
		x1 = p1.x () / EARTH_RADIUS;
		x2 = p2.x () / EARTH_RADIUS;

		incx = GDresx / EARTH_RADIUS;

		if (x1 > PI)	// left and right corner are above 180 degrees
		{
			xx1 = x1 - 2.*PI;
			xx2 = x2 - 2.*PI;
			GDtranslated = TRUE;
		}
		else if (x2 > PI)	// right corner is above 180 degrees
		{
			x = x1;
			for (i=0; i<GDnx; i++)
			{
				x += incx;
				if ( x > PI)
				{
					xx2 = (float)(i) * incx;
					xx1 = x - 2.*PI;
					GDtranslated = TRUE;
					break;
				}
			}
		}

		if (GDtranslated)
		{
			// update box in meters
			p1.x (xx1 * EARTH_RADIUS);
			p2.x (xx2 * EARTH_RADIUS);

			b.init (p1,p2);

// Evaluate the number of lines that may have changed

			GDnewnx = nx = (short)((xx2 - xx1) / incx + 0.5) + 1;

// Evaluate the lookup table to arrange the elements on buffer

			GDlookup = new short[nx];
			GDbuf = new float[GDnx];

			for (i=0; i<nx; i++)
			{
				x = xx1 + (float)i * incx;
				if (x<0) x = 2.*PI + x;
				j = (short)((x - x1) / incx);
				if (j<0 || j>=GDnx)
					GDlookup[i] = -1;
				else
					GDlookup[i] = j;
			}

			return TRUE;
		}

	}
	return FALSE;
}

short
GribData :: ProjectionCode ()
{
//<*deleteme*>	if (GDsec1.grid_def == 255)
	if (1)
	{
		switch (GDsec2->data_type)
		{
			case PROJCYLINDRICAL    : return PROJCYLINDRICAL;
			case PROJSATELLITE	    : return PROJSATELLITE;
			case PROJPOLY	    :
			{
				int i = getint (&GDsec2->data[POL_NCP], POL_NCP_SIZE);
				if (i<=0) return PROJNONE;
				return PROJPOLY;
			}
			case GENERALPROJ    : return getint(&GDsec2->data[GP_PROJ], GP_PROJ_SIZE);
			case PROJGAUSS	    : return PROJGAUSS;

			default : return -1;
				
		}
	}
	else return -1;
}

void
GribData :: Rectangle (Box& brect)
{
	Point	ll, ur;
	double	ksin, k;

	if (GDsec1.grid_def == 255)
	{
		switch (GDsec2->data_type)
		{
			case PROJCYLINDRICAL:
			{
			ll.x((float)getint (&GDsec2->data[CYL_LO1],CYL_LO1_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			ur.x((float)getint (&GDsec2->data[CYL_LO2],CYL_LO2_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			ll.y((float)getint (&GDsec2->data[CYL_LA2],CYL_LA2_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			ur.y((float)getint (&GDsec2->data[CYL_LA1],CYL_LA1_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			break;
			}
			case PROJSATELLITE:
			{
//<*teste*>			ll.x((float)OriginX()*GDresx);
//<*teste*>			ur.x((float)(OriginX()+GDnx-1)*GDresx);
//<*teste*>			ur.y(-(float)OriginY()*GDresy);
//<*teste*>			ll.y(-(float)(OriginY()+GDny-1)*GDresy);

			k = AltitudeSat() - 1.;
			ksin = asin ((double)1./AltitudeSat());
			ll.x((OriginX() * atan(tan(2.*ksin/ApparentDiaX()) * k) * EARTH_RADIUS));
			ur.x((float)((OriginX() + NumCol() - 1) *
				atan(tan(2*ksin/ApparentDiaX()) * k) * EARTH_RADIUS));
			ll.y(-(float)((OriginY() + NumLin() - 1) *
				atan(tan(2*ksin/ApparentDiaY())*k)*EARTH_RADIUS));
			ur.y(-(float)(OriginY() * atan(tan(2*ksin/ApparentDiaY()) * k) * EARTH_RADIUS));
			break;
			}
			case PROJGAUSS:
			{
			ll.x((float)getint (&GDsec2->data[GSS_LO1],GSS_LO1_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			ur.x((float)getint (&GDsec2->data[GSS_LO2],GSS_LO2_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			ll.y((float)getint (&GDsec2->data[GSS_LA2],GSS_LA2_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			ur.y((float)getint (&GDsec2->data[GSS_LA1],GSS_LA1_SIZE)*0.001*PI/180.*EARTH_RADIUS);
			break;
			}
			case GENERALPROJ:
			{
			ll.x((float)getint (&GDsec2->data[GP_I1],GP_I1_SIZE)*0.01);
			ur.x((float)getint (&GDsec2->data[GP_I2],GP_I2_SIZE)*0.01);
			ll.y((float)getint (&GDsec2->data[GP_J2],GP_J2_SIZE)*0.01);
			ur.y((float)getint (&GDsec2->data[GP_J1],GP_J1_SIZE)*0.01);
			break;
			}
			case PROJPOLY:
			{
			ll.x((float)getint (&GDsec2->data[POL_X0],POL_X0_SIZE)*GDresx);
			ur.x((float)(getint (&GDsec2->data[POL_X0],POL_X0_SIZE)+GDnx-1)*GDresx);
			ll.y(-(float)(getint (&GDsec2->data[POL_Y0],POL_Y0_SIZE)+GDny-1)*GDresy);
			ur.y(-(float)getint (&GDsec2->data[POL_Y0],POL_Y0_SIZE)*GDresy);
			break;
			}
		}
	}
	else
	{
		// Coordinates for all the earth
		ll.x(-180.*PI/180.*EARTH_RADIUS);
		ur.x(+180.*PI/180.*EARTH_RADIUS);
		ur.y(+90.*PI/180.*EARTH_RADIUS);
		ll.y(-90.*PI/180.*EARTH_RADIUS);
	}
	brect.init(ll,ur);
}

short
GribData :: SubPointCoordX()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (short)getint(&GDsec2->data[SAT_XP],SAT_XP_SIZE);
		case PROJPOLY : return (short)getint(&GDsec2->data[POL_XP],POL_XP_SIZE);
	}
return 0;
}

short
GribData :: SubPointCoordY()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (short)getint(&GDsec2->data[SAT_YP],SAT_YP_SIZE);
		case PROJPOLY : return (short)getint(&GDsec2->data[POL_YP],POL_YP_SIZE);
	}
return 0;
}

short
GribData :: OriginY()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (short)getint(&GDsec2->data[SAT_Y0],SAT_Y0_SIZE);
		case PROJPOLY : return (short)getint(&GDsec2->data[POL_Y0],POL_Y0_SIZE);
	}
return 0;
}

short
GribData :: OriginX()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (short)getint(&GDsec2->data[SAT_X0],SAT_X0_SIZE);
		case PROJPOLY : return (short)getint(&GDsec2->data[POL_X0],POL_X0_SIZE);
	}
return 0;
}

double
GribData :: AltitudeSat()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (float)getint(&GDsec2->data[SAT_NR],SAT_NR_SIZE)*0.000001;
		case PROJPOLY : return (float)getint(&GDsec2->data[POL_NR],POL_NR_SIZE)*0.000001;
	}
return 0.;
}

double
GribData :: SubPointLat()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (float)getint(&GDsec2->data[SAT_LAP],SAT_LAP_SIZE)*0.001*CDR;
		case PROJPOLY : return (float)getint(&GDsec2->data[POL_LAP],POL_LAP_SIZE)*0.001*CDR;
	}
return 0.;
}

double
GribData :: SubPointLng()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (float)getint(&GDsec2->data[SAT_LOP],SAT_LOP_SIZE)*0.001*CDR;
		case PROJPOLY : return (float)getint(&GDsec2->data[POL_LOP],POL_LOP_SIZE)*0.001*CDR;
	}
return 0.;
}

double
GribData :: Yaw()
{
	switch (GDsec2->data_type)
	{
		case PROJSATELLITE : return (float)getint(&GDsec2->data[SAT_ORIENTATION_GRID],SAT_ORIENTATION_GRID_SIZE)*0.001*CDR;
		case PROJPOLY : return (float)getint(&GDsec2->data[POL_ORIENTATION_GRID],POL_ORIENTATION_GRID_SIZE)*0.001*CDR;
	}
return 0.;
}

short
GribData :: Hemisphere()
{
	if (GDsec2->data_type == GENERALPROJ)
	{
		int hemis = getint(&GDsec2->data[GP_HEMIS], GP_HEMIS_SIZE);
		return (hemis == 2 ? -1 : hemis);
	}
	else	return 0;
}

double
GribData :: OriginParallel()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_LAT0], GP_LAT0_SIZE)*CDR*0.001;
	else	return 0.;
}

double
GribData :: OriginMeridian()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_LON0], GP_LON0_SIZE)*CDR*0.001;
	else	return 0.;
}

double
GribData :: StandardParallelOne()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_LAT1], GP_LAT1_SIZE)*CDR*0.001;
	else	return 0.;
}

double
GribData :: StandardParallelTwo()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_LAT2], GP_LAT2_SIZE)*CDR*0.001;
	else	return 0.;
}

double
GribData :: Flattening()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_FLT], GP_FLT_SIZE)*0.00000001;
	else	return FLATTENING;
}

double
GribData :: EarthRadius()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_RD], GP_RD_SIZE);
	else	return EARTH_RADIUS;
}

double
GribData :: TranslationX()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_DX], GP_DX_SIZE);
	else	return 0.;
}

double
GribData :: TranslationY()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_DY], GP_DY_SIZE);
	else	return 0.;
}

double
GribData :: TranslationZ()
{
	if (GDsec2->data_type == GENERALPROJ)
		return (double)getint(&GDsec2->data[GP_DZ], GP_DZ_SIZE);
	else	return 0.;
}

void
GribData ::	SetReference ( double vmin)
{
	double	x;
	long	n, C, B;
	int	sb = 0;

	memset (GDsec4.reference, '\0', 4);

	GDfmin = vmin;
	
	if (vmin < 0.)
	{
		sb = 1;
		vmin = - vmin;
	}

	if (vmin == 0.)
		putint(GDsec4.reference, sizeof(GDsec4.reference), 0);
	else
	{
		n = (long) (log(vmin) * (1. / log((double)16.)) + 1. + 1.0E-8);

		x = (double) (vmin / pow((double)16., (double)n));

		B = (long) (x / pow(2., (-24)));

		C = 64 + n;

//	The reference value (r) is stored in 4 octets as a floating point number,
//	the high order bit is a sign bit (sb), 0 indicating positive, 1 negative;
//	the next seven bits are the characteristic (C) and the low order 24 bits
//	the mantissa (B) : r = (-1) ** sb * 2 ** (-24) * B * 16 ** (C-64)

		InsertBitField ((long)sb, 0, 1, GDsec4.reference);
		InsertBitField (C, 1, 7, GDsec4.reference);
		InsertBitField (B, 0, 24, &GDsec4.reference[1]);
	}
}

void
GribData :: SetScale ( double vmin, double vmax, int nbits)
{
	long	scale;
	//double	x;

	//x = log10((vmax - vmin) / (pow(2.,(double)(nbits+1.))-1.)) / log10(2.);

	scale = (long) floor((log10(((double)vmax-(double)vmin) / (pow(2.,(double)nbits+1.) -1.)) / log10(2.)) +2);

	GDzscale = pow ( (double)2.0, (double)scale );
//	GDzscale = scale;

//	The scale factor (s) is stored in 16 bits (sign bit and 15 bit integer :
//		s = floor [ log2 ( (Am - r) / (2**(i+1) - 1) ] + 2

	putint(GDsec4.scale, (short)sizeof(GDsec4.scale), (int)scale);
}
