\documentclass[a4paper]{amsart}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{listings, url, xfrac, mathrsfs}
\usepackage{hyperref}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\sqc}[1]{{#1}\cdot \sq[\Kp]}    % local square-class
\newcommand{\qa}{\sqc{a}}
\newcommand{\D}{\Delta}
\newcommand{\qD}{\sqc{\D}}      % square-class of \D
\newcommand{\unif}{\pi}         % uniformizer
\newcommand{\qpi}{\sqc{\unif}}  % square-class of uniformizer
\newcommand{\gp}{\mathfrak{p}}  % place p
\newcommand{\K}{K}              % number field
\newcommand{\Kp}{\K_\gp}        % local field
\newcommand{\Kmodp}{\K(\gp)}    % residue field
\newcommand{\quo}[2]{#1/#2}
\newcommand{\un}[1][\K]{#1^\times}
\newcommand{\sq}[1][\K]{#1^{\times2}}
\newcommand{\sqgd}[1]{\quo{\un[#1]}{\sq[#1]}}
\newcommand{\ord}[1][\gp]{\operatorname{ord}_{#1}}
\newcommand{\qdf}{\mathfrak{d}}
\newcommand{\FF}{\mathbb{F}}
\newcommand{\QQ}{\mathbb{Q}}
\newcommand{\ZZ}{\mathbb{Z}}
\newcommand{\IntRing}[1][\K]{\ZZ_{#1}}  % ring of integers
\newcommand{\ValUn}[1][\gp]{U_{#1}}             % group of \gp-units
\newcommand{\ValRing}[1][\gp]{\mathcal{O}_{#1}} % valuation ring
\newcommand{\setB}{\mathscr{B}}
\newcommand{\setC}{\mathscr{C}}
\newcommand{\st}{\;\mid\;}
\newcommand{\cqf}[1]{\par\noindent\fbox{\lstinline|#1|}\par}
\newcommand{\warning}{\par\noindent\textbf{Warning:} }
\newcommand{\magma}[1]{\lstinline|#1|}
\newcommand{\Magma}{\textsc{Magma}}
\newcommand{\term}[1]{\emph{#1}}
\DeclareMathOperator{\id}{id}
\theoremstyle{definition}
\newtheorem*{exm}{Example}
\newenvironment{cqfcommand}{\par\bigskip}{}
\newcommand{\param}[3]{\begin{tabbing}%
 \hspace{0.3\textwidth}\=\hspace{0.3\textwidth}\=\kill%
 \magma{#1} \> \textsc{#2} \> \textit{Default:} \magma{#3}%
\end{tabbing}}
\newcommand{\testsuite}[1]{\par\bigskip\noindent\textbullet\ {\lstinline!#1!}\par}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\title[User manual]{Local square classes\\User manual}
\author{Przemysław Koprowski}
\date{\today}
\begin{document}
\lstset{%
    basicstyle=\tt,
    language=Pascal, 
    commentstyle=\small\ttfamily\textit,
    morecomment=[l]{//},
    columns=flexible, 
    emph={path_to_package, home_directory, file_name},
    emphstyle=\textsl
}
\maketitle
\tableofcontents

This manual describes a package for computing local groups of square-classes at a non-Archimedean completion (basically dyadic) of a number field. The package comes in two versions. One for \Magma{} \cite{BCP1997} and the other for SageMath. Both versions offer the same functionality. The software can be downloaded for free from the author's webpage at \url{http://www.pkoprowski.eu/local_square_classes}. The package is released under standard MIT license. The full statement of the license is given at the end of this manual.

The library was developed on versions 2.27--2.28 of \Magma{} and 9.5--10.4 of SageMath. The author has not tested it on other versions (yet), but will appropriate any reports on how it works in different setups. Questions and comments should be sent via email to \url{przemyslaw.koprowski@us.edu.pl}.




\section{Package installation and loading}\label{sec:loading}
\subsection{For \Magma{}}
The package does not require any installation. Download the package from the author's web page\\ 
\centerline{\url{http://www.pkoprowski.eu/local_square_classes}}\\ 
It comes in form of a zip archive. Extract the content of the subdirectory \texttt{magma} of the archive to a place where \Magma{} will be able to find it. To test the installation go to the package directory and write
\begin{lstlisting}
magma example.m
\end{lstlisting}
If the output looks something like
\begin{lstlisting}
Loading file "example.m"
Use the built-in method...
Time: 2.260
Use the new method...
Time: 0.040
Verify the correctness... done
\end{lstlisting}
it means that the package works correctly. Notice that the times displayed will vary depending on the actual hardware on which the package is being executed.

The package is loaded into \Magma{} by attaching its file: 
\begin{lstlisting}
> Attach("path_to_package/local_square_classes.m");
\end{lstlisting}
One can also add this command to \Magma{} startup file (specified by  the environment variable \magma{MAGMA_USER_SPEC}) to auto-load the package when \Magma{} starts. For details about \Magma{} startup environment consult \cite[Sections~2.3.9 and~4.3]{CBFS2021}.\bigskip

\begin{warning}
\Magma{} versions prior to V2.28-6 contained a bug that caused our function to crash on some fields. It is strongly recommended to upgrade \Magma{} to the newest version.
\end{warning}


\subsection{For SageMath}
The package does not require any installation. Download the package from the author's web page\\ 
\centerline{\url{http://www.pkoprowski.eu/local_square_classes}}\\ 
It comes in form of a zip archive. Extract the content of the subdirectory \texttt{sage} of the archive to a place where SageMath will be able to find it. To test the installation go to the package directory and write
\begin{lstlisting}
sage example.sage
\end{lstlisting}
If the output looks something like
\begin{lstlisting}
Computing the local group of square-classes
Time: CPU 0.36 s, Wall: 0.36 s
Dimension: 32
\end{lstlisting}
it means that the package works correctly. Notice that the times displayed will vary depending on the actual hardware on which the package is being executed.

The package is loaded into SageMath by the following command: 
\begin{lstlisting}
> load("path_to_package/local_square_classes.sage")
\end{lstlisting}

\section{Package usage}
Let $\K$ be a number field, $\gp$ its finite prime, and $\Kp$ the completion of~$\K$ at~$\gp$. The group of local square-classes $\sqgd{\Kp}$ can be treated as a finitely-dimensional vector space over~$\FF_2$, where vector addition is given by the multiplication of square-classes. This package provides a function that constructs this vector space. 

\subsection{\Magma usage}\mbox{}

\begin{cqfcommand}
\cqf{LocalGroupOfSquaresClasses( p )}
\noindent The input and output specification of this function is identical to the one of the built-in method \magma{LocalMultiplicativeGroupModSquares} (see \cite[\S~39.7.9
 ]{CBFS2021}). Hence, the new routine can be used as a replacement for the built-in function.

If $\gp$ is prime ideal in a ring of algebraic integers~$\IntRing$ of~$\K$, this function outputs two values: $V$ and $\varphi$. Here, $V$ is a vector space over $\FF_2$ isomorphic to $\sqgd{\Kp}$ and $\varphi$ is a map from~$V$ to~$\K$, which induces the isomorphism and can compute inverses. 

Moreover, if $\gp$ lies above~$2$, $e = e(\gp|2) = \ord 2$ denotes the absolute ramification index of~$\gp$ and $f = f(\gp|2 = \bigl(\Kmodp : \FF_2\bigr)$ its inertia degree, then the basis of $V$ corresponds to a basis of~$\sqgd{\Kp}$ of the form:
\[
\setB = \bigl\{ \sqc{\beta_{1}}, \dotsc, \sqc{\beta_{ef}}\}\cup \{\qD, \qpi\}
\]
Here, $\unif$ stands for a uniformizer of~$\gp$ and is the only element of the basis with odd valuation. In particular the first $ef + 1$ basis vectors of~$V$ generate the subspace corresponding to the group $\sqgd{\ValUn(\Kp)}$. Further, the valuations of quadratic defects of $\beta_j$'s form a non-decreasing sequence and $\qD$ is the unique square-class of~$\Kp$ with the quadratic defect $\gp^2$.
\end{cqfcommand}

\begin{exm}
First load the package:
\begin{lstlisting}
> Attach("local_square_classes.m");
\end{lstlisting}
Now, take a number field of degree~$30$:
\begin{lstlisting}
> Qx<x> := PolynomialRing(Rationals());
// LMFDB label:
// 30.0.53154657266426257642795590413722745409780564057387056395976704.1
> K<t> := NumberField(x^30 + 98*x^28 + 3977*x^26 + 88668*x^24 
>   + 1211568*x^22 + 10707886*x^20 + 62933870*x^18 + 248692167*x^16 
>   + 659045418*x^14 + 1153231620*x^12 + 1294105343*x^10 
>   + 889400042*x^8 + 347294458*x^6 + 66308517*x^4 + 4169007*x^2 
>   + 44521);
\end{lstlisting}
This field has a unique prime over~$2$. Denote it~$\gp$:
\begin{lstlisting}
> p :=  PrimeFactorization(K, 2)[1][1];
\end{lstlisting}
We shall now construct the group $\sqgd{\Kp}$. To this end we write
\begin{lstlisting}
> time G, phi := LocalGroupOfSquaresClasses(p);
Time: 0.030
\end{lstlisting}
Next, extract the basis $\setB$ applying the map $\varphi$ to the canonical basis of~$V$.
\begin{lstlisting}
> B := [ g @ phi : g in Basis(G) ];  
> #B;
32
\end{lstlisting}
As explained above, the last element of~$\setB$ is the only one of odd valuation at~$\gp$:
\begin{lstlisting}
> [ Valuation(b, p) : b in B ];
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ]
\end{lstlisting}
The valuations of quadratics defects of the remaining elements form a non-decreasing sequence (recall that \magma{QuadraticDefect} already outputs the valuation):
\begin{lstlisting}
> [ QuadraticDefect(b, p) : b in B ];
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 1 ]
\end{lstlisting}

For comparison construct the same group using the built-in method
\begin{lstlisting}
> time H, psi := LocalMultiplicativeGroupModSquares(p);
Time: 2.260
> C := [ h @ psi : h in Basis(H) ];  
> #C;
32
\end{lstlisting}
The difference in the execution times is evident. Let us verify that both functions outputted the same group. To this end we will construct two matrices: 
\begin{lstlisting}
> M := Matrix([ b @@ psi : b in B ]);
> N := Matrix([ c @@ phi : c in C ]);
\end{lstlisting}
The matrix~$M$ contains the coordinates with respect to~$\setC$ of the vectors from~$\setB$. The matrix~$N$ contains the coordinates with respect to~$\setB$ of the vectors from~$\setC$. The two matrices are mutual inverses and this proves that both functions outputted the same group.
\begin{lstlisting}
> IsOne(M*N);
true
\end{lstlisting}
\end{exm}



%
%
%
\subsection{Sage usage}\mbox{}
\lstset{%
    basicstyle=\tt,
    language=Python, 
    commentstyle=\small\ttfamily\textit,
    morecomment=[l]{//},
    columns=flexible, 
    emph={path_to_package, home_directory, file_name},
    emphstyle=\textsl
}
\begin{cqfcommand}
\cqf{quadratic_defect(K, a, p, check = true)}
\noindent Notice that, as of version 10.2, SageMath contains a bug in the build-in method \magma{K.quadratic_defect(a, p, check = true)} in the class of a number field (see \url{https://github.com/sagemath/sage/issues/37656}). Therefore, we provide a corrected implementation of this function.

Given a number field~$\K$, its element~$a$ and a prime ideal~$\gp$, this function output the valuation $\ord \qdf(a)$ of the quadratic defect of~$a$. Here, the \term{quadratic defect} $\qdf(a)$ is defined by the formula (see \cite[\S63A]{OMeara2000}):
\[
\qdf(a) := \bigcap_{b\in \Kp} (a - b^2)\ValRing(\Kp)
\]
\begin{warning}
Once the the bug in the SageMath implementation of \magma{quadratic_defect} is corrected, this function will be removed from the package.
\end{warning}
\end{cqfcommand}



\begin{cqfcommand}
\cqf{local_group_of_square_classes( p )}
\noindent If $\gp$ is prime ideal in a ring of algebraic integers~$\IntRing$ of~$\K$, this function outputs three values: $V$, $\varphi$, $\psi$. Here, $V$ is a vector space over $\FF_2$ isomorphic to $\sqgd{\Kp}$, $\varphi$ is a map from~$V$ to~$\K$, which induces the isomorphism, and $\psi$ is a map from~$\un$ to~$V$ such that $\psi\circ \varphi = \id_V$. 

Moreover, if $\gp$ lies above~$2$, $e = e(\gp|2) = \ord 2$ denotes the absolute ramification index of~$\gp$ and $f = f(\gp|2 = \bigl(\Kmodp : \FF_2\bigr)$ its inertia degree, then the basis of $V$ corresponds to a basis of~$\sqgd{\Kp}$ of the form:
\[
\setB = \bigl\{ \sqc{\beta_{1}}, \dotsc, \sqc{\beta_{ef}}\}\cup \{\qD, \qpi\}
\]
Here, $\unif$ stands for a uniformizer of~$\gp$ and is the only element of the basis with odd valuation. In particular the first $ef + 1$ basis vectors of~$V$ generate the subspace corresponding to the group $\sqgd{\ValUn(\Kp)}$. Further, the valuations of quadratic defects of $\beta_j$'s form a non-decreasing sequence and $\qD$ is the unique square-class of~$\Kp$ with the quadratic defect $\gp^2$.
\end{cqfcommand}

\begin{exm}
First load the package:
\begin{lstlisting}
sage: load("local_square_classes.sage")
\end{lstlisting}
Now, take a number field of degree~$30$:
\begin{lstlisting}
sage: Qx.<x> = QQ[]
sage: # LMFDB label:
sage: # 30.0.53154657266426257642795590413722745409780564057387056395976704.1
sage: K.<t> = NumberField(x^30 + 98*x^28 + 3977*x^26 + 88668*x^24 
....:   + 1211568*x^22 + 10707886*x^20 + 62933870*x^18 + 248692167*x^16 
....:   + 659045418*x^14 + 1153231620*x^12 + 1294105343*x^10 
....:   + 889400042*x^8 + 347294458*x^6 + 66308517*x^4 + 4169007*x^2 
....:   + 44521)
\end{lstlisting}
This field has a unique prime over~$2$. Denote it~$\gp$:
\begin{lstlisting}
sage: p =  K.primes_above(2)[0]
\end{lstlisting}
We shall now construct the group $\sqgd{\Kp}$. To this end we write
\begin{lstlisting}
sage: time G, phi, psi = local_group_of_square_classes(p)
CPU times: user 369 ms, sys: 0 ns, total: 369 ms
Wall time: 375 ms
\end{lstlisting}
Next, extract the basis $\setB$ applying the map $\varphi$ to the canonical basis of~$V$.
\begin{lstlisting}
sage: B = [ phi(g) for g in G.basis() ]
sage: len(B)
32
\end{lstlisting}
As explained above, the last element of~$\setB$ is the only one of odd valuation at~$\gp$:
\begin{lstlisting}
sage: print([ b.valuation(p) for b in B ])
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ]
\end{lstlisting}
The valuations of quadratics defects of the remaining elements form a non-decreasing sequence. Here, we use our implementation of \magma{quadratic_defect} introduced above:
\begin{lstlisting}
sage: print([ quadratic_defect(K, b, p) for b in B ])
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 1 ]
\end{lstlisting}
\end{exm}


%
%
%
\section{Testing and benchmarking}
The package provides two sets of scripts. One for testing the correctness of the produced results, the other to evaluate the performance of our implementation. They are contained in the folder \texttt{tests} and \texttt{benchmarks}, respectively. 

\begin{description}
\item[Important] Before running any of the following scripts, ensure that the variable \lstinline|PATH_TO_LSC| in the file \texttt{path.[m|sage]} correctly points to the package \texttt{local\_square\_classes.[m|sage]}.
\end{description}

\subsection{Testing}
The tests are available for both \Magma{} and SageMath environments. The \texttt{tests} folder contains the following scripts:

\testsuite{tests.m}
This is the basic test script for \Magma{}. It evaluates $12$ specifically chosen fields to ensure all execution branches of the code are exercised. For each field, the tests constructs the local square class group (viewed as an $\FF_2$-vector space). The test then verifies whether the composition of the maps from the vector space to the field and form the field to the vector space is the identity on the vector space. (This also ensures that these maps are group homomorphisms.) For small dimensions, the test is exhaustive; for larger dimensions, it runs on randomly selected elements.

Successively, the test checks whether elements in the same square class receive identical coordinates in the produced vector space.

The runtime of this test is approximately 100 seconds on Intel\textregistered{} Core\texttrademark~i5 running Magma 2.28-6.

\testsuite{tests.sage}
Sage version of the \texttt{tests.m} suite with one addition field (of degree~$64$). 

The runtime is around 95 minutes on Intel\textregistered{} Core\texttrademark~i5 running Sage~9.8, but over 90 minutes of that is spent on the last field of degree~$64$.


\testsuite{tests_massive.m}
An extended version of \texttt{tests.m}, this script uses fields obtained from the LMFDB \cite{lmfdb} instead of the 12 hand-picked ones. For each pair of ramification index~$e$ and inertia degree~$f$, the script tests up to \lstinline|NUM_OF_FIELDS| (default: 5) dyadic fields. If fewer than \lstinline|NUM_OF_FIELDS| exist in the LMFDB for a given pair $(e, f)$, all available ones are used.

The runtime of this test is about 25 minutes on  Intel\textregistered{} Core\texttrademark~i5 running Magma 2.28-6.

\testsuite{tests_massive.sage}
Sage version of the \texttt{tests\_massive.m} suite with one addition field (of degree~$64$). The runtime is around 150 minutes on  Intel\textregistered{} Core\texttrademark~i5 running~Sage 9.8, but over 90 minutes of that is spent on the last field of degree~$64$.


\testsuite{crosstests.m}
This test uses the same 12 fields from \texttt{tests.m}. For each field, it constructs the groups of local square classes in two ways:
\begin{itemize}
\item Using the function \texttt{LocalGroupOfSquaresClasses} from the package (denote the result by~$H$),
\item using \Magma's built-in command \texttt{LocalMultiplicativeGroupModSquares} (denote the result by~$G$).
\end{itemize}
The script constructs the change-of-basis matrices from~$G$ to~$H$ et vice versa. Then it checks that the two matrices are mutual inverses. 

The runtime is around 4 seconds on Intel\textregistered{} Core\texttrademark~i5 running Magma 2.28-6.

\testsuite{crosstests\_massive.m}
Analogous to \texttt{crosstests.m}, but uses random fields from the LMFDB \cite{lmfdb} instead of a fixed set. For each pair $(e, f)$ pair, up to \lstinline|NUM_OF_FIELDS| (default: 20) fields are selected. The runtime of this test is about 100 seconds on Intel\textregistered{} Core\texttrademark~i5 running Magma 2.28-6.


\subsection{Benchmarking}
The \texttt{benchmmarks} folder contains a set of benchmark scripts for timing computations of local dyadic square classes using \Magma, SageMath, and Pari/GP. Benchmark scripts are named \texttt{benchmark\_XYZ.[m|sage|gp]}, where \texttt{XYZ} is one of: \texttt{totally\_ramified},  \texttt{totally\_ramified\_big}, \texttt{unramified}.

For \Magma{} and SageMath (except \texttt{benchmark\_totally\_ramified\_big.m}), the following operations are timed:
\begin{enumerate}
\item Construction of a basis for the group of local dyadic square classes, treated as a vector space over the binary field.
\item Generation of random representatives of uniformly distributed local dyadic square classes.
\item Coordinate computation of a random nonzero algebraic number with respect to the basis constructed in test~1.
\end{enumerate}
Pari/GP scripts and the \Magma{} script \texttt{benchmark\_totally\_ramified\_big.m} perform only the first of the above tests.

To ensure reliable measurements the number of random samples in tests 2 and 3, and the number of repetitions of test~1, are dynamically adjusted. Moreover, since results of test~1 are prone to caching effects (and \Magma{} is does the caching in a really smart way), an additional consistency check is performed. The computation is repeated~8 times and the results are compared.  We allow for a deviation of no more than 30\%.

The following benchmark scripts are provided:

\testsuite{benchmark_unramified.[m|sage|gp]}
Tests are run for the unique unramified extensions of $\QQ_2$ for degrees~$2$ through~$50$. The main limitation is the time required by \Magma{} to construct the unique dyadic place in the base number field. (Which, by the way, must be the principal ideal generated by~$2$.) 

\testsuite{benchmark_totally_ramified.[m|sage|gp]}
Tests use fields defined by generating polynomials of the form $(x - 1)^d + 2$, where $d$ ranges from~$2$ to~$100$. Such fields are known to have unique totally ramified dyadic places. 

\testsuite{benchmark_totally_ramified_big.[m|gp]}
This test uses the same family of polynomials with degrees $d = 2^k$, for $k \in \{ 7, \dotsc, 11\}$.

\subsubsection*{Output format}
Benchmarks for \Magma{} and SageMath (i.e., with \texttt{.m} and \texttt{.sage} extensions, respectively) write their output to files named\\
\centerline{\texttt{XYZ\_[magma|sage]\_$\langle$\emph{current\_date}$\rangle$.dat}.}
Pari/GP scripts print timings directly to the console.

The \texttt{.dat} files outputted by the \Magma{} scripts \texttt{benchmark\_totally\_ramified.m} and \texttt{benchmark\_unramified.m} consists of the following columns:
\begin{description}
\item[deg] Degree of the field extension.
\item[t1] Average time for constructing the group using \Magma{}’s built-in method \texttt{LocalMultiplicativeGroupModSquares}.
\item[N1] One-eighth of the number of repetitions used to compute \texttt{t1}.
\item[u1] Average time using the function \texttt{LocalGroupOfSquaresClasses} provided by the package.
\item[M1] One-eighth of the repetitions used to compute \texttt{u1}.
\item[t2, u2] Average times to construct a representative of a random local square class using \Magma’s built-in method (\texttt{t2}) and our method (\texttt{u2}).
\item[N2] Number of random classes used to compute \texttt{t2} and \texttt{u2}.
\item[t3, u3] Average times to compute coordinates in the local dyadic square class group using \Magma’s built-in method (\texttt{t3}) and our method (\texttt{u3}).
\item[N3] Number of random elements used to compute \texttt{t3} and {u3}.
\end{description}
The \texttt{.dat} file produced by \texttt{benchmark\_totally\_ramified\_big.m} contains only the columns: \texttt{deg}, \texttt{t1}, \texttt{N1}, \texttt{u1}, and \texttt{M1}.

SageMath does not offer a built-in function to construct groups of local square-classes, Hence, the \texttt{.dat} files produced by Sage scripts contains only the columns: \texttt{deg}, \texttt{u1}, \texttt{M1}, \texttt{u2}, \texttt{N2}, \texttt{u3}, and \texttt{N3}.



%
%
%
\begin{thebibliography}{1}

\bibitem{BCP1997}
Wieb Bosma, John Cannon, and Catherine Playoust.
\newblock The {M}agma algebra system. {I}. {T}he user language.
\newblock {\em J. Symbolic Comput.}, 24(3-4):235--265, 1997.
\newblock Computational algebra and number theory (London, 1993).

\bibitem{CBFS2021}
John Cannon, Wieb Bosma, Claus Fieker, and Allan~Steel (eds.).
\newblock {\em Handbook of Magma Functions}, 2.27-7 edition, 2022.

\bibitem{lmfdb}
The {LMFDB Collaboration}, 2024.
\newblock {The {L}-functions and modular forms database}.
\newblock \url{https://www.lmfdb.org}, accessed on 18 January 2024.


\bibitem{OMeara2000}
O.~Timothy O'Meara.
\newblock {\em Introduction to quadratic forms}.
\newblock Classics in Mathematics. Springer-Verlag, Berlin, 2000.
\newblock Reprint of the 1973 edition.

\end{thebibliography}



\section*{MIT License}
Copyright (c) 2025 Przemysław Koprowski\\

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
\begin{quote}
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
\end{quote}

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

\end{document}
