Math::...:Linalg(3pm)
π Math::GSL::Linalg β the Math::GSL::Linalg manual, part 1 of 3 π
okay bestie, buckle UP, we're doing linear systems today and honestly? she's iconic for it. this is Math::GSL::Linalg, from the User Contributed Perl Documentation girlies, and she is all about solving linear systems. no cap. let's get into it β¨
πΈ NAME πΈ
Math::GSL::Linalg β Functions for solving linear systems. that's it, that's the whole personality, and honestly? a queen who knows what she wants π«Ά
β¨ SYNOPSIS β¨
the vibe check, the entrance, the main character moment:
use Math::GSL::Linalg qw/:all/;
one line. pulls in everything. lowkey obsessed with how easy that is π
π¦ DESCRIPTION π¦
so like, here's a list of all the functions chilling inside this module β every single one, no gatekeeping, periodt:
gsl_linalg_matmultgsl_linalg_matmult_modgsl_linalg_exponential_ssgsl_linalg_householder_transformgsl_linalg_complex_householder_transform
(yes babe, those five are just vibing with no little description next to them. mysterious. we respect it π)
and now for the ones that do spill the tea:
gsl_linalg_householder_hm($tau, $v, $A)β this one takes the Householder matrixP(defined by the scalar$tauand the vector$v) and applies it to the left-hand side of the matrix$A. on output, the resultP Agets stored right back into$A. and the tea? it returns0if it succeeded, and1otherwise. slay π₯gsl_linalg_householder_mh($tau, $v, $A)β same energy, opposite side: applies the Householder matrixP(again defined by the scalar$tauand the vector$v) to the right-hand side of the matrix$A. on output the resultA Pis stored in$A. she's giving mirror image, and we love her for it πgsl_linalg_householder_hv($tau, $v, $w)β here she applies the Householder transformationP(defined by the scalar$tauand the vector$v) to the vector$w. on output, the resultP wis stored in$w. vector on vector action, iykyk πgsl_linalg_householder_hm1β just out here existing, no description, no drama, a true mystery icon πgsl_linalg_givens($a,$b,$c,$s)β performs a Givens rotation on the vector($a,$b)and stores the answer in$cand$s.gsl_linalg_givens_gv($v, $i,$j, $c, $s)β performs a Givens rotation on the$iand$j-th elements of$v, storing them in$cand$s. very targeted. very precise. we stan a function that knows exactly which elements it's about β¨
more of this manual is incoming babe, we're only at part 1 of 3 β don't go anywhere π
gsl_linalg_complex_householder_hm($tau, $v, $A)β bestie it's the exact same move asgsl_linalg_householder_hm, just with the complex glow-up: complex matrix$A, complex value$tau, complex vector$v. same energy, more imaginary parts πgsl_linalg_complex_householder_mh($tau, $v, $A)β and this one isgsl_linalg_householder_mhagain but complex-coded: complex matrix$A, complex value$tau, complex vector$v.gsl_linalg_complex_householder_hv($tau, $v, $w)β same asgsl_linalg_householder_hv, but the complex version: complex value$tau, complex vectors$vand$w. slay πΈgsl_linalg_hessenberg_decomp($A, $tau)β okay so this one computes the Hessenberg decomposition of matrix$Aby applying the similarity transformationH = U^T A U. On output, H is stored in the upper portion of$A. The info you need to build the matrix U lives in the lower triangular portion of$A. U is a product ofN - 2Householder matrices, no cap. The Householder vectors are stored in the lower portion of$A(below the subdiagonal) and the Householder coefficients are stored in the vector$tau.taumust be of lengthN. Returns0if it succeeded,1otherwise. it's giving very precise β¨gsl_linalg_hessenberg_unpack($H, $tau, $U)β this one constructs the orthogonal matrix$Uout of the info stored in the Hessenberg matrix$Hplus the vector$tau. and yeah$Hand$tauare literally the outputs fromgsl_linalg_hessenberg_decomp, so you can't just roll up with randoms here π«Άgsl_linalg_hessenberg_unpack_accum($H, $tau, $V)β similar vibe togsl_linalg_hessenberg_unpack, except it accumulates the matrix U into$V, so thatV' = VU. The matrix$Vmust be initialized before you call this, okay? and setting$Vto the identity matrix gives you the same result asgsl_linalg_hessenberg_unpack. also: if$His orderN, then$Vmust haveNcolumns but it can have any number of rows. flexible queen πgsl_linalg_hessenberg_set_zero($H)β sets the lower triangular portion of$H(below the subdiagonal) to zero. it's the cleanup era: useful for clearing out the Householder vectors after you callgsl_linalg_hessenberg_decomp. self-care honestly π§gsl_linalg_hessenberg_submatrixgsl_linalg_hessenberggsl_linalg_hesstri_decomp($A, $B, $U, $V, $work)β computes the Hessenberg-Triangular decomposition of the matrix pair ($A,$B). On output, H is stored in$A, and R is stored in$B. If$Uand$Vare provided (and yes they may be null, no pressure), the similarity transformations are stored in them. You'll also need additional workspace of lengthNin the vector$work. πgsl_linalg_SV_decomp($A, $V, $S, $work)β this factorizes the M-by-N matrix$Ainto the singular value decompositionA = U S V^TforM >= N. On output the matrix$Ais replaced by U. The diagonal elements of the singular value matrix S are stored in the vector$S. The singular values are non-negative and form a non-increasing sequence fromS_1toS_N. The matrix$Vcontains the elements of V in untransposed form β so to form the productU S V^Tyou gotta take the transpose of V, don't skip that step bestie. A workspace of lengthNis required in vector$work. This routine uses the Golub-Reinsch SVD algorithm. iconic algorithm behavior πgsl_linalg_SV_decomp_mod($A, $X, $V, $S, $work)β computes the SVD using the modified Golub-Reinsch algorithm, which is faster forM>>N. It requires the vector$workof lengthNand the N-by-N matrix$Xas additional working space.$Aand$Vare matrices while$Sis a vector. speed demon energy π₯gsl_linalg_SV_decomp_jacobi($A, $V, $S)β computes the SVD of the M-by-N matrix$Ausing one-sided Jacobi orthogonalization forM >= N. The Jacobi method can compute singular values to higher relative accuracy than Golub-Reinsch algorithms.$Vis a matrix while$Sis a vector. accuracy ate, no crumbs β¨gsl_linalg_SV_solve($U, $V, $S, $b, $x)β solves the systemA x = busing the singular value decomposition ($U,$S,$V) of A given bygsl_linalg_SV_decomp. Only non-zero singular values get used in computing the solution β the parts of the solution corresponding to singular values of zero are just ignored, periodt. Other singular values can be edited out by setting them to zero before you call this function. And in the over-determined case where A has more rows than columns, the system is solved in the least squares sense, returning the solution x which minimizes||A x - b||_2. math but make it serve πgsl_linalg_LU_decomp($a, $p)β factorize the matrix$ainto the LU decompositionPA = LU. On output the diagonal and upper triangular part of the input matrix A contain the matrix U. The lower triangular part of the input matrix (excluding the diagonal) contains L. The diagonal elements of L are unity, and are not stored β they're just implied, lowkey mysterious of them. The function returns two values: the first is0if the operation succeeded,1otherwise, and the second is the sign of the permutation. two-for-one πgsl_linalg_LU_solve($LU, $p, $b, $x)β solves the square systemA x = busing the LU decomposition of the matrix A into (LU,p) given bygsl_linalg_LU_decomp.$LUis a matrix,$pa permutation, and$band$xare vectors. Returns1if the operation succeeded,0otherwise. (yes, flipped from the last one, don't come for me, that's just how it is π)gsl_linalg_LU_svx($LU, $p, $x)β solves the square systemA x = bin-place using the LU decomposition of A into (LU,p). On input$xshould contain the right-hand side b, which gets replaced by the solution on output.$LUis a matrix,$pa permutation,$xis a vector. Returns1if the operation succeeded,0otherwise. πΈgsl_linalg_LU_refine($A, $LU, $p, $b, $x, $residual)β applies an iterative improvement to$x, the solution of$A $x = $b, using the LU decomposition of$Ainto ($LU,$p). The initial residual$r = $A $x - $b(where$xand$bare vectors) is also computed and stored in the vector$residual. glow-up on your answer, literally π«Άgsl_linalg_LU_invert($LU, $p, $inverse)β computes the inverse of a matrix A from its LU decomposition stored in the matrix$LUand the permutation$p, storing the result in the matrix$inverse. flip era πgsl_linalg_LU_det($LU, $signum)β returns the determinant of a matrix A from its LU decomposition stored in the$LUmatrix. It needs the integer$signum, which is the sign of the permutation returned bygsl_linalg_LU_decomp. don't forget to feed it the signum, it will simply not know πgsl_linalg_LU_lndet($LU)β returns the logarithm of the absolute value of the determinant of a matrix A, from its LU decomposition stored in the$LUmatrix. log era, very demure, very mindful β¨gsl_linalg_LU_sgndet($LU, $signum)β computes the sign or phase factor of the determinant of a matrix A,det(A)/|det(A)|, from its LU decomposition,$LU. βgsl_linalg_complex_LU_decomp($A, $p)β does the same operation asgsl_linalg_LU_decompbut on the complex matrix$A. complex girlies rise πgsl_linalg_complex_LU_solve($LU, $p, $b, $x)β solves the square systemA x = busing the LU decomposition of A into ($LU,$p) given bygsl_linalg_complex_LU_decomp. π¦gsl_linalg_complex_LU_svx($LU, $p, $x)β does the same operation asgsl_linalg_LU_svxbut on the complex matrix$LUand the complex vector$x. πgsl_linalg_complex_LU_refine($A, $LU, $p, $b, $x, $residual)β does the same operation asgsl_linalg_LU_refinebut on the complex matrices$Aand$LUand with the complex vectors$b,$xand$residual. β€οΈgsl_linalg_complex_LU_invert($LU, $p, $inverse)β does the same operation asgsl_linalg_LU_invertbut on the complex matrices$LUand$inverse. (the original says "matrces" and honestly? typo queen, we respect her π«Ά) πgsl_linalg_complex_LU_det($LU, $signum)β does the same operation asgsl_linalg_LU_detbut on the complex matrix$LU.gsl_linalg_complex_LU_lndet($LU)β does the same operation asgsl_linalg_LU_detbut on the complex matrix$LU. π₯gsl_linalg_complex_LU_sgndet($LU, $signum)β does the same operation asgsl_linalg_LU_sgndetbut on the complex matrix$LU.gsl_linalg_QR_decomp($a, $tau)β factorize the M-by-N matrix A into the QR decompositionA = Q R. On output the diagonal and upper triangular part of the input matrix$acontain the matrix R. The vector$tauand the columns of the lower triangular part of the matrix$acontain the Householder coefficients and Householder vectors which encode the orthogonal matrix Q. The vectortaumust be of lengthk= min(M,N). write that down πgsl_linalg_QR_solve($QR, $tau, $b, $x)β solves the square systemA x = busing the QR decomposition of A into (QR,tau) given bygsl_linalg_QR_decomp.$QRis a matrix, and$tau,$band$xare vectors.gsl_linalg_QR_svx($QR, $tau, $x)β solves the square systemA x = bin-place using the QR decomposition of A into the matrix$QRand the vector$taugiven bygsl_linalg_QR_decomp. On input, the vector$xshould contain the right-hand side b, which gets replaced by the solution on output. πΈgsl_linalg_QR_lssolve($QR, $tau, $b, $x, $residual)β finds the least squares solution to the overdetermined system$A $x = $bwhere the matrix$Ahas more rows than columns. The least squares solution minimizes the Euclidean norm of the residual,||Ax - b||. The routine uses the$QRdecomposition of$Ainto ($QR,$tau) given bygsl_linalg_QR_decomp. The solution is returned in$x. The residual is computed as a by-product and stored in residual. Returns0if it succeeded,1otherwise. β¨gsl_linalg_QR_QRsolve($Q, $R, $b, $x)β solves the system$R $x = $Q**T $bfor$x. You can use this when the$QRdecomposition of a matrix is available in unpacked form as ($Q,$R). Returns0if it succeeded,1otherwise. π₯gsl_linalg_QR_Rsolve($QR, $b, $x)β solves the triangular systemR $x = $bfor$x. It may be useful if the productb' = Q^T bhas already been computed usinggsl_linalg_QR_QTvec. iykyk π«Άgsl_linalg_QR_Rsvx($QR, $x)β solves the triangular systemR $x = bfor$xin-place. On input$xshould contain the right-hand side b and is replaced by the solution on output. This function may be useful if the productb' = Q^T bhas already been computed usinggsl_linalg_QR_QTvec. Returns0if it succeeded,1otherwise. πgsl_linalg_QR_update($Q, $R, $b, $x)β performs a rank-1 update$w $v**Tof the QR decomposition ($Q,$R). The update is given byQ'R' = Q R + w v^Twhere the output matricesQ'andR'are also orthogonal and right triangular. Note that w is destroyed by the update β she does not survive, bestie, so don't come looking for her after π. Returns0if it succeeded,1otherwise.gsl_linalg_QR_QTvec($QR, $tau, $v)β applies the matrixQ^Tencoded in the decomposition ($QR,$tau) to the vector$v, storing the resultQ^T vin$v. The matrix multiplication is carried out directly using the encoding of the Householder vectors without needing to form the full matrixQ^T. Returns0if it succeeded,1otherwise. so efficient, she's not doing extra work for anybody πgsl_linalg_QR_Qvec($QR, $tau, $v)β applies the matrix Q encoded in the decomposition ($QR,$tau) to the vector$v, storing the resultQ vin$v. The matrix multiplication is carried out directly using the encoding of the Householder vectors without needing to form- ...and that's your full matrix Q right there. This function returns
0if it succeeded,1otherwise. β¨ gsl_linalg_QR_QTmat($QR, $tau, $A)β applies the matrix Q^T that's encoded in the decomposition($QR,$tau)onto the matrix$A, and drops the result Q^T A right back into$A. The tea: it does the whole multiplication directly using the encoding of the Householder vectors, so it never has to actually form the full matrix Q^T. No cap. Returns0if it succeeded,1otherwise. πgsl_linalg_QR_unpack($QR, $tau, $Q, $R)β unpacks that encoded QR decomposition($QR,$tau)into the matrices$Qand$R, where$Qis M-by-M and$Ris M-by-N. Returns0if it succeeded,1otherwise.gsl_linalg_R_solve($R, $b, $x)β solves the triangular system$R$x=$bfor the N-by-N matrix$R. Returns0if it succeeded,1otherwise. π¦gsl_linalg_R_svx($R, $x)β solves the triangular system$R$x= b in-place. On input,$xshould have the right-hand side b in it, and on output that gets replaced by the solution. She swaps. π Returns0if it succeeded,1otherwise.gsl_linalg_QRPT_decomp($A, $tau, $p, $norm)β factorizes the M-by-N matrix$Ainto the QRP^T decompositionA = Q R P^T. On output, the diagonal and upper triangular part of the input matrix hold the matrix R. The permutation matrix P is stored in the permutation$p. This one returns two values: the first is0if the operation succeeded,1otherwise. The second is the sign of the permutation, which has the value(-1)^n, where n is the number of interchanges in the permutation. The vector$tauplus the columns of the lower triangular part of the matrix$Ahold the Householder coefficients and vectors that encode the orthogonal matrix Q. The vector tau must be of lengthk=\min(M,N). The matrix Q is related to these components byQ = Q_k ... Q_2 Q_1whereQ_i = I - \tau_i v_i v_i^Tandv_iis the Householder vectorv_i = (0,...,1,A(i+1,i),A(i+2,i),...,A(m,i)). This is the same storage scheme as used by lapack. The vector norm is a workspace of length N used for column pivoting. The algorithm used to perform the decomposition is Householder QR with column pivoting (Golub & Van Loan, Matrix Computations, Algorithm 5.4.1). πΈgsl_linalg_QRPT_decomp2($A, $q, $r, $tau, $p, $norm)β factorizes the matrix$Ainto the decompositionA = Q R P^Twithout modifying$Aitself, storing the output in the separate matrices$qand$r. For the rest, it operates exactly likegsl_linalg_QRPT_decomp. She's the gentle version that doesn't destroy your original. π₯Ί
okay bestie, heads up: the doc literally just drop-names the next batch of functions with zero description. Not me reading a list of names with no lore. I'm giving you every single one, exactly as written, because we do not skip content around here. π
gsl_linalg_QRPT_solvegsl_linalg_QRPT_svxgsl_linalg_QRPT_QRsolvegsl_linalg_QRPT_Rsolvegsl_linalg_QRPT_Rsvxgsl_linalg_QRPT_updategsl_linalg_LQ_decompgsl_linalg_LQ_solve_Tgsl_linalg_LQ_svx_Tgsl_linalg_LQ_lssolve_Tgsl_linalg_LQ_Lsolve_Tgsl_linalg_LQ_Lsvx_Tgsl_linalg_L_solve_Tgsl_linalg_LQ_vecQgsl_linalg_LQ_vecQTgsl_linalg_LQ_unpackgsl_linalg_LQ_updategsl_linalg_LQ_LQsolvegsl_linalg_PTLQ_decompgsl_linalg_PTLQ_decomp2gsl_linalg_PTLQ_solve_Tgsl_linalg_PTLQ_svx_Tgsl_linalg_PTLQ_LQsolve_Tgsl_linalg_PTLQ_Lsolve_Tgsl_linalg_PTLQ_Lsvx_Tgsl_linalg_PTLQ_update
gsl_linalg_cholesky_decomp($A)β factorizes the symmetric, positive-definite square matrix$Ainto the Cholesky decompositionA = L L^Tand stores it into the matrix$A. And the return value? The doc says it returns0if the operation succeeded,0otherwise. Yes. Both. Zero. That's what it says, and I am reporting, not judging. πgsl_linalg_cholesky_solve($cholesky, $b, $x)β solves the systemA x = busing the Cholesky decomposition of A into the matrix$choleskygiven bygsl_linalg_cholesky_decomp.$band$xare vectors. The function returns0if the operation succeeded,0otherwise. β¨gsl_linalg_cholesky_svx($cholesky, $x)β solves the systemA x = bin-place using the Cholesky decomposition of A into the matrix$choleskygiven bygsl_linalg_cholesky_decomp. On input, the vector$xshould contain the right-hand side b, which is replaced by the solution on output. The function returns0if the operation succeeded,0otherwise. πgsl_linalg_cholesky_decomp_unitβ (name-dropped, no description, but she's here and she counts π)gsl_linalg_complex_cholesky_decomp($A)β factorizes the symmetric, positive-definite square matrix$Athat contains complex numbers into the Cholesky decompositionA = L L^Tand stores it into the matrix$A. Returns0if the operation succeeded,0otherwise. Complex girlies rise πgsl_linalg_complex_cholesky_solve($cholesky, $b, $x)β solves the systemA x = busing the Cholesky decomposition of A into the matrix$choleskygiven bygsl_linalg_complex_cholesky_decomp.$band$xare vectors. Returns0if the operation succeeded,0otherwise.gsl_linalg_complex_cholesky_svx($cholesky, $x)β solves the systemA x = bin-place using the Cholesky decomposition of A into the matrix$choleskygiven bygsl_linalg_complex_cholesky_decomp. On input, the vector$xshould contain the right-hand side b, which is replaced by the solution on output. Returns0if the operation succeeded,0otherwise. π
gsl_linalg_symmtd_decomp($A, $tau)β factorizes the symmetric square matrix$Ainto the symmetric tridiagonal decompositionQ T Q^T. On output, the diagonal and subdiagonal part of the input matrix$Acontain the tridiagonal matrix T. The remaining lower triangular part of the input matrix contains the Householder vectors which, together with the Householder coefficients$tau, encode the orthogonal matrix Q. This storage scheme is the same as used by lapack. The upper triangular part of$Ais not referenced.$tauis a vector. π¦gsl_linalg_symmtd_unpack($A, $tau, $Q, $diag, $subdiag)β unpacks the encoded symmetric tridiagonal decomposition($A, $tau)obtained fromgsl_linalg_symmtd_decompinto the orthogonal matrix$Q, the vector of diagonal elements$diag, and the vector of subdiagonal elements$subdiag. Opening the box, iconic behavior πgsl_linalg_symmtd_unpack_T($A, $diag, $subdiag)β unpacks the diagonal and subdiagonal of the encoded symmetric tridiagonal decomposition($A, $tau)obtained fromgsl_linalg_symmtd_decompinto the vectors$diagand$subdiag. (Just the T part. Minimalist. β¨)gsl_linalg_hermtd_decomp($A, $tau)β factorizes the hermitian matrix$Ainto the symmetric tridiagonal decompositionU T U^T. On output, the real parts of the diagonal and subdiagonal part of the input matrix$Acontain the tridiagonal matrix T. The remaining lower triangular part of the input matrix contains the Householder vectors which, together with the Householder coefficients$tau, encode the orthogonal matrix Q. This storage scheme is the same as used by lapack. The upper triangular part of$Aand the imaginary parts of the diagonal are not referenced.$Ais a complex matrix and$taua complex vector. πgsl_linalg_hermtd_unpack($A, $tau, $U, $diag, $subdiag)β unpacks the encoded tridiagonal decomposition($A, $tau)obtained fromgsl_linalg_hermtd_decompinto the unitary complex matrix$U, the real vector of diagonal elements$diag, and the real vector of subdiagonal elements$subdiag.gsl_linalg_hermtd_unpack_T($A, $diag, $subdiag)β unpacks the diagonal and subdiagonal of the encoded tridiagonal decomposition(A, tau)obtained from thegsl_linalg_hermtd_decompinto the real vectors$diagand$subdiag.
gsl_linalg_HH_solve($a, $b, $x)β solves the system$A$x=$bdirectly using Householder transformations, where$Ais a matrix and$band$xare vectors. On output the solution is stored in$xand$bis not modified. π«Ά
β οΈ But listen β $A is destroyed by the Householder transformations. She ate, and she took the matrix with her. π
gsl_linalg_HH_svx($A, $x)β solves the system$A$x= b in-place using Householder transformations, where$Ais a matrix and$bis a vector. On input,$xshould contain the right-hand side b, which is replaced by the solution on output.
β οΈ And same energy: the matrix$Ais destroyed by the Householder transformations. RIP to$A, she was a real one. π
gsl_linalg_solve_symm_tridiaggsl_linalg_solve_tridiaggsl_linalg_solve_symm_cyc_tridiaggsl_linalg_solve_cyc_tridiaggsl_linalg_bidiag_decompgsl_linalg_bidiag_unpackgsl_linalg_bidiag_unpack2gsl_linalg_bidiag_unpack_Bgsl_linalg_balance_matrixgsl_linalg_balance_accumgsl_linalg_balance_columns
(Again: just names, zero description in the doc. I'm being faithful, bestie, not lazy. π )
So how do you actually use any of this? You have to add the functions you want to use inside the qw /put_function_here / with spaces between each function. And you can also write use Math::GSL::Complex qw/:all/ to use all available functions of the module. Easy β¨
For more information on the functions, we refer you to the GSL official documentation:
<http://www.gnu.org/software/gsl/manual/html_node/>
π§ͺ EXAMPLES π§ͺ
This example shows how to compute the determinant of a matrix with the LU decomposition:
use Math::GSL::Matrix qw/:all/;
use Math::GSL::Permutation qw/:all/;
use Math::GSL::Linalg qw/:all/;
my $Matrix = gsl_matrix_alloc(4,4);
map { gsl_matrix_set($Matrix, 0, $_, $_+1) } (0..3);
gsl_matrix_set($Matrix,1, 0, 2);
gsl_matrix_set($Matrix, 1, 1, 3);
gsl_matrix_set($Matrix, 1, 2, 4);
gsl_matrix_set($Matrix, 1, 3, 1);
gsl_matrix_set($Matrix, 2, 0, 3);
gsl_matrix_set($Matrix, 2, 1, 4);
gsl_matrix_set($Matrix, 2, 2, 1);
gsl_matrix_set($Matrix, 2, 3, 2);
gsl_matrix_set($Matrix, 3, 0, 4);
gsl_matrix_set($Matrix, 3, 1, 1);
gsl_matrix_set($Matrix, 3, 2, 2);
gsl_matrix_set($Matrix, 3, 3, 3);
my $permutation = gsl_permutation_alloc(4);
gsl_permutation_init($permutation);
my ($result, $signum) = gsl_linalg_LU_decomp($Matrix, $permutation);
my $det = gsl_linalg_LU_det($Matrix, $signum);
print "The value of the determinant of the matrix is $det \n";
Determinant acquired, main character energy achieved ππ’
π AUTHORS π
Jonathan "Duke" Leto <jonathan@leto.net> and Thierry Moisan <thierry.moisan@gmail.com>
π COPYRIGHT AND LICENSE π
Copyright (C) 2008-2024 Jonathan "Duke" Leto and Thierry Moisan
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
and that's the whole page, bestie π this was perl v5.42.3, dated 2026-08-20, and she served every single line. go solve something iconic with her ππ«Ά
π¦ where this page comes from
- ubuntu stonking: libmath-gsl-perl 0.49-1build1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms