### ----------------------------------------------------
### -- Math::Libgsl::Complex
### -- Licenses: Artistic-2.0
### -- Authors: Fernando Santagata
### -- File: t/01-raw-complex.rakutest
### ----------------------------------------------------
#!/usr/bin/env raku

use Test;
use lib 'lib';
use Math::Libgsl::Raw::Complex :ALL;
use NativeCall;

subtest {
  my gsl_complex $c1;
  lives-ok { $c1 = alloc_gsl_complex }, 'can allocate a gsl_complex';
  lives-ok { mgsl_complex_rect(1e0, 2e0, $c1) }, 'can initialize a gsl_complex from (x y)';
  ok $c1.dat[0] == 1e0 && $c1.dat[1] == 2e0, 'cartesian initialization successful';
  my gsl_complex $c2 = alloc_gsl_complex;
  lives-ok { mgsl_complex_polar(1e0, 0e0, $c2) }, 'can initialize a gsl_complex from (r, θ)';
  ok $c2.dat[0] == 1e0 && $c2.dat[1] == 0e0, 'polar initialization successful';
  lives-ok { free_gsl_complex($c1) }, 'can free a gsl_complex';
  free_gsl_complex($c2);
}, 'representation';

subtest {
  my gsl_complex $c1  = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $c1);
  my gsl_complex $c2  = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 0e0, $c2);
  ok mgsl_complex_arg($c2) == 0, 'argument of a complex';
  ok mgsl_complex_abs($c2) == 1, 'magnitude of a complex';
  mgsl_complex_rect(12e0, 36e0, $c2);
  ok mgsl_complex_abs2($c2) == 1440, 'magnitude squared';
  is-approx mgsl_complex_logabs($c2), 3.636199196285023, 'logarithm of the magnitude';
  free_gsl_complex($c1);
  free_gsl_complex($c2);
}, 'properties';

subtest {
  my gsl_complex $c1  = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $c1);
  my gsl_complex $c2  = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 0e0, $c2);
  my gsl_complex $res = alloc_gsl_complex;
  lives-ok { mgsl_complex_add($c1, $c2, $res) }, 'can add two gsl_complex';
  ok $res.dat[0] == 2e0 && $res.dat[1] == 2e0, 'sum successful';
  lives-ok { mgsl_complex_sub($c1, $c2, $res) }, 'can subtract two gsl_complex';
  ok $res.dat[0] == 0e0 && $res.dat[1] == 2e0, 'subtraction successful';
  lives-ok { mgsl_complex_mul($c1, $c2, $res) }, 'can multiply two gsl_complex';
  ok $res.dat[0] == 1e0 && $res.dat[1] == 2e0, 'multiplication successful';
  lives-ok { mgsl_complex_div($c2, $c1, $res) }, 'can divide two gsl_complex';
  is-approx $res.dat[0], 0.19999999999999998e0, 'real part of result';
  is-approx $res.dat[1], -0.39999999999999997e0, 'real part of result';
  lives-ok { mgsl_complex_add_real($c1, 2e0, $res) }, 'can add a gsl_complex and a real';
  ok $res.dat[0] == 3e0 && $res.dat[1] == 2e0, 'sum successful';
  lives-ok { mgsl_complex_sub_real($c1, 2e0, $res) }, 'can subtract a gsl_complex and a real';
  ok $res.dat[0] == -1e0 && $res.dat[1] == 2e0, 'subtraction successful';
  lives-ok { mgsl_complex_mul_real($c1, 2e0, $res) }, 'can multiply a gsl_complex and a real';
  ok $res.dat[0] == 2e0 && $res.dat[1] == 4e0, 'multiplication successful';
  lives-ok { mgsl_complex_div_real($c2, 2e0, $res) }, 'can divide a gsl_complex by a real';
  ok $res.dat[0] == .5e0 && $res.dat[1] == 0e0, 'division successful';
  lives-ok { mgsl_complex_add_imag($c1, 2e0, $res) }, 'can add a gsl_complex and an imaginary number';
  ok $res.dat[0] == 1e0 && $res.dat[1] == 4e0, 'sum successful';
  lives-ok { mgsl_complex_sub_imag($c1, 2e0, $res) }, 'can subtract a gsl_complex and an imaginary number';
  ok $res.dat[0] == 1e0 && $res.dat[1] == 0e0, 'subtraction successful';
  lives-ok { mgsl_complex_mul_imag($c1, 2e0, $res) }, 'can multiply a gsl_complex and an imaginary number';
  ok $res.dat[0] == -4e0 && $res.dat[1] == 2e0, 'multiplication successful';
  lives-ok { mgsl_complex_div_imag($c2, 2e0, $res) }, 'can divide a gsl_complex by an imaginary number';
  ok $res.dat[0] == 0e0 && $res.dat[1] == -.5e0, 'division successful';
  lives-ok { mgsl_complex_conjugate($c1, $res) }, 'can conjugate a gsl_complex';
  ok $res.dat[0] == 1e0 && $res.dat[1] == -2e0, 'conjugation successful';
  lives-ok { mgsl_complex_inverse($c1, $res) }, 'can invert a gsl_complex';
  is-approx $res.dat[0], 0.19999999999999998e0, 'real part of inversion';
  is-approx $res.dat[1], -0.39999999999999997e0, 'real part of inversion';
  lives-ok { mgsl_complex_negative($c1, $res) }, 'can negate a gsl_complex';
  ok $res.dat[0] == -1e0 && $res.dat[1] == -2e0, 'negation successful';
  free_gsl_complex($c1);
  free_gsl_complex($c2);
  free_gsl_complex($res);
}, 'arithmetic operators';

