### ----------------------------------------------------
### -- Math::Libgsl::Matrix
### -- Licenses: Artistic-2.0
### -- Authors: Fernando Santagata
### -- File: lib/Math/Libgsl/Matrix.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
;               # view: an object that will contain the view information
my Math::Libgsl::Vector $v = $vv.array($parray);        # create an Math::Libgsl::Vector object
$v[0] = 2;                                              # assign a value to the first vector element
say $v[^10];                                            # output: (2 1 1 1 1 1 1 1 1 1)

=end code

Here are some examples of the "easiest way":

=begin code :lang<raku>

use Math::Libgsl::Vector;

my @array = ^10;                                                    # initialize an array
my ($min, $max) = num64-array-vec(-> $vec { $vec.minmax }, @array); # find min and max value
say "$min $max";                                                    # output: 0 9

=end code

=begin code :lang<raku>

use Math::Libgsl::Constants;
use Math::Libgsl::Vector;
use Math::Libgsl::MovingWindow;

my @array = ^10;                                        # initialize an array
my Math::Libgsl::MovingWindow $mw .= new: :samples(5);  # initialize a MovingWindow object with a 5-sample window
                                                        # compute the moving-window mean of the array
my $w = num64-array-vec(-> $vec { $mw.mean($vec, :endtype(GSL_MOVSTAT_END_PADVALUE)) }, @array);
say $w[^10];                                            # output: (0.6 1.2 2 3 4 5 6 7 7.8 8.4)

=end code

Even using this last construct, if the Vector object is to be used outside of the closure it must be declared before calling array-vec.

=begin code :lang<raku>

use Math::Libgsl::Vector;

my @array = 1 xx 10;                                            # define an array
my Math::Libgsl::Vector $v .= new: 10;                          # declare a Math::Libgsl::Vector
num64-array-vec(-> $vec { $v.copy($vec); $v[0] = 2; }, @array); # assign a value to the first vector element
say $v[^10];                                                    # output: (2 1 1 1 1 1 1 1 1 1)

=end code

=head3 num64-prepvec(@array)

This is just a sub, not a method; it gets a regular array and outputs a I<prepared> array, kept in a scalar variable.
There are similar functions for every data type, so for example if one is working with int16 Vectors, one will use the B<int16-prepvec> sub.
The num64, being the default data type, has a special B<prepvec> alias.
Once I<prepared>, the original array can be discarded.

=head3 array($parray --> Math::Libgsl::Vector)

This method gets a I<prepared> array and returns a Math::Libgsl::Vector object.

=head3 array-stride($array, size_t $stride --> Math::Libgsl::Vector)

This method gets a I<prepared> array and a B<$stride> and returns a Math::Libgsl::Vector object.

=head3 sub num64-array-vec(Block $bl, *@data)

This sub arguments are a Block to execute and a regular Raku array. Internally it uses a vector view to convert the array into libgsl's own vector data type, obtains the Math::Libgsl::Vector from the View, and passes it to the Block. When the Block exits the Math::Libgsl::Vector object ceases to exist, so it must be consumed inside the Block, or copied into another externally defined variable.
The num64, being the default data type, has a special B<array-vec> alias.

=head3 sub num64-array-stride-vec(Block $bl, size_t $stride, *@data)

This sub arguments are a Block to execute, a stride, and a regular Raku array. Internally it uses a vector view to convert the array into libgsl's own vector data type, obtains the Math::Libgsl::Vector from the View, and passes it to the Block. When the Block exits the Math::Libgsl::Vector object ceases to exist, so it must be consumed inside the Block, or copied into another externally defined variable.
The num64, being the default data type, has a special B<array-stride-vec> alias.


=head2 Matrix

=head3 new(Int $size1!, Int $size2!)
=head3 new(Int :$size1!, Int :$size2!)

The constructor accepts two parameters: the matrix sizes; they can be passed as Pairs or as single values.

All the following methods I<throw> on error if they return B<self>, otherwise they I<fail> on error.

=head3 list(--> List)

This method returns all the elements of the Matrix as a Raku List of Lists.

=head3 gist(--> Str)

This method returns the first 10 elements of the first 10 rows of the Matrix interpolated in a Raku Str.
The .gist method is used by the Raku say() function and method to print a representation of an object.

=head3 Str(--> Str)

