#include <stdio.h>
#include "dbhost.h" /* PROTOTYPE_ALLOWED */
#include "dbext.h"  /* FLOAT, etc., MIN*, MAX*, BAD*, ZERO_SCALE */
/*****************************************************************************
*                                                                            *
*      COMMON OCEANOGRAPHIC DATA ACCESS SYSTEM (CODAS)                       *
*                                                                            *
*      WRITTEN BY:  RAMON CABRERA, ERIC FIRING, and JULIE RANADA             *
*                   JOINT INSTITUTE FOR MARINE AND ATMOSPHERIC RESEARCH      *
*                   1000 POPE ROAD  MSB 404                                  *
*                   HONOLULU, HI 96822                                       *
*                                                                            *
*      VERSION:     3.00                                                     *
*                                                                            *
*      DATE:        APRIL 1989                                               *
*                                                                            *
*****************************************************************************/
/*

       FILE:  scale.c

              FLOATING POINT SUPPORT FUNCTIONS

*/
/*-----------------------------------------------------------------------------

  FUNCTIONS:  SCALE_FLOAT_TO_??

      The scaling functions accept an array of floating point numbers and
      perform the inverse of the unscaling operations. The results are 
      returned in an array of the specified data type.

      Note that the scaling and unscaling functions are defined to be
      inverse operations when called with the same arguments for offset
      and scale.

      Results: ??_array[i] = (float_array[i] - *offset) / *scale
               float_array[i] = *offset + *scale * ??_array[i]

      For example, for soundspeed the offset is 1500 m/s and
      the new scale is 1E-2 m/s.

      These functions can be used on single values by calling them
      with the addresses of the variables and setting npts to 1.

   PARAMETERS:

      ??_array = the array of data type specified by ??; for the scaling
                 functions, this array is used to store the result of the
                 scaling operation;

      float_array = the array of floating point numbers; for scaling, this
                    contains the values that must be scaled and converted to
                    some other data type;

      scale = pointer to the number by which a value must be factored in order
              to convert from the units in which it is expressed to some
              other unit.

      offset = pointer to the number by which a value must be reduced/increased
               in order to convert from the units in which it is expressed
               to some other unit.

      nv   = pointer to the number of values to be scaled

      nbad = on exit, this contains the number of values in the
             resulting array that have been set to BAD?? because
             the scaling result was out of range.

*/
#define out_of_range(min, max) (fs < min || fs > max ? printf("\n WARNING: attempt to convert float out of range\n original float: %f\n scaled: %f", float_array[i], fs) : 0 )
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED
int SCALE_FLOAT_TO_BYTE(BYTE byte_array[], 
			FLOAT float_array[], 
			FLOAT *scale, FLOAT *offset, 
			unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_BYTE(byte_array, float_array, scale, offset, nv, nbad)
BYTE byte_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;
   
   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      byte_array[i] = BADBYTE;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range(MINBYTE, MAXBYTE)) byte_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_UBYTE(UBYTE ubyte_array[], 
		  	 FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			 unsigned int *nv, unsigned int *nbad) 
#else 
int SCALE_FLOAT_TO_UBYTE(ubyte_array, float_array, scale, offset, nv, nbad)
UBYTE ubyte_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      ubyte_array[i] = BADUBYTE;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range((double)MINUBYTE, (double)MAXUBYTE)) ubyte_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_SHORT(SHORT short_array[],  
			 FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			 unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_SHORT(short_array, float_array, scale, offset, nv, nbad)
SHORT short_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      short_array[i] = BADSHORT;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (long int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range(MINSHORT, MAXSHORT)) short_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_USHORT(USHORT ushort_array[],  
			  FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			  unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_USHORT (ushort_array, float_array, scale, offset, nv, nbad)
USHORT ushort_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      ushort_array[i] = BADUSHORT;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (unsigned long int) round( (float_array[i] - *offset) / *scale );
         if (!out_of_range((double)MINUSHORT, (double)MAXUSHORT)) ushort_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_LONG(LONG long_array[],  
			FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_LONG (long_array, float_array, scale, offset, nv, nbad)
LONG long_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      long_array[i] = BADLONG;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = round( (float_array[i] - *offset) / *scale );
         if (!out_of_range((double)MINLONG, (double)MAXLONG)) long_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_ULONG(ULONG ulong_array[],  
			 FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			 unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_ULONG (ulong_array, float_array, scale, offset, nv, nbad)
ULONG ulong_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      ulong_array[i] = BADULONG;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = round( (float_array[i] - *offset) / *scale );
         if (!out_of_range(MINULONG, MAXULONG)) ulong_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_FLOAT(FLOAT float2_array[],  
			 FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			 unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_FLOAT (float2_array, float_array, scale, offset, nv, nbad)
FLOAT float2_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      float2_array[i] = BADFLOAT;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (float_array[i] - *offset) / *scale;
         if (!out_of_range(-MAXFLOAT, MAXFLOAT)) float2_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}
/*---------------------------------------------------------------------------*/
#if PROTOTYPE_ALLOWED 
int SCALE_FLOAT_TO_DOUBLE(DOUBLE double_array[],  
			  FLOAT float_array[], FLOAT *scale, FLOAT *offset, 
			  unsigned int *nv, unsigned int *nbad)
#else
int SCALE_FLOAT_TO_DOUBLE(double_array, float_array, scale, offset, nv, nbad)
DOUBLE double_array[];
FLOAT float_array[], *scale, *offset;
unsigned int *nv, *nbad;
#endif
{
   unsigned int i;
   double fs;

   if (*scale == 0.0) return(ZERO_SCALE);
   *nbad = 0;
   for (i = 0; i < *nv; i++)
   {
      double_array[i] = BADDOUBLE;
      if (float_array[i] < ADJ_BADFLOAT)
      {
         fs = (float_array[i] - *offset) / *scale;
         if (!out_of_range(-MAXDOUBLE, MAXDOUBLE)) double_array[i] = fs;
         else (*nbad)++;
      }
   }
   return(0);
}

#if PROTOTYPE_ALLOWED
int (*scale[])(void *out_array, FLOAT in_array[], FLOAT *scale,
   FLOAT *offset, unsigned int *nv, unsigned int *nbad) =
#else
int (*scale[])() =
#endif
{
   SCALE_FLOAT_TO_BYTE,
   SCALE_FLOAT_TO_UBYTE,
   SCALE_FLOAT_TO_UBYTE,  /* CHAR is UBYTE */
   SCALE_FLOAT_TO_SHORT,
   SCALE_FLOAT_TO_USHORT,
   SCALE_FLOAT_TO_LONG,
   SCALE_FLOAT_TO_ULONG,
   SCALE_FLOAT_TO_FLOAT,
   SCALE_FLOAT_TO_DOUBLE
};

