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

        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	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<math.h>

#include	"base.h"
#include "base.hpp"
#include	"base_ct.h"
#include	"filter.h"
#include	"log.h"
#include	"memory.h"



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



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



/*\\\ PROTOTYPES DES FONCTIONS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



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

const char	*const FILT_biquad_name_0 [FILT_NBR_BIQUAD_TYPES] =
{
	"Low Pass", "High Pass", "Band Pass", "Band Reject"
};



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



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



/*==========================================================================*/
/*      Nom: FILT_init_filter                                               */
/*      Description: Intialise une fraction rationnelle de filtrage dans    */
/*                   l'espace de Laplace. Ce fraction est en fait 1 (aucun  */
/*                   filtrage particulier).                                 */
/*      Parametres en sortie:                                               */
/*        - l_num_ptr: pointeur sur les coefficients du numerateur de la    */
/*                     fraction rationnelle de la fonction de transfert.    */
/*                     L'indice des coefficients correspond a leur          */
/*                     puissance. Il doit y avoir de la place pour          */
/*                     au moins 1 coefficient.                              */
/*        - l_den_ptr: meme chose que l_num_ptr, pour le denominateur.      */
/*        - nbr_coef: indique le nombre de coefficients utilises en entree. */
/*                    Ce nombre est mis a 1 par la fonction.                */
/*==========================================================================*/

void	FILT_init_filter (double *l_num_ptr, double *l_den_ptr, int &nbr_coef)
{
	*l_num_ptr = 1.0;
	*l_den_ptr = 1.0;
	nbr_coef = 1;
}



/*==========================================================================*/
/*      Nom: FILT_add_biquad                                                */
/*      Description: Ajoute un biquad a une fonction de transfert en p      */
/*                   normalise. Le biquad est un filtre du 2eme ordre,      */
/*                   du type:                                               */
/*                                                                          */
/*                                                                          */
/*                                                                          */
/*      Parametres en entree:                                               */
/*        - fech: frequence d'echantillonnage utilisee.                     */
/*        - fref: frequence numerique de reference pour la normalisation.   */
/*              Doit etre inferieure a fech / 2.                            */
/*        - f0: frequence numerique de coupure (ou centrale) du biquad.     */
/*                Doit etre inferieure a fech / 2.                          */
/*        - q: facteur de qualite du filtre. q > 0.5 donne de la            */
/*             resonnance.                                                  */
/*        - type: type du filtre (passe-bas/haut/bande, coupe-bande).       */
/*        - mix_flag: indique que le biquad doit etre mixe avec le filtre   */
/*                    d'origine et non multiplie.                           */
/*      Parametres en entree/sortie:                                        */
/*        - l_num_ptr: pointeur sur les coefficients du numerateur de la    */
/*                     fraction rationnelle de la fonction de transfert.    */
/*                     L'indice des coefficients correspond a leur          */
/*                     puissance. Il doit y avoir de la place pour          */
/*                     nbr_coef + 2 coefficients.                           */
/*        - l_den_ptr: meme chose que l_num_ptr, pour le denominateur.      */
/*        - nbr_coef: indique le nombre de coefficients utilises en entree. */
/*                    Ce nombre est augmente de 2 apres l'ajout du biquad.  */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILT_add_biquad (double *l_num_ptr, double *l_den_ptr, int &nbr_coef, double fech, double fref, double f0, double q, int type, bool mix_flag)
{
	double	num [3] = { 1, 0, 0 };		// H(p) = 1 par defaut.
	double	den [3] = { 1, 0, 0 };
	double	p;
	int		coef_cnt;
	int		biq_cnt;
	double	coef_num;
	double	coef_den;
	double	*temp_ptr;
	double	*temp_num_ptr;
	double	*temp_den_ptr;
	long		table_size;
	int		order;

	/* Si on est au dessus de la frequence de Nyquist, on coupe tout ou
	   on laisse tout passer suivant le type de filtre. */
	if (f0 >= fech / 2.001)
	{
		switch (type)
		{
		case	FILT_BIQUAD_HIGHP:
		case	FILT_BIQUAD_BANDP:
			num [0] = 0;
			break;
		}

		order = 1;
	}

	/* Sinon on calcule le filtre normalement */
	else
	{
		p = tan (fref * PI / fech) / tan (f0 * PI / fech);

		den [0] = 1;
		den [1] = p / q;
		den [2] = p * p;

		switch (type)
		{
		case	FILT_BIQUAD_LOWP:
			num [0] = 1;
			num [1] = 0;
			num [2] = 0;
			break;

		case	FILT_BIQUAD_HIGHP:
			num [0] = 0;
			num [1] = 0;
			num [2] = p * p;
			break;

		case	FILT_BIQUAD_BANDP:
			num [0] = 0;
			num [1] = p / q;
			num [2] = 0;
			break;
		
		case	FILT_BIQUAD_NOTCH:
			num [0] = 1;
			num [1] = 0;
			num [2] = p * p;
			break;
		}

		order = 3;
	}

	table_size = (nbr_coef + order - 1) * 2 * sizeof (*temp_ptr);
	temp_ptr = (double *) MALLOC (table_size);
	if (temp_ptr == NULL)
	{
		LOG_printf ("FILT_add_biquad: Error: couldn't allocate memory for intermediate results.\n");
		return (-1);
	}
	memset (temp_ptr, 0, table_size);
	temp_num_ptr = temp_ptr;
	temp_den_ptr = temp_num_ptr + nbr_coef + order - 1;

	for (coef_cnt = 0; coef_cnt < nbr_coef; coef_cnt ++)
	{
		coef_num = l_num_ptr [coef_cnt];
		coef_den = l_den_ptr [coef_cnt];

		for (biq_cnt = 0; biq_cnt < order; biq_cnt ++)
		{
			if (mix_flag)
			{
				temp_num_ptr [coef_cnt + biq_cnt] += coef_num * den [biq_cnt] + num [biq_cnt] * coef_den;
			}
			else
			{
				temp_num_ptr [coef_cnt + biq_cnt] += coef_num * num [biq_cnt];
			}
			temp_den_ptr [coef_cnt + biq_cnt] += coef_den * den [biq_cnt];
		}
	}

	memcpy (l_num_ptr, temp_num_ptr, (nbr_coef + order - 1) * sizeof (*l_num_ptr));
	memcpy (l_den_ptr, temp_den_ptr, (nbr_coef + order - 1) * sizeof (*l_den_ptr));
	nbr_coef += order - 1;

	FREE (temp_ptr);

	return (0);
}




/*==========================================================================*/
/*      Nom: FILT_laplace_to_z                                              */
/*      Description: Transforme une fonction de transfert de filtre sous    */
/*                   forme Laplace normalisee en une fonction en z. La      */
/*                   fonction de transfert est une fraction rationnelle     */
/*                   dans les deux cas.                                     */
/*                                                                          */
/*                           ---     k                  ---     -k          */
/*                           \   a .p                   \   a'.z            */
/*                           /__  k                     /__  k              */
/*                   H(p) = -----------   ==>   H(z) = ------------         */
/*                           ---     k                  ---     -k          */
/*                           \   b .p                   \   b'.z            */
/*                           /__  k                     /__  k              */
/*                                                                          */
/*      Parametres en entree:                                               */
/*        - l_num_ptr: pointeur sur le tableau des coefficients du          */
/*                     numerateur de la fonction de Laplace. L'indice       */
/*                     correspond a l'ordre (puissance) du coefficient. Les */
/*                     coef non utilises doivent etre mis a 0.              */
/*        - l_den_ptr: idem, pour le denominateur. Les coefficients ne      */
/*                     doivent pas etre tous nuls.                          */
/*        - nbr_coef: indique le nombre de coefficients au numerateur et au */
/*                    denominateur, aussi bien dans la fonction de Laplace  */
/*                    que dans la fonction en z.                            */
/*        - fech: frequence d'echantillonnage pour la transformee en z.     */
/*        - f0: frequence sur laquelle la variable p a ete normalisee.      */
/*              Doit etre inferieure a fech / 2.                            */
/*      Parametres en sortie:                                               */
/*        - z_num_ptr: pointeur sur le tableau des coefficients du          */
/*                     numerateurde la fonction en z. Meme chose que pour   */
/*                     l_num_ptr. Il doit y avoir de la place pour au moins */
/*                     2 coefficients.                                      */
/*        - z_den_ptr: idem, pour le denominateur.                          */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILT_laplace_to_z (const double *l_num_ptr, const double *l_den_ptr, int nbr_coef, double fech, double f0, double *z_num_ptr, double *z_den_ptr)
{
	long		table_size;
	double	*temp_d_ptr;
	long		*temp_l_ptr;
	long		*t1_ptr;		// (1+r)^q
	long		*t1_a_ptr;
	long		*t2_ptr;		// (1-r)^q
	long		*t2_a_ptr;
	int		p_limit;
	int		sign;
	double	k;
	double	k_pow_q;
	int		q;
	int		p;
	int		t;
	double	val;
	int		nbr_coef_used;

	/* Coefficients a 0 par defaut */
	nbr_coef_used = MAX (nbr_coef, 2);
	memset (z_num_ptr, 0, nbr_coef_used * sizeof (*z_num_ptr));
	memset (z_den_ptr, 0, nbr_coef_used * sizeof (*z_num_ptr));

	/* Reserve de la place pour les calculs intermediaires */
	table_size = BASE_power (nbr_coef, 2) * 2;
	temp_l_ptr = (long *) MALLOC (table_size * sizeof (*temp_l_ptr));
	if (temp_l_ptr == NULL)
	{
		LOG_printf ("FILT_laplace_to_z: Error: couldn't allocate memory for intermediate results.\n");
		return (-1);
	}
	memset (temp_l_ptr, 0, table_size * sizeof (*temp_l_ptr));
	t1_ptr = temp_l_ptr;
	t2_ptr = t1_ptr + BASE_power (nbr_coef, 2);

	temp_d_ptr = (double *) MALLOC (nbr_coef * sizeof (*temp_d_ptr));
	if (temp_d_ptr == NULL)
	{
		LOG_printf ("FILT_laplace_to_z: Error: couldn't allocate memory for intermediate results.\n");
		FREE (temp_l_ptr);
		return (-1);
	}

	/* Calcule les (1+r)^q et (1-r)^q, q ds [0...nbr_coef-1] */
	t1_ptr [0] = 1;
	t2_ptr [0] = 1;
	t1_a_ptr = t1_ptr + nbr_coef;
	t2_a_ptr = t2_ptr + nbr_coef;
	for (q = 1; q < nbr_coef; q ++)
	{
		/* Calcule les (1+r)^q en fonction des (1+r)^(q-1) */
		*t1_a_ptr = 1;
		t1_a_ptr [q] = 1;
		p_limit = q >> 1;
		for (p = 1; p <= p_limit; p ++)
		{
				t1_a_ptr [p] =   t1_a_ptr [p - 1 - nbr_coef]
			               + t1_a_ptr [p - nbr_coef];
			t1_a_ptr [q - p] = t1_a_ptr [p];
		}

		/* Deduit les (1-r)^q */
		sign = 1;
		for (p = 0; p <= q; p ++)
		{
			*t2_a_ptr++ = *t1_a_ptr++ * sign;
			sign = - sign;
		}

		t1_a_ptr += nbr_coef - q - 1;
		t2_a_ptr += nbr_coef - q - 1;
	}
	
	/* Calcul du coef de la TAB */
	k = 1.0 / tan (f0 * PI / fech);

	/* Calcul (k^q).((1-r)^q).((1+r)^(n-1-q)), multiplication
		par aq et bq et addition finale */
	k_pow_q = 1.0;
	for (q = 0; q < nbr_coef; q ++)
	{
		memset (temp_d_ptr, 0, nbr_coef * sizeof (*temp_d_ptr));

		/* Multiplication des trois polynomes de base */
		for (p = nbr_coef - q - 1; p >= 0; p --)	// p indice dans (1+r)^(n-1-q)
		{
			val = t1_ptr [nbr_coef * (nbr_coef - q - 1) + p] * k_pow_q;
			for (t = 0; t <= q; t ++)					// t indice dans (1-r)^q
			{
				temp_d_ptr [p + t] += val * t2_ptr [nbr_coef * q + t];
			}
		}

		/* Multiplication par aq et bq et additions aux polynomes finaux */
		for (p = 0; p < nbr_coef; p ++)
		{
			z_num_ptr [p] += temp_d_ptr [p] * l_num_ptr [q];
			z_den_ptr [p] += temp_d_ptr [p] * l_den_ptr [q];
		}

		k_pow_q *= k;
	}

	FREE (temp_d_ptr);
	FREE (temp_l_ptr);

	return (0);
}



/*==========================================================================*/
/*      Nom: FILT_z_to_diff_eq                                              */
/*      Description: Convertit une fonction de transfert en z (fraction     */
/*                   rationnelle en coefficients de l'equation aux          */
/*                   differences utilisee par l'algorithme du filtre IIR.   */
/*      Parametres en entree:                                               */
/*        - z_num_ptr, z_den_ptr: pointeurs sur le numerateur et            */
/*                                denominateur de la fonction de transfert  */
/*                                en z.                                     */
/*        - nbr_coef: nombre de coefficients dans le numerateur et le       */
/*                    denominateur.                                         */
/*      Parametres en sortie:                                               */
/*        - x_coef_ptr: pointeur sur les coefficients appliques au sample   */
/*                      source.                                             */
/*        - y_coef_ptr: pointeur sur les coefficients appliques au sample   */
/*                      destination. Le coef 0 sera toujours 1.0.           */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILT_z_to_diff_eq (const double *z_num_ptr, const double *z_den_ptr, int nbr_coef, double *x_coef_ptr, double *y_coef_ptr)
{
	int		p;
	double	multiplier;

	if (BASE_is_null (*z_den_ptr))
	{
		LOG_printf ("FILT_z_to_diff_eq: Error: Order 0 coefficient of the z-function denominator can't be null.\n");
		return (-1);
	}

	multiplier = 1.0 / (*z_den_ptr);

	for (p = 0; p < nbr_coef; p ++)
	{
		*x_coef_ptr++ = *z_num_ptr++ * multiplier;
		*y_coef_ptr++ = *z_den_ptr++ * multiplier;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: FILT_do_iir_filtering                                          */
/*      Description: Effectue un filtrage de type IIR sur un sample.        */
/*                   Applique l'equation de recurence:                      */
/*                                                                          */
/*                          n-1               n-1                           */
/*                          ---               ---                           */
/*                   y[t] = \   a[k].x[t-k] - \   b[k].y[t_k]               */
/*                          /__               /__                           */
/*                          k=0               k=1                           */
/*                                                                          */
/*      Parametres en entree:                                               */
/*        - src_ptr: pointeur sur le sample source.                         */
/*        - s_stereo: indique le nombre de voies des samples source.        */
/*        - d_stereo: indique le nombre de voies des samples destination.   */
/*        - x_coef_ptr: tableau des coefficients a appliquer sur la source. */
/*                      Ces coefficients sont places en memoire par ordre   */
/*                      croissant de retard.                                */
/*        - y_coef_ptr: tableau des coefficients a appliquer sur la sortie  */
/*                      (tampon en fait). Le coef 0 n'est pas utilise.      */
/*        - nbr_coef: indique le nombre de coefficients utilises par le     */
/*                    filtre (ordre + 1), aussi bien en entree qu'en        */
/*                    sortie. Doit etre > 1 (si == 1, on doit avoir         */
/*                    y_coef_ptr [1] = 0).                                  */
/*        - buffer_len: taille du buffer en samples. Doit etre une          */
/*                      puissance de 2 et superieur ou egal a nbr_coef.     */
/*                      Si buffer_len <= 0, la meilleure taille est trouvee */
/*                      automatiquement.                                    */
/*        - length: indique le nombre de samples a traiter.                 */
/*      Parametres en sortie:                                               */
/*        - dest_ptr: pointeur sur le sample destination. Si s_stereo >=    */
/*                    d_stereo, les pointeurs source et destination peuvent */
/*                    etre identiques.                                      */
/*      Parametres en entree/sortie:                                        */
/*        - ?_buffer_ptr: pointent sur des buffers circulaires dont la      */
/*                      la taille est la puissance de deux immediatement    */
/*                      sup ou egale a nbr_coef. Ce buffer peut etre mono   */
/*                      ou stereo. Il doit etre initialise en principe a 0. */
/*        - buffer_pos: position courante dans le buffer.                   */
/*==========================================================================*/

/* Version a 34.510 sec pour 44,100,000 samples, a l'ordre 4. */
void	FILT_do_iir_filtering (const float *src_ptr,
		                       float *dest_ptr,
		                       int s_stereo,
									  int	d_stereo,
		                       const double *x_coef_ptr,
		                       const double *y_coef_ptr,
		                       int nbr_coef,
		                       float *x_buffer_ptr,
		                       double *y_buffer_ptr,
		                       int &buffer_pos,
		                       int buffer_len,
		                       long length)
{
	long		sample_cnt;
	long		sample_cnt_2;
	int		buffer_pos_mask;
	float *	chn_x_buf_ptr;
	double	*chn_y_buf_ptr;
	int		cur_buf_pos;
	int		chn_cnt;
	int		coef_cnt;
	double	val;

	if (length <= 0)
	{
		return;
	}

	/* Recherche la taille du buffer */
	if (buffer_len <= 0)
	{
		buffer_len = 1;
		while (buffer_len < nbr_coef)
		{
			buffer_len <<= 1;
		}
	}
	buffer_pos_mask = buffer_len - 1;

	sample_cnt = 0;

	/* Mono -> mono, stereo -> stereo */
	if (s_stereo == d_stereo)
	{
		length *= s_stereo;

		do
		{
			chn_x_buf_ptr = x_buffer_ptr;
			chn_y_buf_ptr = y_buffer_ptr;
			chn_cnt = 0;
			do
			{
				/* Init du sample */
				val = 0.0;
				chn_x_buf_ptr [buffer_pos] = src_ptr [sample_cnt];

				/* Application des coefficients au sample source */
				cur_buf_pos = buffer_pos;
				coef_cnt = 0;
				do
				{
					val += chn_x_buf_ptr [cur_buf_pos] * x_coef_ptr [coef_cnt];
					cur_buf_pos = (cur_buf_pos - 1) & buffer_pos_mask;
					coef_cnt ++;
				}
				while (coef_cnt < nbr_coef);

				/* Application des coefficients au buffer (sample destination) */
				cur_buf_pos = (buffer_pos - 1) & buffer_pos_mask;
				coef_cnt = 1;
				do
				{
					val -= chn_y_buf_ptr [cur_buf_pos] * y_coef_ptr [coef_cnt];
					cur_buf_pos = (cur_buf_pos - 1) & buffer_pos_mask;
					coef_cnt ++;
				}
				while (coef_cnt < nbr_coef);

				/* Enregistre le sample calcule */
				chn_y_buf_ptr [buffer_pos] = val;
				dest_ptr [sample_cnt] = float (val);

				/* Met a jour les pointeurs des buffers pour le canal suivant. */
				chn_x_buf_ptr += buffer_len;
				chn_y_buf_ptr += buffer_len;
				chn_cnt ++;
				sample_cnt ++;
			}
			while (chn_cnt < s_stereo);

			buffer_pos = (buffer_pos + 1) & buffer_pos_mask;
		}
		while (sample_cnt < length);
	}

	/* Stereo -> mono: mixage brutal */
	else if (s_stereo > d_stereo)
	{
		sample_cnt_2 = sample_cnt;
		do
		{
			chn_x_buf_ptr = x_buffer_ptr;
			chn_y_buf_ptr = y_buffer_ptr;

			/* Init du sample */
			val = 0.0;
			chn_cnt = 0;
			chn_x_buf_ptr [buffer_pos] = 0;
			do
			{
				chn_x_buf_ptr [buffer_pos] += src_ptr [sample_cnt_2];
				sample_cnt_2 ++;
				chn_cnt ++;
			}
			while (chn_cnt < s_stereo);

			/* Application des coefficients au sample source */
			cur_buf_pos = buffer_pos;
			coef_cnt = 0;
			do
			{
				val += chn_x_buf_ptr [cur_buf_pos] * x_coef_ptr [coef_cnt];
				cur_buf_pos = (cur_buf_pos - 1) & buffer_pos_mask;
				coef_cnt ++;
			}
			while (coef_cnt < nbr_coef);

			/* Application des coefficients au buffer (sample destination) */
			cur_buf_pos = (buffer_pos - 1) & buffer_pos_mask;
			coef_cnt = 1;
			do
			{
				val -= chn_y_buf_ptr [cur_buf_pos] * y_coef_ptr [coef_cnt];
				cur_buf_pos = (cur_buf_pos - 1) & buffer_pos_mask;
				coef_cnt ++;
			}
			while (coef_cnt < nbr_coef);

			/* Enregistre le sample calcule */
			chn_y_buf_ptr [buffer_pos] = val;
			dest_ptr [sample_cnt] = float (val);

			buffer_pos = (buffer_pos + 1) & buffer_pos_mask;
			sample_cnt ++;
		}
		while (sample_cnt < length);
	}

	/* Mono -> stereo */
	else
	{
		sample_cnt_2 = sample_cnt;
		do
		{
			chn_x_buf_ptr = x_buffer_ptr;
			chn_y_buf_ptr = y_buffer_ptr;

			/* Init du sample */
			val = 0.0;
			chn_x_buf_ptr [buffer_pos] = src_ptr [sample_cnt];

			/* Application des coefficients au sample source */
			cur_buf_pos = buffer_pos;
			coef_cnt = 0;
			do
			{
				val += chn_x_buf_ptr [cur_buf_pos] * x_coef_ptr [coef_cnt];
				cur_buf_pos = (cur_buf_pos - 1) & buffer_pos_mask;
				coef_cnt ++;
			}
			while (coef_cnt < nbr_coef);

			/* Application des coefficients au buffer (sample destination) */
			cur_buf_pos = (buffer_pos - 1) & buffer_pos_mask;
			coef_cnt = 1;
			do
			{
				val -= chn_y_buf_ptr [cur_buf_pos] * y_coef_ptr [coef_cnt];
				cur_buf_pos = (cur_buf_pos - 1) & buffer_pos_mask;
				coef_cnt ++;
			}
			while (coef_cnt < nbr_coef);

			/* Enregistre le sample calcule */
			chn_y_buf_ptr [buffer_pos] = val;
			val /= d_stereo;
			chn_cnt = 0;
			do
			{
				dest_ptr [sample_cnt_2] = float (val);
				sample_cnt_2 ++;
				chn_cnt ++;
			}
			while (chn_cnt < d_stereo);

			buffer_pos = (buffer_pos + 1) & buffer_pos_mask;
			sample_cnt ++;
		}
		while (sample_cnt < length);
	}
}



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



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

