| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME COMPONENTS -- |
| -- -- |
| -- ADA.NUMERICS.GENERIC_COMPLEX_ELEMENTARY_FUNCTIONS -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. -- |
| -- -- |
| -- As a special exception under Section 7 of GPL version 3, you are granted -- |
| -- additional permissions described in the GCC Runtime Library Exception, -- |
| -- version 3.1, as published by the Free Software Foundation. -- |
| -- -- |
| -- You should have received a copy of the GNU General Public License and -- |
| -- a copy of the GCC Runtime Library Exception along with this program; -- |
| -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- |
| -- <http://www.gnu.org/licenses/>. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Ada.Numerics.Generic_Elementary_Functions; |
| |
| package body Ada.Numerics.Generic_Complex_Elementary_Functions is |
| |
| package Elementary_Functions is new |
| Ada.Numerics.Generic_Elementary_Functions (Real'Base); |
| use Elementary_Functions; |
| |
| PI : constant := 3.14159_26535_89793_23846_26433_83279_50288_41971; |
| PI_2 : constant := PI / 2.0; |
| Sqrt_Two : constant := 1.41421_35623_73095_04880_16887_24209_69807_85696; |
| Log_Two : constant := 0.69314_71805_59945_30941_72321_21458_17656_80755; |
| |
| subtype T is Real'Base; |
| |
| Epsilon : constant T := 2.0 ** (1 - T'Model_Mantissa); |
| Square_Root_Epsilon : constant T := Sqrt_Two ** (1 - T'Model_Mantissa); |
| Inv_Square_Root_Epsilon : constant T := Sqrt_Two ** (T'Model_Mantissa - 1); |
| Root_Root_Epsilon : constant T := Sqrt_Two ** |
| ((1 - T'Model_Mantissa) / 2); |
| Log_Inverse_Epsilon_2 : constant T := T (T'Model_Mantissa - 1) / 2.0; |
| |
| Complex_Zero : constant Complex := (0.0, 0.0); |
| Complex_One : constant Complex := (1.0, 0.0); |
| Complex_I : constant Complex := (0.0, 1.0); |
| Half_Pi : constant Complex := (PI_2, 0.0); |
| |
| -------- |
| -- ** -- |
| -------- |
| |
| function "**" (Left : Complex; Right : Complex) return Complex is |
| begin |
| if Re (Right) = 0.0 |
| and then Im (Right) = 0.0 |
| and then Re (Left) = 0.0 |
| and then Im (Left) = 0.0 |
| then |
| raise Argument_Error; |
| |
| elsif Re (Left) = 0.0 |
| and then Im (Left) = 0.0 |
| and then Re (Right) < 0.0 |
| then |
| raise Constraint_Error; |
| |
| elsif Re (Left) = 0.0 and then Im (Left) = 0.0 then |
| return Left; |
| |
| elsif Right = (0.0, 0.0) then |
| return Complex_One; |
| |
| elsif Re (Right) = 0.0 and then Im (Right) = 0.0 then |
| return 1.0 + Right; |
| |
| elsif Re (Right) = 1.0 and then Im (Right) = 0.0 then |
| return Left; |
| |
| else |
| return Exp (Right * Log (Left)); |
| end if; |
| end "**"; |
| |
| function "**" (Left : Real'Base; Right : Complex) return Complex is |
| begin |
| if Re (Right) = 0.0 and then Im (Right) = 0.0 and then Left = 0.0 then |
| raise Argument_Error; |
| |
| elsif Left = 0.0 and then Re (Right) < 0.0 then |
| raise Constraint_Error; |
| |
| elsif Left = 0.0 then |
| return Compose_From_Cartesian (Left, 0.0); |
| |
| elsif Re (Right) = 0.0 and then Im (Right) = 0.0 then |
| return Complex_One; |
| |
| elsif Re (Right) = 1.0 and then Im (Right) = 0.0 then |
| return Compose_From_Cartesian (Left, 0.0); |
| |
| else |
| return Exp (Log (Left) * Right); |
| end if; |
| end "**"; |
| |
| function "**" (Left : Complex; Right : Real'Base) return Complex is |
| begin |
| if Right = 0.0 |
| and then Re (Left) = 0.0 |
| and then Im (Left) = 0.0 |
| then |
| raise Argument_Error; |
| |
| elsif Re (Left) = 0.0 |
| and then Im (Left) = 0.0 |
| and then Right < 0.0 |
| then |
| raise Constraint_Error; |
| |
| elsif Re (Left) = 0.0 and then Im (Left) = 0.0 then |
| return Left; |
| |
| elsif Right = 0.0 then |
| return Complex_One; |
| |
| elsif Right = 1.0 then |
| return Left; |
| |
| else |
| return Exp (Right * Log (Left)); |
| end if; |
| end "**"; |
| |
| ------------ |
| -- Arccos -- |
| ------------ |
| |
| function Arccos (X : Complex) return Complex is |
| Result : Complex; |
| |
| begin |
| if X = Complex_One then |
| return Complex_Zero; |
| |
| elsif abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return Half_Pi - X; |
| |
| elsif abs Re (X) > Inv_Square_Root_Epsilon or else |
| abs Im (X) > Inv_Square_Root_Epsilon |
| then |
| return -2.0 * Complex_I * Log (Sqrt ((1.0 + X) / 2.0) + |
| Complex_I * Sqrt ((1.0 - X) / 2.0)); |
| end if; |
| |
| Result := -Complex_I * Log (X + Complex_I * Sqrt (1.0 - X * X)); |
| |
| if Im (X) = 0.0 |
| and then abs Re (X) <= 1.00 |
| then |
| Set_Im (Result, Im (X)); |
| end if; |
| |
| return Result; |
| end Arccos; |
| |
| ------------- |
| -- Arccosh -- |
| ------------- |
| |
| function Arccosh (X : Complex) return Complex is |
| Result : Complex; |
| |
| begin |
| if X = Complex_One then |
| return Complex_Zero; |
| |
| elsif abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| Result := Compose_From_Cartesian (-Im (X), -PI_2 + Re (X)); |
| |
| elsif abs Re (X) > Inv_Square_Root_Epsilon or else |
| abs Im (X) > Inv_Square_Root_Epsilon |
| then |
| Result := Log_Two + Log (X); |
| |
| else |
| Result := 2.0 * Log (Sqrt ((1.0 + X) / 2.0) + |
| Sqrt ((X - 1.0) / 2.0)); |
| end if; |
| |
| if Re (Result) <= 0.0 then |
| Result := -Result; |
| end if; |
| |
| return Result; |
| end Arccosh; |
| |
| ------------ |
| -- Arccot -- |
| ------------ |
| |
| function Arccot (X : Complex) return Complex is |
| Xt : Complex; |
| |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return Half_Pi - X; |
| |
| elsif abs Re (X) > 1.0 / Epsilon or else |
| abs Im (X) > 1.0 / Epsilon |
| then |
| Xt := Complex_One / X; |
| |
| if Re (X) < 0.0 then |
| Set_Re (Xt, PI - Re (Xt)); |
| return Xt; |
| else |
| return Xt; |
| end if; |
| end if; |
| |
| Xt := Complex_I * Log ((X - Complex_I) / (X + Complex_I)) / 2.0; |
| |
| if Re (Xt) < 0.0 then |
| Xt := PI + Xt; |
| end if; |
| |
| return Xt; |
| end Arccot; |
| |
| -------------- |
| -- Arccoth -- |
| -------------- |
| |
| function Arccoth (X : Complex) return Complex is |
| R : Complex; |
| |
| begin |
| if X = (0.0, 0.0) then |
| return Compose_From_Cartesian (0.0, PI_2); |
| |
| elsif abs Re (X) < Square_Root_Epsilon |
| and then abs Im (X) < Square_Root_Epsilon |
| then |
| return PI_2 * Complex_I + X; |
| |
| elsif abs Re (X) > 1.0 / Epsilon or else |
| abs Im (X) > 1.0 / Epsilon |
| then |
| if Im (X) > 0.0 then |
| return (0.0, 0.0); |
| else |
| return PI * Complex_I; |
| end if; |
| |
| elsif Im (X) = 0.0 and then Re (X) = 1.0 then |
| raise Constraint_Error; |
| |
| elsif Im (X) = 0.0 and then Re (X) = -1.0 then |
| raise Constraint_Error; |
| end if; |
| |
| begin |
| R := Log ((1.0 + X) / (X - 1.0)) / 2.0; |
| |
| exception |
| when Constraint_Error => |
| R := (Log (1.0 + X) - Log (X - 1.0)) / 2.0; |
| end; |
| |
| if Im (R) < 0.0 then |
| Set_Im (R, PI + Im (R)); |
| end if; |
| |
| if Re (X) = 0.0 then |
| Set_Re (R, Re (X)); |
| end if; |
| |
| return R; |
| end Arccoth; |
| |
| ------------ |
| -- Arcsin -- |
| ------------ |
| |
| function Arcsin (X : Complex) return Complex is |
| Result : Complex; |
| |
| begin |
| -- For very small argument, sin (x) = x |
| |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| |
| elsif abs Re (X) > Inv_Square_Root_Epsilon or else |
| abs Im (X) > Inv_Square_Root_Epsilon |
| then |
| Result := -Complex_I * (Log (Complex_I * X) + Log (2.0 * Complex_I)); |
| |
| if Im (Result) > PI_2 then |
| Set_Im (Result, PI - Im (X)); |
| |
| elsif Im (Result) < -PI_2 then |
| Set_Im (Result, -(PI + Im (X))); |
| end if; |
| |
| return Result; |
| end if; |
| |
| Result := -Complex_I * Log (Complex_I * X + Sqrt (1.0 - X * X)); |
| |
| if Re (X) = 0.0 then |
| Set_Re (Result, Re (X)); |
| |
| elsif Im (X) = 0.0 |
| and then abs Re (X) <= 1.00 |
| then |
| Set_Im (Result, Im (X)); |
| end if; |
| |
| return Result; |
| end Arcsin; |
| |
| ------------- |
| -- Arcsinh -- |
| ------------- |
| |
| function Arcsinh (X : Complex) return Complex is |
| Result : Complex; |
| |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| |
| elsif abs Re (X) > Inv_Square_Root_Epsilon or else |
| abs Im (X) > Inv_Square_Root_Epsilon |
| then |
| Result := Log_Two + Log (X); -- may have wrong sign |
| |
| if (Re (X) < 0.0 and then Re (Result) > 0.0) |
| or else (Re (X) > 0.0 and then Re (Result) < 0.0) |
| then |
| Set_Re (Result, -Re (Result)); |
| end if; |
| |
| return Result; |
| end if; |
| |
| Result := Log (X + Sqrt (1.0 + X * X)); |
| |
| if Re (X) = 0.0 then |
| Set_Re (Result, Re (X)); |
| elsif Im (X) = 0.0 then |
| Set_Im (Result, Im (X)); |
| end if; |
| |
| return Result; |
| end Arcsinh; |
| |
| ------------ |
| -- Arctan -- |
| ------------ |
| |
| function Arctan (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| |
| else |
| return -Complex_I * (Log (1.0 + Complex_I * X) |
| - Log (1.0 - Complex_I * X)) / 2.0; |
| end if; |
| end Arctan; |
| |
| ------------- |
| -- Arctanh -- |
| ------------- |
| |
| function Arctanh (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| else |
| return (Log (1.0 + X) - Log (1.0 - X)) / 2.0; |
| end if; |
| end Arctanh; |
| |
| --------- |
| -- Cos -- |
| --------- |
| |
| function Cos (X : Complex) return Complex is |
| begin |
| return |
| Compose_From_Cartesian |
| (Cos (Re (X)) * Cosh (Im (X)), |
| -(Sin (Re (X)) * Sinh (Im (X)))); |
| end Cos; |
| |
| ---------- |
| -- Cosh -- |
| ---------- |
| |
| function Cosh (X : Complex) return Complex is |
| begin |
| return |
| Compose_From_Cartesian |
| (Cosh (Re (X)) * Cos (Im (X)), |
| Sinh (Re (X)) * Sin (Im (X))); |
| end Cosh; |
| |
| --------- |
| -- Cot -- |
| --------- |
| |
| function Cot (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return Complex_One / X; |
| |
| elsif Im (X) > Log_Inverse_Epsilon_2 then |
| return -Complex_I; |
| |
| elsif Im (X) < -Log_Inverse_Epsilon_2 then |
| return Complex_I; |
| end if; |
| |
| return Cos (X) / Sin (X); |
| end Cot; |
| |
| ---------- |
| -- Coth -- |
| ---------- |
| |
| function Coth (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return Complex_One / X; |
| |
| elsif Re (X) > Log_Inverse_Epsilon_2 then |
| return Complex_One; |
| |
| elsif Re (X) < -Log_Inverse_Epsilon_2 then |
| return -Complex_One; |
| |
| else |
| return Cosh (X) / Sinh (X); |
| end if; |
| end Coth; |
| |
| --------- |
| -- Exp -- |
| --------- |
| |
| function Exp (X : Complex) return Complex is |
| ImX : constant Real'Base := Im (X); |
| EXP_RE_X : constant Real'Base := Exp (Re (X)); |
| |
| begin |
| return Compose_From_Cartesian (EXP_RE_X * Cos (ImX), |
| EXP_RE_X * Sin (ImX)); |
| end Exp; |
| |
| function Exp (X : Imaginary) return Complex is |
| ImX : constant Real'Base := Im (X); |
| |
| begin |
| return Compose_From_Cartesian (Cos (ImX), Sin (ImX)); |
| end Exp; |
| |
| --------- |
| -- Log -- |
| --------- |
| |
| function Log (X : Complex) return Complex is |
| ReX : Real'Base; |
| ImX : Real'Base; |
| Z : Complex; |
| |
| begin |
| if Re (X) = 0.0 and then Im (X) = 0.0 then |
| raise Constraint_Error; |
| |
| elsif abs (1.0 - Re (X)) < Root_Root_Epsilon |
| and then abs Im (X) < Root_Root_Epsilon |
| then |
| Z := X; |
| Set_Re (Z, Re (Z) - 1.0); |
| |
| return (1.0 - (1.0 / 2.0 - |
| (1.0 / 3.0 - (1.0 / 4.0) * Z) * Z) * Z) * Z; |
| end if; |
| |
| begin |
| ReX := Log (Modulus (X)); |
| |
| exception |
| when Constraint_Error => |
| ReX := Log (Modulus (X / 2.0)) - Log_Two; |
| end; |
| |
| ImX := Arctan (Im (X), Re (X)); |
| |
| if ImX > PI then |
| ImX := ImX - 2.0 * PI; |
| end if; |
| |
| return Compose_From_Cartesian (ReX, ImX); |
| end Log; |
| |
| --------- |
| -- Sin -- |
| --------- |
| |
| function Sin (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon |
| and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| end if; |
| |
| return |
| Compose_From_Cartesian |
| (Sin (Re (X)) * Cosh (Im (X)), |
| Cos (Re (X)) * Sinh (Im (X))); |
| end Sin; |
| |
| ---------- |
| -- Sinh -- |
| ---------- |
| |
| function Sinh (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| |
| else |
| return Compose_From_Cartesian (Sinh (Re (X)) * Cos (Im (X)), |
| Cosh (Re (X)) * Sin (Im (X))); |
| end if; |
| end Sinh; |
| |
| ---------- |
| -- Sqrt -- |
| ---------- |
| |
| function Sqrt (X : Complex) return Complex is |
| ReX : constant Real'Base := Re (X); |
| ImX : constant Real'Base := Im (X); |
| XR : constant Real'Base := abs Re (X); |
| YR : constant Real'Base := abs Im (X); |
| R : Real'Base; |
| R_X : Real'Base; |
| R_Y : Real'Base; |
| |
| begin |
| -- Deal with pure real case, see (RM G.1.2(39)) |
| |
| if ImX = 0.0 then |
| if ReX > 0.0 then |
| return |
| Compose_From_Cartesian |
| (Sqrt (ReX), 0.0); |
| |
| elsif ReX = 0.0 then |
| return X; |
| |
| else |
| return |
| Compose_From_Cartesian |
| (0.0, Real'Copy_Sign (Sqrt (-ReX), ImX)); |
| end if; |
| |
| elsif ReX = 0.0 then |
| R_X := Sqrt (YR / 2.0); |
| |
| if ImX > 0.0 then |
| return Compose_From_Cartesian (R_X, R_X); |
| else |
| return Compose_From_Cartesian (R_X, -R_X); |
| end if; |
| |
| else |
| R := Sqrt (XR ** 2 + YR ** 2); |
| |
| -- If the square of the modulus overflows, try rescaling the |
| -- real and imaginary parts. We cannot depend on an exception |
| -- being raised on all targets. |
| |
| if R > Real'Base'Last then |
| raise Constraint_Error; |
| end if; |
| |
| -- We are solving the system |
| |
| -- XR = R_X ** 2 - Y_R ** 2 (1) |
| -- YR = 2.0 * R_X * R_Y (2) |
| -- |
| -- The symmetric solution involves square roots for both R_X and |
| -- R_Y, but it is more accurate to use the square root with the |
| -- larger argument for either R_X or R_Y, and equation (2) for the |
| -- other. |
| |
| if ReX < 0.0 then |
| R_Y := Sqrt (0.5 * (R - ReX)); |
| R_X := YR / (2.0 * R_Y); |
| |
| else |
| R_X := Sqrt (0.5 * (R + ReX)); |
| R_Y := YR / (2.0 * R_X); |
| end if; |
| end if; |
| |
| if Im (X) < 0.0 then -- halve angle, Sqrt of magnitude |
| R_Y := -R_Y; |
| end if; |
| return Compose_From_Cartesian (R_X, R_Y); |
| |
| exception |
| when Constraint_Error => |
| |
| -- Rescale and try again |
| |
| R := Modulus (Compose_From_Cartesian (Re (X / 4.0), Im (X / 4.0))); |
| R_X := 2.0 * Sqrt (0.5 * R + 0.5 * Re (X / 4.0)); |
| R_Y := 2.0 * Sqrt (0.5 * R - 0.5 * Re (X / 4.0)); |
| |
| if Im (X) < 0.0 then -- halve angle, Sqrt of magnitude |
| R_Y := -R_Y; |
| end if; |
| |
| return Compose_From_Cartesian (R_X, R_Y); |
| end Sqrt; |
| |
| --------- |
| -- Tan -- |
| --------- |
| |
| function Tan (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| |
| elsif Im (X) > Log_Inverse_Epsilon_2 then |
| return Complex_I; |
| |
| elsif Im (X) < -Log_Inverse_Epsilon_2 then |
| return -Complex_I; |
| |
| else |
| return Sin (X) / Cos (X); |
| end if; |
| end Tan; |
| |
| ---------- |
| -- Tanh -- |
| ---------- |
| |
| function Tanh (X : Complex) return Complex is |
| begin |
| if abs Re (X) < Square_Root_Epsilon and then |
| abs Im (X) < Square_Root_Epsilon |
| then |
| return X; |
| |
| elsif Re (X) > Log_Inverse_Epsilon_2 then |
| return Complex_One; |
| |
| elsif Re (X) < -Log_Inverse_Epsilon_2 then |
| return -Complex_One; |
| |
| else |
| return Sinh (X) / Cosh (X); |
| end if; |
| end Tanh; |
| |
| end Ada.Numerics.Generic_Complex_Elementary_Functions; |