//-----------------------------------------------------------------------------
// Copyright © 2006 - Philip Howard - All rights reserved
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/arith
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro	rescale_mpf
//
// purpose	Given a new linear value range and an old linear value range,
//		rescale a value from the old range to the new range and return
//		the result.
//
// arguments	1 (number) result of rescale
//		2 (number) 1st value of new value range
//		3 (number) 2nd value of new value range
//		4 (number) 1st value of old value range
//		5 (number) 2nd value of old value range
//		6 (number) value to be rescaled
//
// yields	(number) value rescaled to new range
//
// note		Ranges can be reversed to invert the scaling.
//
// note		For reasonable results, all arguments should be floating point.
//		However, integers will work to some extent.
//
// note		Values to be rescaled which are outside the old range will be
//		extrapolated linearly outside of the new range.
//-----------------------------------------------------------------------------
#define rescale_mpf(r,na,nz,oa,oz,v) ({						\
	mpf_t libh__n;								\
	mpf_t libh__o;								\
	mpf_t libh__v;								\
	mpf_t libh__1;								\
	mpf_t libh__2;								\
	mpf_init( libh__v );							\
	mpf_init( libh__1 );							\
	mpf_init( libh__2 );							\
	mpf_init_set( libh__n, (na) );						\
	mpf_init_set( libh__o, (oa) );						\
	mpf_sub( libh__1, (v), libh__o );					\
	mpf_sub( libh__2, (nz), libh__n );					\
	mpf_mul( libh__v, libh__2, libh__1 );					\
	mpf_sub( libh__o, (oz), libh__o );					\
	mpf_mul( libh__1, libh__n, libh__o );					\
	mpf_add( libh__2, libh__v, libh__1 );					\
	mpf_div( (r), libh__2, libh__o );					\
	mpf_clear( libh__2 );							\
	mpf_clear( libh__1 );							\
	mpf_clear( libh__v );							\
	mpf_clear( libh__o );							\
	mpf_clear( libh__n );							\
})
#define rescale_mpf_reference(na,nz,oa,oz,v) ({					\
	__typeof__(na)	libh__n;						\
	__typeof__(oa)	libh__o;						\
	__typeof__(v)	libh__v;						\
	libh__n = (na);								\
	libh__o = (oa);								\
	libh__v = ( (nz) - libh__n ) * ( (v) - libh__o );			\
	libh__o = (oz) - libh__o;						\
	( libh__v + ( libh__n * libh__o ) ) / libh__o;				\
})

//-----------------------------------------------------------------------------
// macro	rescale0_mpf
//
// purpose	Given a new linear value range and an old linear value range
//		which always begins with 0.0, rescale a value from the old
//		range to the new range and return the result.  With the first
//		value of the old range being 0.0, the calculation is slightly
//		less involved.
//
// arguments	1 (number) 1st value of new value range
//		2 (number) 2nd value of new value range
//		3 (number) 2nd value of old value range (1st is zero)
//		4 (number) value to be rescaled
//
// yields	(number) value rescaled to new range
//
// note		Ranges can be reversed to invert the scaling.
//
// note		For reasonable results, all arguments should be floating point.
//		However, integers will work to some extent.
//
// note		Values to be rescaled which are outside the old range will be
//		extrapolated linearly outside of the new range.
//-----------------------------------------------------------------------------
#define rescale0_mpf(r,na,nz,oz,v) ({						\
	mpf_t libh__n;								\
	mpf_t libh__o;								\
	mpf_t libh__v;								\
	mpf_t libh__1;								\
	mpf_t libh__2;								\
	mpf_init( libh__v );							\
	mpf_init( libh__1 );							\
	mpf_init( libh__2 );							\
	mpf_init_set( libh__n, (na) );						\
	mpf_sub( libh__1, (nz), libh__n );					\
	mpf_mul( libh__v, libh__1, (v) );					\
	mpf_init_set( libh__o, (oz) );						\
	mpf_mul( libh__1, libh__n, libh__o );					\
	mpf_add( libh__2, libh__v, libh__1 );					\
	mpf_div( (r), libh__2, libh__o );					\
	mpf_clear( libh__2 );							\
	mpf_clear( libh__1 );							\
	mpf_clear( libh__v );							\
	mpf_clear( libh__o );							\
	mpf_clear( libh__n );							\
})
#define rescale0_mpf_reference(na,nz,oz,v) ({					\
	__typeof__(na)	libh__n;						\
	__typeof__(oz)	libh__o;						\
	__typeof__(v)	libh__v;						\
	libh__n = (na);								\
	libh__v = ( (nz) - libh__n ) * (v);					\
	libh__o = (oz);								\
	( libh__v + ( libh__n * libh__o ) ) / libh__o;				\
})

__FMACRO_END__