This method returns all the elements of the Matrix interpolated in a Raku Str.
The .Str method is used by the Raku put() function and method to print an object.

=head3 get(Int:D $i! where * < $!matrix.size1, Int:D $j! where * < $!matrix.size2 --> Num)

This method returns the value of a matrix element.
It is possible to address a matrix element as a Raku shaped array element:

=begin code :lang<raku>
say $matrix[1;2];
=end code

=head3 set(Int:D $i! where * < $!matrix.size1, Int:D $j! where * < $!matrix.size2, Num(Cool)

This method sets the value of a matrix element.
This method can be chained.
It is possible to address a matrix element as a Raku shaped array element:

=begin code :lang<raku>
$matrix[1;3] = 3;
=end code

=head3 setall(Num(Cool))

Sets all the elements of the matrix to the same value.
This method can be chained.

=head3 zero()

Sets all the elements of the matrix to zero.
This method can be chained.

=head3 identity()

Sets all elements of the matrix to the corrisponding elements of the identity matrix.

=head3 size1(--> UInt)
=head3 size2(--> UInt)
=head3 size(--> List)

These methods return the first, second, or both the matrix sizes.

=head3 write(Str $filename!)

Writes the matrix to a file in binary form.
This method can be chained.

=head3 read(Str $filename!)

Reads the matrix from a file in binary form.
This method can be chained.

=head3 printf(Str $filename!, Str $format!)

Writes the matrix to a file using the specified format.
This method can be chained.

=head3 scanf(Str $filename!)

Reads the matrix from a file containing formatted data.
This method can be chained.

=head3 copy(Math::Libgsl::Matrix $src where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2)

This method copies the $src matrix into the current one.
This method can be chained.

=head3 swap(Math::Libgsl::Matrix $src where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2)

This method swaps elements of the $src matrix and the current one.
This method can be chained.

=head3 tricpy(Math::Libgsl::Matrix $src where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2, Int $Uplo, Int $Diag)

This method copies the upper or lower trianglular matrix from B<$src> to B<self>.
Use the B<cblas-uplo> enumeration to specify which triangle copy.
Use the B<cblas-diag> enumeration to specify whether to copy the matrix diagonal.
This method can be chained.

=head3 get-row(Int:D $i where * < $!matrix.size1)

This method returns an array from row number $i.

=head3 get-col(Int:D $j where * < $!matrix.size2)

This method returns an array from column number $j.

=head3 set-row(Int:D $i where * ≤ $!matrix.size1, @row where *.elems == $!matrix.size2)
=head3 set-row(Int:D $i where * ≤ $!matrix.size1, Math::Libgsl::Vector $v where .vector.size == $!matrix.size2)

These methods set row number $i of the matrix from a Raku array or a Vector object.
This method can be chained.

=head3 set-col(Int:D $j where * ≤ $!matrix.size2, @col where *.elems == $!matrix.size1)
=head3 set-col(Int:D $j where * ≤ $!matrix.size2, Math::Libgsl::Vector $v where .vector.size == $!matrix.size1)

These methods set column number $j of the matrix from a Raku array or a Vector object.
This method can be chained.

=head3 swap-rows(Int:D $i where * ≤ $!matrix.size1, Int:D $j where * ≤ $!matrix.size1)

This method swaps rows $i and $j.
This method can be chained.

=head3 swap-cols(Int:D $i where * ≤ $!matrix.size2, Int:D $j where * ≤ $!matrix.size2)

This method swaps columns $i and $j.
This method can be chained.

=head3 swap-rowcol(Int:D $i where * ≤ $!matrix.size1, Int:D $j where * ≤ $!matrix.size1)

This method exchanges row number $i with column number $j of a square matrix.
This method can be chained.

=head3 copy-transpose(Math::Libgsl::Matrix $src where $!matrix.size1 == .matrix.size2 && $!matrix.size2 == .matrix.size1)

This method copies a matrix into the current one, while transposing the elements.
This method can be chained.

=head3 transpose()

This method transposes the current matrix.
This method can be chained.

=head3 transpose-tricpy(Math::Libgsl::Matrix $src where $!matrix.size1 == .matrix.size2 && $!matrix.size2 == .matrix.size1, Int $Uplo, Int $Diag)

This method copies a triangle from the B<$src> matrix into the current one, while transposing the elements.
Use the B<cblas-uplo> enumeration to specify which triangle copy.
Use the B<cblas-diag> enumeration to specify whether to copy the matrix diagonal.
This method can be chained.

=head3 add(Math::Libgsl::Matrix $b where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2)

This method adds a matrix to the current one element-wise.
This method can be chained.

=head3 sub(Math::Libgsl::Matrix $b where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2)

This method subtracts a matrix from the current one element-wise.
This method can be chained.

=head3 mul(Math::Libgsl::Matrix $b where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2)

This method multiplies a matrix to the current one element-wise.
This method can be chained.

=head3 div(Math::Libgsl::Matrix $b where $!matrix.size1 == .matrix.size1 && $!matrix.size2 == .matrix.size2)

This method divides the current matrix by another one element-wise.
This method can be chained.

=head3 scale(Num(Cool) $x)

This method multiplies the elements of the current matrix by a constant value.
This method can be chained.

=head3 scale-rows(Math::Libgsl::Vector $x where .size == $!matrix.size1)

This method scales the rows of the M-by-N matrix by the elements of the vector $x, of length N. The i-th row of the matrix is multiplied by $xᵢ.
This method can be chained.
This method fails if the underlying C library's version is less than 2.7.

=head3 scale-columns(Math::Libgsl::Vector $x where .size == $!matrix.size1)

This method scales the columns of the M-by-N matrix by the elements of the vector $x, of length N. The j-th column of the matrix is multiplied by $xⱼ.
This method can be chained.
This method fails if the underlying C library's version is less than 2.7.

=head3 add-constant(Num(Cool) $x)

This method adds a constant to the elements of the current matrix.
This method can be chained.

=head3 max(--> Num)
=head3 min(--> Num)

These two methods return the min and max value in the matrix.
Not available in Math::Libgsl::Matrix::Complex32 and Math::Libgsl::Matrix::Complex64.

=head3 minmax(--> List)

This method returns a list of two values: the min and max value in the matrix.
Not available in Math::Libgsl::Matrix::Complex32 and Math::Libgsl::Matrix::Complex64.

=head3 max-index(--> Int)
=head3 min-index(--> Int)

These two methods return the index of the min and max value in the matrix.
Not available in Math::Libgsl::Matrix::Complex32 and Math::Libgsl::Matrix::Complex64.

=head3 minmax-index(--> List)

This method returns a list of two values: the indices of the min and max value in the matrix.
Not available in Math::Libgsl::Matrix::Complex32 and Math::Libgsl::Matrix::Complex64.

=head3 is-null(--> Bool)
=head3 is-pos(--> Bool)
=head3 is-neg(--> Bool)
=head3 is-nonneg(--> Bool)

These methods return True if all the elements of the matrix are zero, strictly positive, strictly negative, or non-negative.

=head3 is-equal(Math::Libgsl::Matrix $b --> Bool)

This method returns True if the matrices are equal element-wise.

=head3 norm1(--> Num)

This method returns the 1-norm of the m-by-n matrix, defined as the maximum column sum.
This method fails if the underlying C library's version is less than 2.7.

=head2 Matrix View

=begin code :lang<raku>

use Math::Libgsl::Matrix;

my Math::Libgsl::Matrix $m1 .= new(:size1(3), :size2(4));      # create a 3x4 matrix
$m1.setall(1);                                                 # set all elements to 1
my Math::Libgsl::Matrix::View $mv .= new;                      # create a Matrix View
my Math::Libgsl::Matrix $m2 = $mv.submatrix($m1, 1, 1, 2, 2);  # get a submatrix
$m2.setall(12);                                                # set the submatrix elements to 12
$m1.get-row($_)».fmt('%2d').put for ^3;                        # print the original matrix
# output:
# 1  1  1  1
# 1 12 12  1
# 1 12 12  1

=end code

There are three kinds of Matrix Views: a Matrix View on a Matrix, a Matrix View on a Vector, and a Matrix View on a Raku array.

This is the View of the first kind:

=head3 submatrix(Math::Libgsl::Matrix $m, size_t $k1 where * < $m.size1, size_t $k2 where * < $m.size2, size_t $n1, size_t $n2 --> Math::Libgsl::Matrix)

Creates a view on a subset of the matrix, starting from coordinates ($k1, $k2) with $n1 rows and $n2 columns.
This method returns a new Matrix object.
Any operation done on the returned matrix affects the original matrix as well.


These three methods create a matrix view on a Raku array:

There are three ways to view a Raku array as a Math::Libgsl::Vector object:

=item The hard way - use NativeCall's CArray to create a C-language-happy array.
=item The easy way - use the *-prepvec sub to convert a Raku array into a C array.
=item The easiest way - use the *-array-vec sub, pass it the Raku array or list and a closure or anonymous sub.

This is an example of the "hard way":

=begin code :lang<raku>

use Math::Libgsl::Matrix;
use NativeCall;

my CArray[num64] $array .= new: (1..6)».Num;            # define a CArray
my Math::Libgsl::Matrix::View $mv .= new;               # view: an object that will contain the view information
my Math::Libgsl::Matrix $m = $mv.array($array, 2, 3);   # create an Math::Libgsl::Matrix object
$m[0;0] = 2;                                            # assign a value to the first matrix element
say $m.get-row($_) for ^2;
# output:
# 2 2 3
# 4 5 6

=end code

This is an example of the "easy way":

Views on a Raku array are a bit more complex, because it's not possible to simply pass a Raku array to a C-language function.
So for this to work the programmer has to I<prepare> the array to be passed to the library. Both the View object and the I<prepared> array must not go out of scope.

=begin code :lang<raku>

use Math::Libgsl::Matrix;

my @array = 1 xx 10;                                    # define an array
my $parray = prepmat(@array);                           # prepare the array to be used as a Math::Libgsl::Matrix
my Math::Libgsl::Matrix::View $mv .= new;               # view: an object that will contain the view information
my Math::Libgsl::Matrix $m = $mv.array($parray, 2, 5);  # create an Math::Libgsl::Matrix object
$m[0;0] = 2;                                            # assign a value to the first matrix element
$m.get-row($_).put for ^2;
# output:
# 2 1 1 1 1
# 1 1 1 1 1

=end code

This is an example of the "easiest way":

=begin code :lang<raku>

use Math::Libgsl::Matrix;

my @array = 1..6;                                                                 # initialize an array
my Math::Libgsl::Matrix $matrix .= new: 2, 3;                                     # declare a Math::Libgsl::Matrix
num64-array-mat(-> $mat { $matrix.copy($mat); $matrix[0;0] = 0 }, 2, 3, @array);  # assign 0 to matrix element [0;0]
$matrix.get-row($_).put for ^2;
# output:
# 0 2 3
# 4 5 6

=end code

=head3 num64-prepmat(@array)

This is just a sub, not a method; it gets a regular array and outputs a I<prepared> array, kept in a scalar variable.
There are similar functions for every data type, so for example if one is working with int16 Vectors, one will use the B<int16-prepmat> sub.
The num64, being the default data type, has a special B<prepmat> alias.
Once I<prepared>, the original array can be discarded.

=head3 array($array, UInt $size1, UInt $size2 --> Math::Libgsl::Matrix)

This method gets a I<prepared> array and returns a Math::Libgsl::Matrix object.

=head3 array-tda($array, UInt $size1, UInt $size2, size_t $tda where * > $size2 --> Math::Libgsl::Matrix)

This method gets a I<prepared> array with a number of physical columns B<$tda>, which may differ from the corresponding dimension of the matrix, and returns a Math::Libgsl::Matrix object.

=head3 sub num64-array-mat(Block $bl, UInt $size1, UInt $size2, *@data)

This sub arguments are a Block to execute, the sizes of the desired resulting matrix, and a regular Raku array. Internally it uses a matrix view to convert the array into libgsl's own matrix data type, obtains the Math::Libgsl::Matrix from the View, and passes it to the Block. When the Block exits the Math::Libgsl::Matrix object ceases to exist, so it must be consumed inside the Block, or copied into another externally defined variable.
The num64, being the default data type, has a special B<array-mat> alias.

=head3 sub num64-array-tda-mat(Block $bl, UInt $size1, UInt $size2, size_t $tda where * > $size2, *@data)

This sub arguments are a Block to execute, the sizes of the desired resulting matrix, the number of physical columns B<$tda>, and a regular Raku array. Internally it uses a matrix view to convert the array into libgsl's own matrix data type, obtains the Math::Libgsl::Matrix from the View, and passes it to the Block. When the Block exits the Math::Libgsl::Matrix object ceases to exist, so it must be consumed inside the Block, or copied into another externally defined variable.
The num64, being the default data type, has a special B<array-tda-mat> alias.


There are two methods to create a Matrix View on a Vector:

=head3 vector(Math::Libgsl::Vector $v, size_t $n1, size_t $n2 --> Math::Libgsl::Matrix)

This method creates a Matrix object from a Vector object. The resultimg matrix will have $n1 rows and $n2 columns.

=head3 vector-tda(Math::Libgsl::Vector $v, size_t $n1, size_t $n2, size_t $tda where * > $n2 --> Math::Libgsl::Matrix)

This method creates a Matrix object from a Vector object, with a physical number of columns $tda which may differ from the correspondig dimension of the matrix. The resultimg matrix will have $n1 rows and $n2 columns.


=head2 Vector View on a Matrix

There is a fourth kind of View that involves a Matrix: a Vector View on a Matrix.
The following are methods of the Matrix class, not of the Matrix::View class, take a Vector::View argument, and deliver a Vector object.
The Matrix object must not go out of scope while one is operating on the resulting Vector.

=head3 row-view(Math::Libgsl::Vector::View $vv, size_t $i where * < $!matrix.size1 --> Math::Libgsl::Vector)

This method creates a Vector object from row $i of the matrix.

=head3 col-view(Math::Libgsl::Vector::View $vv, size_t $j where * < $!matrix.size2 --> Math::Libgsl::Vector)

This method creates a Vector object from column $j of the matrix.

=head3 subrow-view(Math::Libgsl::Vector::View $vv, size_t $i where * < $!matrix.size1, size_t $offset, size_t $n --> Math::Libgsl::Vector)

This method creates a Vector object from row $i of the matrix, starting from $offset and containing $n elements.

=head3 subcol-view(Math::Libgsl::Vector::View $vv, size_t $j where * < $!matrix.size2, size_t $offset, size_t $n --> Math::Libgsl::Vector)

This method creates a Vector object from column $j of the matrix, starting from $offset and containing $n elements.

=head3 diagonal-view(Math::Libgsl::Vector::View $vv --> Math::Libgsl::Vector)

This method creates a Vector object from the diagonal of the matrix.

=head3 subdiagonal-view(Math::Libgsl::Vector::View $vv, size_t $k where * < min($!matrix.size1, $!matrix.size2) --> Math::Libgsl::Vector)

This method creates a Vector object from the subdiagonal number $k of the matrix.

=head3 superdiagonal-view(Math::Libgsl::Vector::View $vv, size_t $k where * < min($!matrix.size1, $!matrix.size2) --> Math::Libgsl::Vector)

This method creates a Vector object from the superdiagonal number $k of the matrix.


=head1 C Library Documentation

For more details on libgsl see L<https://www.gnu.org/software/gsl/>.
The excellent C Library manual is available here L<https://www.gnu.org/software/gsl/doc/html/index.html>, or here L<https://www.gnu.org/software/gsl/doc/latex/gsl-ref.pdf> in PDF format.

=head1 Prerequisites

This module requires the libgsl library to be installed. Please follow the instructions below based on your platform:

=head2 Debian Linux and Ubuntu 20.04

=begin code
sudo apt install libgsl23 libgsl-dev libgslcblas0
=end code

That command will install libgslcblas0 as well, since it's used by the GSL.

=head2 Ubuntu 18.04

libgsl23 and libgslcblas0 have a missing symbol on Ubuntu 18.04.
I solved the issue installing the Debian Buster version of those three libraries:

=item L<http://http.us.debian.org/debian/pool/main/g/gsl/libgslcblas0_2.5+dfsg-6_amd64.deb>
=item L<http://http.us.debian.org/debian/pool/main/g/gsl/libgsl23_2.5+dfsg-6_amd64.deb>
=item L<http://http.us.debian.org/debian/pool/main/g/gsl/libgsl-dev_2.5+dfsg-6_amd64.deb>

=head1 Installation

To install it using zef (a module management tool):

=begin code
$ zef install Math::Libgsl::Matrix
=end code

=head1 AUTHOR

Fernando Santagata <nando.santagata@gmail.com>

=head1 COPYRIGHT AND LICENSE

Copyright 2020 Fernando Santagata

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod


