#ifndef GPIB_PARSER_NUMUTILS_H
#define GPIB_PARSER_NUMUTILS_H

 #include <ctype.h> // isalpha, isdigit
 #include <stddef.h> // ptrdiff_t
 #include <string.h> // NULL
 #include <stdint.h> // size_t
 #include "gpib_parser_casts.h"
 #include "gpib_parser_chars.h"              // Service characters definitions
 #include "gpib_parser_validators_numbers.h"

 // String check mode for GPIB_StrChkFormat()
 typedef enum 
 { 
    eSCM_DEC,         // Check mode: for demical number
    eSCM_HEX,         // Check mode: for hexademical number
    eSCM_FLOAT,       // Check mode: for float number
    eSCM_BIN,         // Check mode: for binary number
    eSCM_OCT,         // Check mode: for octal number
    eSCM_EXP,         // Check mode: for exponential number
    
    eSCM_MNEMONIC,    // Check mode: for mnemonic parameter
    eSCM_DIGIT        // Check mode: for demical digits
 }   
 eStringCheckMode_t;

 //-----------------------------------------------------------------------------
 // sFormatDouble2Str_t:
 // Compound Literal structure for GPIB_Double2Str()
 typedef union
 {
    uint32_t format;
    
    struct
    {
        uint32_t nLeadDigits : 8;
        uint32_t nFracDigits : 8;
        uint32_t bSciFormat  : 1;
        uint32_t reserved    :15;        
    };
 }
 sFormatDouble2Str_t;
 //-----------------------------------------------------------------------------
 // sFormatInt2Str_t:
 // Compound Literal structure for GPIB_Integer2Str()
 typedef union
 {
    uint32_t format;
    
    struct
    {
        uint32_t nLeadDigits : 4;                
        uint32_t reserved    :28;        
    };
 }
 sFormatInt2Str_t;
 
 #define Format_Float2Str_NdotN   ((sFormatDouble2Str_t){ .bSciFormat = 0, .nLeadDigits = 0, .nFracDigits = 0 }).format
 #define Format_Float2Str_Ndot3   ((sFormatDouble2Str_t){ .bSciFormat = 0, .nLeadDigits = 0, .nFracDigits = 3 }).format
 #define Format_Float2Str_2dot3   ((sFormatDouble2Str_t){ .bSciFormat = 0, .nLeadDigits = 2, .nFracDigits = 3 }).format
 #define Format_Float2Str_Sci     ((sFormatDouble2Str_t){ .bSciFormat = 1, .nLeadDigits = 0, .nFracDigits = 0 }).format
 
 #define Format_Double2Str_NdotN  ((sFormatDouble2Str_t){ .bSciFormat = 0, .nLeadDigits = 0, .nFracDigits = 0 }).format
 #define Format_Double2Str_Ndot3  ((sFormatDouble2Str_t){ .bSciFormat = 0, .nLeadDigits = 0, .nFracDigits = 3 }).format
 #define Format_Double2Str_2dot3  ((sFormatDouble2Str_t){ .bSciFormat = 0, .nLeadDigits = 2, .nFracDigits = 3 }).format
 #define Format_Double2Str_Sci    ((sFormatDouble2Str_t){ .bSciFormat = 1, .nLeadDigits = 0, .nFracDigits = 0 }).format

 #define Format_Int2Str_N         ( (sFormatInt2Str_t) { .nLeadDigits = 0 }).format
 #define Format_Int2Str_10N       ( (sFormatInt2Str_t) { .nLeadDigits = 10 }).format
 
 size_t GPIB_Float2Str( float value, char * strBuf, size_t strBufSize, uint32_t compound_format );
 size_t GPIB_Double2Str( double value, char * strBuf, size_t strBufSize, uint32_t compound_format );
 size_t GPIB_Integer2Str( int32_t value, char * strBuf, size_t strBufSize, uint32_t compound_format );
 //-----------------------------------------------------------------------------

   
 // GPIB_NUMBER_NSYSPREFIX_SIZE:
 // Numeric string-prefix length in bytes:  
 // e.g. For '#HDEADBEED' the prefix  '#H' specifies the hexademical numeric system.
 // There are three types of numeric systems: binary (#B), octal(#O) and hexademical (#H).
 // The prefix length for all of them is 2.
 #define GPIB_NUMBER_NSYSPREFIX_SIZE        (2)
 
 //-----------------------------------------------------------------------------
 // eGPIB_ParseNumbers_type_t:
 // Mode of GPIB_ParseNumbers() call.
 // Describes the @type parameter.
 typedef enum
 {
     USBTMC_PARSENUMBERS_TYPE_FLOAT,
     USBTMC_PARSENUMBERS_TYPE_INT,
 }   
 eGPIB_ParseNumbers_type_t;
 //-----------------------------------------------------------------------------

 // ChooseLowestPtr:
 // Checks two pointers @ptr_a and @ptr_b and prefers the lowest in absolute addresses,
 // The function prefers non-NULL pointers, and returns NULL only if both @ptr_a and
 // @ptr_b are NULL.
 static inline void * ChooseLowestPtr( void * ptr_a, void * ptr_b )
 {
    // First, check the @ptr_a for NULL
    // and prefer the other pointer, even if it's NULL too,
    // because there no choice is.
    if( NULL == ptr_a )
        return ptr_b;
    else
    // Then, check the @ptr_b for NULL
    // and prefer the other pointer, even if it's NULL too,
    // because there no choice is.
    if( NULL == ptr_b )
        return ptr_a;  
    
    // Since @ptr_a and @ptr_b aren't NULL for sure, determine which of the 
    // pointers is greater than the other in absolute values.    
    ptrdiff_t diff = (ptrdiff_t)ptr_b - (ptrdiff_t)ptr_a;
    
    // @ptr_a is less
    if( diff > 0 )
        // prefer @ptr_a
        return ptr_a; 
    
    // Otherwise, prefer @ptr_b
    return ptr_b;
    
    /* #define GetMin(A,B) ((TOUINT(A)>TOUINT(B))?(B):(A)) */
    /* #define GetMax(A,B) ((TOUINT(A)<TOUINT(B))?(B):(A)) */
    /* #define GetNearPointer(P1,P2) ((P1!=NULL && P2!=NULL)?GetMin(P1,P2):((P1!=NULL)?P1:P2)) */
 }
 
 // ChooseLowestPtrConst:
 // Checks two pointers @ptr_a and @ptr_b and prefers the lowest in absolute addresses,
 // The function prefers non-NULL pointers, and returns NULL only if both @ptr_a and
 // @ptr_b are NULL.
 static inline const void * ChooseLowestPtrConst( const void * ptr_a, const void * ptr_b )
 {
    // First, check the @ptr_a for NULL
    // and prefer the other pointer, even if it's NULL too,
    // because there no choice is.
    if( NULL == ptr_a )
        return ptr_b;
    else
    // Then, check the @ptr_b for NULL
    // and prefer the other pointer, even if it's NULL too,
    // because there no choice is.
    if( NULL == ptr_b )
        return ptr_a;  
    
    // Since @ptr_a and @ptr_b aren't NULL for sure, determine which of the 
    // pointers is greater than the other in absolute values.    
    ptrdiff_t diff = (ptrdiff_t)ptr_b - (ptrdiff_t)ptr_a;
    
    // @ptr_a is less
    if( diff > 0 )
        // prefer @ptr_a
        return ptr_a; 
    
    // Otherwise, prefer @ptr_b
    return ptr_b;
    
    /* #define GetMin(A,B) ((TOUINT(A)>TOUINT(B))?(B):(A)) */
    /* #define GetMax(A,B) ((TOUINT(A)<TOUINT(B))?(B):(A)) */
    /* #define GetNearPointer(P1,P2) ((P1!=NULL && P2!=NULL)?GetMin(P1,P2):((P1!=NULL)?P1:P2)) */
 }
 // ----------------------------------------------------------------------------
 
 // SubPointers:
 // Substracts the @ptr_sub from the @ptr_src pointer and returns the difference.
 static inline ptrdiff_t SubPointers( const void * ptr_src, const void * ptr_sub )
 {
    ptrdiff_t diff = (ptrdiff_t)ptr_src - (ptrdiff_t)ptr_sub;
    
    return diff;
    /* #define SubPointers(P1,P2) TOUINT((TOUINT(TOPCHAR(P1))-TOUINT(TOPCHAR(P2)))) */  
 } 
 // ----------------------------------------------------------------------------
 
 // DecCharToNum:
 // Converts a demical charater into the number
 static inline int DecCharToNum( const char ch )
 {
    // In case the given @ch isn't numeric character return zero
    return ( IsDemicalChar(ch) ? (int)ch - '0' : 0 );
    
    /* #define CharToDec(character) ((IsDEC(character))?(int)character-'0':0);*/
 }
 
 #define BinCharToNum(ch)   (DecCharToNum(ch))     // binary system: convert the character into the demical number
 #define OctCharToNum(ch)   (DecCharToNum(ch))     // octal system: convert the character into the demical number
 // ----------------------------------------------------------------------------
 
 // HexCharToNum:
 // Converts a hexademical charater into the number
 static inline int HexCharToNum( const char ch )
 {
    // In case the given @ch isn't numeric character return zero
    return ( IsHexademicalChar(ch) ? (int)(tolower(ch) - 'a') + 10 : 0 );
    
    /* #define CharHexToDec( character )  (   ( IsDemicalChar (character) )? ((character)-'0'): ( ( IsHexademicalChar(character))? ((character)-'A'+0x0A): 0 ) ) */
 }
 
 // ----------------------------------------------------------------------------
 
 // HexCharToNumber:
 // Converts a hex-literal character into the number
 static inline int HexCharToNumber( const char ch )
 {
    return ( IsDemicalChar(ch) ? DecCharToNum(ch) : HexCharToNum(ch) );
    
    /* #define CharHexToDec( character )  (   ( IsDemicalChar (character) )? ((character)-'0'): ( ( IsHexademicalChar(character))? ((character)-'A'+0x0A): 0 ) ) */
 } 
 
 // ----------------------------------------------------------------------------
 
 // IsBinarySystem:
 // Parses the number's string prefix and detects if it relates to a binary numeric system
 // Prefix is "#B", e.g. "#B1000"
 static inline BOOL IsBinarySystem( const char * prefix )
 {
    return MAKE_BOOL(
                    GPIB_CHAR_NMBSYSTEM_SIGN == prefix[0]           // First character: is a numeric system prefix character
                 &&                      'b' == tolower(prefix[1])  // Second character: 'b' or 'B'
                 );
    /* #define IsBinSystem( string )     (  (*(string)=='#' && ( (*(string+1) & 0xDF)=='B' ))?TRUE:FALSE ) */
 }
 
 // ----------------------------------------------------------------------------
 
 // IsHexademicalSystem:
 // Parses the number's string prefix and detects if it relates to a hexademical numeric system
 // Prefix is "#H", e.g. "#HA41C"
 static inline BOOL IsHexademicalSystem( const char * prefix )
 {
    return MAKE_BOOL(
                    GPIB_CHAR_NMBSYSTEM_SIGN == prefix[0]           // First character: is a numeric system prefix character
                 &&                      'h' == tolower(prefix[1])  // Second character: 'h' or 'H'
                 );
    /* #define IsHexSystem( string )     (  (*(string)=='#' && ( (*(string+1) & 0xDF)=='H' ))?TRUE:FALSE ) */
 }
  
 // ----------------------------------------------------------------------------
 
 // IsOctalSystem:
 // Parses the number's string prefix and detects if it relates to an octal numeric system
 // Prefix is "#O" or "#Q", e.g. "#O606", "#Q606"
 static inline BOOL IsOctalSystem( const char * prefix )
 {
    return MAKE_BOOL(
                    GPIB_CHAR_NMBSYSTEM_SIGN == prefix[0]           // First character: is a numeric system prefix character
                 &&   (                   'o' == tolower(prefix[1]) // Second character: 'o' or 'O'
                      ||                  'q' == tolower(prefix[1]) // Second character: 'q' or 'Q'
                      )
                    );  
    /* #define IsOctSystem( string )     (  (*(string)=='#' && ( (*(string+1) & 0xDF)=='O' ))?TRUE:FALSE ) */
 }
 
 // ----------------------------------------------------------------------------
 
 // IsNumericSystem:
 // Checks if the given @prefix relates to a numeric system: hexademical, octal or binary.
 static inline BOOL IsNumericSystem( const char * prefix )
 {
    return MAKE_BOOL(   IsHexademicalSystem(prefix)  // Check first for hexademical
                  || IsBinarySystem(prefix)       // Next check for binary
                  || IsOctalSystem(prefix) );     // At last check for octal
                  
    /* #define IsNumericSystem( string ) ( ( IsOctalSystem( string ) || IsHexademicalSystem( string ) || IsBinarySystem( string ))?TRUE:FALSE ) */
 }
 
 // ----------------------------------------------------------------------------
 
 // GetNumericSystemBase:
 // Returns a number system base if any system detected based on @prefix
 static inline int GetNumericSystemBase( const char * prefix )
 {
    if( IsHexademicalSystem( prefix ) )
        return 16;
    
    if( IsOctalSystem( prefix ) )
        return 8;
    
    if( IsBinarySystem( prefix ) )
        return 2;
    
    return 0;
    
    /*#define GetNumericSystem( string ) ( (IsNumericSystem(string))?( (IsBinarySystem( string ))?2:( (IsOctalSystem( string ))?8:( (IsHexademicalSystem( string ))?16:(0) ) ) ):0)*/
 }
 
 /* protos */ 
 BOOL ConvertNumerationSystem( const char * str, int nsys, uint32_t * pResult, const char * * ppEnd ); // "gpib_parser_numutils.c"
 int GPIB_ParseNumbers( const char * str, eGPIB_ParseNumbers_type_t type, size_t count, int * , ... ); // "gpib_parser_numutils.c"
#endif