/* complex.c -- UMB Scheme, complex number package.

UMB Scheme Interpreter  				$Revision: 2.2 $
Copyright (C) 1988, 1991 William R Campbell

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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.

UMB Scheme was written by Bill Campbell with help from Karl Berry,
Barbara Dixey, Ira Gerstein, Mary Glaser, Kathy Hargreaves, Bill McCabe,
Long Nguyen, Susan Quina, Jeyashree Sivasubram, Bela Sohoni and Thang Quoc Tran.

For additional information about UMB Scheme, contact the author:

	Bill Campbell
	Department of Mathematics and Computer Science
	University of Massachusetts at Boston
	Harbor Campus
	Boston, MA 02125

	Telephone: 617-287-6449		Internet: bill@cs.umb.edu

*/


#include "portable.h"
#include "eval.h"
#include "object.h"
#include "architecture.h"
#include "number.h"
#include "fixnum.h"
#include "bignum.h"
#include "rational.h"
#include "real.h"
#include "complex.h"
#include "steering.h"
#include "io.h"
#include <math.h>
#include <errno.h>

#define A (Get_Number_Complex_Real_Part(Top(2)))
#define B (Get_Number_Complex_Imaginary_Part(Top(2)))
#define C (Get_Number_Complex_Real_Part(Top(1)))
#define D (Get_Number_Complex_Imaginary_Part(Top(1)))

#define PI	3.14159265358979323846
#define HALF_PI	1.57079632679489661923


/* Predicates. */

Public Boolean Is_Complex_Zero()
{
	return ( (C == 0.0) && (D == 0.0) );
}


Public Boolean Is_Complex_Positive()
{
        Error("No Positive/Negative Concept about Complex!");
        return FALSE;
}


Public Boolean Is_Complex_Negative()
{
        Error("No Positive/Negative Concept about Complex!");
        return FALSE;
}



Public Boolean Is_Complex_Odd()
{
        Error("No Odd/Even Concept about Complex!");
        return FALSE;
}



Public Boolean Is_Complex_Even()
{
        Error("No Odd/Even Concept about Complex!");
        return FALSE;
}



Public Boolean Is_Complex_Exact()
{
	return FALSE;	/* Complex numbers are always inexact */
}


Public Boolean Is_Complex_Inexact()
{
        return TRUE;	/* Complex numbers are always inexact */
}

/* Comparisons. */


Public Boolean Complex_Less_Than()
{
        Error("No Inequality about Complex!");
	return FALSE;
}



Public Boolean Complex_Greater_Than()
{
        Error("No Inequality about Complex!");
	return FALSE;
}



Public Boolean Complex_Equal()
{
	return ( (A == C) && (B == D) );
}


Public Boolean Complex_Less_Than_Or_Equal()
{
        Error("No Inequality about Complex!");
	return FALSE;
}


Public Boolean Complex_Greater_Than_Or_Equal()
{
        Error("No Inequality about Complex!");
        return FALSE;
}


/* Arithmetic. */


Public void Complex_Add()
{
	Make_Complex_Number( A + C, B + D );
}



Public void Complex_Subtract()
{
	Make_Complex_Number( A - C, B - D );
}



Public void Complex_Multiply()
{
	Make_Complex_Number( ( (A*C) - (B*D) ), ( (A*D) + (B*C) ) );
}



Public void Complex_Divide()
{
	if (Is_Complex_Zero())           /* C+Di = 0+0i, it's on top(1) */
		Error("Complex division by ZERO !!!!");
	else
		Make_Complex_Number( 	( (A*C)+(B*D) ) / ( (C*C)+(D*D) ),
			     		( (B*C)-(A*D) ) / ( (C*C)+(D*D) ) );
}



Public void Complex_Quotient()
{
        Error("No Quotient Operation on Complex!");
}



Public void Complex_Remainder()
{
        Error("No Remainder Operation on Complex!");
}



Public void Complex_Modulo()
{
	Error("No Modulo Operation on Complex!");
} 




Public void Complex_Negate()
{
	Value_Register = Copy_Object(Top(1), Complex_Size);

	Get_Number_Complex_Real_Part(Value_Register) =
		- Get_Number_Complex_Real_Part(Value_Register);


	Get_Number_Complex_Imaginary_Part(Value_Register) =
		- Get_Number_Complex_Imaginary_Part(Value_Register); 

}