subtest {
  my gsl_complex $z = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $z);
  my gsl_complex $res = alloc_gsl_complex;
  lives-ok { mgsl_complex_sqrt($z, $res) }, 'can sqrt a gsl_complex';
  is-approx $res.dat[0], 1.272019649514069e0, 'real part of';
  is-approx $res.dat[1], 0.7861513777574233e0, 'imaginary part of';
  lives-ok { mgsl_complex_sqrt_real(-1e0, $res) }, 'can sqrt a negative real';
  is-approx $res.dat[0], 0e0, 'real part';
  is-approx $res.dat[1], 1e0, 'imaginary part';
  my gsl_complex $a = alloc_gsl_complex;
  mgsl_complex_rect(2e0, 2e0, $a);
  lives-ok { mgsl_complex_pow($z, $a, $res) }, 'can do z₁**z₂';
  is-approx $res.dat[0], -0.4239354190739234e0, 'real part';
  is-approx $res.dat[1], -0.34432518246746985e0, 'imaginary part';
  lives-ok { mgsl_complex_pow_real($z, 2e0, $res) }, 'can do z**x';
  is-approx $res.dat[0], -2.9999999999999987e0, 'real part';
  is-approx $res.dat[1], 4e0, 'imaginary part';
  lives-ok { mgsl_complex_exp($z, $res) }, 'can do exp(z)';
  is-approx $res.dat[0], -1.1312043837568135e0, 'real part';
  is-approx $res.dat[1], 2.4717266720048188e0, 'imaginary part';
  lives-ok { mgsl_complex_log($z, $res) }, 'can do log(z)';
  is-approx $res.dat[0], 0.8047189562170501e0, 'real part';
  is-approx $res.dat[1], 1.1071487177940904e0, 'imaginary part';
  lives-ok { mgsl_complex_log10($z, $res) }, 'can do log₁₀(z)';
  is-approx $res.dat[0], 0.3494850021680093e0, 'real part';
  is-approx $res.dat[1], 0.48082857878423396e0, 'imaginary part';
  lives-ok { mgsl_complex_log_b($z, $a, $res) }, 'can do logb(z)';
  is-approx $res.dat[0], 1.0049273671321273e0, 'real part';
  is-approx $res.dat[1], 0.3057365190885732e0, 'imaginary part';
  free_gsl_complex($z);
  free_gsl_complex($a);
  free_gsl_complex($res);
}, 'elementary functions';

subtest {
  my gsl_complex $z = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $z);
  my gsl_complex $res = alloc_gsl_complex;
  lives-ok { mgsl_complex_sin($z, $res) }, 'can sin(z)';
  is-approx $res.dat[0], 3.165778513216168e0, 'real part';
  is-approx $res.dat[1], 1.9596010414216063e0, 'imaginary part';
  lives-ok { mgsl_complex_cos($z, $res) }, 'can cos(z)';
  is-approx $res.dat[0], 2.0327230070196656e0, 'real part';
  is-approx $res.dat[1], -3.0518977991518e0, 'imaginary part';
  lives-ok { mgsl_complex_tan($z, $res) }, 'can tan(z)';
  is-approx $res.dat[0], 0.033812826079896684e0, 'real part';
  is-approx $res.dat[1], 1.0147936161466335e0, 'imaginary part';
  lives-ok { mgsl_complex_sec($z, $res) }, 'can sec(z)';
  is-approx $res.dat[0], 0.1511762982655772e0, 'real part';
  is-approx $res.dat[1], 0.22697367539372157e0, 'imaginary part';
  lives-ok { mgsl_complex_csc($z, $res) }, 'can csc(z)';
  is-approx $res.dat[0], 0.22837506559968657e0, 'real part';
  is-approx $res.dat[1], -0.14136302161240782e0, 'imaginary part';
  lives-ok { mgsl_complex_cot($z, $res) }, 'can cot(z)';
  is-approx $res.dat[0], 0.03279775553375259e0, 'real part';
  is-approx $res.dat[1], -0.984329226458191e0, 'imaginary part';
  free_gsl_complex($z);
  free_gsl_complex($res);
}, 'trigonometric functions';

subtest {
  my gsl_complex $z = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $z);
  my gsl_complex $res = alloc_gsl_complex;
  lives-ok { mgsl_complex_arcsin($z, $res) }, 'can arcsin(z)';
  is-approx $res.dat[0], 0.42707858639247614e0, 'real part';
  is-approx $res.dat[1], 1.5285709194809982e0, 'imaginary part';
  lives-ok { mgsl_complex_arcsin_real(-2e0, $res) }, 'can arcsin(x)';
  is-approx $res.dat[0], -1.5707963267948966e0, 'real part';
  is-approx $res.dat[1], 1.3169578969248166e0, 'imaginary part';
  lives-ok { mgsl_complex_arccos($z, $res) }, 'can arccos(z)';
  is-approx $res.dat[0], 1.1437177404024204e0, 'real part';
  is-approx $res.dat[1], -1.5285709194809982e0, 'imaginary part';
  lives-ok { mgsl_complex_arccos_real(-2e0, $res) }, 'can arccos(x)';
  is-approx $res.dat[0], 3.141592653589793e0, 'real part';
  is-approx $res.dat[1], -1.3169578969248166e0, 'imaginary part';
  lives-ok { mgsl_complex_arctan($z, $res) }, 'can arctan(z)';
  is-approx $res.dat[0], 1.3389725222944937e0, 'real part';
  is-approx $res.dat[1], 0.4023594781085251e0, 'imaginary part';
  lives-ok { mgsl_complex_arcsec($z, $res) }, 'can arcsec(z)';
  is-approx $res.dat[0], 1.384478272687081e0, 'real part';
  is-approx $res.dat[1], 0.3965682301123289e0, 'imaginary part';
  lives-ok { mgsl_complex_arcsec_real(.5e0, $res) }, 'can arcsec(x)';
  is-approx $res.dat[0], 0e0, 'real part';
  is-approx $res.dat[1], 1.3169578969248166e0, 'imaginary part';
  lives-ok { mgsl_complex_arccsc($z, $res) }, 'can arccsc(z)';
  is-approx $res.dat[0], 0.1863180541078155e0, 'real part';
  is-approx $res.dat[1], -0.3965682301123289e0, 'imaginary part';
  lives-ok { mgsl_complex_arccsc_real(.5e0, $res) }, 'can arccsc(x)';
  is-approx $res.dat[0], 1.5707963267948966e0, 'real part';
  is-approx $res.dat[1], -1.3169578969248166e0, 'imaginary part';
  lives-ok { mgsl_complex_arccot($z, $res) }, 'can arccot(z)';
  is-approx $res.dat[0], 0.23182380450040302e0, 'real part';
  is-approx $res.dat[1], -0.40235947810852507e0, 'imaginary part';
  free_gsl_complex($z);
  free_gsl_complex($res);
}, 'inverse trigonometric functions';

subtest {
  my gsl_complex $z = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $z);
  my gsl_complex $res = alloc_gsl_complex;
  lives-ok { mgsl_complex_sinh($z, $res) }, 'can sinh(z)';
  is-approx $res.dat[0], -0.4890562590412937e0, 'real part';
  is-approx $res.dat[1], 1.4031192506220405e0, 'imaginary part';
  lives-ok { mgsl_complex_cosh($z, $res) }, 'can cosh(z)';
  is-approx $res.dat[0], -0.64214812471552e0, 'real part';
  is-approx $res.dat[1], 1.0686074213827783e0, 'imaginary part';
  lives-ok { mgsl_complex_tanh($z, $res) }, 'can tanh(z)';
  is-approx $res.dat[0], 1.16673625724092e0, 'real part';
  is-approx $res.dat[1], -0.24345820118572528e0, 'imaginary part';
  lives-ok { mgsl_complex_sech($z, $res) }, 'can sech(z)';
  is-approx $res.dat[0], -0.41314934426693994e0, 'real part';
  is-approx $res.dat[1], -0.6875274386554789e0, 'imaginary part';
  lives-ok { mgsl_complex_csch($z, $res) }, 'can csch(z)';
  is-approx $res.dat[0], -0.22150093085050945e0, 'real part';
  is-approx $res.dat[1], -0.6354937992539e0, 'imaginary part';
  lives-ok { mgsl_complex_coth($z, $res) }, 'can coth(z)';
  is-approx $res.dat[0], 0.8213297974938518e0, 'real part';
  is-approx $res.dat[1], 0.17138361290918508e0, 'imaginary part';
  free_gsl_complex($z);
  free_gsl_complex($res);
}, 'hyperbolic functions';

subtest {
  my gsl_complex $z = alloc_gsl_complex;
  mgsl_complex_rect(1e0, 2e0, $z);
  my gsl_complex $res = alloc_gsl_complex;
  lives-ok { mgsl_complex_arcsinh($z, $res) }, 'can arcsinh(z)';
  is-approx $res.dat[0], 1.4693517443681854e0, 'real part';
  is-approx $res.dat[1], 1.0634400235777521e0, 'imaginary part';
  lives-ok { mgsl_complex_arccosh($z, $res) }, 'can arccosh(z)';
  is-approx $res.dat[0], 1.5285709194809982e0, 'real part';
  is-approx $res.dat[1], 1.1437177404024204e0, 'imaginary part';
  lives-ok { mgsl_complex_arccosh_real(0e0, $res) }, 'can arccosh(x)';
  is-approx $res.dat[0], 0e0, 'real part';
  is-approx $res.dat[1], 1.5707963267948966e0, 'imaginary part';
  lives-ok { mgsl_complex_arctanh($z, $res) }, 'can arctanh(z)';
  is-approx $res.dat[0], 0.17328679513998635e0, 'real part';
  is-approx $res.dat[1], 1.1780972450961724e0, 'imaginary part';
  lives-ok { mgsl_complex_arctanh_real(2e0, $res) }, 'can arctanh(x)';
  is-approx $res.dat[0], 0.5493061443340548e0, 'real part';
  is-approx $res.dat[1], -1.5707963267948966e0, 'imaginary part';
  lives-ok { mgsl_complex_arcsech($z, $res) }, 'can arcsech(z)';
  is-approx $res.dat[0], 0.3965682301123289e0, 'real part';
  is-approx $res.dat[1], -1.384478272687081e0, 'imaginary part';
  lives-ok { mgsl_complex_arccsch($z, $res) }, 'can arccsch(z)';
  is-approx $res.dat[0], 0.21561241855582963e0, 'real part';
  is-approx $res.dat[1], -0.4015863916678061e0, 'imaginary part';
  lives-ok { mgsl_complex_arccoth($z, $res) }, 'can arccoth(z)';
  is-approx $res.dat[0], 0.17328679513998635e0, 'real part';
  is-approx $res.dat[1], -0.39269908169872414e0, 'imaginary part';
  free_gsl_complex($z);
  free_gsl_complex($res);
}, 'inverse hyperbolic functions';

done-testing;


