/*****************************************************************************

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include <cstdio>
#include <climits>
#include <cstring>
#include <cctype>
#include <cmath>
#include	<cfloat>

#include "archi.h"
#include "base.h"
#include "base.hpp"
#include "base_ct.h"
#include "memory.h"



/*\\\ CONSTANTES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PROTOTYPES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Indice des jours de chaque mois dans l'annee de 365 jours */
const int	BASE_day_index [12] =
{
	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/****************************************************************************/
/*                                                                          */
/*      ROUTINES DE CONVERSIONS DIVERSES                                    */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: -                                                              */
/*      Description: Conversions de mots et de mots longs de l'architecture */
/*                   courante dans les architectures Intel et Motorola,     */
/*                   et inversement.                                        */
/*      Parametres en entree:                                               */
/*        - Le mot a convertir.                                             */
/*      Retour: Le mot converti.                                            */
/*==========================================================================*/

WORD	BASE_intel_word (WORD a)
{
	#if (OS_BYTE_ORDER == 0)
	return (BASE_intel_word_to_moto (a));
	#else
	return (a);
	#endif
}



LWORD	BASE_intel_lword (LWORD a)
{
	#if (OS_BYTE_ORDER == 0)
	return (BASE_intel_lword_to_moto (a));
	#else
	return (a);
	#endif
}



WORD	BASE_moto_word (WORD a)
{
	#if (OS_BYTE_ORDER != 0)
	return (BASE_intel_word_to_moto (a));
	#else
	return (a);
	#endif
}



LWORD	BASE_moto_lword (LWORD a)
{
	#if (OS_BYTE_ORDER != 0)
	return (BASE_intel_lword_to_moto (a));
	#else
	return (a);
	#endif
}



WORD	BASE_intel_word_to_moto (WORD a)
{
	return ((WORD) (((a<<8) & 0xFF00) + ((a>>8) & 0x00FF)));
}



LWORD	BASE_intel_lword_to_moto (LWORD a)
{
	return (((a<<24) & 0xFF000000L) + ((a<<8) & 0xFF0000L)
			 + ((a>>8) & 0xFF00L) + ((a>>24) & 0xFFL));
}



/*==========================================================================*/
/*      Nom: BASE_dpeek                                                     */
/*      Description: Lit un mot de 16 bits en memoire.                      */
/*      Parametres en entree:                                               */
/*        - adresse: adresse du mot a lire.                                 */
/*      Retour: Le mot.                                                     */
/*==========================================================================*/

WORD	BASE_dpeek_moto (const void *adresse)
{
	return (BASE_moto_word (*((WORD *)adresse)));
}



WORD	BASE_dpeek_intel (const void *adresse)
{
	return (BASE_intel_word (*((WORD *)adresse)));
}



/*==========================================================================*/
/*      Nom: BASE_dpoke                                                     */
/*      Description: Ecrit un mot de 16 bits en memoire.                    */
/*      Parametres en entree:                                               */
/*        - adresse: adresse du mot a ecrire.                               */
/*        - mot: le mot                                                     */
/*==========================================================================*/

void	BASE_dpoke_moto (void *adresse, WORD mot)
{
	*((WORD *)adresse) = BASE_moto_word (mot);
}



void	BASE_dpoke_intel (void *adresse, WORD mot)
{
	*((WORD *)adresse) = BASE_intel_word (mot);
}



/*==========================================================================*/
/*      Nom: BASE_lpeek                                                     */
/*      Description: Lit un mot long de 32 bits en memoire.                 */
/*      Parametres en entree:                                               */
/*        - adresse: adresse du mot long a lire.                            */
/*      Retour: Le mot long.                                                */
/*==========================================================================*/

LWORD	BASE_lpeek_moto (const void *adresse)
{
	return (BASE_moto_lword (*((LWORD *)adresse)));
}



LWORD	BASE_lpeek_intel (const void *adresse)
{
	return (BASE_intel_lword (*((LWORD *)adresse)));
}



/*==========================================================================*/
/*      Nom: BASE_lpoke                                                     */
/*      Description: Ecrit un mot long de 32 bits en memoire.               */
/*      Parametres en entree:                                               */
/*        - adresse: adresse du mot long a ecrire.                          */
/*        - mot: le mot long                                                */
/*==========================================================================*/

void	BASE_lpoke_moto (void *adresse, LWORD mot_long)
{
	*((LWORD *)adresse) = BASE_moto_lword (mot_long);
}



void	BASE_lpoke_intel (void *adresse, LWORD mot_long)
{
	*((LWORD *)adresse) = BASE_intel_lword (mot_long);
}



/*==========================================================================*/
/*      Nom: BASE_invert                                                    */
/*      Description: Inverse intel/moto un mot de 16 ou 32 bits en memoire. */
/*      Parametres en entree:                                               */
/*        - adresse: adresse du mot long a ecrire.                          */
/*==========================================================================*/

void	BASE_invert_word (WORD *adresse)
{
	*((WORD *)adresse) = BASE_intel_word_to_moto (*adresse);
}



void	BASE_invert_lword (LWORD *adresse)
{
	*((LWORD *)adresse) = BASE_intel_lword_to_moto (*adresse);
}



/*==========================================================================*/
/*      Nom: BASE_asciidbl_to_double                                        */
/*      Description: Convertit un nombre flottant stoque sous forme ASCII   */
/*                   en un double.                                          */
/*      Parametres en entree:                                               */
/*        - ascii: tableau de 24 caracteres contenant la chaine (pas        */
/*                 forcement terminee par des 0).                           */ 
/*      Retour: le nombre converti.                                         */
/*==========================================================================*/

double	BASE_asciidbl_to_double (const ASCIIDBL ascii)
{
	char		temp_0 [31+1];	/* Noramlement 25 suffit mais on prefere etre immunise contre les depassements */
	double	nbr;

	memcpy (temp_0, ascii, 24);
	temp_0 [24] = '\0';
	sscanf (temp_0, "%lg", &nbr);

	return (nbr);
}



/*==========================================================================*/
/*      Nom: BASE_double_to_asciidbl                                        */
/*      Description: Convertit un double en un nombre flottant stoque sous  */
/*                   forme ASCII.                                           */
/*      Parametres en entree:                                               */
/*        - nbr: nombre a convertir.                                        */
/*      Parametres en sortie:                                               */
/*        - ascii: tableau de 24 caracteres contenant la chaine (pas        */
/*                 forcement terminee par des 0).                           */ 
/*==========================================================================*/

void		BASE_double_to_asciidbl (double nbr, ASCIIDBL ascii)
{
	char		temp_0 [31+1];	/* Noramlement 25 suffit mais on prefere etre immunise contre les depassements */
	
	sprintf (temp_0, "%+#.16E", nbr);
	memcpy (ascii, temp_0, 24);
}



/*==========================================================================*/
/*      Nom: BASE_quartet_to_int                                            */
/*      Description: Transforme un quartet non signe en int signe.          */
/*      Parametres en entree:                                               */
/*        - Le quartet a convertir (en int).                                */
/*      Retour: L'int converti.                                             */
/*==========================================================================*/

signed int	BASE_quartet_to_int (signed int quartet)
{
	quartet &= 0xF;
	if (quartet > 7)
	{
		quartet -= 0x10;
	}
	return (quartet);
}



/*==========================================================================*/
/*      Nom: BASE_get_quartet                                               */
/*      Description: Retourne un quartet extrait du nombre parametre.       */
/*      Parametres en entree:                                               */
/*        - number: nombre dont on veut extraire un quartet.                */
/*        - pos: numero quartet a extraire (0 = poids faible).              */
/*      Retour: Le quartet (non signe).                                     */
/*==========================================================================*/

int	BASE_get_quartet (long number, int pos)
{
	return ((number >> (pos * 4)) & 0xF);
}



/*==========================================================================*/
/*      Nom: BASE_digit_to_char                                             */
/*      Description: Convertit un chiffre en un caractere (0 - 9, A - Z).   */
/*      Parametres en entree:                                               */
/*        - d: le chiffre e convertir.                                      */
/*      Retour: la valeur ASCII du caractere representant ce chiffre.       */
/*==========================================================================*/

char	BASE_digit_to_char (int d)
{
	if (d <= 9)
	{
		return (d + '0');
	}

	return (d + 'A' - 10);
}



/*==========================================================================*/
/*      Nom: BASE_slword_to_base                                            */
/*      Description: Converti un entier signe en une chaine de caracteres   */
/*                   (se terminant par 0) dans la base indiquee, en         */
/*                   utilisant une longueur maximum pour la conversion.     */
/*      Parametres en entree:                                               */
/*        - n: le nombre a convertir.                                       */
/*        - base: base du nombre a convertir.                               */
/*        - maxlen: nombre maximum de caractere pour la conversion (sans    */
/*                  le 0 final). S'il n'y a pas assez de place, le nombre   */
/*                  est tronque a gauche (pds faible restant). S'il y en a  */
/*                  trop, le nombre est pousse a droite et comble par des   */
/*                  espaces. Si maxlen est negatif, la chaine est convertie */
/*                  dans les bonnes dimensions.                             */
/*      Parametres en sortie:                                               */
/*        - text0: pointeur sur la chaine convertie. L'espace doit deja     */
/*                 etre reserve, en comptant le 0.  Le nombre est converti  */
/*                 en majuscules pour des bases > 10.                       */
/*==========================================================================*/

void BASE_slword_to_base (SLWORD n, char *text0, int base, int maxlen)
{
	int	maxlen2;
	int	i;
	const LWORD	signe = n;

	if (maxlen == 0)				/* Longueur nulle: chaine nulle */
	{
		text0 [0] = 0;
		return;
	}

	if (maxlen < 0)			/* Longueur negative: on met en fait une    */
	{								/* longueur maximum qui correspond a la     */
		maxlen2 = 33;			/* plus grande taille pour un LWORD negatif */
	}								/* en binaire.                              */
	else
	{
		maxlen2 = maxlen;
	}

	if (signe < 0)				/* Signe negatif: on le note et on fait la */
	{								/* Conversion d'un nombre positif.         */
		n = -n;
	}

	i = 0;
	text0 [i] = BASE_digit_to_char (n % base);	/* Convertit au moins le 1er caractere */
	i++;
	while ((n/=base)>0 && i<maxlen2)					/* Tant qu'il reste de la place... */
	{
		text0 [i] = BASE_digit_to_char (n % base);
		i++;
	}

	if ((signe<0) && (i<maxlen2))		/* Met le '-' s'il reste de la place */
	{
		text0 [i] = '-';
		i++;
	}
	while (i < maxlen)					/* Rajoute des espaces... */
	{
		text0 [i] = ' ';
		i++;
	}
	text0 [i] = 0;							/* Le 0 de fin */

	BASE_invert_string (text0);		/* inversion de la chaine */
}



/*==========================================================================*/
/*      Nom: BASE_ulword_to_base                                            */
/*      Description: Converti un entier non signe en une chaine de          */
/*                   caracteres (se terminant par 0) dans la base indiquee, */
/*                   en utilisant une longueur maximum pour la conversion.  */
/*      Parametres en entree:                                               */
/*        - n: le nombre a convertir.                                       */
/*        - base: base du nombre a convertir.                               */
/*        - maxlen: nombre maximum de caractere pour la conversion (sans    */
/*                  le 0 final). S'il n'y a pas assez de place, le nombre   */
/*                  est tronque a gauche (pds faible restant). S'il y en a  */
/*                  trop, le nombre est pousse a droite et comble par des   */
/*                  0. Si maxlen est negatif, la chaine est convertie       */
/*                  dans les bonnes dimensions.                             */
/*      Parametres en sortie:                                               */
/*        - text0: pointeur sur la chaine convertie. L'espace doit deja     */
/*                 etre reserve, en comptant le 0.  Le nombre est converti  */
/*                 en majuscules pour des bases > 10.                       */
/*==========================================================================*/

void BASE_ulword_to_base (ULWORD n, char *text0, int base, int maxlen)
{
	int	maxlen2;
	int	i;

	if (maxlen == 0)				/* Longueur nulle: chaine nulle */
	{
		text0 [0] = 0;
		return;
	}

	if (maxlen < 0)			/* Longueur negative: on met en fait une    */
	{								/* longueur maximum qui correspond a la     */
		maxlen2 = 32;			/* plus grande taille pour un LWORD en      */
	}								/* binaire.                                 */
	else
	{
		maxlen2 = maxlen;
	}

	i = 0;
	text0 [i] = BASE_digit_to_char (n % base);	/* Convertit au moins le 1er caractere */
	i++;
	while ((n/=base)>0 && i<maxlen2)					/* Tant qu'il reste de la place... */
	{
		text0 [i] = BASE_digit_to_char (n % base);
		i++;
	}

	while (i < maxlen)					/* Rajoute des 0... */
	{
		text0 [i] = '0';
		i++;
	}
	text0 [i] = 0;							/* Le 0 de fin */

	BASE_invert_string (text0);		/* inversion de la chaine */
}



/*==========================================================================*/
/*      Nom: BASE_pourcent                                                  */
/*      Description: Mets sous forme de pourcentage n1/n2, avec un chiffre  */
/*                   apres la virgule, le tout sur 5 caracteres.            */
/*                   L'arrondissement se fait par exces.                    */
/*      Parametres en entree:                                               */
/*        - n1: nombre a "pourcenter".                                      */
/*        - n2: base du pourcentage.                                        */
/*      Parametres en sortie:                                               */
/*        - text0: pointeur sur une chaine de 5 caracteres destinee a       */
/*                 recevoir le pourcentage, se termiant par 0.              */
/*==========================================================================*/

void	BASE_pourcent (ULWORD n1, ULWORD n2, char *text0)
{
	unsigned int	pourmille;

	pourmille = (unsigned int)((double) n1*1000/n2 + 0.999);	/* C'est le nombre affiche, sous forme entiere */
	text0 [5] = 0;										/* 0 final */
	text0 [4] = (char)(pourmille % 10) + '0';	/* Decimale */
	pourmille /= 10;
	text0 [3] = '.';									/* Point decimal */
	text0 [2] = (char)(pourmille % 10) + '0';	/* Unite */
	pourmille /= 10;

	if (pourmille > 0)								/* Si >= 10.0 % */
	{
		text0 [1] = (char)(pourmille % 10) + '0';	/* Le chiffre des dizaines */
		pourmille /= 10;

		if (pourmille > 0)							/* Si >= 100.0 % */
		{
			text0 [0] = (char)(pourmille % 10) + '0';	/* Le chiffre des centaines */
		}
		else												/* Sinon 1 espace devant les */
		{													/* dizaines                  */
			text0 [0] = ' ';
		}
	}

	else													/* Ou sinon 2 espaces devant */
	{														/* les unites                */
		text0 [0] = ' ';
		text0 [1] = ' ';
	}
}



/*==========================================================================*/
/*      Nom: BASE_truefalse, BASE_onoff, BASE_noyes                         */
/*      Description: Renvoie les chaines "true", "on", "yes" ou "false",    */
/*                   "off", "no" suivant la valeur booleenne de l'argument. */
/*      Parametres en entree:                                               */
/*        - flag: valeur d'entree.                                          */
/*      Parametres en sortie:                                               */
/*        - chaine_0: chaine de 4 (2) ou 5 (3)caracteres + 0 final          */
/*                    contenant le resultat.                                */
/*==========================================================================*/

void	BASE_truefalse (bool flag, char *chaine_0)
{
	if (flag)
	{
		strcpy (chaine_0, "true");
	}
	else
	{
		strcpy (chaine_0, "false");
	}
}

void	BASE_onoff (bool flag, char *chaine_0)
{
	if (flag)
	{
		strcpy (chaine_0, "on");
	}
	else
	{
		strcpy (chaine_0, "off");
	}
}

void	BASE_yesno (bool flag, char *chaine_0)
{
	if (flag)
	{
		strcpy (chaine_0, "yes");
	}
	else
	{
		strcpy (chaine_0, "no");
	}
}



/*==========================================================================*/
/*      Nom: BASE_compare_id4                                               */
/*      Description: Compare deux identificateurs de 4 caracteres.          */
/*      Parametres en entree:                                               */
/*        - id1_ptr: pointeur sur le premier identificateur.                */
/*        - id2_ptr: pointeur sur le deuxieme identificateur.               */
/*      Retour: true si les deux identificateurs sont identiques, false     */
/*              sinon.                                                      */
/*==========================================================================*/

bool	BASE_compare_id4 (const void *id1_ptr, const void *id2_ptr)
{
	if (*((const LWORD *) id1_ptr) == *((const LWORD *) id2_ptr))
	{
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: BASE_power                                                     */
/*      Description: Calcule la puissance d'un entier.                      */
/*      Parametres en entree:                                               */
/*        - nbr: nombre a elever a la puissance (peut etre signe)           */
/*        - pwr: puissance desiree ( >= 0).                                 */
/*      Retour: Le resultat.                                                */
/*==========================================================================*/

signed long	BASE_power (signed long nbr, int pwr)
{
	signed long	result;

	result = 1;
	for ( ; pwr > 0; pwr --)
	{
		result *= nbr;
	}
	return (result);
}



/*==========================================================================*/
/*      Nom: BASE_double_to_unsigned_q                                      */
/*      Description: Convertit un nombre a virgule flottante en une         */
/*                   fraction d'entiers positifs. La meilleure precision    */
/*                   possible est trouvee si possible, sinon on prend la    */
/*                   fraction se rapprochant le plus et se conformant aux   */
/*                   exigences demandees.                                   */
/*      Parametres en entree:                                               */
/*        - nbr: le nombre flottant a convertir.                            */
/*        - max_num: valeur maximum pouvant etre prise par le numerateur.   */
/*        - max_denom: valeur maximum pouvant etre prise par le             */
/*                     denominateur.                                        */
/*      Parametres en sortie:                                               */
/*        - num: numerateur trouve (compris entre 0 et max_num - 1).        */
/*        - num: denominateur trouve (compris entre 1 et max_denom - 1).    */
/*      Retour: true si la conversion s'est effectuee correctement,         */
/*              false si le nombre sortait des valeurs representables.      */
/*==========================================================================*/

bool	BASE_double_to_unsigned_q (double nbr, int &num, int &denom, int max_num, int max_denom)
{
	int		best_denom;
	double	error;
	double	min_error;

	/* Teste si on est hors limite */
	if (nbr < 0.0)
	{
		num = 0;
		denom = 1;
		return (false);
	}
	else if (nbr > max_num)
	{
		num = max_num;
		denom = 1;
		return (false);
	}

	min_error = DBL_MAX;
	best_denom = 1;

	/* Recherche la meilleure fraction possible */
	for (denom = 1; denom <= max_denom; denom ++)
	{
		num = (int) floor (denom * nbr + 0.5);
		error = fabs ((double)num / denom - nbr);
		if (error < min_error)
		{
			min_error =error;
			best_denom = denom;
			if (BASE_is_null (min_error))
			{
				break;
			}
		}
	}

	num = (int) floor (best_denom * nbr + 0.5);
	denom = best_denom;

	return (true);
}



/*==========================================================================*/
/*      Nom: BASE_get_distance_in_circular_buffer                           */
/*      Description: Donne la distance separant deux curseurs dans un       */
/*                   buffer circulaire. Cette distance est en fait la       */
/*                   position du curseur de test dans le buffer si on       */
/*                   suppose que le curseur de reference est place au debut */
/*                   du buffer. Le resultat est donc toujours un nombre     */
/*                   positif, et inferieur a la taille du buffer.           */
/*                   Les curseurs peuvent etre en dehors du buffer, avant   */
/*                   ou apres.                                              */
/*      Parametres en entree:                                               */
/*        - length: taille du buffer. Doit etre toujours > 0.               */
/*        - reference_cursor: position du curseur de reference.             */
/*        - test_cursor: position du curseur de test.                       */
/*      Retour: La distance, comprise entre 0 et length - 1.                */
/*==========================================================================*/

long	BASE_get_distance_in_circular_buffer (long length, signed long reference_cursor, signed long test_cursor)
{
	signed long	distance;

	distance = (test_cursor - reference_cursor) % length;
	if (distance < 0)
	{
		distance += length;
	}

	return ((long) distance);
}



void	BASE_date_to_absolute_day (long &abs_day, int day, int month, int year)
{
	bool		bi_flag;

	/* L'annee fait-elle 366 jours ? */
	bi_flag =    ((year % 4) == 0)
				 && (   (year % 100) != 0
					  || (year % 400) == 0);

	/* Indice du jour a partir du 1/1/0 */
	abs_day =   (long)year * 365	/* 365 jours par annee normale */
				 + (year / 4)			/* Plus 1 jour par annee bissextile */
				 - (year / 100)		/* Les annee seculaires ne sont pas bissextiles */
				 + (year / 400)		/* Mais les annees quadri-seculaires le sont. */
				 + BASE_day_index [month]		/* Tout ca,          */
				 + day								/* c'est pour        */
				 + (month >= 2 && bi_flag);	/* l'annee courante. */
}



void  BASE_absolute_day_to_date (long abs_day, int &day, int &month, int &year)
{
	int		bi_year;
	int		sec_year;
	int		quad_year;
	int		bi_flag;

	/* Trouve l'annee */
	quad_year = (int) (abs_day / (400L * 365 + 100 - 4 + 1));	/* Nombre d'annees quadri-seculaires */
	abs_day -= (long)quad_year * (400L * 365 + 100 - 4 + 1);
	sec_year = (int) (abs_day / (100L * 365 + 25 - 1));		/* Nombre d'annees seculaires */
	abs_day -= (long)sec_year * (100L * 365 + 25 - 1);
	bi_year  = (int) (abs_day / (4 * 365 + 1));			/* Nombre d'annees bissextiles */
	abs_day -= (long)bi_year * (4 * 365 + 1);
	year = (int) (abs_day / 365);
	abs_day -= (long)year * 365;
	year += bi_year * 4;
	year += sec_year * 100;
	year += quad_year * 400;	/* Ouf ! */

	/* L'annee fait-elle 366 jours ? */
	bi_flag =    ((year % 4) == 0)
				 && (   (year % 100) != 0
					  || (year % 400) == 0);

	/* Trouve le mois */
	for (month = 11; month > 0; month --)	/* A la sortie de la boucle, month = 0 */
	{
		if (BASE_day_index [month] + (month >= 2 && bi_flag) < abs_day)
		{
			break;
		}
	}

	day = (int)abs_day - (BASE_day_index [month] + (month >= 2 && bi_flag));
}



int	BASE_get_fader_pos (double val, double inf_val, double sup_val, int length, bool log_flag)
{
	int		fader_pos;
	double	log_inf_val;

	if (log_flag)
	{
		log_inf_val = log (inf_val);
		fader_pos = (int) (  (length * log_inf_val / (log (sup_val) - log_inf_val))
		                   * (log (val) / log_inf_val - 1));
	}

	else
	{
		fader_pos = (int) (length * (val - inf_val) / (sup_val - inf_val));
	}

	fader_pos = MIN (fader_pos, length);
	fader_pos = MAX (fader_pos, 0);

	return (fader_pos);
}



double	BASE_get_fader_val (signed int pos, double inf_val, double sup_val, int length, bool log_flag)
{
	double	log_inf_val;

	if (log_flag)
	{
		log_inf_val = log (inf_val);

		return (exp (  (pos * (log (sup_val) - log_inf_val)
		             / (length * log_inf_val) + 1) * log_inf_val));
	}

	return (pos * (sup_val - inf_val) / length + inf_val);
}



/****************************************************************************/
/*                                                                          */
/*      GESTION DES CHAINES                                                 */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: BASE_double_to_fix                                             */
/*      Description: Convertit un nombre en une chaine en virgule fixe.     */
/*      Parametres en entree:                                               */
/*        - nbr: le nombre a convertir.                                     */
/*        - pre: le nombre de chiffres avant la virgule, plus le signe si   */
/*               le nombre est negatif.                                     */
/*        - post: le nombre de chiffres apres la virgule.                   */
/*      Parametres en sortie:                                               */
/*        - string_0: la chaine qui accueille le nombre. Elle doit faire    */
/*                    pre + 1 + post + 1 caracteres.                        */
/*==========================================================================*/

void	BASE_double_to_fix (char *string_0, double nbr, int pre, int post)
{
	long		nbr_int;
	double	rest;
	bool		negative_flag;
	char		temp_0 [23+1];

	negative_flag = false;
	if (nbr < 0)
	{
		negative_flag = true;
		nbr = -nbr;
	}

	/* A cause des erreurs d'arrondis qui arrivent parfois, on est oblige de
		rajouter un poil au nombre de facon a faire afficher 1.000 avec
		0.99999... */
	nbr += pow (10.0, -post) / 4;		// +1/4 sur le dernier chiffre.

	nbr_int = (long) floor (nbr);
	rest = modf (nbr, &nbr);
	if (negative_flag)
	{
		nbr_int = -nbr_int;
	}

	sprintf (temp_0, "%*ld", pre, nbr_int);
	memcpy (string_0, temp_0, pre);
	sprintf (temp_0, "%.*f", post, rest);
	memcpy (string_0 + pre, temp_0 + 1, post + 1);
	string_0 [pre + post + 1] = '\0';
}



/*==========================================================================*/
/*      Nom: BASE_double_to_float                                           */
/*      Description: Convertit un nombre en chaine en virgule flottante, en */
/*                   le faisant tenir a coup sur dans la chaine.            */
/*      Parametres en entree:                                               */
/*        - nbr: le nombre a convertir.                                     */
/*        - len: la longueur de la chaine, avec le point decimal et/ou le   */
/*               signe.                                                     */
/*      Parametres en sortie:                                               */
/*        - string_0: la chaine qui accueille le nombre. Elle doit faire    */
/*                    len + 1 caracteres.                                   */
/*==========================================================================*/

void	BASE_double_to_float (char *string_0, double nbr, int len)
{
	int		nbr_digits;
	double	max_nbr;
	double	abs_nbr;
	bool		negative_flag;
	int		precision;
	char		temp_0 [255+1];

	nbr_digits = len;
	if (nbr < 0)
	{
		nbr_digits --;
		negative_flag = true;
		abs_nbr = -nbr;
	}
	else
	{
		negative_flag = false;
		abs_nbr = nbr;
	}
	max_nbr = pow (10.0, nbr_digits);

	/* Le nombre ne tient pas tel quel dans la chaine */
	if (abs_nbr >= max_nbr)
	{
		if (nbr_digits >= 5)
		{
			precision = MAX (nbr_digits - 6, 0);
			sprintf (string_0, "%*.*e", len, precision, nbr);
		}
		else
		{
			memset (string_0, '?', len);
		}
	}

	/* On peut faire tenir le nombre dans la chaine */
	else
	{
		if (BASE_is_null (nbr))
		{
			sprintf (string_0, "%*d", len, 0);
		}
		else
		{
			sprintf (temp_0, "%*g", len, nbr);
			temp_0 [len] = '\0';
			strcpy (string_0, temp_0);
		}
	}

	string_0 [len] = '\0';
}



/*==========================================================================*/
/*      Nom: BASE_double_to_simple                                          */
/*      Description: Convertit un nombre en chaine en virgule flottante, en */
/*                   simplifiant au maximum l'ecriture. Le nombre est       */
/*                   toujours tronque a 12 decimales.                       */
/*      Parametres en entree:                                               */
/*        - nbr: le nombre a convertir.                                     */
/*      Parametres en sortie:                                               */
/*        - string_0: la chaine qui accueille le nombre. Elle doit etre     */
/*                    suffisamment longue.                                  */
/*==========================================================================*/

void	BASE_double_to_simple (char *string_0, double nbr)
{
	size_t	len;
	char		temp_0 [63+1];

	sprintf (temp_0, "%20.12f", nbr);
	BASE_trim_string (temp_0);
	len = strlen (temp_0);
	while (temp_0 [len - 1] == '0')
	{
		len --;
		temp_0 [len] = '\0';
	}
	if (temp_0 [len - 1] == '.')
	{
		len --;
		temp_0 [len] = '\0';
	}
	strcpy (string_0, temp_0);
}



/*==========================================================================*/
/*      Nom: BASE_fill_string                                               */
/*      Description: La chaine parametre est remplie de n caracteres c.     */
/*                   Un zero est ajoute a la fin, s'il reste de la place.   */
/*      ParamŠtres en entree:                                               */
/*        - n: nombre de caracteres a remplir.                              */
/*        - c: caractere de remplissage.                                    */
/*        - sizemax: espace libre pour la chaine de caracteres.             */
/*      Parametres en sortie:                                               */
/*        - s: pointeur sur la chaine.                                      */
/*==========================================================================*/

void BASE_fill_string (char *s, char c, size_t n, size_t sizemax)
{
	size_t	i;

	/* Remplit la chaine jusqu'a sa fin, ou jusqu'au nombre de car. indique */
	for (i = 0; (i < sizemax) && (i < n); i++)
	{
		*s++ = c;
	}

	/* Ajoute le 0 final s'il reste de la place */
	if (n < sizemax)
	{
		*s = 0;
	}
}



/*==========================================================================*/
/*      Nom: BASE_copy_string                                               */
/*      Description: La chaine est copiee, les caracteres inferieurs a 32   */
/*                   sont convertis en espaces. Il N'y a PAS d'ajout de 0 a */
/*                   la fin de la chaine. Par contre la fin est remplie     */
/*                   d'espaces.                                             */
/*      Parametres en entree:                                               */
/*        - ss: pointeur sur la chaine source.                              */
/*        - n: nombre de caracteres a copier.                               */
/*        - destlen: longueur de la nouvelle chaine, a remplir avec des     */
/*                   espaces si n<destlen.                                  */
/*      Parametres en sortie:                                               */
/*        - ds: pointeur sur la chaine destination.                         */
/*==========================================================================*/

void BASE_copy_string (const char *ss, char *sd, size_t n, size_t destlen)
{
	char		a;
	size_t	i;

	for (i = 0; i < destlen; i++)
	{
		a = *ss++;
		if (a>=' ' && i<n)			/* On copie le caractere s'il est ASCII */
		{
			*sd++ = a;
		}
		else								/* Sinon on le transforme en espace */
		{
			*sd++ = ' ';
		}
	}
}



/*==========================================================================*/
/*      Nom: BASE_invert_string                                             */
/*      Description: Inverse une chaine terminee par un 0.                  */
/*      Parametres en entree/sortie:                                        */
/*        - text0: Pointeur sur la chaine a inverser.                       */
/*      Retour: -                                                           */
/*==========================================================================*/

void	BASE_invert_string (char *text0)
{
	long			length = long (strlen (text0));
	if (length > 1)			/* On n'inverse que s'il y a au moins 2 caracteres */
	{
		char *			text0_b = text0 + length;
		for (length >>= 1; length > 0; length--)
		{
			const char		temp = *text0;
			*text0++ = *--text0_b;
			*text0_b = temp;
		}
	}
}



/*==========================================================================*/
/*      Nom: BASE_trim_string                                               */
/*      Description: Renvoie la chaine source privee de ses espaces         */
/*                   exterieurs.                                            */
/*      Parametres en entree/sortie:                                        */
/*        - text0: pointeur sur la chaine, terminee par 0.                  */
/*==========================================================================*/

void	BASE_trim_string (char *text0)
{
	char		*ptr_debut;
	char		*ptr_fin;

	/* Recherche du debut */
	ptr_debut = text0;
	while (*ptr_debut == ' ' && *ptr_debut != 0)
	{
		ptr_debut ++;
	}

	/* Recherche de la longueur (de la fin en fait) */
	ptr_fin = text0 + strlen (text0) - 1;
	while (*ptr_fin == ' ' && ptr_fin - ptr_debut >= 0)
	{
		ptr_fin --;
	}

	/* Deplace la chaine */
	if (ptr_fin - ptr_debut >= 0)
	{
		memmove (text0, ptr_debut, ptr_fin - ptr_debut + 1);
	}
	text0 [ptr_fin - ptr_debut + 1] = 0;
}



/*==========================================================================*/
/*      Nom: BASE_complete_string                                           */
/*      Description: Complete une chaine a droite avec des espaces. Rajoute */
/*                   le 0 final. L'espace aura deja du etre reserve.        */
/*      Parametres en entree:                                               */
/*        - new_len: nouvelle longueur de la chaine (sans compter le 0).    */
/*                   Celle-ci peut etre inferieure a celle de l'ancienne.   */
/*      Parametres en entree/sortie:                                        */
/*        - string_0: chaine a completer, se termine par 0.                 */
/*==========================================================================*/

void	BASE_complete_string (char *string_0, size_t new_len)
{
	size_t	position;
	
	string_0 [new_len] = 0;
	position = strlen (string_0);
	string_0 += position;
	while (position < new_len)
	{
		*string_0++ = ' ';
		position ++;
	}
}



/*==========================================================================*/
/*      Nom: BASE_compare_string                                            */
/*      Description: Compare deux chaines en mode case unsensitive et sans  */
/*                   tenir compte des espaces en debut et en fin de chaine. */
/*      Parametres en entree:                                               */
/*        - s1_0: pointeur sur la premiere chaine.                          */
/*        - s2_0: pointeur sur la deuxieme chaine.                          */
/*      Retour: 0 si les deux chaines sont similaires.                      */
/*==========================================================================*/

signed int	BASE_compare_string (const char *s1_0, const char *s2_0)
{
	do
	{
		while (   *s1_0 == ' '
				 || *s1_0 == '\t'
				 || *s1_0 == '\n'
				 || *s1_0 == '\f'
				 || *s1_0 == '\v')
		{
			s1_0 ++;
		}

		while (   *s2_0 == ' '
				 || *s2_0 == '\t'
				 || *s2_0 == '\n'
				 || *s2_0 == '\f'
				 || *s2_0 == '\v')
		{
			s2_0 ++;
		}

		if (toupper (*s1_0) != toupper (*s2_0))
		{
			return (toupper (*s1_0) - toupper (*s2_0));
		}

	}
	while (   *s1_0++ != '\0'
	       && *s2_0++ != '\0');

	return (0);
}



/*==========================================================================*/
/*      Nom: BASE_search_in_string_nocase                                   */
/*      Description: Recherche l'indice d'une chaine dans une autre sans    */
/*                   tenir compte de la casse.                              */
/*      Parametres en entree:                                               */
/*        - s1_0: pointeur sur la chaine a rechercher, terminee par 0.      */
/*        - s2_0: pointeur sur la chaine de recherche, terminee par 0.      */
/*      Retour: Indice de la chaine 1 dans la chaine 2,                     */
/*              -1 si on n'a pas trouve la chaine 1 dans la chaine 2.       */
/*==========================================================================*/

signed long	BASE_search_in_string_nocase (const char *s1_0, const char *s2_0)
{
	long		offset;
	long		cnt;

	offset = 0;
	for ( ; ; )
	{
		cnt = 0;
		while (s1_0 [cnt] != '\0')
		{
			if (s2_0 [cnt + offset] == '\0')
			{
				return (-1);
			}
			if (toupper (s1_0 [cnt]) != toupper (s2_0 [cnt + offset]))
			{
				break;
			}
			cnt ++;
		}

		if (s1_0 [cnt] == '\0')
		{
			return (offset);
		}

		offset ++;
	}
}



/*==========================================================================*/
/*      Nom: BASE_parse_command_line                                        */
/*      Description: Divise une ligne de commandes en ses differents        */
/*                   arguments, sous le meme format que argc et argv dans   */
/*                   la fonction main (int argc, char *argv []) en C.       */
/*      Parametres en entree:                                               */
/*        - max_argc: nombre maximum d'arguments a convertir (sans compter  */
/*                    le NULL final). argv devra donc avoir max_argc + 1    */
/*                    cases de reservees.                                   */
/*        - delimiter_0: liste de caracteres servant de separateurs entre   */
/*                       les lexemes. La chaine doit se finir par '\0'.     */
/*                       Si cette variable vaut NULL, une liste par defaut  */
/*                       est utilisee.                                      */
/*      Parametres en sortie:                                               */
/*        - argv: pointeur sur un tableau de pointeurs sur les differents   */
/*                arguments. La derniere case de ce tableau est remplie par */
/*                NULL. Il doit y avoir suffisamment de memoire reservee    */
/*                pour remplir ce tableau.                                  */
/*      Parametres en entree/sortie:                                        */
/*        - text_0: Pointeur sur la ligne de commande. Tous les separateurs */
/*                  de lexemes sont remplaces par des '/0'. Les arguments   */
/*                  peuvent etre entoures de '"' pour etre insecables.      */
/*      Retour: Le nombre d'arguments trouves (argc)                        */
/*==========================================================================*/

signed int	BASE_parse_command_line (char *text_0, char *argv [], int max_argc, const char *delimiter_0)
{
	int		argc;
	bool		quote_flag;
	bool		argument_flag;
	bool		delimiter_flag;
	const char	default_delimiter_0 [] = " \t";

	if (delimiter_0 == NULL)
	{
		delimiter_0 = default_delimiter_0;
	}

	argc = 0;
	quote_flag = false;
	argument_flag = false;

	while (*text_0 != 0 && argc < max_argc)
	{
		delimiter_flag = BASE_is_delimiter (*text_0, delimiter_0);

		if (! quote_flag)
		{
			if (*text_0 == '"')
			{
				if (argument_flag)
				{
					*text_0 = '\0';
					argument_flag = false;
				}

				quote_flag = true;
			}

			else if (argument_flag)
			{
				if (delimiter_flag)
				{
					*text_0 = '\0';
					argument_flag = false;
				}
			}

			else if (! delimiter_flag)
			{
				argv [argc] = text_0;
				argc ++;
				argument_flag = true;
			}
		}

		else
		{
			if (! argument_flag)
			{
				argv [argc] = text_0;
				argc ++;
				argument_flag = true;
			}

			if (*text_0 == '"')
			{
				*text_0 = '\0';
				quote_flag = false;
				argument_flag = false;
			}
		}

		text_0 ++;
	}

	argv [argc] = NULL;

	return (argc);
}



/*==========================================================================*/
/*      Nom: BASE_is_delimiter                                              */
/*      Description: Cherche si un caractere fait partie d'une liste.       */
/*      Parametres en entree:                                               */
/*        - delimiter_0: liste de caracteres a tester. La chaine doit se    */
/*                       finir par '\0'.                                    */
/*        - c: caractere a tester.                                          */
/*      Retour: true si le caractere fait partie de la liste, false sinon.  */
/*==========================================================================*/

bool	BASE_is_delimiter (char	c, const char *delimiter_0)
{
	while (*delimiter_0 != '\0')
	{
		if (c == *delimiter_0)
		{
			return (true);
		}
		delimiter_0 ++;
	}

	return (false);
}



std::wstring BASE_conv_utf8_to_utf16 (const char *utf8_0)
{
	std::wstring   utf16;
	BASE_push_back_utf8_to_utf16 (utf16, utf8_0, false);

	return utf16;
}



std::string	BASE_conv_utf16_to_utf8 (const wchar_t *utf16_0)
{
	std::string    dst;
	int            len = 0;
	int            msk = 0;
	int            pos = 0;
	int32_t        val = 0;

	while (utf16_0 [pos] != L'\0')
	{
		wchar_t        c = utf16_0 [pos];
		if (len <= 0)
		{
			val = 0;
			len = 1;
			msk = 0xFFFF;
			if ((c & 0xFC00) == 0xDC00)   // Invalid UTF-16 lead word
			{
				dst += '\?';
				len = 0;
			}
			else if ((c & 0xF800) == 0xD800)
			{
				len = 2;
				msk = 0x03FF;
			}
		}

		if (len > 0)
		{
			val <<= 10;
			val |= c & msk;
			if (len == 2)
			{
				val += 0x0040;
			}
			-- len;
			if (len == 0)
			{
				if (     val <  0x00007F)
				{
					dst += char (  val                      );
				}
				else if (val <= 0x0007FF)
				{
					dst += char (((val >>  6) & 0x1F) | 0xC0);
					dst += char (( val        & 0x3F) | 0x80);
				}
				else if (val <= 0x00FFFF)
				{
					dst += char (((val >> 12) & 0x0F) | 0xE0);
					dst += char (((val >>  6) & 0x3F) | 0x80);
					dst += char (( val        & 0x3F) | 0x80);
				}
				else if (val <= 0x10FFFF)
				{
					dst += char (((val >> 18) & 0x07) | 0xF0);
					dst += char (((val >> 12) & 0x3F) | 0x80);
					dst += char (((val >>  6) & 0x3F) | 0x80);
					dst += char (( val        & 0x3F) | 0x80);
				}
				else
				{
					dst += '\?';	// Out of range
				}
			}
		}

		++ pos;
	}

	if (len > 0)
	{
		dst += '\?';
	}

	return dst;
}



bool	BASE_is_utf8_lead_byte (char c)
{
	const int      cc = c & 0xFF; // Signed to unsigned

	return cc < 0x80 || (cc >= 0xC2 && cc <= 0xF4);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