Public void Complex_Abs()
{
        Make_Real_Number(sqrt( (C*C) + (D*D) ));
}



Public void Complex_Numerator()
{
	Error("No Numerator Operation on Complex!");
}



Public void Complex_Denominator()
{
	Error("No Denominator Operation on Complex!");
}
 


Public void Complex_Rationalize()
{
	Error("No Rationalize Operation on Complex!");
}


 
Public void Complex_Max()
{
        Error("No Max Operation on Complex!");
}



Public void Complex_Min()
{
        Error("No Min Operation on Complex!");
}



Public void Complex_GCD()
{
	Error("No GCD Operation on Complex!");
}


 
Public void Complex_LCM()
{
	Error("No LCM Operation on Complex!");
}



Public void Complex_Floor()
{
	Error("No Floor Operation on Complex!");
}

 

Public void Complex_Ceiling()
{
	Error("No Ceiling Operation on Complex!");
}

 

Public void Complex_Truncate()
{
	Error("No Truncate Operation on Complex!");
}


 
Public void Complex_Round()
{
	Error("No Round Operation on Complex!");
}


 
 
Public void Complex_Sqrt()
	/* sqrtz = sqrt(|z|)*( cos((angle+2kPi)/2) + isin((angle+2kPi)/2) ) */
{
        Double mag_sqrt = sqrt(sqrt( (C*C) + (D*D) ));
	Double ang = ( C == 0.0 && D > 0.0 ) ? HALF_PI :
		     ( ( C == 0.0 && D < 0.0 ) ? -HALF_PI : atan2(D,C) );

	/* select a proper angle so that 
				the real part of the result is non-negative */

        if (cos(ang/2.0) >= 0.0) ang /= 2.0;
        else ang = (ang/2.0) + PI;

        Make_Complex_Number(mag_sqrt * cos(ang), mag_sqrt * sin(ang));
}



Public void Complex_Exp()
{
        Make_Complex_Number( exp(C) * cos(D) , exp(C) * sin(D) );
}



Public void Complex_Log()
	/* logz = log(magnitude(z)) + angle(z)i */
{
	Double ang = ( C == 0.0 && D > 0.0 ) ? HALF_PI :
		     ( ( C == 0.0 && D < 0.0 ) ? -HALF_PI : atan2(D,C) );

	if (Is_Complex_Zero()) Display_Error("Bad arg to Log : ", Top(1) );
	else  Make_Complex_Number( log(sqrt(C*C + D*D)), ang ); 
}



Public void Complex_Expt()
        /* z1**z2 = e**z2log(z1)   ;  0**0 is defined to be 1 */
{

#define Z1_and_Z2_are_Zeros (A == 0.0 && B == 0.0) && (C == 0.0 && D == 0.0)
#define Z1_is_Zero  A == 0.0 && B == 0.0

	Promote( 2 , COMPLEX_LEVEL );
	if ( Z1_and_Z2_are_Zeros )  Make_Complex_Number(1.0, 0.0);
	else if ( Z1_is_Zero )    Display_Error("Bad arg to Expt:", Top(2));
	else 
	{
        	Push(Top(2)); 		/* push z1 */
        	Complex_Log();  	/* compute logz1 */
        	Pop(1);			/* remove z1 */
        	Push(Value_Register);	/* push logz1 */
        	Complex_Multiply();     /* compute z2logz1 */
        	Pop(1);			/* remove logz1 */
        	Push(Value_Register);	/* push z2logz1 */
        	Complex_Exp();		/* compute e**z2logz1 */
        	Pop(1);			/* remove z2logz1 */
     	}
#undef Z1_and_Z2_are_Zeros
#undef Z1_is_Zero 
}



Public void Complex_Sin()
	/* sinz = (e**iz - e**-iz) / 2i */
{
        Make_Complex_Number( ((exp(-D) * sin(C)) - (exp(D) * sin(-C))) / 2.0 , 
			     ((exp(D) * cos(-C)) - (exp(-D) * cos(C))) / 2.0 );
}



Public void Complex_Cos()
	/* cosz = (e**iz + e**-iz) / 2 */
{
        Make_Complex_Number(((exp(-D) * cos(C)) + (exp(D) * cos(-C))) / 2.0 ,
                            ((exp(-D) * sin(C)) + (exp(D) * sin(-C))) / 2.0 );
}



Public void Complex_Tan()
	/* tanz = sinz / cosz */
{
        Complex_Sin();          /* compute sin(z) */
        Push( Value_Register ); /* push sin(z) onto stack */
        Push( Top(2) );         /* swap z upto top to compute cos(z) */
        Complex_Cos();          /* compute cos(z) */
        Pop(1);                 /* remove the temp z */
        Push( Value_Register ); /* push cos(z) onto stack */
        Complex_Divide();       /* compute tan(z) = sin(z) / cos(z) */
        Pop(2);                 /* remove sin(z) and cos(z) off the stack */
}



Public void Complex_Asin()
	/* asinz = -ilog(iz + sqrt(1-z**2)) */
{
        Make_Complex_Number( 1.0 - (C*C) + (D*D), -(2.0*(C*D)) ); 
						/* Make 1-z**2 */
	Push( Value_Register ); 	/* push 1-z**2 onto stack */
        Complex_Sqrt();         	/* compute sqrt (1-z**2) */
        Pop(1);				/* remove 1-z**2 */
        Push( Value_Register ); 	/* push sqrt(1-z**2) */
        Make_Complex_Number( -B, A);	/* make iz */
        Push( Value_Register ); 	/* push iz */
        Complex_Add();			/* compute iz + sqrt(1-z**2) */
        Pop(2);				/* remove iz and sqrt(1-z**2) */
        Push( Value_Register ); 	/* push iz + sqrt(1-z**2) */
        Complex_Log();			/* compute log(iz + sqrt(1-z**2)) */
        Pop(1);				/* remove iz + sqrt(1-z**2) */
        Push( Value_Register );		/* push log(iz + sqrt(1-z**2)) */
        Make_Complex_Number( D, -C);	/* compute -ilog(iz + sqrt(1-z**2)) */
        Pop(1);				/* remove log(iz + sqrt(1-z**2)) */

}




Public void Complex_Acos()
	/* acosz = -ilog(z + i*sqrt(1-z**2)) */
{
        Make_Complex_Number(1.0-(C*C)+(D*D), -(2.0*(C*D)) ); /* make 1-z**2 */
        Push( Value_Register ); 	/* push 1-z**2 */
        Complex_Sqrt();         	/* compute sqrt (1-z**2) */
        Pop(1);				/* remove 1-z*2 */
        Push( Value_Register );		/* push sqrt(1-z**2) */
        Make_Complex_Number( 0.0,1.0);  /* make i */
        Push( Value_Register ); 	/* push i */
        Complex_Multiply();		/* compute i*sqrt(1-z**2) */
        Pop(2);				/* remove i and sqrt(1-z**2) */
        Push( Value_Register ); 	/* push i*sqrt(1-z**2) */
        Complex_Add();			/* compute z + i*sqrt(1-z**2) */
        Pop(1);				/* remove i*sqrt(1-z**2) */
        Push( Value_Register );		/* push z + i*sqrt(1-z**2) */
        Complex_Log();			/* compute log(z + i*sqrt(1-z**2)) */
        Pop(1);				/* remove z + i*sqrt(1-z**2) */
        Push( Value_Register );		/* push log(z + i*sqrt(1-z**2)) */
        Make_Complex_Number( D, -C);	/* compute -ilog(z + i*sqrt(1-z**2)) */
        Pop(1);				/* remove log(z + i*sqrt(1-z**2)) */

}



Public void Complex_Atan()
	/* atanz = -ilog((1+iz)*sqrt(1/(1+z**2))) */
{
        Make_Complex_Number(1.0,0.0);	/* make 1 */
        Push(Value_Register);		/* push 1 */
        Make_Complex_Number(1.0+(A*A)-(B*B), 2.0*(A*B)); /* make 1+z**2 */
        Push( Value_Register ); 	/* push 1+z**2 */
        Complex_Divide();		/* compute 1 / (1+z**2) */
        Pop(2);				/* remove 1 and 1+z**2 */
        Push(Value_Register);		/* push 1/(1+z**2) */
        Complex_Sqrt();         	/* compute sqrt(1/(1+z**2)) */
        Pop(1);				/* remove 1/(1+z**2) */
        Push( Value_Register );		/* push sqrt(1/(1+z**2)) */
        Make_Complex_Number( 1.0 - B, A); /* make 1+iz */
        Push( Value_Register );		/* push 1+iz */
	Complex_Multiply();     	/* compute (1+iz)(sqrt(1/(1+z**2))) */
        Pop(2); 			/* remove 1+iz and sqrt(1/(1+z**2)) */
        Push( Value_Register ); 	/* push (1+iz)(sqrt(1/(1+z**2))) */
        Complex_Log(); 		/* compute log((1+iz)(sqrt(1/(1+z**2))) */
        Pop(1);				/* remove (1+iz)(sqrt(1/(1+z**2))) */
        Push( Value_Register ); 	/* push log((1+iz)(sqrt(1/(1+z**2))) */
        Make_Complex_Number( D, -C);/* compute -ilog((1+iz)(sqrt(1/(1+z**2))) */
        Pop(1);			/* remove log((1+iz)(sqrt(1/(1+z**2))) */

}



Public void Complex_Atan2()
{
	Error("No Atan2 Operation on Complex!");
}


 

Public void Complex_Exact_To_Inexact()
{
	Value_Register = Top( 1 );
}



Public void Complex_Inexact_To_Exact()
{
        Error("inexact->to->exact is not applicable to complex numbers");
}




Public void Complex_To_String()
{
	Import	void	String_Append();
	Integer	radix = Number_To_Integer( Top(1) );

	if ( A == 0.0 )
	{
		Make_Constant_String( 	radix == 2 ? "#b" :
					radix == 8 ? "#o" :
					radix == 10 ? "" : "#x" );
		Push( Value_Register );
	}
	else
	{
		Make_Real_Number( A );
		Push( Value_Register );
		Push( Top(2) );
		Number_To_String(); Pop(2);
		Push( Value_Register );
	}

	if ( Get_Number_Complex_Imaginary_Part( Top(3) ) >= 0 )
	{
		Make_Constant_String( "+" );
		Push( Value_Register );
		String_Append(); Pop(2);
		Push( Value_Register );
	}

	Make_Real_Number( Get_Number_Complex_Imaginary_Part( Top(3) ) );
	Push( Value_Register );
	Push( Top(3) );
	Number_To_String(); Pop(2);
	Push( Value_Register );
	if ( radix != 10 )
	{
		/* remove radix prefix from imaginary part */

		Make_Constant_String( Get_String_Value( Top(1) ) + 2 ); Pop(1);
		Push( Value_Register );
	}
	String_Append(); Pop(2);
	Push( Value_Register );

	Make_Constant_String( "i" );
	Push( Value_Register );
	String_Append(); Pop(2);
}

Public void Complex_Make_Rectangular()
        /* Make a Complex in rectangular form; z = x + yi */
{
        Tower_Position argtypeX = Get_Number_Tower_Position(Top(2));
        Tower_Position argtypeY = Get_Number_Tower_Position(Top(1));

        if (argtypeX == COMPLEX_LEVEL || argtypeY == COMPLEX_LEVEL)
        {
                Error("Bad arguments to Make-Rectangular.");
        }

}



Public void Complex_Make_Polar()
{
        Tower_Position argtypeX = Get_Number_Tower_Position(Top(2));
        Tower_Position argtypeY = Get_Number_Tower_Position(Top(1));

        if (argtypeX == COMPLEX_LEVEL || argtypeY == COMPLEX_LEVEL)
        {
                Error("Bad arguments to Make-Rectangular.");
        }
}



Public void Complex_Real_Part()
        /* Return the real part of a complex in rectangular form */
{
        Make_Real_Number( C );
}




Public void Complex_Imaginary_Part()
        /* Return the imaginary part of a complex in rectangular form */
{
        Make_Real_Number( D );
}




Public void Complex_Magnitude()
        /* Return the Magnitude of a complex, */
{
        Make_Real_Number( sqrt( (C*C) + (D*D) ) );
}




Public void Complex_Angle()
        /* Return the angle of a complex */
{
	Double ang;
	
	ang = ( C == 0.0 && D > 0.0 ) ? HALF_PI :
		     ( ( C == 0.0 && D < 0.0 ) ? -HALF_PI : atan2(D,C) );

	if (Is_Complex_Zero()) Make_Real_Number( 0.0 );
        else Make_Real_Number( ang );

}


#undef A
#undef B
#undef C
#undef D
