### ----------------------------------------------------
### -- Math::Libgsl::Permutation
### -- Licenses: Artistic-2.0
### -- Authors: Fernando Santagata
### -- File: t/02-permutation.rakutest
### ----------------------------------------------------
#!/usr/bin/env raku

use Test;
use lib 'lib';
use Math::Libgsl::Permutation;
use Math::Libgsl::Vector;
use Math::Libgsl::Matrix;
use Math::Libgsl::Exception;
use Math::Libgsl::Constants;

subtest {
  my Math::Libgsl::Permutation $p .= new(:elems(10));
  isa-ok $p, Math::Libgsl::Permutation, 'new using pair';
  ok $p.get(9) == 9, 'get an element';
  throws-like { $p.get(10) }, X::Libgsl, message => /'out of range'/, 'fails if requested element is out of range';
  my Math::Libgsl::Permutation $p1 .= new(10);
  isa-ok $p1, Math::Libgsl::Permutation, 'new using single value';
  ok $p1.get(9) == 9, 'get an element';
  lives-ok { $p.swap(9, 8) }, "swap lives";
  ok $p.get(9) == 8, 'elements swapped';
  throws-like { $p.swap(9, 10) }, X::Libgsl, message => /'out of range'/, 'fails if one element is out of range';
  ok $p.size() == 10, 'permutation size';
  ok $p.is-valid, 'valid permutation';
  lives-ok { $p.reverse }, 'reverse permutation lives';
  is-deeply $p.all, (8, 9, 7, 6, 5, 4, 3, 2, 1, 0), 'reversed data ok';
  my Math::Libgsl::Permutation $p2 .= new(:elems(10));
  lives-ok { $p2.copy($p) }, 'permutation copy returns ok';
  is-deeply $p2.all, (8, 9, 7, 6, 5, 4, 3, 2, 1, 0), 'copied data ok';
  lives-ok { $p2.init }, 'init returns ok';
  is-deeply $p2.all, (^10).list, 'init data ok';
  lives-ok { $p.inverse($p2) }, 'inverse permutation lives';
  is-deeply $p2.all, (9, 8, 7, 6, 5, 4, 3, 2, 0, 1), 'inverted data ok';
  ok Math::Libgsl::Permutation.new(:elems(10)).reverse.get(9) == 0, 'operation chaining';
}, 'basics';

subtest {
  my Math::Libgsl::Permutation $p .= new(:elems(5));
  lives-ok { $p.next }, 'can get next permutation';
  is-deeply $p.all, (0, 1, 2, 4, 3), 'next permutation ok';
  my $filename = 't/permutation.dat';
  LEAVE { with $filename.IO { .unlink if .e } }
  ok $p.write($filename) == GSL_SUCCESS, 'can write data to file';
  ok $p.read($filename) == GSL_SUCCESS, 'can read data from file';
  is-deeply $p.all, (0, 1, 2, 4, 3), 'verify data';
  lives-ok { $p.prev }, 'can get previous permutation';
  is-deeply $p.all, (0, 1, 2, 3, 4), 'previous permutation ok';
  $p.init;
  nok $p.bprev, 'bprev returns False when there is no previous permutation';
  while $p.bnext {}
  nok $p.bnext, 'bnext returns False at end of available permutations';
}, 'next & prev';

subtest {
  my Math::Libgsl::Permutation $p .= new(10).swap(9, 8).reverse;
  my @data = 100..109;
  is-deeply $p.permute(@data, 1),
    (108e0, 109e0, 107e0, 106e0, 105e0, 104e0, 103e0, 102e0, 101e0, 100e0), 'permute an array';
  @data = 100..109;
  is-deeply $p.permute-inverse(@data, 1),
    (109e0, 108e0, 107e0, 106e0, 105e0, 104e0, 103e0, 102e0, 100e0, 101e0), 'permute-inverse an array';
  my Complex @dataz = 100+i…109+i;
  is-deeply $p.permute-complex64(@dataz, 1),
    (108+1i, 109+1i, 107+1i, 106+1i, 105+1i, 104+1i, 103+1i, 102+1i, 101+1i, 100+1i), 'permute a complex array';
  @dataz = 100+i…109+i;
  is-deeply $p.permute-complex64-inverse(@dataz, 1),
    (109+1i, 108+1i, 107+1i, 106+1i, 105+1i, 104+1i, 103+1i, 102+1i, 100+1i, 101+1i), 'permute-inverse a complex array';
  @dataz = 100+i…109+i;
  is-deeply $p.permute-complex32(@dataz, 1),
    (108+1i, 109+1i, 107+1i, 106+1i, 105+1i, 104+1i, 103+1i, 102+1i, 101+1i, 100+1i), 'permute a complex float array';
  @dataz = 100+i…109+i;
  is-deeply $p.permute-complex32-inverse(@dataz, 1),
    (109+1i, 108+1i, 107+1i, 106+1i, 105+1i, 104+1i, 103+1i, 102+1i, 100+1i, 101+1i), 'permute-inverse a complex float array';
  my Math::Libgsl::Vector $v .= new(10);
  $v[$_] = $_ for ^10;
  $p.permute-vector($v);
  is-deeply $v[^10], (8, 9, 7, 6, 5, 4, 3, 2, 1, 0)».Num, 'permute a vector';
  $p.permute-vector-inv($v);
  is-deeply $v[^10], (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)».Num, 'inverse permute a vector';
  my Math::Libgsl::Matrix $m .= new(10,10);
  $m.set-row($_, (^10).list «*» $_) for ^10;
  $p.permute-matrix($m);
  is-deeply $m.get-col(0), [0, 8, 16, 24, 32, 40, 48, 56, 64, 72]».Num, 'permute a matrix';
}, 'apply permutation';

subtest {
  my Math::Libgsl::Permutation $p1  .= new(:elems(5));
  my Math::Libgsl::Permutation $p2  .= new(:elems(5));
  my Math::Libgsl::Permutation $res .= new(:elems(5));
  $p1.next;
  $p2.next;
  $p2.next;
  $p1.multiply($res, $p2);
  is-deeply $res.all, (0, 1, 3, 4, 2), 'multiply two permutations';
}, 'multiplication';

subtest {
  my Math::Libgsl::Permutation $p1  .= new(:elems(5));
  my Math::Libgsl::Permutation $res .= new(:elems(5));
  lives-ok { $p1.to-canonical($res) }, 'linear to canonical';
  is-deeply $res.all, (4, 3, 2, 1, 0), 'canonical data';
  lives-ok { $res.to-linear($p1) }, 'canonical to linear';
  is-deeply $p1.all, (0, 1, 2, 3, 4), 'linear data';
  ok $res.inversions == 10, 'number of inversions';
  ok $p1.linear-cycles == 5, 'count linear cycles';
  ok $p1.canonical-cycles == 1, 'count canonical cycles';
}, 'cyclic form';

done-testing;


