Repository: pygae/galgebra (language: python)

Issue:
Sync upstream updates
In the latest [GAlgebra Primer](http://www.faculty.luther.edu/~macdonal/GAlgebraPrimer.pdf) dated November 29, 2022, users are guided to download http://www.faculty.luther.edu/~macdonal/GAfiles.zip and incorporate a few files into a pip-installed galgebra, which makes it the de facto code base for the users of GAlgebra. These files are

> gprinter.py, lt.py, mv.py, and GAlgebraInit.py

which should include some improvements contributed by Greg Grunberg ( @Greg1950 ).
Improvements (I hope) to the mv.py and lt.py modules
I have made a number of changes to the **mv.py** and **lt.py** modules, changes which I believe address the issues about which I've previously posted.  I've improved the treatment of spinor-based orthogonal transformations so that now one can instantiate an orthogonal transformation using versors, a broader set of multivectors than are spinors.  And I've added to the multivector methods in **mv.py** several new methods, most of them inspired by Dorst, Fontijne, & Mann's *Geometric Algebra for Computer Science*. 

The changed modules have met every test I've made of them.  They are placed in the **galgebra** folder of the attached zip file.  Another folder, **notebooks**, contains four Jupyter notebooks.  Two of them describe precisely the changes I've made to the modules' code.  The other two are oriented towards GAlgebra users (as opposed to programmers).  A *Read Me First* file has been placed at the top level within the archive.

I'm much better at mathematics than programming.  My level of skill at the latter goes no further than a couple of introductory CS courses from M.I.T.'s edX division.  So I would appreciate it if readers of this GitHub issues forum, who are almost certainly better programmers than me, would look over the modified modules and test their functioning.  

(Changes in the indicated modules are delimited by comment lines `### GSG code starts ###` and `### GSG code ends ###`.  Those comment lines are there to aid real programmers in locating the modifications I've made.  They should be stripped out if it's decided by the PyGAE GAlgebra group that the modification should be kept.)

I can be contacted at g.s.grunberg@gmail.com.

Greg Grunberg

[GSG_2021-11-05_GAlgebra_fixes.zip](https://github.com/pygae/galgebra/files/7481560/GSG_2021-11-05_GAlgebra_fixes.zip)

Mv class return 1 for all negative integer power
The __ pow __ method claims supporting integer power but it is returning 1 for any negative integer power, it also didn't raise exception if it do not support negative integer power, the __ pow __ method should check if n < 0 and return self.inv() to the power abs(n)
diff and pdiff method of class Mv throws exception when differentiate by a coordinate symbol
x is a coordinate symbol (a plain sympy Symbol object) and ex is a Mv instance of a Ga basis symbol. I tried to invoke (x*ex).diff(x) and the method throws exception. From the exception stack frame below, it looks like Mv.diff passed self.obj to Ga.pDiff and pDiff try to resolve .obj again from obj.

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-45-3d3396722bf3> in <cell line: 1>()
----> 1 (x*ex).diff(x)

1 frames
/usr/local/lib/python3.10/dist-packages/galgebra/mv.py in diff(self, coord)
   1070                         obj += tmp1 * tmp2
   1071         else:
-> 1072             obj = self.Ga.pDiff(self.obj, coord)
   1073         return Mv(obj, ga=self.Ga)
   1074 

/usr/local/lib/python3.10/dist-packages/galgebra/ga.py in pDiff(self, A, coord)
   2050         # variable, but including case of non-constant basis vectors
   2051 
-> 2052         dA = self.mv(expand(diff(A.obj, coord)))
   2053 
   2054         if self.connect_flg and self.dslot == -1 and not A.is_scalar():  # Basis blades are function of coordinates

AttributeError: 'Mul' object has no attribute 'obj'
Mv class return 1 for all negative integer power
The __ pow __ method claims supporting integer power but it is returning 1 for any negative integer power, it also didn't raise exception if it do not support negative integer power, the __ pow __ method should check if n < 0 and return self.inv() to the power abs(n)
Mv.__pow__ is not returning Mv instance as return value when raise to power of zero
@utensil I have just realized there are another issue related to `Mv.__pow__`, in both primer-updates branch and version 0.5.1:
```
ga = Ga('e', g=[1,1,1], coords=S.symbols(f"0:{3}", real=True), wedge=False)
x,y,z = ga.mv()
(x**0).grade
```
I expect (x**0) returns ga.mv(1) and the .grade property will return [0]. However it raised this exception:
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
[<ipython-input-4-97f54e720c11>](https://localhost:8080/#) in <cell line: 3>()
      1 ga = Ga('e', g=[1,1,1], coords=S.symbols(f"0:{3}", real=True), wedge=False)
      2 x,y,z = ga.mv()
----> 3 (x**0).grade

AttributeError: 'One' object has no attribute 'grade'
```
Clearly it is because the method returning sympy.S.One instead of an Mv instance in this case
Mv class return 1 for all negative integer power
The __ pow __ method claims supporting integer power but it is returning 1 for any negative integer power, it also didn't raise exception if it do not support negative integer power, the __ pow __ method should check if n < 0 and return self.inv() to the power abs(n)
Mv.__pow__ is not returning Mv instance as return value when raise to power of zero
@utensil I have just realized there are another issue related to `Mv.__pow__`, in both primer-updates branch and version 0.5.1:
```
ga = Ga('e', g=[1,1,1], coords=S.symbols(f"0:{3}", real=True), wedge=False)
x,y,z = ga.mv()
(x**0).grade
```
I expect (x**0) returns ga.mv(1) and the .grade property will return [0]. However it raised this exception:
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
[<ipython-input-4-97f54e720c11>](https://localhost:8080/#) in <cell line: 3>()
      1 ga = Ga('e', g=[1,1,1], coords=S.symbols(f"0:{3}", real=True), wedge=False)
      2 x,y,z = ga.mv()
----> 3 (x**0).grade

AttributeError: 'One' object has no attribute 'grade'
```
Clearly it is because the method returning sympy.S.One instead of an Mv instance in this case


Discussion / hints:

x-ref: #475 







Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9b37beb6..2dc787a9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,6 +41,27 @@ jobs:
     - name: Lint
       run: |
         flake8 -v
+    - uses: hidakatsuya/action-setup-diff-pdf@v1.3.0
+      if: "matrix.python-version == '3.11'"
+      with:
+        diff-pdf-version: '0.5'
+    - uses: actions/cache@v3
+      name: Tectonic Cache
+      if: "matrix.python-version == '3.11'"
+      with:
+        path: ~/.cache/Tectonic
+        key: ${{ runner.os }}-tectonic-${{ hashFiles('**/*.tex') }}
+        restore-keys: |
+          ${{ runner.os }}-tectonic-
+    - uses: wtfjoke/setup-tectonic@v2
+      if: "matrix.python-version == '3.11'"
+      with:
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+        biber-version: "latest"
+    - name: Set environment variables
+      if: "matrix.python-version == '3.11'"
+      run: |
+        echo "TEST_GXPDF=1" >> "$GITHUB_ENV"
     - name: Test
       run: |
         PYTEST_ARGS=();
@@ -49,8 +70,10 @@ jobs:
           PYTEST_ARGS+=(-k "$PYTEST_K_FILTER");
         fi;
         pytest \
+          -vv --durations=50 \
           --cov=galgebra \
           --nbval examples/ipython/ \
+          --nbval examples/primer/ \
           test \
           --nbval-current-env \
           --nbval-sanitize-with test/.nbval_sanitize.cfg \
@@ -60,6 +83,11 @@ jobs:
       uses: codecov/codecov-action@v3
       with:
         token: ${{ secrets.CODECOV_TOKEN }}
+    - uses: actions/upload-artifact@v3
+      if: "matrix.python-version == '3.11' && failure()"
+      with:
+        name: PDF-diffs
+        path: test/diff
 
   release:
     name: Create release and send to PyPI
diff --git a/.gitignore b/.gitignore
index 9b724e36..11bd29c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -139,3 +139,7 @@ examples/**/*.tex
 
 # Mac
 .DS_Store
+
+test/generated
+test/diff
+examples/GSG/
diff --git a/README.md b/README.md
index c0f0b088..899cab2f 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,11 @@ Development Status
 
 The fork supports Python 3, increases test coverage, sets up CI and linters, maintains releases to [PyPI](https://pypi.org/project/galgebra/#history), improves [docs](http://galgebra.readthedocs.io) and has many bug fixes, see [Changelog](https://galgebra.readthedocs.io/en/latest/changelog.html).
 
+> [!NOTE]
+> Readers of Prof. Alan Macdonald's [Linear and Geometric Algebra](http://www.faculty.luther.edu/~macdonal/laga/index.html) and [Vector and Geometric Calculus](http://www.faculty.luther.edu/~macdonal/vagc/index.html), please check out [**Migrating guide for readers of LAGA&VAGC**](#migrating-guide-for-readers-of-lagavagc) below.
+> 
+> If you are coming from [sympy.galgebra](https://docs.sympy.org/0.7.6.1/modules/galgebra/) or [brombo/galgebra](https://github.com/brombo/galgebra), please check out section [Migration Guide](#migration-guide) below.
+
 Features
 --------------------
 
@@ -54,8 +59,8 @@ Overloaded Python operators for basic GA operations:
   AB &=  \texttt{A*B} \\
   A \wedge B &=  \mathtt{A \verb!^! B} \\
   A \cdot B &=  \texttt{A|B} \\
-  A \rfloor B &=  \texttt{A<B} \\
-  A \lfloor B &=  \texttt{A>B} \\
+  A \rfloor B &=  \mathtt{A \lt B} \\
+  A \lfloor B &=  \mathtt{A \gt B} \\
   A/B &=  \texttt{A/B} \\
 \end{aligned}\end{split}
 ```
@@ -77,10 +82,10 @@ The various derivatives of a multivector function is accomplished by multiplying
   F {\wedge}\bar{\nabla} &=  \mathtt{F \verb!^! rgrad} \\
   \nabla \cdot F &=  \texttt{grad|F} \\
   F \cdot \bar{\nabla} &=  \texttt{F|rgrad} \\
-  \nabla \rfloor F &=  \texttt{grad<F} \\
-  F \rfloor \bar{\nabla} &=  \texttt{F<rgrad} \\
-  \nabla \lfloor F &=  \texttt{grad>F} \\
-  F \lfloor \bar{\nabla} &= \texttt{F>rgrad}
+  \nabla \rfloor F &=  \mathtt{grad \lt F} \\
+  F \rfloor \bar{\nabla} &=  \mathtt{F \lt rgrad} \\
+  \nabla \lfloor F &=  \mathtt{grad \gt F} \\
+  F \lfloor \bar{\nabla} &= \mathtt{F \gt rgrad}
 \end{aligned}
 ```
 
@@ -92,10 +97,10 @@ The various derivatives of a multivector function is accomplished by multiplying
   \bar{\nabla} {\wedge}F &=  \mathtt{rgrad \verb!^! F} \\
   F \cdot \nabla &=  \texttt{F|grad} \\
   \bar{\nabla}\cdot F &=  \texttt{rgrad|F} \\
-  F \rfloor \nabla &=  \texttt{F<grad} \\
-  \bar{\nabla} \rfloor F &=  \texttt{rgrad<F} \\
-  F \lfloor \nabla &=  \texttt{F>grad} \\
-  \bar{\nabla} \lfloor F &= \texttt{rgrad>F}
+  F \rfloor \nabla &=  \mathtt{F \lt grad} \\
+  \bar{\nabla} \rfloor F &=  \mathtt{rgrad \lt F} \\
+  F \lfloor \nabla &=  \mathtt{F \gt grad} \\
+  \bar{\nabla} \lfloor F &= \mathtt{rgrad \gt F}
 \end{aligned}
 ```
 
@@ -152,8 +157,6 @@ You may also check out more examples [here](https://github.com/pygae/galgebra/bl
 
 For detailed documentation, please visit https://galgebra.readthedocs.io/ .
 
-**NOTE:** If you are coming from [sympy.galgebra](https://docs.sympy.org/0.7.6.1/modules/galgebra/) or [brombo/galgebra](https://github.com/brombo/galgebra), please check out section [Migration Guide](#migration-guide) below.
-
 <!-- end: getting-started -->
 <!-- begin: installation -->
 
@@ -201,7 +204,7 @@ Further, to run the complete test suite including the ones using [nbval](https:/
 
 ```bash
 pip install nbval
-pytest --nbval examples/ipython/ test --nbval-current-env --nbval-sanitize-with test/.nbval_sanitize.cfg
+pytest --nbval examples/ipython/ --nbval examples/primer/ test --nbval-current-env --nbval-sanitize-with test/.nbval_sanitize.cfg
 ```
 
 This could take more than 10 minutes, please be patient.
@@ -214,6 +217,15 @@ Migration Guide
 
 > Note: The APIs have changed since the era of `sympy.galgebra` and `brombo/galgebra`, some properties and methods are deprecated, the supported versions of Python and SymPy have also changed, please check [Changelog](https://galgebra.readthedocs.io/en/latest/changelog.html) and further update your scripts accordingly besides the following. If you encounter any problems, feel free to [open an issue](https://github.com/pygae/galgebra/issues/new)!
 
+### Migrating guide for readers of LAGA&VAGC
+
+Readers of [Linear and Geometric Algebra](http://www.faculty.luther.edu/~macdonal/laga/index.html) and [Vector and Geometric Calculus](http://www.faculty.luther.edu/~macdonal/vagc/index.html) might be guided by [GAlgebra Primer](http://www.faculty.luther.edu/~macdonal/GAlgebraPrimer.pdf) (version November 29, 2022, accessed May, 2024) to download [GAfiles.zip](http://www.faculty.luther.edu/~macdonal/GAfiles.zip) and copy `gprinter.py`, `lt.py`, `mv.py`, and `GAlgebraInit.py`¸ into where GAlgebra is installed. These steps are NO LONGER NEEDED since GAlgebra 0.6.0 as they are merge into GAlgebra with tests, copying these files will cause conflicts and regressions of fixed bugs.
+
+For minor differences to those files, please check out [the change log for GAlgebra 0.6.0](https://galgebra.readthedocs.io/en/latest/changelog.html#0.6.0). Also please note that:
+
+- `GAlgebraInit.py` is renamed to `primer.py` and can be imported like `from galgebra.primer import *` but it's usage is discouraged, although it saves some boilerplate code, this is not part of GAlgebra's maintained API, GAlgebra might remove it in future.
+- Some notebooks from the zip is included in GAlgebra in `examples/primer`.
+
 ### Migrating from [sympy.galgebra](https://docs.sympy.org/0.7.6.1/modules/galgebra/)
 
 GAlgebra is no longer part of SymPy since 1.0.0, if you have an import like this in your source:
@@ -249,6 +261,7 @@ Note that in the [doc/books](https://github.com/pygae/galgebra/blob/master/doc/b
 - `BookGA.pdf` which is a collection of notes on Geometric Algebra and Calculus based of "Geometric Algebra for Physicists" by Doran and Lasenby and on some papers by Lasenby and Hestenes.
 - `galgebra.pdf` which is the original main doc of GAlgebra in PDF format, while the math part is still valid, the part describing the installation and usage of GAlgebra is outdated, please read with caution or visit https://galgebra.readthedocs.io/ instead.
 - `Macdonald` which contains bundled supplementary materials for [Linear and Geometric Algebra](http://www.faculty.luther.edu/~macdonal/laga/index.html) and [Vector and Geometric Calculus](http://www.faculty.luther.edu/~macdonal/vagc/index.html) by Alan Macdonald, see [here](https://github.com/pygae/galgebra/blob/master/doc/books/Macdonald/) and [here](https://github.com/pygae/galgebra/blob/master/examples/Macdonald/) for more information.
+  - Particularly, `GAlgebraPrimer.pdf` is an archived version of [GAlgebra Primer](http://www.faculty.luther.edu/~macdonal/GAlgebraPrimer.pdf) by Alan Macdonald, last updated on November 29, 2022.
 
 <!-- end: bundled-resources -->
 
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..f0e50a7b
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,10 @@
+coverage:
+  status:
+    project:
+      default:
+        # Keep the project coverage target to auto
+        target: auto
+    patch:
+      default:
+        # Set the patch coverage target to 75%
+        target: 75%
diff --git a/doc/books/Macdonald/GAlgebraPrimer.pdf b/doc/books/Macdonald/GAlgebraPrimer.pdf
index a4feb7b5..22d43c27 100644
Binary files a/doc/books/Macdonald/GAlgebraPrimer.pdf and b/doc/books/Macdonald/GAlgebraPrimer.pdf differ
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 489fed42..ee20e9b0 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -2,6 +2,78 @@
 Changelog
 =========
 
+.. math::
+  \newcommand {\lt}[1] {\mathsf{#1}}
+  \newcommand {\mbf}[1] {\mathbf{#1}}
+  \newcommand {\es}[1] {\mathbf{e}_{#1}}
+  \newcommand {\til}[1] {\widetilde{#1}}
+
+- :bug:`518` :class:`~galgebra.mv.Mv` now correctly returns ``Mv`` instance when raise to power of zero. But in general, if one needs to call ``Mv`` methods on a result returned by some GA operations, it would be more prudent to initialize it as an ``Mv`` instance first, as sometimes the result becomes a ``sympy`` object.
+
+- :bug:`516` :attr:`~galgebra.mv.Mv.grades` no longer incorrectly returns ``None`` under some circumstances, as now all initialization branch will correctly call :meth:`~galgebra.mv.Mv.characterise_Mv`.
+
+- :bug:`513` :class:`~galgebra.mv.Mv` now correctly handles differentiating by a coordinate symbol.
+
+- :bug:`511` :class:`~galgebra.mv.Mv` now correctly handles negative integer power.
+
+- :feature:`510` Added :class:`~galgebra.gprinter.gprint` by Alan Bromborsky developed back in 2020, which improved printing of text and LaTeX for multiple GA objects in Jupytper notebooks, and is the recommended way to print GA formulas, see :doc:`tutorials/gprint` for a demonstration. Testability and test coverage are improved.
+
+- :support:`510` Testing infrastructure for generating and diffing PDFs is added, see ``test/test_gprinter.py`` and ``.github/workflows/ci.yml`` for details.
+
+- :feature:`510` Improved functionality for :class:`~galgebra.mv.Mv`, :class:`~galgebra.lt.Lt` by Greg Grunberg, with improved test coverage and minor fixes.
+
+  Changes by Greg Grunberg include (see the attached notebooks in :issue:`478` for details):
+
+  * The following methods of ``Mv`` are added, along with their corresponding functions (applied to multivectors ``A`` and ``B``):
+
+    * Undualization: :meth:`A.undual() <galgebra.mv.Mv.undual>` and :func:`undual(A) <galgebra.mv.undual>`
+    * Grade involution: :meth:`A.g_invol() <galgebra.mv.Mv.g_invol>` and :func:`g_invol(A) <galgebra.mv.g_invol>`
+    * Clifford conjugation: :meth:`A.ccon() <galgebra.mv.Mv.ccon>` and :func:`ccon(A) <galgebra.mv.ccon>`
+    * Scalar product: :meth:`A.sp(B) <galgebra.mv.Mv.sp>` and :func:`sp(A,B) <galgebra.mv.sp>`
+    * Quadratic form: :meth:`A.qform() <galgebra.mv.Mv.qform>` and :func:`qform(A) <galgebra.mv.qform>`
+    * Magnitude squared: :meth:`A.mag2() <galgebra.mv.Mv.mag2>` and :func:`mag2(A) <galgebra.mv.mag2>`
+    * Magnitude: :meth:`A.mag() <galgebra.mv.Mv.mag>` and :func:`mag(A) <galgebra.mv.mag>`
+
+  * The following methods of ``Mv`` are improved, along with their corresponding functions:
+
+    * Norm squared: :meth:`A.norm2() <galgebra.mv.Mv.norm2>` and :func:`norm2(A) <galgebra.mv.norm2>` now returns the absolute value of the quadratic form of ``A``.
+    * Norm: :meth:`A.norm() <galgebra.mv.Mv.norm>` and :func:`norm(A) <galgebra.mv.norm>`
+
+  * :class:`~galgebra.lt.Lt` is significantly improved and fixed, see also :doc:`tutorials/lt`:
+
+    * :class:`~galgebra.lt.Lt` adopts the contravariant-covariant indexing notation long used in tensor analysis and differential geometry, which is consistent with the indexing notation GAlgebra already uses for the display of a multivector's basis blade expansion
+    * :meth:`~galgebra.lt.Lt.Symbolic_Matrix` allows the creation, for a symbolic transformation :math:`\lt{T}`, of matrices with entries of any of the four forms :math:`{T^i}_j`, :math:`T^{ij}`, :math:`T_{ij}`, or :math:`{T_i}^j`, although GAlgebra will use only the first two.
+    * :meth:`~galgebra.lt.Lt.Dictionary_to_Matrix` fixes a bug that incorrectly raises an exception if `T` maps one or more of the basis vectors to the zero multivector, and improves the readability.
+    * :class:`~galgebra.lt.Lt` fixes a bug that erroneously post multiplies the transformation's standard matrix by ``self.Ga.g_inv`` (the reciprocal metric tensor), resulting in a contravariant-contravariant matrix  :math:`[T^{ij}] = [{T^i}_k g^{kj}]` instead of the standard matrix :math:`[{T^i}_j]`. In the same spirit, :meth:`~galgebra.lt.Lt.matrix` now returns the standard matrix $[{T^i}_j]$ instead of the product matrix :math:`[{T^i}_j][g_{ij}]`.
+    * :class:`~galgebra.lt.Lt` now distinguishes symmetric and antisymmetric transformations from general transformations, ``Amat`` will be the standard matrix :math:`[{T^i}_j]` of the transformation when ``mode=='g'``, , but will be :math:`[T_{ij}]` when ``mode in ['s','a']``.  Since :math:`[g^{ij}][T_{ij}] = [{T^i}_j]`, in either case ``Lt.__init__`` will receive the standard matrix as its first parameter.
+    * :class:`~galgebra.lt.Lt` now correctly handles Versor input, and initializes the internal ``lt_dict`` for the versor-based linear transformation.
+    * :class:`~galgebra.lt.Lt` adds support for both even and odd versors, after generalization of spinors to versors in ``Mv``.
+    * For versor based transformations, the inverse transformation :meth:`~galgebra.lt.Lt.inv` is now based on simply :math:`\til{\mbf{V}}` instead of :math:`\mbf{V}^{-1} = {\frac 1 {\mbf{V} \til{\mbf{V}}}} {\til{\mbf{V}}}`, as a versor-based transformation is independent of taking nonzero scalar multiples of the versor.
+    * :class:`~galgebra.lt.Lt` adds support for LaTeX printing of versor-based transformations.
+    * The determinant method :meth:`~galgebra.lt.Lt.det` for a linear transformation is fixed, it now uses directly the geometric algebra definition of :math:`\lt{L}`'s determinant: :math:`\det(\lt{L}) = \lt{L}(\mbf{E}) \mbf{E}^{-1}`, where :math:`\mbf{E}` denotes the basis blade :math:`\mbf{E} = \es{1} \wedge \cdots \wedge \es{n}` for the grade space of pseudoscalars.
+
+  Minor fixes includes:
+
+  * ``norm`` is fixed by using ``metric.square_root_of_expr`` instead of ``sqrt``
+  * making a "spinor" mv is kept for backward compatibility with existing tests, which is still aliased to making an "even" mv, see discussions ``10. An unimplemented suggestion`` in ``Changes to module lt.py`` attached in :issue:`478`
+  * fixed linting errors reported by ``flake8``
+  * fixed some typos of ``return``, ``raise`` etc.
+
+- :feature:`510` Added example notebooks for typical GAs by Greg Grunberg, and they are now part of the tests:
+
+  * Standard 2D Model :math:`\mathcal{G}_2`: :doc:`g2 <tutorials/g2>`
+  * Standard 3D Model :math:`\mathcal{G}_3`: :doc:`g3 <tutorials/g3>`
+  * Standard 4D Model :math:`\mathcal{G}_4`: :doc:`g4 <tutorials/g4>`
+  * 3D Homogeneous Coordinates :math:`\mathcal{G}_4`: :doc:`h3 <tutorials/h3>`
+  * 3D Conformal Model, Amsterdam convention :math:`\mathcal{G}_{4,1}`: :doc:`cm3 <tutorials/cm3>`
+  * Spacetime algebra: :doc:`spacetime <tutorials/spacetime>`
+  * Sphere-related algebras:
+
+    * Geometric algebra for unit sphere in :math:`\mathbb{R}^3` using spherical coordinates: :doc:`sp2 <tutorials/sp2>`
+    * Unit sphere :math:`\mathbb{R}^3` as a submanifold of :math:`\mathcal{G}_3` in cartesian coordinates: :doc:`sp2g3 <tutorials/sp2g3>`
+    * A geometric algebra for the unit sphere in :math:`\mathbb{R}^3` as a submanifold of :math:`\mathbb{R}^3` with spherical coordintes: :doc:`sp2sp3 <tutorials/sp2sp3>`
+    * Spherical Coordinates in :math:`\mathbb{R}^3`: :doc:`sp3 <tutorials/sp3>`
+
 - :release:`0.5.2 <2024.05.01>`
 
 - :support:`517` Add citation info, star history, contributors to README, and fix zenodo citation issue for ``0.5.1``.
diff --git a/doc/index.rst b/doc/index.rst
index 7b1043c6..7c6561de 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -23,6 +23,8 @@
     :hidden:
 
     tutorials/algebra
+    tutorials/lt
+    tutorials/gprint
 
 .. toctree::
     :caption: API
diff --git a/doc/tutorials/cm3.nblink b/doc/tutorials/cm3.nblink
new file mode 100644
index 00000000..b3362829
--- /dev/null
+++ b/doc/tutorials/cm3.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/cm3.ipynb"
+}
diff --git a/doc/tutorials/g2.nblink b/doc/tutorials/g2.nblink
new file mode 100644
index 00000000..aaaea087
--- /dev/null
+++ b/doc/tutorials/g2.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/g2.ipynb"
+}
diff --git a/doc/tutorials/g3.nblink b/doc/tutorials/g3.nblink
new file mode 100644
index 00000000..4296628e
--- /dev/null
+++ b/doc/tutorials/g3.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/g3.ipynb"
+}
diff --git a/doc/tutorials/g4.nblink b/doc/tutorials/g4.nblink
new file mode 100644
index 00000000..792efcd3
--- /dev/null
+++ b/doc/tutorials/g4.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/g4.ipynb"
+}
diff --git a/doc/tutorials/gprint.nblink b/doc/tutorials/gprint.nblink
new file mode 100644
index 00000000..8e24f846
--- /dev/null
+++ b/doc/tutorials/gprint.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/gprint.ipynb"
+}
diff --git a/doc/tutorials/h3.nblink b/doc/tutorials/h3.nblink
new file mode 100644
index 00000000..26e2a89e
--- /dev/null
+++ b/doc/tutorials/h3.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/h3.ipynb"
+}
diff --git a/doc/tutorials/lt.nblink b/doc/tutorials/lt.nblink
new file mode 100644
index 00000000..5b8c4a56
--- /dev/null
+++ b/doc/tutorials/lt.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/ipython/lt.ipynb"
+}
diff --git a/doc/tutorials/sp2.nblink b/doc/tutorials/sp2.nblink
new file mode 100644
index 00000000..04ff90dc
--- /dev/null
+++ b/doc/tutorials/sp2.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/sp2.ipynb"
+}
diff --git a/doc/tutorials/sp2g3.nblink b/doc/tutorials/sp2g3.nblink
new file mode 100644
index 00000000..53c81af0
--- /dev/null
+++ b/doc/tutorials/sp2g3.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/sp2g3.ipynb"
+}
diff --git a/doc/tutorials/sp2sp3.nblink b/doc/tutorials/sp2sp3.nblink
new file mode 100644
index 00000000..56b4de64
--- /dev/null
+++ b/doc/tutorials/sp2sp3.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/sp2sp3.ipynb"
+}
diff --git a/doc/tutorials/sp3.nblink b/doc/tutorials/sp3.nblink
new file mode 100644
index 00000000..5d2df21e
--- /dev/null
+++ b/doc/tutorials/sp3.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/sp3.ipynb"
+}
diff --git a/doc/tutorials/spacetime.nblink b/doc/tutorials/spacetime.nblink
new file mode 100644
index 00000000..b27ea42d
--- /dev/null
+++ b/doc/tutorials/spacetime.nblink
@@ -0,0 +1,3 @@
+{
+    "path": "../../examples/primer/spacetime.ipynb"
+}
diff --git a/examples/ipython/LaTeX.ipynb b/examples/ipython/LaTeX.ipynb
index 60b98304..fab88b66 100644
--- a/examples/ipython/LaTeX.ipynb
+++ b/examples/ipython/LaTeX.ipynb
@@ -1519,43 +1519,43 @@
        "\\begin{equation*} \\boldsymbol{\\nabla} \\cdot A = \\partial_{u} A^{u}  + \\partial_{v} A^{v} \\end{equation*}\n",
        "\\begin{equation*} \\boldsymbol{\\nabla}  A = \\left ( \\partial_{u} A^{u}  + \\partial_{v} A^{v} \\right )  + \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\partial_{v} A^{u}  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\partial_{u} A^{u}  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\partial_{v} A^{v}  + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  \\partial_{u} A^{v} }{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} \\end{equation*}\n",
        "3d orthogonal ($A,\\;B$ are linear transformations)\n",
-       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{xx} \\boldsymbol{e}_{x} + A_{yx} \\boldsymbol{e}_{y} + A_{zx} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{xy} \\boldsymbol{e}_{x} + A_{yy} \\boldsymbol{e}_{y} + A_{zy} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{xz} \\boldsymbol{e}_{x} + A_{yz} \\boldsymbol{e}_{y} + A_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} \\f{mat}{A} = \\left[\\begin{array}{ccc}A_{xx} & A_{xy} & A_{xz}\\\\A_{yx} & A_{yy} & A_{yz}\\\\A_{zx} & A_{zy} & A_{zz}\\end{array}\\right] \\end{equation*}\n",
-       "\\begin{equation*} \\f{\\det}{A} = A_{xz} \\left(A_{yx} A_{zy} - A_{yy} A_{zx}\\right) - A_{yz} \\left(A_{xx} A_{zy} - A_{xy} A_{zx}\\right) + A_{zz} \\left(A_{xx} A_{yy} - A_{xy} A_{yx}\\right) \\end{equation*}\n",
-       "\\begin{equation*} \\overline{A} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{xx} \\boldsymbol{e}_{x} + A_{xy} \\boldsymbol{e}_{y} + A_{xz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{yx} \\boldsymbol{e}_{x} + A_{yy} \\boldsymbol{e}_{y} + A_{yz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{zx} \\boldsymbol{e}_{x} + A_{zy} \\boldsymbol{e}_{y} + A_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} \\f{\\Tr}{A} = A_{xx} + A_{yy} + A_{zz} \\end{equation*}\n",
-       "\\begin{equation*} \\f{A}{e_x\\W e_y} = \\left ( A_{xx} A_{yy} - A_{xy} A_{yx}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + \\left ( A_{xx} A_{zy} - A_{xy} A_{zx}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + \\left ( A_{yx} A_{zy} - A_{yy} A_{zx}\\right ) \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\end{equation*}\n",
-       "\\begin{equation*} \\f{A}{e_x}\\W \\f{A}{e_y} = \\left ( A_{xx} A_{yy} - A_{xy} A_{yx}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + \\left ( A_{xx} A_{zy} - A_{xy} A_{zx}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + \\left ( A_{yx} A_{zy} - A_{yy} A_{zx}\\right ) \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\end{equation*}\n",
+       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto {A^{x}}_{x} \\boldsymbol{e}_{x} + {A^{y}}_{x} \\boldsymbol{e}_{y} + {A^{z}}_{x} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto {A^{x}}_{y} \\boldsymbol{e}_{x} + {A^{y}}_{y} \\boldsymbol{e}_{y} + {A^{z}}_{y} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto {A^{x}}_{z} \\boldsymbol{e}_{x} + {A^{y}}_{z} \\boldsymbol{e}_{y} + {A^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} \\f{mat}{A} = \\left[\\begin{array}{ccc}{A^{x}}_{x} & {A^{x}}_{y} & {A^{x}}_{z}\\\\{A^{y}}_{x} & {A^{y}}_{y} & {A^{y}}_{z}\\\\{A^{z}}_{x} & {A^{z}}_{y} & {A^{z}}_{z}\\end{array}\\right] \\end{equation*}\n",
+       "\\begin{equation*} \\f{\\det}{A} = {A^{x}}_{x} {A^{y}}_{y} {A^{z}}_{z} - {A^{x}}_{x} {A^{y}}_{z} {A^{z}}_{y} - {A^{x}}_{y} {A^{y}}_{x} {A^{z}}_{z} + {A^{x}}_{y} {A^{y}}_{z} {A^{z}}_{x} + {A^{x}}_{z} {A^{y}}_{x} {A^{z}}_{y} - {A^{x}}_{z} {A^{y}}_{y} {A^{z}}_{x} \\end{equation*}\n",
+       "\\begin{equation*} \\overline{A} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto {A^{x}}_{x} \\boldsymbol{e}_{x} + {A^{x}}_{y} \\boldsymbol{e}_{y} + {A^{x}}_{z} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto {A^{y}}_{x} \\boldsymbol{e}_{x} + {A^{y}}_{y} \\boldsymbol{e}_{y} + {A^{y}}_{z} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto {A^{z}}_{x} \\boldsymbol{e}_{x} + {A^{z}}_{y} \\boldsymbol{e}_{y} + {A^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} \\f{\\Tr}{A} = {A^{x}}_{x} + {A^{y}}_{y} + {A^{z}}_{z} \\end{equation*}\n",
+       "\\begin{equation*} \\f{A}{e_x\\W e_y} = \\left ( {A^{x}}_{x} {A^{y}}_{y} - {A^{x}}_{y} {A^{y}}_{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + \\left ( {A^{x}}_{x} {A^{z}}_{y} - {A^{x}}_{y} {A^{z}}_{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + \\left ( {A^{y}}_{x} {A^{z}}_{y} - {A^{y}}_{y} {A^{z}}_{x}\\right ) \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\end{equation*}\n",
+       "\\begin{equation*} \\f{A}{e_x}\\W \\f{A}{e_y} = \\left ( {A^{x}}_{x} {A^{y}}_{y} - {A^{x}}_{y} {A^{y}}_{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + \\left ( {A^{x}}_{x} {A^{z}}_{y} - {A^{x}}_{y} {A^{z}}_{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + \\left ( {A^{y}}_{x} {A^{z}}_{y} - {A^{y}}_{y} {A^{z}}_{x}\\right ) \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} \\end{equation*}\n",
        "\\begin{equation*} g = \\left[\\begin{array}{ccc}1 & 0 & 0\\\\0 & 1 & 0\\\\0 & 0 & 1\\end{array}\\right] \\end{equation*}\n",
        "\\begin{equation*} g^{-1} = \\left[\\begin{array}{ccc}1 & 0 & 0\\\\0 & 1 & 0\\\\0 & 0 & 1\\end{array}\\right] \\end{equation*}\n",
-       "\\begin{equation*} A + B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto \\left ( A_{xx} + B_{xx}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yx} + B_{yx}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zx} + B_{zx}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto \\left ( A_{xy} + B_{xy}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yy} + B_{yy}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zy} + B_{zy}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto \\left ( A_{xz} + B_{xz}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yz} + B_{yz}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zz} + B_{zz}\\right ) \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} AB = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto \\left ( A_{xx} B_{xx} + A_{xy} B_{yx} + A_{xz} B_{zx}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yx} B_{xx} + A_{yy} B_{yx} + A_{yz} B_{zx}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zx} B_{xx} + A_{zy} B_{yx} + A_{zz} B_{zx}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto \\left ( A_{xx} B_{xy} + A_{xy} B_{yy} + A_{xz} B_{zy}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yx} B_{xy} + A_{yy} B_{yy} + A_{yz} B_{zy}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zx} B_{xy} + A_{zy} B_{yy} + A_{zz} B_{zy}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto \\left ( A_{xx} B_{xz} + A_{xy} B_{yz} + A_{xz} B_{zz}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yx} B_{xz} + A_{yy} B_{yz} + A_{yz} B_{zz}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zx} B_{xz} + A_{zy} B_{yz} + A_{zz} B_{zz}\\right ) \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} A - B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto \\left ( A_{xx} - B_{xx}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yx} - B_{yx}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zx} - B_{zx}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto \\left ( A_{xy} - B_{xy}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yy} - B_{yy}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zy} - B_{zy}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto \\left ( A_{xz} - B_{xz}\\right ) \\boldsymbol{e}_{x} + \\left ( A_{yz} - B_{yz}\\right ) \\boldsymbol{e}_{y} + \\left ( A_{zz} - B_{zz}\\right ) \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A + B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto \\left ( {A^{x}}_{x} + {B^{x}}_{x}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{x} + {B^{y}}_{x}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{x} + {B^{z}}_{x}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto \\left ( {A^{x}}_{y} + {B^{x}}_{y}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{y} + {B^{y}}_{y}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{y} + {B^{z}}_{y}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto \\left ( {A^{x}}_{z} + {B^{x}}_{z}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{z} + {B^{y}}_{z}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{z} + {B^{z}}_{z}\\right ) \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} AB = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto \\left ( {A^{x}}_{x} {B^{x}}_{x} + {A^{x}}_{y} {B^{y}}_{x} + {A^{x}}_{z} {B^{z}}_{x}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{x} {B^{x}}_{x} + {A^{y}}_{y} {B^{y}}_{x} + {A^{y}}_{z} {B^{z}}_{x}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{x} {B^{x}}_{x} + {A^{z}}_{y} {B^{y}}_{x} + {A^{z}}_{z} {B^{z}}_{x}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto \\left ( {A^{x}}_{x} {B^{x}}_{y} + {A^{x}}_{y} {B^{y}}_{y} + {A^{x}}_{z} {B^{z}}_{y}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{x} {B^{x}}_{y} + {A^{y}}_{y} {B^{y}}_{y} + {A^{y}}_{z} {B^{z}}_{y}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{x} {B^{x}}_{y} + {A^{z}}_{y} {B^{y}}_{y} + {A^{z}}_{z} {B^{z}}_{y}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto \\left ( {A^{x}}_{x} {B^{x}}_{z} + {A^{x}}_{y} {B^{y}}_{z} + {A^{x}}_{z} {B^{z}}_{z}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{x} {B^{x}}_{z} + {A^{y}}_{y} {B^{y}}_{z} + {A^{y}}_{z} {B^{z}}_{z}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{x} {B^{x}}_{z} + {A^{z}}_{y} {B^{y}}_{z} + {A^{z}}_{z} {B^{z}}_{z}\\right ) \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A - B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto \\left ( {A^{x}}_{x} - {B^{x}}_{x}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{x} - {B^{y}}_{x}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{x} - {B^{z}}_{x}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto \\left ( {A^{x}}_{y} - {B^{x}}_{y}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{y} - {B^{y}}_{y}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{y} - {B^{z}}_{y}\\right ) \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto \\left ( {A^{x}}_{z} - {B^{x}}_{z}\\right ) \\boldsymbol{e}_{x} + \\left ( {A^{y}}_{z} - {B^{y}}_{z}\\right ) \\boldsymbol{e}_{y} + \\left ( {A^{z}}_{z} - {B^{z}}_{z}\\right ) \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
        "\\begin{equation*} General Symmetric Linear Transformation \\end{equation*}\n",
-       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{xx} \\boldsymbol{e}_{x} + A_{xy} \\boldsymbol{e}_{y} + A_{xz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{xy} \\boldsymbol{e}_{x} + A_{yy} \\boldsymbol{e}_{y} + A_{yz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{xz} \\boldsymbol{e}_{x} + A_{yz} \\boldsymbol{e}_{y} + A_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{1} \\boldsymbol{e}_{x} + A_{2} \\boldsymbol{e}_{y} + A_{3} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{2} \\boldsymbol{e}_{x} + A_{4} \\boldsymbol{e}_{y} + A_{5} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{3} \\boldsymbol{e}_{x} + A_{5} \\boldsymbol{e}_{y} + A_{6} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
        "\\begin{equation*} General Antisymmetric Linear Transformation \\end{equation*}\n",
-       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto - A_{xy} \\boldsymbol{e}_{y} - A_{xz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{xy} \\boldsymbol{e}_{x} - A_{yz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{xz} \\boldsymbol{e}_{x} + A_{yz} \\boldsymbol{e}_{y} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto - A_{1} \\boldsymbol{e}_{y} - A_{2} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{1} \\boldsymbol{e}_{x} - A_{3} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{2} \\boldsymbol{e}_{x} + A_{3} \\boldsymbol{e}_{y} \\end{aligned} \\right\\} \\end{equation*}\n",
        "2d general ($A,\\;B$ are linear transformations)\n",
        "\\begin{equation*} g = \\left[\\begin{array}{cc}\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  & \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) \\\\\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  & \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) \\end{array}\\right] \\end{equation*}\n",
        "\\begin{equation*} g^{-1} = \\left[\\begin{array}{cc}\\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) }{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} & - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) }{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\\\- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) }{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} & \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) }{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\end{array}\\right] \\end{equation*}\n",
        "\\begin{equation*} gg^{-1} = \\left[\\begin{array}{cc}1 & 0\\\\0 & 1\\end{array}\\right] \\end{equation*}\n",
-       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} \\f{mat}{A} = \\left[\\begin{array}{cc}A_{uu} & A_{uv}\\\\A_{vu} & A_{vv}\\end{array}\\right] \\end{equation*}\n",
-       "\\begin{equation*} \\f{\\det}{A} = \\frac{- \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} + \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v}}{\\sqrt{\\left (\\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u} + \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{v}\\cdot \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u} + \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{v}\\right )  \\left (\\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u} + \\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{v}\\cdot \\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u} + \\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{v}\\right )  - \\left (\\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u} + \\left(- \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{v}\\cdot \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{u} + \\left(\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\right) \\boldsymbol{e}_{v}\\right ) ^{2}}} \\end{equation*}\n",
-       "\\begin{equation*} \\overline{A} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{3} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} \\f{mat}{\\overline{A}} = \\left[\\begin{array}{cc}\\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left(\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu}\\right) - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left(\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vv} - \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vu}\\right)}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} & \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vv} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4} A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}}\\\\\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{4} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{3} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{3} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vv} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} & \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left(\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{3} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}\\right) + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  \\left(\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vu}\\right)}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} - 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}}\\end{array}\\right] \\end{equation*}\n",
-       "\\begin{equation*} \\f{\\Tr}{A} = - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{vv}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} A_{uu}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{uv}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{3} A_{vu}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} + 2 \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{4}} \\end{equation*}\n",
-       "\\begin{equation*} \\f{A}{e_u\\W e_v} = \\frac{A_{uu} A_{vv} - A_{uv} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} \\end{equation*}\n",
-       "\\begin{equation*} \\f{A}{e_u}\\W \\f{A}{e_v} = \\frac{A_{uu} A_{vv} - A_{uv} A_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} \\end{equation*}\n",
-       "\\begin{equation*} B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  B_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  B_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} A + B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  B_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  B_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} A - B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} - \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vv} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vv} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} - \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  B_{uv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{uu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{uu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  A_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  B_{vv} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  A_{vu} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  B_{vu}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto {A^{u}}_{u} \\boldsymbol{e}_{u} + {A^{v}}_{u} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto {A^{u}}_{v} \\boldsymbol{e}_{u} + {A^{v}}_{v} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} \\f{mat}{A} = \\left[\\begin{array}{cc}{A^{u}}_{u} & {A^{u}}_{v}\\\\{A^{v}}_{u} & {A^{v}}_{v}\\end{array}\\right] \\end{equation*}\n",
+       "\\begin{equation*} \\f{\\det}{A} = - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} {A^{v}}_{v}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{v} {A^{v}}_{u}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{u} {A^{v}}_{v}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{v} {A^{v}}_{u}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\end{equation*}\n",
+       "\\begin{equation*} \\overline{A} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} {A^{u}}_{v} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{u} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{u} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} \\f{mat}{\\overline{A}} = \\left[\\begin{array}{cc}\\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} & \\frac{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v} + \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\\\\\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right ) ^{2} {A^{u}}_{v} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} & \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{v} + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{u} - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{u}}{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  - \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}}\\end{array}\\right] \\end{equation*}\n",
+       "\\begin{equation*} \\f{\\Tr}{A} = - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{u}}_{u}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} - \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  {A^{v}}_{v}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{u}}_{u}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} + \\frac{\\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2} {A^{v}}_{v}}{- \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{u}\\right )  \\left (\\boldsymbol{e}_{v}\\cdot \\boldsymbol{e}_{v}\\right )  + \\left (\\boldsymbol{e}_{u}\\cdot \\boldsymbol{e}_{v}\\right ) ^{2}} \\end{equation*}\n",
+       "\\begin{equation*} \\f{A}{e_u\\W e_v} = \\left ( {A^{u}}_{u} {A^{v}}_{v} - {A^{u}}_{v} {A^{v}}_{u}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} \\end{equation*}\n",
+       "\\begin{equation*} \\f{A}{e_u}\\W \\f{A}{e_v} = \\left ( {A^{u}}_{u} {A^{v}}_{v} - {A^{u}}_{v} {A^{v}}_{u}\\right ) \\boldsymbol{e}_{u}\\wedge \\boldsymbol{e}_{v} \\end{equation*}\n",
+       "\\begin{equation*} B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto {B^{u}}_{u} \\boldsymbol{e}_{u} + {B^{v}}_{u} \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto {B^{u}}_{v} \\boldsymbol{e}_{u} + {B^{v}}_{v} \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A + B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\left ( {A^{u}}_{u} + {B^{u}}_{u}\\right ) \\boldsymbol{e}_{u} + \\left ( {A^{v}}_{u} + {B^{v}}_{u}\\right ) \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\left ( {A^{u}}_{v} + {B^{u}}_{v}\\right ) \\boldsymbol{e}_{u} + \\left ( {A^{v}}_{v} + {B^{v}}_{v}\\right ) \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} A - B = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{u} &\\mapsto \\left ( {A^{u}}_{u} - {B^{u}}_{u}\\right ) \\boldsymbol{e}_{u} + \\left ( {A^{v}}_{u} - {B^{v}}_{u}\\right ) \\boldsymbol{e}_{v} \\\\ \\boldsymbol{e}_{v} &\\mapsto \\left ( {A^{u}}_{v} - {B^{u}}_{v}\\right ) \\boldsymbol{e}_{u} + \\left ( {A^{v}}_{v} - {B^{v}}_{v}\\right ) \\boldsymbol{e}_{v} \\end{aligned} \\right\\} \\end{equation*}\n",
        "\\begin{equation*} a\\cdot \\f{\\overline{A}}{b}-b\\cdot \\f{\\underline{A}}{a} =  0 \\end{equation*}\n",
        "\\begin{equation*} g = \\left[\\begin{array}{cccc}1 & 0 & 0 & 0\\\\0 & -1 & 0 & 0\\\\0 & 0 & -1 & 0\\\\0 & 0 & 0 & -1\\end{array}\\right] \\end{equation*}\n",
-       "\\begin{equation*} \\underline{T} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{t} &\\mapsto T_{tt} \\boldsymbol{e}_{t} + T_{xt} \\boldsymbol{e}_{x} + T_{yt} \\boldsymbol{e}_{y} + T_{zt} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{x} &\\mapsto - T_{tx} \\boldsymbol{e}_{t} - T_{xx} \\boldsymbol{e}_{x} - T_{yx} \\boldsymbol{e}_{y} - T_{zx} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto - T_{ty} \\boldsymbol{e}_{t} - T_{xy} \\boldsymbol{e}_{x} - T_{yy} \\boldsymbol{e}_{y} - T_{zy} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto - T_{tz} \\boldsymbol{e}_{t} - T_{xz} \\boldsymbol{e}_{x} - T_{yz} \\boldsymbol{e}_{y} - T_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} \\overline{T} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{t} &\\mapsto T_{tt} \\boldsymbol{e}_{t} + T_{tx} \\boldsymbol{e}_{x} + T_{ty} \\boldsymbol{e}_{y} + T_{tz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{x} &\\mapsto - T_{xt} \\boldsymbol{e}_{t} - T_{xx} \\boldsymbol{e}_{x} - T_{xy} \\boldsymbol{e}_{y} - T_{xz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto - T_{yt} \\boldsymbol{e}_{t} - T_{yx} \\boldsymbol{e}_{x} - T_{yy} \\boldsymbol{e}_{y} - T_{yz} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto - T_{zt} \\boldsymbol{e}_{t} - T_{zx} \\boldsymbol{e}_{x} - T_{zy} \\boldsymbol{e}_{y} - T_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
-       "\\begin{equation*} \\f{\\det}{\\underline{T}} = T_{tz} \\left(T_{xt} T_{yx} T_{zy} - T_{xt} T_{yy} T_{zx} - T_{xx} T_{yt} T_{zy} + T_{xx} T_{yy} T_{zt} + T_{xy} T_{yt} T_{zx} - T_{xy} T_{yx} T_{zt}\\right) - T_{xz} \\left(T_{tt} T_{yx} T_{zy} - T_{tt} T_{yy} T_{zx} - T_{tx} T_{yt} T_{zy} + T_{tx} T_{yy} T_{zt} + T_{ty} T_{yt} T_{zx} - T_{ty} T_{yx} T_{zt}\\right) + T_{yz} \\left(T_{tt} T_{xx} T_{zy} - T_{tt} T_{xy} T_{zx} - T_{tx} T_{xt} T_{zy} + T_{tx} T_{xy} T_{zt} + T_{ty} T_{xt} T_{zx} - T_{ty} T_{xx} T_{zt}\\right) - T_{zz} \\left(T_{tt} T_{xx} T_{yy} - T_{tt} T_{xy} T_{yx} - T_{tx} T_{xt} T_{yy} + T_{tx} T_{xy} T_{yt} + T_{ty} T_{xt} T_{yx} - T_{ty} T_{xx} T_{yt}\\right) \\end{equation*}\n",
-       "\\begin{equation*} \\f{\\mbox{tr}}{\\underline{T}} = T_{tt} - T_{xx} - T_{yy} - T_{zz} \\end{equation*}\n",
+       "\\begin{equation*} \\underline{T} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{t} &\\mapsto {T^{t}}_{t} \\boldsymbol{e}_{t} + {T^{x}}_{t} \\boldsymbol{e}_{x} + {T^{y}}_{t} \\boldsymbol{e}_{y} + {T^{z}}_{t} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{x} &\\mapsto {T^{t}}_{x} \\boldsymbol{e}_{t} + {T^{x}}_{x} \\boldsymbol{e}_{x} + {T^{y}}_{x} \\boldsymbol{e}_{y} + {T^{z}}_{x} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto {T^{t}}_{y} \\boldsymbol{e}_{t} + {T^{x}}_{y} \\boldsymbol{e}_{x} + {T^{y}}_{y} \\boldsymbol{e}_{y} + {T^{z}}_{y} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto {T^{t}}_{z} \\boldsymbol{e}_{t} + {T^{x}}_{z} \\boldsymbol{e}_{x} + {T^{y}}_{z} \\boldsymbol{e}_{y} + {T^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} \\overline{T} = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{t} &\\mapsto {T^{t}}_{t} \\boldsymbol{e}_{t} - {T^{t}}_{x} \\boldsymbol{e}_{x} - {T^{t}}_{y} \\boldsymbol{e}_{y} - {T^{t}}_{z} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{x} &\\mapsto - {T^{x}}_{t} \\boldsymbol{e}_{t} + {T^{x}}_{x} \\boldsymbol{e}_{x} + {T^{x}}_{y} \\boldsymbol{e}_{y} + {T^{x}}_{z} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto - {T^{y}}_{t} \\boldsymbol{e}_{t} + {T^{y}}_{x} \\boldsymbol{e}_{x} + {T^{y}}_{y} \\boldsymbol{e}_{y} + {T^{y}}_{z} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto - {T^{z}}_{t} \\boldsymbol{e}_{t} + {T^{z}}_{x} \\boldsymbol{e}_{x} + {T^{z}}_{y} \\boldsymbol{e}_{y} + {T^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\} \\end{equation*}\n",
+       "\\begin{equation*} \\f{\\det}{\\underline{T}} = {T^{t}}_{t} {T^{x}}_{x} {T^{y}}_{y} {T^{z}}_{z} - {T^{t}}_{t} {T^{x}}_{x} {T^{y}}_{z} {T^{z}}_{y} - {T^{t}}_{t} {T^{x}}_{y} {T^{y}}_{x} {T^{z}}_{z} + {T^{t}}_{t} {T^{x}}_{y} {T^{y}}_{z} {T^{z}}_{x} + {T^{t}}_{t} {T^{x}}_{z} {T^{y}}_{x} {T^{z}}_{y} - {T^{t}}_{t} {T^{x}}_{z} {T^{y}}_{y} {T^{z}}_{x} - {T^{t}}_{x} {T^{x}}_{t} {T^{y}}_{y} {T^{z}}_{z} + {T^{t}}_{x} {T^{x}}_{t} {T^{y}}_{z} {T^{z}}_{y} + {T^{t}}_{x} {T^{x}}_{y} {T^{y}}_{t} {T^{z}}_{z} - {T^{t}}_{x} {T^{x}}_{y} {T^{y}}_{z} {T^{z}}_{t} - {T^{t}}_{x} {T^{x}}_{z} {T^{y}}_{t} {T^{z}}_{y} + {T^{t}}_{x} {T^{x}}_{z} {T^{y}}_{y} {T^{z}}_{t} + {T^{t}}_{y} {T^{x}}_{t} {T^{y}}_{x} {T^{z}}_{z} - {T^{t}}_{y} {T^{x}}_{t} {T^{y}}_{z} {T^{z}}_{x} - {T^{t}}_{y} {T^{x}}_{x} {T^{y}}_{t} {T^{z}}_{z} + {T^{t}}_{y} {T^{x}}_{x} {T^{y}}_{z} {T^{z}}_{t} + {T^{t}}_{y} {T^{x}}_{z} {T^{y}}_{t} {T^{z}}_{x} - {T^{t}}_{y} {T^{x}}_{z} {T^{y}}_{x} {T^{z}}_{t} - {T^{t}}_{z} {T^{x}}_{t} {T^{y}}_{x} {T^{z}}_{y} + {T^{t}}_{z} {T^{x}}_{t} {T^{y}}_{y} {T^{z}}_{x} + {T^{t}}_{z} {T^{x}}_{x} {T^{y}}_{t} {T^{z}}_{y} - {T^{t}}_{z} {T^{x}}_{x} {T^{y}}_{y} {T^{z}}_{t} - {T^{t}}_{z} {T^{x}}_{y} {T^{y}}_{t} {T^{z}}_{x} + {T^{t}}_{z} {T^{x}}_{y} {T^{y}}_{x} {T^{z}}_{t} \\end{equation*}\n",
+       "\\begin{equation*} \\f{\\mbox{tr}}{\\underline{T}} = {T^{t}}_{t} + {T^{x}}_{x} + {T^{y}}_{y} + {T^{z}}_{z} \\end{equation*}\n",
        "\\begin{equation*} a\\cdot \\f{\\overline{T}}{b}-b\\cdot \\f{\\underline{T}}{a} =  0 \\end{equation*}\n",
        "\\begin{equation*} f = f \\end{equation*}\n",
        "\\begin{equation*} \\boldsymbol{\\nabla}  f = \\partial_{u} f  \\boldsymbol{e}_{u} + \\frac{\\partial_{v} f }{\\sin{\\left (u \\right )}} \\boldsymbol{e}_{v} \\end{equation*}\n",
diff --git a/examples/ipython/issue-511.ipynb b/examples/ipython/issue-511.ipynb
new file mode 100644
index 00000000..5902a167
--- /dev/null
+++ b/examples/ipython/issue-511.ipynb
@@ -0,0 +1,200 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "c037437a-5863-4af2-82fb-3f715a984ad0",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(e_0, e_0)"
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "from galgebra.ga import Ga\n",
+    "import sympy as S\n",
+    "\n",
+    "ga = Ga('e', g=[1,1,1], coords=S.symbols(f\"0:{3}\", real=True), wedge=False)\n",
+    "ex,ey,ez = ga.mv()\n",
+    "ex**-1, ga.mv(1)/ex"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "ea2d702a-dd04-4f5c-8794-1efefc8e7ee9",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\frac{1}{8} \\mathbf{e}_{0}$"
+      ],
+      "text/plain": [
+       "e_0/8"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(2*ex)**-3"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "09faf76e-f43c-4023-be91-3f2460de0456",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\frac{1}{65536}$"
+      ],
+      "text/plain": [
+       "1/65536"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(2*ex)**-16"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "91e20a05-ba7d-48cd-8916-d7460fb99e87",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(2*e_0/29 + 3*e_1/29 + 4*e_2/29, 2*e_0/841 + 3*e_1/841 + 4*e_2/841)"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "((2*ex + 3*ey + 4*ez).inv(), (2*ex + 3*ey + 4*ez)**-3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "bdf6ce81-ab58-4e3b-aa72-6cd320e6062d",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\frac{1}{841}$"
+      ],
+      "text/plain": [
+       "1/841"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(2*ex + 3*ey + 4*ez).inv() * (2*ex + 3*ey + 4*ez)**-3"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "2a557448-ac80-4b1c-87a0-0d25d731953c",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle 1$"
+      ],
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "((2*ex + 3*ey + 4*ez)**3) * (2*ex + 3*ey + 4*ez)**-3"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "fd88f171-7f74-4112-9123-e986d8d92d76",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle 1$"
+      ],
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(2*ex + 3*ey + 4*ez)**-3 * ((2*ex + 3*ey + 4*ez)**3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "47ca185d-a790-4ba9-9c45-265572e6675d",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.18"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/ipython/issue-513.ipynb b/examples/ipython/issue-513.ipynb
new file mode 100644
index 00000000..5e42d46c
--- /dev/null
+++ b/examples/ipython/issue-513.ipynb
@@ -0,0 +1,196 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "26c91d51-9f62-4c3e-9d02-2ca3a0977913",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from galgebra.ga import Ga\n",
+    "import sympy as S\n",
+    "\n",
+    "x,y,z = coords = S.symbols('x y z', real=True)\n",
+    "ga = Ga('e', g=[1,1,1], coords=coords, wedge=False)\n",
+    "ex,ey,ez = ga.mv()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "5d541f6c-f9f8-4adb-a4b5-5ab27a9293dc",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{e}_{x} \\frac{\\partial}{\\partial x} + \\mathbf{e}_{y} \\frac{\\partial}{\\partial y} + \\mathbf{e}_{z} \\frac{\\partial}{\\partial z}$"
+      ],
+      "text/plain": [
+       "e_x*D{x} + e_y*D{y} + e_z*D{z}"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ga.grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "d07a0e79-b467-4cef-9ea6-3b49e925bf0e",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle 1$"
+      ],
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ga.grad * (x*ex)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "417d322c-30f5-4353-9f32-cc39cb7b2d8e",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\mathbf{e}_{x}$"
+      ],
+      "text/plain": [
+       "e_x"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(x*ex).diff(x)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "7d66fe00-5011-4946-ad4f-9ae6f38c315d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x,y,z = coords = S.symbols('x y z', real=True)\n",
+    "ga = Ga('e', g=[1,1,1], coords=coords, wedge=False)\n",
+    "ex,ey,ez = ga.mv()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "7bac45e6-985f-497d-b13e-b40f9150dfd5",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{e}_{x} \\frac{\\partial}{\\partial x} + \\mathbf{e}_{y} \\frac{\\partial}{\\partial y} + \\mathbf{e}_{z} \\frac{\\partial}{\\partial z}$"
+      ],
+      "text/plain": [
+       "e_x*D{x} + e_y*D{y} + e_z*D{z}"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ga.grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "2f8161af-c147-4fcf-adfa-53613b24210f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle 1$"
+      ],
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ga.grad * (x*ex)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "231177c9-3422-4e33-9ace-d914502ba060",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\mathbf{e}_{x}$"
+      ],
+      "text/plain": [
+       "e_x"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(x*ex).pdiff(x) # notice this is pdiff() not diff() this tim"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.18"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/ipython/issue-514.ipynb b/examples/ipython/issue-514.ipynb
new file mode 100644
index 00000000..135a7c80
--- /dev/null
+++ b/examples/ipython/issue-514.ipynb
@@ -0,0 +1,83 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "c98f4750-3af1-4749-b2a6-8a17764511bd",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(e_xyz, e_xyz, 'Iinv+')"
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "from galgebra.ga import Ga\n",
+    "import sympy as S\n",
+    "\n",
+    "Ga.dual_mode('Iinv+')\n",
+    "\n",
+    "ga = Ga('e', g=[1,1,1], coords=S.symbols('x y z', real=True), wedge=False)\n",
+    "ex,ey,ez = ga.mv()\n",
+    "I = ga.i\n",
+    "(ex*I) * (ex*I).dual(), (ex*I).dual() * (ex*I), ga.dual_mode_value"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "95c6f201-9f55-4e5d-9996-6a19b0f915ff",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(ex*I).dual().undual() == (ex*I)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f8d2ce01-aae9-46ff-be2d-225138fa30cf",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.18"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/ipython/issue-516.ipynb b/examples/ipython/issue-516.ipynb
new file mode 100644
index 00000000..5360e912
--- /dev/null
+++ b/examples/ipython/issue-516.ipynb
@@ -0,0 +1,59 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "ee7c8882-7356-441a-920e-3603d081002b",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[1]\n",
+      "[1]\n"
+     ]
+    }
+   ],
+   "source": [
+    "from galgebra.ga import Ga\n",
+    "import sympy as S\n",
+    "\n",
+    "ga = Ga('e', g=[1,1,1], coords=S.symbols(f\"0:3\", real=True), wedge=False)\n",
+    "x,y,z = ga.mv()\n",
+    "a = ga.mv('A','mv')\n",
+    "print((a.grade(0)*x).grades)\n",
+    "print((a.grade(0)^x).grades)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8920d631-eb4f-4955-9558-9dc158b5171e",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.18"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/ipython/issue-518.ipynb b/examples/ipython/issue-518.ipynb
new file mode 100644
index 00000000..2e891d10
--- /dev/null
+++ b/examples/ipython/issue-518.ipynb
@@ -0,0 +1,86 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "2956314d-53f6-4150-ab15-90eaa84e8815",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle 1$"
+      ],
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "from galgebra.ga import Ga\n",
+    "import sympy as S\n",
+    "\n",
+    "ga = Ga('e', g=[1,1,1], coords=S.symbols(f\"0:{3}\", real=True), wedge=False)\n",
+    "x,y,z = ga.mv()\n",
+    "(x**0).grade()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "2fa9ab19-eb44-4eb8-b369-453cd779a249",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle 1$"
+      ],
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "(x**2).grade()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9b287705-2c5a-4a6b-88f4-ba7c7c336947",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.18"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/ipython/lt.ipynb b/examples/ipython/lt.ipynb
new file mode 100644
index 00000000..2bfab038
--- /dev/null
+++ b/examples/ipython/lt.ipynb
@@ -0,0 +1,1858 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "6db85fa4",
+   "metadata": {},
+   "source": [
+    "# Linear Transformations / Outermorphisms in GAlgebra\n",
+    "\n",
+    "Author:  Greg Grunberg\n",
+    "\n",
+    "Last updated:  2021-11-04\n",
+    "\n",
+    "Original name: `Linear Transformations in GAlgebra.ipynb` in [GSG_2021-11-05_GAlgebra_fixes.zip](https://github.com/pygae/galgebra/issues/478)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3fa7d107",
+   "metadata": {},
+   "source": [
+    "## 1. Preliminaries\n",
+    "\n",
+    "This Markdown cell implements certain user-defined LaTeX macros which I'm in the habit of using.  (Not all are used by this notebook.)  The macros ease creation of LaTeX/MathJax code, which in turn allows production of more readable Markdown cells and In[ ] cell output.    \n",
+    "- Enter edit mode to see definitions of the macros.  \n",
+    "- Exit edit mode (Shift+Enter) to activate the macros in this notebook.  \n",
+    "- An example of each macro follows its definition.  The typeset result of each macro appears when not in edit mode.\n",
+    "\n",
+    "$$\\newcommand {\\Rn}[1] {\\mathbb{R}^{#1}}$$  $\\Rn{p,q}$ scalar product space of signature $(p,q)$  \n",
+    "$$\\newcommand {\\Gn}[1]{\\mathbb{G}^{#1}}$$   $\\Gn{p,q}$ geometric algebra of signature $(p,q)$\n",
+    "$$\\newcommand {\\op} {\\wedge}$$              $A \\op B$  outer product\n",
+    "$$\\newcommand {\\ip} {\\cdot}$$               $A \\ip B$  dot product\n",
+    "$$\\newcommand {\\lc} {\\rfloor}$$             $A \\lc B$  left contraction\n",
+    "$$\\newcommand {\\rc} {\\lfloor}$$             $A \\rc B$  right contraction\n",
+    "$$\\newcommand {\\dual} {^\\star}$$            $A\\dual$   dual of multivector\n",
+    "$$\\newcommand {\\undual} {^{-\\star}}$$       $A\\undual$ undual of multivector \n",
+    "$$\\newcommand {\\rev} {^\\dagger}$$           $A\\rev$    reverse of multivector (dagger notation)\n",
+    "$$\\newcommand {\\til}[1] {\\widetilde{#1}}$$  $\\til{A}$  reverse of multivector (tilde notation)\n",
+    "$$\\newcommand {\\ginvol}[1] {\\widehat{#1}}$$ $\\ginvol{A}$  grade involute of multivector\n",
+    "$$\\newcommand {\\ccon}[1] {\\overline{#1}}$$  $\\ccon{A}$ Clifford conjugate of multivector \n",
+    "$$\\newcommand {\\lt}[1] {\\mathsf{#1}}$$      $\\lt{T}$   linear transformation / outermorphism     \n",
+    "$$\\newcommand {\\ad}[1] {\\mathsf{#1}^\\ast}$$ $\\ad{T}$   adjoint of linear transformation / outermorphism \n",
+    "$$\\newcommand {\\es}[1] {\\mathbf{e}_{#1}}$$  $\\es{j}$   (orthonormal) basis vector\n",
+    "$$\\newcommand {\\eS}[1] {\\mathbf{e}^{#1}}$$  $\\eS{i}$   (orthonormal) reciprocal basis vector\n",
+    "$$\\newcommand {\\bas}[2] {\\mathbf{#1}_{#2}}$$ $\\bas{b}{j}$  basis vector\n",
+    "$$\\newcommand {\\baS}[2] {\\mathbf{#1}^{#2}}$$ $\\baS{b}{i}$  reciprocal basis vector\n",
+    "$$\\newcommand {\\oder}[3] {\\dfrac{d^{#3}{#1}}{d{#2}^{#3}}}$$ $\\oder{y}{x}{k}$ $k$th ordinary derivative    \n",
+    "$$\\newcommand {\\pder}[3] {\\dfrac{\\partial^{#3} {#1}}{\\partial{#2}^{#3}}}$$     \n",
+    "$\\pder{u}{(x^i)}{k}$ $k$th partial derivative\n",
+    "$$\\newcommand {\\qform}[1] {\\mathscr{Q}\\left({#1}\\right)}$$ $\\qform{A}$  quadratic form    \n",
+    "$$\\newcommand {\\norm}[1] {\\left\\|{#1}\\right\\|}$$ $\\norm{A}$  norm   \n",
+    "$$\\newcommand {\\normsq}[1] {\\left\\|{#1}\\right\\|^2}$$ $\\normsq{A}$ normsquared  \n",
+    "$$\\newcommand {\\mag}[1] {\\left|{#1}\\right|}$$ $\\mag{A}$  magnitude    \n",
+    "$$\\newcommand {\\magsq}[1] {\\left|{#1}\\right|^2}$$ $\\magsq{A}$ magnitude squared  \n",
+    "$$\\newcommand {\\mbf}[1] {\\mathbf{#1}}$$     $\\mbf{A}$  mathboldface font \n",
+    "$$\\newcommand {\\msf}[1] {\\mathsf{#1}}$$     $\\msf{A}$  mathsansserif font\n",
+    "$$\\newcommand {\\mbs}[1] {\\boldsymbol{#1}}$$ $\\mbs{\\kappa}$  boldsymbol font     \n",
+    "$$\\newcommand {\\scrB} {\\mathscr{B}}$$       $\\scrB$ basis \n",
+    "$$\\newcommand {\\scrE} {\\mathscr{E}}$$       $\\scrE$ (orthonormal) basis"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "b65eb12b",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textsf{This notebook is now using} \\\\\\qquad\\bullet~ \\textsf{Python }3.11.8\\qquad\\bullet~ \\textsf{SymPy }1.12\\qquad\\bullet~ \\textsf{GAlgebra }0.5.1.$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Initialize this notebook to use SymPy and GAlgebra:\n",
+    "import platform\n",
+    "import sympy \n",
+    "import galgebra\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.lt import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()\n",
+    "Ga.dual_mode('Iinv+')  \n",
+    "gprint(r'\\textsf{This notebook is now using} \\\\',\n",
+    "       r'\\qquad\\bullet~ \\textsf{Python }', platform.python_version(),\n",
+    "       r'\\qquad\\bullet~ \\textsf{SymPy }', sympy.__version__[:],\n",
+    "       r'\\qquad\\bullet~ \\textsf{GAlgebra }', galgebra.__version__[:], r'.')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9d4c3b94",
+   "metadata": {},
+   "source": [
+    "**Important:**  GAlgebra 0.5.0 is the version available as of this file date from the PyGAE GAlgebra website at https://github.com/pygae/galgebra.  This notebook actually uses GAlgebra 0.5.0 but with two of its modules, **lt.py** and **mv.py**, modified.  The modifications have corrected those version 0.5.0 bugs of which I'm aware.  The changes have also added to the capabilities offered by those modules.\n",
+    "\n",
+    "This notebook also uses module **gprinter.py**, provided to me by Alan Bromborsky (the original author of GAlgebra), which is not part of GAlgebra 0.5.0.  That module's `gprint` function is used extensively in this notebook's In[ ] cells to produce beautifully formatted output."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "35aaabcf",
+   "metadata": {},
+   "source": [
+    "## 2.  `m3`, the geometric algebra used in examples"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fcafa8d7",
+   "metadata": {},
+   "source": [
+    "This notebook's examples use `m3`, a model of the geometric algebra $\\Gn{1,2}$ generated by an orthonormal basis of 3-dimensional Minkowski space."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "ede9a31e",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{m3: a model of }\\Gn{1,2}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{basis:}~~\\scrE= \\left(  \\boldsymbol{\\mbf{e}}_{1}, \\   \\boldsymbol{\\mbf{e}}_{2}, \\   \\boldsymbol{\\mbf{e}}_{3}\\right) \\qquad\\text{reciprocal basis:}~~\\scrE^{-1}= \\left( \\boldsymbol{\\mbf{e}}^{1}, \\  \\boldsymbol{\\mbf{e}}^{2}, \\  \\boldsymbol{\\mbf{e}}^{3}\\right) = \\left(  \\boldsymbol{\\mbf{e}}_{1}, \\  - \\boldsymbol{\\mbf{e}}_{2}, \\  - \\boldsymbol{\\mbf{e}}_{3}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{metric tensor:}~~[g_{ij}]= \\left[\\begin{array}{ccc}1 & 0 & 0\\\\0 & -1 & 0\\\\0 & 0 & -1\\end{array}\\right] \\quad\\text{reciprocal metric tensor:}~~[g^{ij}]= \\left[\\begin{array}{ccc}1 & 0 & 0\\\\0 & -1 & 0\\\\0 & 0 & -1\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{unit pseudoscalar:}~~\\mbf{I}=  \\boldsymbol{\\mbf{e}}_{123} \\qquad\\text{square of unit pseudoscalar:}~~\\mbf{I}^2= -1 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\text{generic}\\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\quad\\text{scalar (0-vector):}~~ \\kappa  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\quad\\text{vector:}~~\\mbf{x}= x^{1} \\boldsymbol{\\mbf{e}}_{1} + x^{2} \\boldsymbol{\\mbf{e}}_{2} + x^{3} \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\quad\\text{multivector:}~~\\mbf{X}= X  + X^{1} \\boldsymbol{\\mbf{e}}_{1} + X^{2} \\boldsymbol{\\mbf{e}}_{2} + X^{3} \\boldsymbol{\\mbf{e}}_{3} + X^{12} \\boldsymbol{\\mbf{e}}_{12} + X^{13} \\boldsymbol{\\mbf{e}}_{13} + X^{23} \\boldsymbol{\\mbf{e}}_{23} + X^{123} \\boldsymbol{\\mbf{e}}_{123} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "index_values = symbols('1 2 3', int=True)\n",
+    "coords = (x1, x2, x3) = symbols('x__1, x__2, x__3', real=True)\n",
+    "m3= Ga('\\mbf{e}*1|2|3', g=[1,-1,-1], coords=index_values, wedge=False)\n",
+    "e1, e2, e3 = m3.mv()         # basis vectors \n",
+    "re1, re2, re3 = m3.mvr()     # reciprocal basis vectors\n",
+    "kappa = m3.mv('kappa', 0)    # generic 0-vector\n",
+    "x = m3.mv('x',1)             # generic 1-vector\n",
+    "X = m3.mv('X', 'mv')         # generic multivector\n",
+    "I = m3.I()                   # unit pseudoscalar \n",
+    "gprint(r'\\textbf{m3: a model of }\\Gn{1,2}')\n",
+    "gprint(r'\\text{basis:}~~\\scrE=', m3.mv(),\n",
+    "       r'\\qquad\\text{reciprocal basis:}~~\\scrE^{-1}=', tuple(m3.r_symbols),\n",
+    "       '=', m3.mvr())\n",
+    "gprint(r'\\text{metric tensor:}~~[g_{ij}]=', m3.g,\n",
+    "       r'\\quad\\text{reciprocal metric tensor:}~~[g^{ij}]=', m3.g_inv)\n",
+    "gprint(r'\\text{unit pseudoscalar:}~~\\mbf{I}=', I, \n",
+    "       r'\\qquad\\text{square of unit pseudoscalar:}~~\\mbf{I}^2=', I**2)\n",
+    "gprint(r'\\text{generic}',\n",
+    "       r'\\\\\\quad\\text{scalar (0-vector):}~~', kappa, \n",
+    "       r'\\\\\\quad\\text{vector:}~~\\mbf{x}=', x, \n",
+    "       r'\\\\\\quad\\text{multivector:}~~\\mbf{X}=', X)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5a249570",
+   "metadata": {},
+   "source": [
+    "The `wedge=False` specification of `m3`'s instantiations means that \"no wedge\" notation is being used, in which $\\es{i_1 \\cdots i_g}$ is an abbreviation for the basis blade $\\es{i_1} \\op \\cdots \\op \\es{i_g}$."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6ca8af1f",
+   "metadata": {},
+   "source": [
+    "It will be useful to have the following function for simplifying the coefficients in a multivector's basis blade expansion.  The function doesn't always accomplish its purpose, but seems to do an adequate job when the coefficients are trigonometric expressions."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "7d764d41",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from sympy.simplify.fu import fu \n",
+    "def mv_simplify(M:Mv, simp=simplify) -> Mv:\n",
+    "    \"\"\"\n",
+    "    Returns the multivector M but with the coefficients of its basis\n",
+    "    blade expansion simplified.  Does not modify the original multivector.\n",
+    "    Uses the SymPy simplification function specified by the simp parameter.\n",
+    "    \"\"\"\n",
+    "    # Create `basis_blades`, a list of all basis blades for the geometric\n",
+    "    # algebra to which multivector M belongs.  The blades are listed in \n",
+    "    # ascending grade order.  Blades of the same grade are listed with \n",
+    "    # ascending strictly ordered indexes.\n",
+    "    basis_blades = []\n",
+    "    for k_blades in M.Ga.blades:\n",
+    "        for blade in k_blades:\n",
+    "            basis_blades.append(M.Ga.mv(blade))\n",
+    "    # Create a list `coefficients` of M's scalar coefficients in its basis\n",
+    "    # blade expansion.  Then simplify each coefficient in the list.\n",
+    "    coefficients = M.blade_coefs(basis_blades)\n",
+    "    for k in range(len(coefficients)):\n",
+    "        coefficients[k] = simp(coefficients[k])\n",
+    "    # Create and return a new version of M with simplified coefficients.\n",
+    "    simplified_M = 0\n",
+    "    for k in range(len(basis_blades)):\n",
+    "        simplified_M += coefficients[k]*basis_blades[k]\n",
+    "    return simplified_M"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e45cbd7b",
+   "metadata": {},
+   "source": [
+    "## 3. Contravariant/covariant indexing in GAlgebra's output"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "af3da0c5",
+   "metadata": {},
+   "source": [
+    "Most introductory linear algebra textbooks, including Alan Macdonald's *Linear and Geometric Algebra*, write the basis expansion $\\mbf{x}=\\sum_{i=1}^n x_i \\es{i}$ of a vector $\\mbf{x}$ with the scalar coefficients $x_i$ labelled by a subscript.\n",
+    "\n",
+    "GAlgebra uses a somewhat different notational scheme, one borrowed from *tensor algebra*.  GAlgebra places the labelling index $i$ of the scalar coefficient as a *superscript* and thus writes the basis expansion of $\\mbf{x}$ in the form \n",
+    "\n",
+    "$$\\mbf{x}=\\sum_{i=1}^n x^i \\es{i} = x^1 \\es{1} + x^2 \\es{2} + \\dots + x^n \\es{n},$$ \n",
+    "\n",
+    "where $n=p+q$ is the dimension of the scalar product space $\\Rn{p,q}$.  Indexes written in superscript position, which should not be confused with exponents, are called *contravariant*, while those written as subscripts (as on the basis vectors) are called *covariant*.  (Although not very relevant for GAlgebra, which uses only one basis per geometric algebra model, contravariant/covariant index positioning carries information about how an indexed quantity changes under a change of basis.)\n",
+    "\n",
+    "Something similar happens with the basis blade expansion of a multivector $\\mbf{X}$.  GAlgebra labels the coefficient which multiplies basis blade $\\es{i_1 \\cdots i_g} := \\es{i_1} \\op \\cdots \\op \\es{i_g}$ with *contravariant indices* identical to the *covariant indices* on the basis blade multiplied.  Thus $\\mbf{X}$'s expansion has the form \n",
+    "\n",
+    "$$\\mbf{X} = \\sum_{g=0}^n \\left< {\\mbf{X}} \\right>_g\n",
+    "= X + \\sum_{g=1}^n \\left( \\sum_{1 \\le i_1 < \\cdots < i_g \\le n} X^{i_1 \\cdots i_g} \\es{i_1 \\cdots i_g} \\right).$$\n",
+    "\n",
+    "$X = \\left<\\mbf{X}\\right>_0$ has no indices and is displayed in normal math italic so as to distinguish it from the multivector $\\mbf{X}$ of which it is the grade-zero part.  In order to keep linearly independent the set of basis blades used in the expansion, only *strictly ordered* index values, $1 \\le i_1 < \\cdots < i_g \\le n$, are included in the summation."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2450d643",
+   "metadata": {},
+   "source": [
+    "$\\lt{T}(\\es{j})$, the image by a linear transformation $\\lt{T}$ of a basis vector $\\es{j}$, is itself a vector, so the image's expansion coefficients are also labelled by a contravariant index.  Convention is to write the expansion coefficient of $\\lt{T}(\\es{j})$ which multiplies the $i$th basis vector $\\es{i}$ as ${T^i}_j$.  Therefore\n",
+    "\n",
+    "$$\\lt{T}(\\es{j}) = \\sum_{i=1}^n {T^i}_j \\es{i} = {T^1}_j \\es{1} + {T^2}_j \\es{2} + \\cdots + {T^n}_j \\es{n}.$$   \n",
+    "\n",
+    "Notice that the coefficient's contravariant index $i$, which denotes which basis vector the coefficient is to multiply, is written not only as a superscript but also to the left of the covariant index $j$ which indicates the image vector in question.  This is done so that when the coefficients of the various basis vector images are placed into a matrix, a coefficient's left index specifies the row in which a coefficient is placed while its right index specifies the column.  With indexes so placed, the image vector may be written\n",
+    "\n",
+    "$$\\begin{align}\\lt{T}(\\mbf{x}) \n",
+    "= \\lt{T}\\left(\\sum_{j=1}^n x^j \\es{j}\\right) \n",
+    "= \\sum_{j=1}^n x^j \\lt{T}(\\es{j})\n",
+    "= \\sum_{j=1}^n x^j \\sum_{i=1}^n {T^i}_j \\es{i} \n",
+    "= \\sum_{i=1}^n \\left( \\sum_{j=1}^n {T^i}_j x^j \\right) \\es{i}. \n",
+    "\\end{align}$$ \n",
+    "\n",
+    "and the matrix of all expansion coefficients becomes\n",
+    "\n",
+    "$$[\\lt{T}]_\\scrE = \\left[ {T^i}_j \\right]\n",
+    "= \\left[ \\begin{matrix} {T^1}_1 & \\cdots & {T^1}_j & \\cdots & {T^1}_n \\\\\n",
+    "                        \\vdots & & \\vdots & & \\vdots \\\\\n",
+    "                        {T^i}_1 & \\cdots & {T^i}_j & \\cdots & {T^i}_n \\\\\n",
+    "                        \\vdots & & \\vdots & & \\vdots \\\\\n",
+    "                        {T^n}_1 & \\cdots & {T^n}_j & \\cdots & {T^n}_n \\\\\n",
+    "          \\end{matrix} \\right].$$\n",
+    "\n",
+    "This *contravariant-covariant matrix* is the standard matrix of the linear transformation $\\lt{T}$ with respect to the basis $\\scrE = \\left(\\es{1}, \\dots, \\es{n}\\right)$.  The definitions employed are the same as those in introductory textbooks; the only difference is that the matrix entries have been written with the row index appearing as a left superscript rather than a left subscript.  \n",
+    "\n",
+    "Notice that the $j$th column of $\\lt{T}$'s matrix consists of the expansion coefficients of $\\lt{T}(\\es{j})$, the $j$th basis image vector.\n",
+    "\n",
+    "A GAlgebra user must keep in mind that Python indexing starts at $0$ while it's traditional in mathematics to start at $1$.  The difference in start values means that for the SymPy matrix `T.matrix()`, it's the quantity `T.matrix()[i-1, j-1]` which retutrns ${T^i}_j$.  It may not *display* as such, however, as we will see when we examine the matrices for symmetric and antisymmetric transformations.\n",
+    "\n",
+    "Beside the standard matrix $\\left[{T^i}_j\\right]$ (the contravariant-covariant matrix) associated with $\\lt{T}$, there exists a different matrix, useful when discussing symmetric and antisymmetric transformations, that we will refer to as the *covariant-covariant matrix* $\\left[T_{ij}\\right]$.  The entries of the two matrices are related by the formulas\n",
+    "\n",
+    "$$T_{ij} = \\sum_{k=1}^n g_{ik}{T^k}_j \\qquad\\text{and}\\qquad {T^i}_j = \\sum_{k=1}^n g^{ik} T_{kj}.$$ \n",
+    "\n",
+    "Use of Euclidean metrics and orthonormal bases is common in introductory textbooks.  Since $\\left[g_{ij}\\right]$ is the identity matrix in such situations, the above relations then reduce to $T_{ij} = {T^i}_j$, which is one reason introductory textbooks do not make the contravariant/covariant distinction.  But if the metric is non-Euclidean or the basis is not orthonormal, the distinction is essential."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ad4283a5",
+   "metadata": {},
+   "source": [
+    "## 4. General symbolic transformations; transformation operations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0a6c0a65",
+   "metadata": {},
+   "source": [
+    "As illustrated by the next In[ ] cell, GAlgebra may be used to: \n",
+    "- Instantiate a *general* symbolic linear transformation $\\lt{G}$.    \n",
+    "- Display how $\\lt{G}$ maps each basis vector to a linear combination of basis vectors.\n",
+    "- Find the matrix of $\\lt{G}$.\n",
+    "- Compute $\\lt{G}$'s action on a generic vector $\\mbf{x}$.\n",
+    "- Compute $\\lt{G}$'s action on a generic multivector $\\mbf{X}$.\n",
+    "- Find the determinant of $\\lt{G}$.\n",
+    "- Find the trace of $\\lt{G}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "281d5ac4",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mbf{G}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle [\\lt{G}]_\\scrE = \\left[\\begin{array}{ccc}{G^{1}}_{1} & {G^{1}}_{2} & {G^{1}}_{3}\\\\{G^{2}}_{1} & {G^{2}}_{2} & {G^{2}}_{3}\\\\{G^{3}}_{1} & {G^{3}}_{2} & {G^{3}}_{3}\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{G.matrix()[3-1, 1-1]}= {G^{3}}_{1} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\lt{G}(\\mbf{x})=  \\begin{aligned}[t]  & \\left ( x^{1} {G^{1}}_{1} + x^{2} {G^{1}}_{2} + x^{3} {G^{1}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} \\\\  &  + \\left ( x^{1} {G^{2}}_{1} + x^{2} {G^{2}}_{2} + x^{3} {G^{2}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} \\\\  &  + \\left ( x^{1} {G^{3}}_{1} + x^{2} {G^{3}}_{2} + x^{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\lt{G}(\\mbf{X})=  \\begin{aligned}[t]  & X  \\\\  &  + \\left ( X^{1} {G^{1}}_{1} + X^{2} {G^{1}}_{2} + X^{3} {G^{1}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} \\\\  &  + \\left ( X^{1} {G^{2}}_{1} + X^{2} {G^{2}}_{2} + X^{3} {G^{2}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} \\\\  &  + \\left ( X^{1} {G^{3}}_{1} + X^{2} {G^{3}}_{2} + X^{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\  &  + \\left ( X^{12} {G^{1}}_{1} {G^{2}}_{2} - X^{12} {G^{1}}_{2} {G^{2}}_{1} + X^{13} {G^{1}}_{1} {G^{2}}_{3} - X^{13} {G^{1}}_{3} {G^{2}}_{1} + X^{23} {G^{1}}_{2} {G^{2}}_{3} - X^{23} {G^{1}}_{3} {G^{2}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{12} \\\\  &  + \\left ( X^{12} {G^{1}}_{1} {G^{3}}_{2} - X^{12} {G^{1}}_{2} {G^{3}}_{1} + X^{13} {G^{1}}_{1} {G^{3}}_{3} - X^{13} {G^{1}}_{3} {G^{3}}_{1} + X^{23} {G^{1}}_{2} {G^{3}}_{3} - X^{23} {G^{1}}_{3} {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{13} \\\\  &  + \\left ( X^{12} {G^{2}}_{1} {G^{3}}_{2} - X^{12} {G^{2}}_{2} {G^{3}}_{1} + X^{13} {G^{2}}_{1} {G^{3}}_{3} - X^{13} {G^{2}}_{3} {G^{3}}_{1} + X^{23} {G^{2}}_{2} {G^{3}}_{3} - X^{23} {G^{2}}_{3} {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{23} \\\\  &  + X^{123} \\left({G^{1}}_{1} {G^{2}}_{2} {G^{3}}_{3} - {G^{1}}_{1} {G^{2}}_{3} {G^{3}}_{2} - {G^{1}}_{2} {G^{2}}_{1} {G^{3}}_{3} + {G^{1}}_{2} {G^{2}}_{3} {G^{3}}_{1} + {G^{1}}_{3} {G^{2}}_{1} {G^{3}}_{2} - {G^{1}}_{3} {G^{2}}_{2} {G^{3}}_{1}\\right) \\boldsymbol{\\mbf{e}}_{123}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\det(\\lt{G})= {G^{1}}_{1} {G^{2}}_{2} {G^{3}}_{3} - {G^{1}}_{1} {G^{2}}_{3} {G^{3}}_{2} - {G^{1}}_{2} {G^{2}}_{1} {G^{3}}_{3} + {G^{1}}_{2} {G^{2}}_{3} {G^{3}}_{1} + {G^{1}}_{3} {G^{2}}_{1} {G^{3}}_{2} - {G^{1}}_{3} {G^{2}}_{2} {G^{3}}_{1} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{tr}(\\lt{G})= {G^{1}}_{1} + {G^{2}}_{2} + {G^{3}}_{3} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "G = m3.lt('G')                            # instantiate general symbolic transformation    \n",
+    "gprint(r'\\mbf{G}:~', G)                   # transformation's action on basis vectors\n",
+    "gprint(r'[\\lt{G}]_\\scrE =', G.matrix())   # matrix with respect to basis\n",
+    "gprint(r'\\text{G.matrix()[3-1, 1-1]}=',  G.matrix()[3-1, 1-1])\n",
+    "                                          # example: (3,1) matrix entry\n",
+    "gprint(r'\\lt{G}(\\mbf{x})=', G(x).Fmt(3))  # action on generic vector\n",
+    "gprint(r'\\lt{G}(\\mbf{X})=', G(X).Fmt(3))  # action on generic multivector\n",
+    "gprint(r'\\det(\\lt{G})=', G.det())         # transformation's determinant\n",
+    "gprint(r'\\text{tr}(\\lt{G})=', G.tr())     # transformation's trace"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5b062eb6",
+   "metadata": {},
+   "source": [
+    "- GAlgebra can multiply a linear transformation by a scalar provided it's a SymPy scalar.    \n",
+    "If multiplying the transformation by a 0-vector (i.e. a GAlgebra scalar), the 0-vector has to first be converted into a SymPy scalar by use of the multivector method `.scalar()`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "b31c6cd4",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\kappa  \\text{ belongs to } \\text{<class 'galgebra.mv.Mv'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{converted } \\kappa  \\text{ belongs to } \\text{<class 'sympy.core.symbol.Symbol'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\kappa\\lt{G}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto \\kappa  {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} + \\kappa  {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{2} + \\kappa  {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\kappa  {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{1} + \\kappa  {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + \\kappa  {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto \\kappa  {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{1} + \\kappa  {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{2} + \\kappa  {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\lambda  \\text{ belongs to } \\text{<class 'sympy.core.symbol.Symbol'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\lambda\\lt{G}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto \\lambda  {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} + \\lambda  {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{2} + \\lambda  {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\lambda  {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{1} + \\lambda  {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + \\lambda  {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto \\lambda  {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{1} + \\lambda  {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{2} + \\lambda  {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "kappa = m3.mv('kappa', 0)             # a 0-vector, not a SymPy scalar\n",
+    "gprint(kappa, r'\\text{ belongs to }', type(kappa))\n",
+    "gprint(r'\\text{converted }', kappa.scalar(), r'\\text{ belongs to }', type(kappa.scalar()))\n",
+    "gprint(r'\\kappa\\lt{G}:~', kappa.scalar()*G)  \n",
+    "                                      # product of 0-vector and transformation\n",
+    "gprint()\n",
+    "lamda = symbols('lambda', real=True)  # a SymPy scalar, not a 0-vector\n",
+    "gprint(lamda, r'\\text{ belongs to }', type(lamda))\n",
+    "gprint(r'\\lambda\\lt{G}:~', lamda*G)   # product of SymPy scalar and transformation"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "93d54c19",
+   "metadata": {},
+   "source": [
+    "GAlgebra can combine transformations $\\lt{F}$ and $\\lt{G}$ using\n",
+    "- addition, $\\lt{F} + \\lt{G}$,    \n",
+    "- subtraction, $\\lt{F} - \\lt{G}$, and/or    \n",
+    "- compositional multiplication, $\\lt{F}\\lt{G}$.\n",
+    "\n",
+    "The compositional product $\\lt{FG} \\equiv \\lt{F}\\circ\\lt{G}$ is obtained from GAlgebra expression `F*G`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "0ccb8a6f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\lt{F} + \\lt{G}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto \\left ( {F^{1}}_{1} + {G^{1}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{1} + {G^{2}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{1} + {G^{3}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\left ( {F^{1}}_{2} + {G^{1}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{2} + {G^{2}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{2} + {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto \\left ( {F^{1}}_{3} + {G^{1}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{3} + {G^{2}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{3} + {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad[\\lt{F}+\\lt{G}]_\\scrE = \\left[\\begin{array}{ccc}{F^{1}}_{1} + {G^{1}}_{1} & {F^{1}}_{2} + {G^{1}}_{2} & {F^{1}}_{3} + {G^{1}}_{3}\\\\{F^{2}}_{1} + {G^{2}}_{1} & {F^{2}}_{2} + {G^{2}}_{2} & {F^{2}}_{3} + {G^{2}}_{3}\\\\{F^{3}}_{1} + {G^{3}}_{1} & {F^{3}}_{2} + {G^{3}}_{2} & {F^{3}}_{3} + {G^{3}}_{3}\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\lt{F} - \\lt{G}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto \\left ( {F^{1}}_{1} - {G^{1}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{1} - {G^{2}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{1} - {G^{3}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\left ( {F^{1}}_{2} - {G^{1}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{2} - {G^{2}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{2} - {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto \\left ( {F^{1}}_{3} - {G^{1}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{3} - {G^{2}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{3} - {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad[\\lt{F}-\\lt{G}]_\\scrE = \\left[\\begin{array}{ccc}{F^{1}}_{1} - {G^{1}}_{1} & {F^{1}}_{2} - {G^{1}}_{2} & {F^{1}}_{3} - {G^{1}}_{3}\\\\{F^{2}}_{1} - {G^{2}}_{1} & {F^{2}}_{2} - {G^{2}}_{2} & {F^{2}}_{3} - {G^{2}}_{3}\\\\{F^{3}}_{1} - {G^{3}}_{1} & {F^{3}}_{2} - {G^{3}}_{2} & {F^{3}}_{3} - {G^{3}}_{3}\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\lt{F}\\lt{G}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto \\left ( {F^{1}}_{1} {G^{1}}_{1} + {F^{1}}_{2} {G^{2}}_{1} + {F^{1}}_{3} {G^{3}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{1} {G^{1}}_{1} + {F^{2}}_{2} {G^{2}}_{1} + {F^{2}}_{3} {G^{3}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{1} {G^{1}}_{1} + {F^{3}}_{2} {G^{2}}_{1} + {F^{3}}_{3} {G^{3}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\left ( {F^{1}}_{1} {G^{1}}_{2} + {F^{1}}_{2} {G^{2}}_{2} + {F^{1}}_{3} {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{1} {G^{1}}_{2} + {F^{2}}_{2} {G^{2}}_{2} + {F^{2}}_{3} {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{1} {G^{1}}_{2} + {F^{3}}_{2} {G^{2}}_{2} + {F^{3}}_{3} {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto \\left ( {F^{1}}_{1} {G^{1}}_{3} + {F^{1}}_{2} {G^{2}}_{3} + {F^{1}}_{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( {F^{2}}_{1} {G^{1}}_{3} + {F^{2}}_{2} {G^{2}}_{3} + {F^{2}}_{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( {F^{3}}_{1} {G^{1}}_{3} + {F^{3}}_{2} {G^{2}}_{3} + {F^{3}}_{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad[\\lt{F}\\lt{G}]_\\scrE = \\left[\\begin{array}{ccc}{F^{1}}_{1} {G^{1}}_{1} + {F^{1}}_{2} {G^{2}}_{1} + {F^{1}}_{3} {G^{3}}_{1} & {F^{1}}_{1} {G^{1}}_{2} + {F^{1}}_{2} {G^{2}}_{2} + {F^{1}}_{3} {G^{3}}_{2} & {F^{1}}_{1} {G^{1}}_{3} + {F^{1}}_{2} {G^{2}}_{3} + {F^{1}}_{3} {G^{3}}_{3}\\\\{F^{2}}_{1} {G^{1}}_{1} + {F^{2}}_{2} {G^{2}}_{1} + {F^{2}}_{3} {G^{3}}_{1} & {F^{2}}_{1} {G^{1}}_{2} + {F^{2}}_{2} {G^{2}}_{2} + {F^{2}}_{3} {G^{3}}_{2} & {F^{2}}_{1} {G^{1}}_{3} + {F^{2}}_{2} {G^{2}}_{3} + {F^{2}}_{3} {G^{3}}_{3}\\\\{F^{3}}_{1} {G^{1}}_{1} + {F^{3}}_{2} {G^{2}}_{1} + {F^{3}}_{3} {G^{3}}_{1} & {F^{3}}_{1} {G^{1}}_{2} + {F^{3}}_{2} {G^{2}}_{2} + {F^{3}}_{3} {G^{3}}_{2} & {F^{3}}_{1} {G^{1}}_{3} + {F^{3}}_{2} {G^{2}}_{3} + {F^{3}}_{3} {G^{3}}_{3}\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(F*G).matrix() == F.matrix() * G.matrix()}:~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "F = m3.lt('F')    # a symbolic linear transformation\n",
+    "G = m3.lt('G')    # another symbolic transformation\n",
+    "gprint(r'\\lt{F} + \\lt{G}:~', F + G,\n",
+    "       r'\\qquad[\\lt{F}+\\lt{G}]_\\scrE =', (F + G).matrix())\n",
+    "gprint(r'\\lt{F} - \\lt{G}:~', F - G,\n",
+    "       r'\\qquad[\\lt{F}-\\lt{G}]_\\scrE =', (F - G).matrix())\n",
+    "gprint(r'\\lt{F}\\lt{G}:~', F * G,\n",
+    "       r'\\qquad[\\lt{F}\\lt{G}]_\\scrE =', (F * G).matrix())\n",
+    "# Test:  Is the matrix of two transformations' compositional product\n",
+    "# the same as the product of the transformations' matrices?\n",
+    "gprint(r'\\text{(F*G).matrix() == F.matrix() * G.matrix()}:~',\n",
+    "       (F*G).matrix() == F.matrix() * G.matrix())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f43bd94f",
+   "metadata": {},
+   "source": [
+    "Observe that the $(i,j)$ entry in the above output for $[\\lt{F}\\lt{G}]_\\scrE$ is given by $\\sum_{k=1}^n {F^i}_k {G^k}_j$ (where $n=3$ for $\\Gn{1,2}$), as it should be."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0190f4e4",
+   "metadata": {},
+   "source": [
+    "- The GAlgebra expression `G.adj()` returns the adjoint $\\ad{G}$ of transformation $\\lt{G}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "3494bb1f",
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{adjoint }\\ad{G}\\text{ of general symbolic transformation }\\lt{G}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\ad{G}: \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} - {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{2} - {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto - {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto - {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{1} + {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad[\\ad{G}]_\\scrE= \\left[\\begin{array}{ccc}{G^{1}}_{1} & - {G^{2}}_{1} & - {G^{3}}_{1}\\\\- {G^{1}}_{2} & {G^{2}}_{2} & {G^{3}}_{2}\\\\- {G^{1}}_{3} & {G^{2}}_{3} & {G^{3}}_{3}\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\ad{G}(\\mbf{x})=  \\begin{aligned}[t]  & \\left ( x^{1} {G^{1}}_{1} - x^{2} {G^{2}}_{1} - x^{3} {G^{3}}_{1}\\right ) \\boldsymbol{\\mbf{e}}_{1} \\\\  &  + \\left ( - x^{1} {G^{1}}_{2} + x^{2} {G^{2}}_{2} + x^{3} {G^{3}}_{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} \\\\  &  + \\left ( - x^{1} {G^{1}}_{3} + x^{2} {G^{2}}_{3} + x^{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3}  \\end{aligned}  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad {{G^*}^1}_2=(1,2)\\text{ entry of matrix of }\\ad{G}= - {G^{2}}_{1} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'\\text{adjoint }\\ad{G}\\text{ of general symbolic transformation }\\lt{G}:')\n",
+    "gprint(r'\\qquad\\ad{G}:', G.adj(),\n",
+    "       r'\\qquad[\\ad{G}]_\\scrE=', G.adj().matrix(),\n",
+    "       r'\\\\\\qquad\\ad{G}(\\mbf{x})=', G.adj()(x).Fmt(3))\n",
+    "gprint(r'\\qquad {{G^*}^1}_2=(1,2)\\text{ entry of matrix of }\\ad{G}=',\n",
+    "       G.adj().matrix()[1-1,2-1])    # example matrix entry"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4e2ca7a3",
+   "metadata": {},
+   "source": [
+    "*Remark:*  As the above output shows, the adjoint's matrix is not necessarily the transpose of the transformation's matrix.  The two are necessarily equal only when the basis used is orthonormal and the metric has a Euclidean signature.  The algebra being used, `m3`, fails the second of those two requirements."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "de6e7f4c",
+   "metadata": {},
+   "source": [
+    "**Internal representation of a linear transformation.**  The model `G` of a transformation $\\lt{G}$ is stored internally as a Python dictionary, accessible as the attribute `G.lt_dict`.  The dictionary's *key: value* pairs, when printed, *appear* to have the form *basis vector: $\\lt{G}$(basis vector)*, but appearances are deceiving.  Instead the keys are basis *symbols*, i.e. are objects which are mapped to the basis *vectors* by application of the method `m3.mv()`.  And the values are linear combinations of the basis symbols, i.e. are objects which `m3.mv()` maps to basis image vectors.  This distinction will be important in Section 6, when we explore the different ways one can specify a non-symbolic linear transformation."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "21468025",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{basis vectors}:~~\\text{m3.mv()}= \\left(  \\boldsymbol{\\mbf{e}}_{1}, \\   \\boldsymbol{\\mbf{e}}_{2}, \\   \\boldsymbol{\\mbf{e}}_{3}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{basis symbols}:~~\\text{m3.basis}= \\left[ \\boldsymbol{\\mbf{e}}_{1}, \\  \\boldsymbol{\\mbf{e}}_{2}, \\  \\boldsymbol{\\mbf{e}}_{3}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{internal dictionary}:~~\\text{G.lt_dict}= \\left\\{ \\boldsymbol{\\mbf{e}}_{1} : {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{3}, \\  \\boldsymbol{\\mbf{e}}_{2} : {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{3}, \\  \\boldsymbol{\\mbf{e}}_{3} : {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3}\\right\\} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\boldsymbol{\\mbf{e}}_{1} \\text{ from m3.basis belongs to } \\text{<class 'galgebra.atoms.BasisVectorSymbol'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{3} \\text{ belongs to } \\text{<class 'sympy.core.add.Add'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\boldsymbol{\\mbf{e}}_{2} \\text{ from m3.basis belongs to } \\text{<class 'galgebra.atoms.BasisVectorSymbol'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{3} \\text{ belongs to } \\text{<class 'sympy.core.add.Add'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\boldsymbol{\\mbf{e}}_{3} \\text{ from m3.basis belongs to } \\text{<class 'galgebra.atoms.BasisVectorSymbol'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\text{ belongs to } \\text{<class 'sympy.core.add.Add'>} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'\\text{basis vectors}:~~\\text{m3.mv()}=', m3.mv())\n",
+    "gprint(r'\\text{basis symbols}:~~\\text{m3.basis}=', m3.basis)\n",
+    "gprint(r'\\text{internal dictionary}:~~\\text{G.lt_dict}=', G.lt_dict)\n",
+    "for base in m3.basis:\n",
+    "    gprint(base, r'\\text{ from m3.basis belongs to }', type(base))\n",
+    "    gprint(G.lt_dict[base], r'\\text{ belongs to }',\n",
+    "           type(G.lt_dict[base]))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "80cc64e8",
+   "metadata": {},
+   "source": [
+    "Versor-based transformations will be discussed in Section 7.  Such a transformation stores a versor internally in its `.V` attribute.  Each transformation, whether dictionary based or versor based, have a boolean `.versor` attribute which, when `True`, signals the existence of the `.V` attribute."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e30fa966",
+   "metadata": {},
+   "source": [
+    "## 5. Symmetric and antisymmetric symbolic transformations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f951af56",
+   "metadata": {},
+   "source": [
+    "Besides *general* symbolic transformations, encountered in the previous section, GAlgebra can create *symmetric* (a.k.a. self-adjoint) and *antisymmetric* (a.k.a. skew) symbolic transformations."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "76906c9d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "G = m3.lt('G', mode='g')    # instantiate a general symbolic transformation\n",
+    "    # specification mode='g' is not strictly necessary as 'g' is the default\n",
+    "    # value of the mode parameter\n",
+    "S = m3.lt('S', mode='s')    # instantiate a symmetric symbolic transformation\n",
+    "A = m3.lt('A', mode='a')    # instantiate an antisymmetric symbolic transformation"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "48f61f17",
+   "metadata": {},
+   "source": [
+    "Unlike a *general* symbolic transformation $\\lt{G}$, the entries in `S.matrix()` for a symmetric symbolic transformation $\\lt{S}$ will not *appear* to be entries in the standard (contravariant-covariant) matrix for that transformation, although they are.  Instead they will appear as linear combinations of doubly-subscripted symbols $S_{ij}$, where $1 \\le i \\le j \\le n$.  Those symbols have the significance of being the entries on or above the diagonal of $\\lt{S}$'s covariant-covariant matrix.\n",
+    "\n",
+    "Similarly, the entries in `A.matrix()` for an antisymmetric symbolic transformation $\\lt{A}$ will appear as linear combinations of doubly-subscripted symbols $A_{ij}$, where $1 \\le i < j \\le n$.  Those symbols have the significance of being the entries  above the diagonal of $\\lt{A}$'s covariant-covariant matrix.  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "952ad12b",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{general symbolic transformation }\\lt{G}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{G}: \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto {G^{1}}_{1} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{1} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{1} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto {G^{1}}_{2} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{2} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{2} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto {G^{1}}_{3} \\boldsymbol{\\mbf{e}}_{1} + {G^{2}}_{3} \\boldsymbol{\\mbf{e}}_{2} + {G^{3}}_{3} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad[\\lt{G}]_\\scrE= \\left[\\begin{array}{ccc}{G^{1}}_{1} & {G^{1}}_{2} & {G^{1}}_{3}\\\\{G^{2}}_{1} & {G^{2}}_{2} & {G^{2}}_{3}\\\\{G^{3}}_{1} & {G^{3}}_{2} & {G^{3}}_{3}\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{G}(\\mbf{x})= \\left ( x^{1} {G^{1}}_{1} + x^{2} {G^{1}}_{2} + x^{3} {G^{1}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( x^{1} {G^{2}}_{1} + x^{2} {G^{2}}_{2} + x^{3} {G^{2}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( x^{1} {G^{3}}_{1} + x^{2} {G^{3}}_{2} + x^{3} {G^{3}}_{3}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad(3,1)\\text{ entry of the contravariant-covariant matrix is } {G^{3}}_{1} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\text{covariant-covariant matrix of }\\lt{G}= \\left[\\begin{array}{ccc}{G^{1}}_{1} & {G^{1}}_{2} & {G^{1}}_{3}\\\\- {G^{2}}_{1} & - {G^{2}}_{2} & - {G^{2}}_{3}\\\\- {G^{3}}_{1} & - {G^{3}}_{2} & - {G^{3}}_{3}\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{symmetric symbolic transformation }\\lt{S}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{S}: \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto S_{1} \\boldsymbol{\\mbf{e}}_{1} - S_{2} \\boldsymbol{\\mbf{e}}_{2} - S_{3} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto S_{2} \\boldsymbol{\\mbf{e}}_{1} - S_{4} \\boldsymbol{\\mbf{e}}_{2} - S_{5} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto S_{3} \\boldsymbol{\\mbf{e}}_{1} - S_{5} \\boldsymbol{\\mbf{e}}_{2} - S_{6} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad[\\lt{S}]_\\scrE= \\left[\\begin{array}{ccc}S_{1} & S_{2} & S_{3}\\\\- S_{2} & - S_{4} & - S_{5}\\\\- S_{3} & - S_{5} & - S_{6}\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{S}(\\mbf{x})= \\left ( S_{1} x^{1} + S_{2} x^{2} + S_{3} x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( - S_{2} x^{1} - S_{4} x^{2} - S_{5} x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( - S_{3} x^{1} - S_{5} x^{2} - S_{6} x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad(3,1)\\text{ entry of the contravariant-covariant matrix is } - S_{3} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\text{covariant-covariant matrix of }\\lt{S}= \\left[\\begin{array}{ccc}S_{1} & S_{2} & S_{3}\\\\S_{2} & S_{4} & S_{5}\\\\S_{3} & S_{5} & S_{6}\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{antisymmetric symbolic transformation }\\lt{A}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{A}: \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto A_{1} \\boldsymbol{\\mbf{e}}_{2} + A_{2} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto A_{1} \\boldsymbol{\\mbf{e}}_{1} + A_{3} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto A_{2} \\boldsymbol{\\mbf{e}}_{1} - A_{3} \\boldsymbol{\\mbf{e}}_{2} \\end{aligned} \\right\\} \\qquad[\\lt{A}]_\\scrE= \\left[\\begin{array}{ccc}0 & A_{1} & A_{2}\\\\A_{1} & 0 & - A_{3}\\\\A_{2} & A_{3} & 0\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{A}(\\mbf{x})= \\left ( A_{1} x^{2} + A_{2} x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( A_{1} x^{1} - A_{3} x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( A_{2} x^{1} + A_{3} x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad(3,1)\\text{ entry of the contravariant-covariant matrix is } A_{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\text{covariant-covariant matrix of }\\lt{A}= \\left[\\begin{array}{ccc}0 & A_{1} & A_{2}\\\\- A_{1} & 0 & A_{3}\\\\- A_{2} & - A_{3} & 0\\end{array}\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'\\text{general symbolic transformation }\\lt{G}:')\n",
+    "gprint(r'\\qquad\\lt{G}:', G,\n",
+    "       r'\\qquad[\\lt{G}]_\\scrE=', G.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{G}(\\mbf{x})=', G(x))\n",
+    "gprint(r'\\qquad(3,1)\\text{ entry of the contravariant-covariant matrix is }',\n",
+    "       G.matrix()[3-1,1-1])               # example matrix entry\n",
+    "gprint(r'\\qquad\\text{covariant-covariant matrix of }\\lt{G}=', m3.g * G.matrix())\n",
+    "gprint()\n",
+    "gprint(r'\\text{symmetric symbolic transformation }\\lt{S}:')\n",
+    "gprint(r'\\qquad\\lt{S}:', S,\n",
+    "       r'\\qquad[\\lt{S}]_\\scrE=', S.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{S}(\\mbf{x})=', S(x))\n",
+    "gprint(r'\\qquad(3,1)\\text{ entry of the contravariant-covariant matrix is }',\n",
+    "       S.matrix()[3-1,1-1])               # example matrix entry\n",
+    "gprint(r'\\qquad\\text{covariant-covariant matrix of }\\lt{S}=', m3.g * S.matrix())\n",
+    "gprint()\n",
+    "gprint(r'\\text{antisymmetric symbolic transformation }\\lt{A}:')\n",
+    "gprint(r'\\qquad\\lt{A}:', A,\n",
+    "       r'\\qquad[\\lt{A}]_\\scrE=', A.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{A}(\\mbf{x})=', A(x))\n",
+    "gprint(r'\\qquad(3,1)\\text{ entry of the contravariant-covariant matrix is }',\n",
+    "       A.matrix()[3-1,1-1])               # example matrix entry\n",
+    "gprint(r'\\qquad\\text{covariant-covariant matrix of }\\lt{A}=', m3.g * A.matrix())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "77ceaac3",
+   "metadata": {},
+   "source": [
+    "Notice in the above output that the covariant-covariant matrices of the general transformation $\\lt{G}$, the symmetric transformation $\\lt{S}$, and the antisymmetric transformation $\\lt{A}$ are respectively neither symmetric nor antisymmetric, symmetric, and antisymmetric.  None of the transformations display symmetry or antisymmetry in their standard (contravariant-covariant) matrices."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ca059499",
+   "metadata": {},
+   "source": [
+    "## 6.  Non-symbolic, dictionary-based transformations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d1c36ad5",
+   "metadata": {},
+   "source": [
+    "All examples in Sections 4--6 were of symbolic transformations.  To create such transformations, all that was needed was a one-letter string, used to specify the kernel symbol of the matrix entries, and specification as to whether the transformation was to be general (`mode='g'`), symmetric (`mode='s'`), or antisymmetric (`mode='a'`).\n",
+    "\n",
+    "Besides one-letter strings, the transformation constructor `m3.lt` can accept objects of other types for its instantiation parameter.  Such objects can be:\n",
+    "\n",
+    "- `lt_list`, a list of lists, has its $j$th entry `lt_list[j]` a list of the expansion coefficients of the $j$the image vector.     \n",
+    "- `lt_matrix`, a SymPy matrix, is identical to the matrix of the desired tranformation.    \n",
+    "- `lt_dict`, a Python dictionary, consists of *key:value* pairs, where each *key* is a basis symbol (entry in `m3.basis`) and each *value* is a linear combination of basis symbols.       \n",
+    "- `lt_func`, a linear vector-valued function of a vector argument, has the same action on vectors as the desired linear transformation/outermorphism.\n",
+    "\n",
+    "Each such parameter is a different ways of specifying the images of the basis vectors by the desired transformation/outermorphism.  The `mode` parameter is not specified since symmetry or antisymmetry, if either, is determined by the basis vector images."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9d442aad",
+   "metadata": {},
+   "source": [
+    "**An example.**  Suppose we want to instantiate a transformation $\\lt{T}$ the actions of which on basis vectors are\n",
+    "\n",
+    "$$\\left\\{~ \\begin{array}{llrrr}\n",
+    "\\lt{T}(\\es{1}) &= &(0)\\es{1} + &(-1)\\es{2} + &(2)\\es{3} \\\\\n",
+    "\\lt{T}(\\es{2}) &= &(5)\\es{1} + &(2)\\es{2}  + &(3)\\es{3} \\\\\n",
+    "\\lt{T}(\\es{3}) &= &(2)\\es{1} + &(0)\\es{2}  + &(0)\\es{3} \\\\\n",
+    "\\end{array} ~\\right\\}.$$\n",
+    "\n",
+    "We create different type objects each of which encodes the information in the above set of equations:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "3d14e81d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# type list (of lists)\n",
+    "lt_list = [[0, -1, 2], [5, 2, 3], [2, 0, 0]]       \n",
+    "\n",
+    "# type Matrix\n",
+    "lt_matrix = Matrix([[0, 5, 2], [-1, 2, 0], [2, 3, 0]])\n",
+    "\n",
+    "# type dict\n",
+    "lt_dict = {}          # create empty dictionary, then add key:value pairs\n",
+    "b1,b2,b3 = m3.basis   # basis symbols corresponding to basis vectors\n",
+    "lt_dict[b1] = -b2 + 2*b3          # 1st linear combination of basis symbols\n",
+    "lt_dict[b2] = 5*b1 + 2*b2 + 3*b3  # 2nd linear combination of basis symbols\n",
+    "lt_dict[b3] = 2*b1                # 3rd linear combination of basis symbols\n",
+    "\n",
+    "# type function\n",
+    "lt_func = lambda x: (re1<x)*(   0*e1 + 5*e2 + 2*e3)\\\n",
+    "                  + (re2<x)*((-1)*e1 + 2*e2 + 0*e3)\\\n",
+    "                  + (re3<x)*(   2*e1 + 3*e2 + 0*e3)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "af191d89",
+   "metadata": {},
+   "source": [
+    "**Note:**  `lt_list` is different from the list of lists provided to `Matrix` when creating `lt_matrix`.  `lt_list` organizes the entries of the transformation's matrix by column, but the argument given to `Matrix` organizes them by row.   \n",
+    "\n",
+    "**Note:** The construction of `lt_func` was motivated by the formula \n",
+    "$\\mbf{x} \\mapsto \\sum_{j=1}^n x^j \\lt{T}(\\es{j})  \n",
+    "= \\sum_{j=1}^n (\\eS{j}\\cdot\\mbf{x}) \\left( \\sum_{i=1}^n {T^i}_j \\es{i}\\right) $.\n",
+    "\n",
+    "Next we instantiate linear transformations $\\lt{T}_1$, $\\lt{T}_2$, $\\lt{T}_3$, and $\\lt{T}_4$ for each of the respective objects `lt_list`, `lt_matrix`, `lt_dict`, and `lt_func`.  Each transformation is then examined for its action on the basis, its standard matrix, and its action on a generic vector $\\mbf{x}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "id": "7003fd2c",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{T1 = m3.lt(lt_list)}, \\quad\\text{where lt_list}= \\left[ \\left[ 0, \\  -1, \\  2\\right], \\  \\left[ 5, \\  2, \\  3\\right], \\  \\left[ 2, \\  0, \\  0\\right]\\right] :$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_1:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto - \\boldsymbol{\\mbf{e}}_{2} + 2 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto 5 \\boldsymbol{\\mbf{e}}_{1} + 2 \\boldsymbol{\\mbf{e}}_{2} + 3 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto 2 \\boldsymbol{\\mbf{e}}_{1} \\end{aligned} \\right\\} \\qquad\\left[ \\lt{T}_1 \\right]= \\left[\\begin{array}{ccc}0 & 5 & 2\\\\-1 & 2 & 0\\\\2 & 3 & 0\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_1(\\mbf{x})= \\left ( 5 x^{2} + 2 x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( - x^{1} + 2 x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( 2 x^{1} + 3 x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{T2 = m3.lt(lt_matrix)}, \\quad\\text{where lt_matrix}= \\left[\\begin{array}{ccc}0 & 5 & 2\\\\-1 & 2 & 0\\\\2 & 3 & 0\\end{array}\\right] :$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_2:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto - \\boldsymbol{\\mbf{e}}_{2} + 2 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto 5 \\boldsymbol{\\mbf{e}}_{1} + 2 \\boldsymbol{\\mbf{e}}_{2} + 3 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto 2 \\boldsymbol{\\mbf{e}}_{1} \\end{aligned} \\right\\} \\qquad\\left[ \\lt{T}_2 \\right]= \\left[\\begin{array}{ccc}0 & 5 & 2\\\\-1 & 2 & 0\\\\2 & 3 & 0\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_2(\\mbf{x})= \\left ( 5 x^{2} + 2 x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( - x^{1} + 2 x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( 2 x^{1} + 3 x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{T3 = m3.lt(lt_dict)}, \\quad\\text{where lt_dict}= \\left\\{ \\boldsymbol{\\mbf{e}}_{1} : - \\boldsymbol{\\mbf{e}}_{2} + 2 \\boldsymbol{\\mbf{e}}_{3}, \\  \\boldsymbol{\\mbf{e}}_{2} : 5 \\boldsymbol{\\mbf{e}}_{1} + 2 \\boldsymbol{\\mbf{e}}_{2} + 3 \\boldsymbol{\\mbf{e}}_{3}, \\  \\boldsymbol{\\mbf{e}}_{3} : 2 \\boldsymbol{\\mbf{e}}_{1}\\right\\} :$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_3:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto - \\boldsymbol{\\mbf{e}}_{2} + 2 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto 5 \\boldsymbol{\\mbf{e}}_{1} + 2 \\boldsymbol{\\mbf{e}}_{2} + 3 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto 2 \\boldsymbol{\\mbf{e}}_{1} \\end{aligned} \\right\\} \\qquad\\left[ \\lt{T}_3 \\right]= \\left[\\begin{array}{ccc}0 & 5 & 2\\\\-1 & 2 & 0\\\\2 & 3 & 0\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_3(\\mbf{x})= \\left ( 5 x^{2} + 2 x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( - x^{1} + 2 x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( 2 x^{1} + 3 x^{2}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{T4 = m3.lt(lt_func)}\\qquad\\text{where lt_func is defined by}\\\\\\qquad\\text{lambda x: (e2+2*e3)*(re1<x) + (5*e1+2*e2+3*e3)*(re2<x) + (2*e1)*(re3<x)}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_4:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto 5 \\boldsymbol{\\mbf{e}}_{2} + 2 \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto - \\boldsymbol{\\mbf{e}}_{1} + 2 \\boldsymbol{\\mbf{e}}_{2} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto 2 \\boldsymbol{\\mbf{e}}_{1} + 3 \\boldsymbol{\\mbf{e}}_{2} \\end{aligned} \\right\\} \\qquad\\left[ \\lt{T}_4 \\right]= \\left[\\begin{array}{ccc}0 & -1 & 2\\\\5 & 2 & 3\\\\2 & 0 & 0\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_4(\\mbf{x})= \\left ( - x^{2} + 2 x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{1} + \\left ( 5 x^{1} + 2 x^{2} + 3 x^{3}\\right ) \\boldsymbol{\\mbf{e}}_{2} + 2 x^{1} \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "T1 = m3.lt(lt_list)    # instantiate transformation using list-type object\n",
+    "gprint(r'\\textbf{T1 = m3.lt(lt_list)}, \\quad\\text{where lt_list}=', lt_list, ':')\n",
+    "gprint(r'\\qquad\\lt{T}_1:~', T1,\n",
+    "       r'\\qquad\\left[ \\lt{T}_1 \\right]=', T1.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{T}_1(\\mbf{x})=', T1(x))\n",
+    "gprint()\n",
+    "\n",
+    "T2 = m3.lt(lt_matrix)  # instantiate transformation using Matrix-type object\n",
+    "gprint(r'\\textbf{T2 = m3.lt(lt_matrix)}, \\quad\\text{where lt_matrix}=', lt_matrix, ':')\n",
+    "gprint(r'\\qquad\\lt{T}_2:~', T2,\n",
+    "       r'\\qquad\\left[ \\lt{T}_2 \\right]=', T2.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{T}_2(\\mbf{x})=', T2(x))\n",
+    "gprint()\n",
+    "\n",
+    "T3 = m3.lt(lt_dict)    # instantiate transformation using dict-type object\n",
+    "gprint(r'\\textbf{T3 = m3.lt(lt_dict)}, \\quad\\text{where lt_dict}=', lt_dict, ':')\n",
+    "gprint(r'\\qquad\\lt{T}_3:~', T3,\n",
+    "       r'\\qquad\\left[ \\lt{T}_3 \\right]=', T3.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{T}_3(\\mbf{x})=', T3(x))\n",
+    "gprint()\n",
+    "\n",
+    "T4 = m3.lt(lt_func)    # instantiate transformation using function-type object\n",
+    "gprint(r'\\textbf{T4 = m3.lt(lt_func)}\\qquad\\text{where lt_func is defined by}\\\\\\qquad'\n",
+    "       + r'\\text{lambda x: (e2+2*e3)*(re1<x) + (5*e1+2*e2+3*e3)*(re2<x) + (2*e1)*(re3<x)}:')\n",
+    "gprint(r'\\qquad\\lt{T}_4:~', T4,\n",
+    "       r'\\qquad\\left[ \\lt{T}_4 \\right]=', T4.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{T}_4(\\mbf{x})=', T4(x))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0185644f",
+   "metadata": {},
+   "source": [
+    "Comparison shows that each of the differently instantiated transformations are equivalent."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "73ac6327",
+   "metadata": {},
+   "source": [
+    "## 7. Versor-based orthogonal transformations"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eec69422",
+   "metadata": {},
+   "source": [
+    "A **versor** $\\mbf{V} = \\mbf{v}_k \\cdots \\mbf{v}_1$ is the geometric product of a sequence of invertible vectors.  The $i$th vector defines a reflection $\\mbf{x} \\mapsto -\\mbf{v}_i \\mbf{x} \\mbf{v}_i^{-1}$ in the hyperplane with normal vector $\\mbf{v}_i$.  Consequently\n",
+    "\n",
+    "$$\\mbf{x} \\mapsto \\lt{T}_\\mbf{V}(\\mbf{x}) \n",
+    "=-{\\mbf{v}_k}(\\cdots(-{\\mbf{v}_1}\\mbf{x}{\\mbf{v}_1}^{-1})\\cdots){\\mbf{v}_k}^{-1} = (-1)^k \\mbf{V}\\mbf{x} \\mbf{V}^{-1} \n",
+    "= \\ginvol{\\mbf{V}}\\mbf{x} \\mbf{V}^{-1} $$\n",
+    "\n",
+    "defines the orthogonal transformation $\\lt{T}_\\mbf{V}$ which arises by composing the $k$ hyperplane reflections.  The *Cartan-Dieudonné Theorem* says that any orthogonal transformation can be so expressed.  \n",
+    "\n",
+    "When the transformation is extended to an outermorphism, its action on a multivector is described by\n",
+    "\n",
+    "$$\\lt{T}_\\mbf{V}(\\mbf{X}) \n",
+    "= \\begin{cases}\n",
+    "  \\mbf{V} \\mbf{X} \\mbf{V}^{-1} & \\text{if versor }\\mbf{V}\\text{ is even} \\\\\n",
+    "  \\mbf{V} \\ginvol{\\mbf{X}} \\mbf{V}^{-1} & \\text{if versor }\\mbf{V}\\text{ is odd}\n",
+    "  \\end{cases}.$$\n",
+    "\n",
+    "The mapping $\\mbf{V} \\mapsto \\lt{T}_\\mbf{V}$ from the group of versors to the group of orthogonal transformations is a group morphism, so   \n",
+    "\n",
+    "$$\\lt{T}_{\\mbf{V}_2} \\lt{T}_{\\mbf{V}_1} \n",
+    "= \\lt{T}_{\\mbf{V}_2 \\mbf{V}_1}.$$\n",
+    "\n",
+    "Furthermore the morphism's kernel is the versor subgroup of all invertible 0-vectors; if $\\kappa$ is a nonzero 0-vector, then $\\lt{T}_\\kappa$ will be the identity transformation $\\lt{Id}_{\\Gn{p,q}}$.  Take $\\kappa = \\mbf{V}\\til{\\mbf{V}} \\in \\Rn{\\ne 0}$.  By the group morphism's product preservation property we have  \n",
+    "\n",
+    "$$\\lt{T}_\\mbf{V} \\lt{T}_{\\til{\\mbf{V}}} \n",
+    "= \\lt{T}_{\\mbf{V}\\til{\\mbf{V}}}\n",
+    "= \\lt{T}_{\\kappa} = \\lt{Id}_{\\Gn{p,q}},$$\n",
+    "\n",
+    "whence $\\lt{T}_{\\til{\\mbf{V}}} = {\\lt{T}_\\mbf{V}}^{-1}$.  (Another way of obtaining the inverse of $\\lt{T}_\\mbf{V}$ is $\\lt{T}_{\\mbf{V}^{-1}}$, as follows from $\\mbf{V}^{-1}$ and $\\til{\\mbf{V}}$ being scalar multiples of one another.)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c90980fe",
+   "metadata": {},
+   "source": [
+    "Provided the desired transformation is orthogonal, there's one final type of parameter that can be used to instantiate a model of the transformation:\n",
+    "\n",
+    "- `lt_versor`, a versor, defines the composition of a sequence of hyperplane reflections."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2e1bc29f",
+   "metadata": {},
+   "source": [
+    "**Example.**  Suppose we want an orthogonal transformation that is the composition of a rotation through angle $\\theta$ in the (spacelike) $x^2 x^3$ plane and of a reflection in that plane, which has normal vector $\\es{1}$.  Reflecting first through the plane orthogonal to $\\mbf{v}_1 = \\es{2}$ and then through the plane orthogonal to $\\mbf{v}_2 = \\cos(\\theta/2) \\es{2} + \\sin(\\theta/2) \\es{3}$ accomplishes the rotation.  A third reflection through the plane orthogonal to $\\mbf{v}_3 = \\es{1}$ completes the overall transformation."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "id": "1434f1bd",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{T_V = m3.lt(lt_versor)}, \\quad\\text{where lt_versor}= - \\cos{\\left (\\frac{\\theta }{2} \\right )} \\boldsymbol{\\mbf{e}}_{1} - \\sin{\\left (\\frac{\\theta }{2} \\right )} \\boldsymbol{\\mbf{e}}_{123} :$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_\\mbf{V}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto - \\boldsymbol{\\mbf{e}}_{1} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\cos{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{2} + \\sin{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto - \\sin{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{2} + \\cos{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad\\left[\\lt{T}_\\mbf{V}\\right]= \\left[\\begin{array}{ccc}- \\frac{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} - \\frac{{\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} & 0 & 0\\\\0 & - \\frac{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} + \\frac{{\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} & - \\frac{2 \\sin{\\left (\\frac{\\theta }{2} \\right )} \\cos{\\left (\\frac{\\theta }{2} \\right )}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}\\\\0 & \\frac{2 \\sin{\\left (\\frac{\\theta }{2} \\right )} \\cos{\\left (\\frac{\\theta }{2} \\right )}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} & - \\frac{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} + \\frac{{\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad\\lt{T}_\\mbf{V}(\\mbf{x})= - x^{1} \\boldsymbol{\\mbf{e}}_{1} + \\left ( x^{2} \\cos{\\left (\\theta  \\right )} - x^{3} \\sin{\\left (\\theta  \\right )}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( x^{2} \\sin{\\left (\\theta  \\right )} + x^{3} \\cos{\\left (\\theta  \\right )}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\mathscr{Q}(\\lt{T}_\\mbf{V}(\\mbf{x}))= {\\left ( x^{1} \\right )}^{2} - {\\left ( x^{2} \\right )}^{2} - {\\left ( x^{3} \\right )}^{2} \\qquad\\mathscr{Q}(\\mbf{x})= {\\left ( x^{1} \\right )}^{2} - {\\left ( x^{2} \\right )}^{2} - {\\left ( x^{3} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\mathscr{Q}(\\lt{T}_\\mbf{V}(\\mbf{x})) = \\mathscr{Q}(\\mbf{x}):~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\det(\\lt{T}_\\mbf{V})= -1 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "theta = symbols('theta', real=True)\n",
+    "v1 = e2\n",
+    "v2 = cos(theta/2)*e2 + sin(theta/2)*e3\n",
+    "v3 = e1\n",
+    "lt_versor = v3 * v2 * v1\n",
+    "T_V = m3.lt(lt_versor)    # instantiate versor-based orthogonal transformation\n",
+    "\n",
+    "gprint(r'\\textbf{T_V = m3.lt(lt_versor)}, \\quad\\text{where lt_versor}=', lt_versor, ':')\n",
+    "gprint(r'\\qquad\\lt{T}_\\mbf{V}:~', T_V,\n",
+    "       r'\\qquad\\left[\\lt{T}_\\mbf{V}\\right]=', T_V.matrix(),\n",
+    "       r'\\\\\\qquad\\lt{T}_\\mbf{V}(\\mbf{x})=', mv_simplify(T_V(x), fu))\n",
+    "gprint(r'\\qquad\\mathscr{Q}(\\lt{T}_\\mbf{V}(\\mbf{x}))=', qform(T_V(x)),\n",
+    "       r'\\qquad\\mathscr{Q}(\\mbf{x})=', qform(x))\n",
+    "gprint(r'\\qquad\\mathscr{Q}(\\lt{T}_\\mbf{V}(\\mbf{x})) = \\mathscr{Q}(\\mbf{x}):~',\n",
+    "       fu(qform(T_V(x))) == fu(qform(x)))\n",
+    "gprint(r'\\qquad\\det(\\lt{T}_\\mbf{V})=', trigsimp(T_V.det()))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "086ee861",
+   "metadata": {},
+   "source": [
+    "The output's penultimate line confirms that $\\lt{T}_\\mbf{V}$ is orthogonal, for a tranformation is orthogonal if and only if it preserves the quadratic form $\\mbf{x} \\mapsto \\mathscr{Q}(\\mbf{x}) = \\mbf{x}^2$.  That $\\det(\\lt{T}_\\mbf{V}) = -1$ is a consequence of the transformation being a composition of three orientation-reversing reflections.\n",
+    "\n",
+    "The compositional inverse method `.inv()` is only implemented in GAlgebra for versor-based transformations.  The next In[ ] cell computes the compositional inverse of the transformation `T_V` found in the previous cell.  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "id": "75a0ce52",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{T_Vinv = T_V.inv()}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad{\\lt{T}_\\mbf{V}}^{-1}:~ \\left\\{ \\begin{aligned} \\boldsymbol{\\mbf{e}}_{1} &\\mapsto - \\boldsymbol{\\mbf{e}}_{1} \\\\ \\boldsymbol{\\mbf{e}}_{2} &\\mapsto \\cos{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{2} - \\sin{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{3} \\\\ \\boldsymbol{\\mbf{e}}_{3} &\\mapsto \\sin{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{2} + \\cos{\\left (\\theta  \\right )} \\boldsymbol{\\mbf{e}}_{3} \\end{aligned} \\right\\} \\qquad\\left[{\\lt{T}_\\mbf{V}}^{-1}\\right]= \\left[\\begin{array}{ccc}-1 & 0 & 0\\\\0 & - \\frac{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} + \\frac{{\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} & \\frac{2 \\sin{\\left (\\frac{\\theta }{2} \\right )} \\cos{\\left (\\frac{\\theta }{2} \\right )}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}\\\\0 & - \\frac{2 \\sin{\\left (\\frac{\\theta }{2} \\right )} \\cos{\\left (\\frac{\\theta }{2} \\right )}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} & - \\frac{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}} + \\frac{{\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}{{\\sin{\\left (\\frac{\\theta }{2} \\right )}}^{2} + {\\cos{\\left (\\frac{\\theta }{2} \\right )}}^{2}}\\end{array}\\right] \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\qquad{\\lt{T}_\\mbf{V}}^{-1}(\\mbf{x})= - x^{1} \\boldsymbol{\\mbf{e}}_{1} + \\left ( x^{2} \\cos{\\left (\\theta  \\right )} + x^{3} \\sin{\\left (\\theta  \\right )}\\right ) \\boldsymbol{\\mbf{e}}_{2} + \\left ( - x^{2} \\sin{\\left (\\theta  \\right )} + x^{3} \\cos{\\left (\\theta  \\right )}\\right ) \\boldsymbol{\\mbf{e}}_{3} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\mathscr{Q}({\\lt{T}_\\mbf{V}}^{-1}(\\mbf{x}))= {\\left ( x^{1} \\right )}^{2} - {\\left ( x^{2} \\right )}^{2} - {\\left ( x^{3} \\right )}^{2} \\qquad\\mathscr{Q}(\\mbf{x})= {\\left ( x^{1} \\right )}^{2} - {\\left ( x^{2} \\right )}^{2} - {\\left ( x^{3} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\mathscr{Q}({\\lt{T}_\\mbf{V}}^{-1}(\\mbf{x})) = \\mathscr{Q}(\\mbf{x}):~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\det({\\lt{T}_\\mbf{V}}^{-1})= - \\frac{\\left(1 - \\cos{\\left (\\theta  \\right )}\\right)^{2}}{2} - \\cos{\\left (\\theta  \\right )} + \\frac{\\cos{\\left (2 \\theta  \\right )}}{4} - \\frac{1}{4} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\left(\\lt{T}_\\mbf{V}{\\lt{T}_\\mbf{V}}^{-1}\\right)(\\mbf{{x}})= x^{1} \\boldsymbol{\\mbf{e}}_{1} + x^{2} \\boldsymbol{\\mbf{e}}_{2} + x^{3} \\boldsymbol{\\mbf{e}}_{3} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad\\left(\\lt{T}_\\mbf{V}{\\lt{T}_\\mbf{V}}^{-1}\\right)(\\mbf{{x}})==\\mbf{x}:~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "T_Vinv = T_V.inv()\n",
+    "  # compositional inverse of versor-based transformation\n",
+    "gprint(r'\\textbf{T_Vinv = T_V.inv()}:')\n",
+    "gprint(r'\\qquad{\\lt{T}_\\mbf{V}}^{-1}:~', T_Vinv,\n",
+    "       r'\\qquad\\left[{\\lt{T}_\\mbf{V}}^{-1}\\right]=', T_Vinv.matrix(),\n",
+    "       r'\\\\\\qquad{\\lt{T}_\\mbf{V}}^{-1}(\\mbf{x})=', mv_simplify(T_Vinv(x), fu))\n",
+    "gprint(r'\\qquad\\mathscr{Q}({\\lt{T}_\\mbf{V}}^{-1}(\\mbf{x}))=', fu(qform(T_Vinv(x))),\n",
+    "       r'\\qquad\\mathscr{Q}(\\mbf{x})=', fu(qform(x)))\n",
+    "gprint(r'\\qquad\\mathscr{Q}({\\lt{T}_\\mbf{V}}^{-1}(\\mbf{x})) = \\mathscr{Q}(\\mbf{x}):~',\n",
+    "       fu(qform(T_Vinv(x))) == fu(qform(x)))\n",
+    "gprint(r'\\qquad\\det({\\lt{T}_\\mbf{V}}^{-1})=', trigsimp(T_Vinv.det()))\n",
+    "gprint(r'\\qquad\\left(\\lt{T}_\\mbf{V}{\\lt{T}_\\mbf{V}}^{-1}\\right)(\\mbf{{x}})=',\n",
+    "       (T_V*T_Vinv)(x))\n",
+    "gprint(r'\\qquad\\left(\\lt{T}_\\mbf{V}{\\lt{T}_\\mbf{V}}^{-1}\\right)(\\mbf{{x}})==\\mbf{x}:~',\n",
+    "       (T_V*T_Vinv)(x) == x)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8594bcc5-e9a6-4de3-807f-7c3d9acc9c36",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/primer/cm3.ipynb b/examples/primer/cm3.ipynb
new file mode 100644
index 00000000..cbd79aac
--- /dev/null
+++ b/examples/primer/cm3.ipynb
@@ -0,0 +1,389 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<h2>*Introduction*</h2>\n",
+    "\n",
+    "This file enables a user to construct and manipulate geometric objects in $\\mathbb{R}^3$. The constructions and manipulations are performed using a conformal model of $\\mathbb{R}^3$. A user need not know much about the conformal model, as all constructions and manipulations are via the functions provided here.   \n",
+    "\n",
+    "My intent is that the functions are self documenting through their code and comments. \n",
+    "\n",
+    "Vectors passed to the functions must be in conformal representation. \n",
+    "Exceptions: pt, which converts a 3D point to a conformal point; a 3D normal vector $\\mathbf{n}$; and a 3D parallel bivector $\\mathbf{B}$.\n",
+    "\n",
+    "Objects returned by the functions are in conformal representation. Exception: tp, which returns a 3D point.\n",
+    "\n",
+    "To start, pull down the \"Run\" menu item and choose \"Run All Cells\".\n",
+    "\n",
+    "Comments and proposed changes or additions are welcome."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Conformal Model, Amsterdam convention.  Dorst et al. p. 361\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 3D confiormakl model\n",
+    "\n",
+    "cm3coords = (o,x,y,z,infty) = symbols('o 1 2 3 infty', real=True)\n",
+    "cm3g = '0 0 0 0 -1, 0 1 0 0 0, 0 0 1 0 0, 0 0 0 1 0, -1 0 0 0 0'\n",
+    "cm3 = Ga('o \\mathbf{e}_1 \\mathbf{e}_2 \\mathbf{e}_3 \\infty', g = cm3g,  coords = cm3coords)\n",
+    "(eo, e1, e2, e3, eoo) = cm3.mv()\n",
+    "ep = eo - eoo/2  # ep^2 = +1  Geometric Algebra for Computer Science 408\n",
+    "em = eo + eoo/2  # em^2 = -1\n",
+    "E = eo^eoo\n",
+    "Ga.dual_mode('Iinv+')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def pt(arg): # R^3 vector --> conformal point. \n",
+    "    if isinstance(arg,str):           # Return general 3D point\n",
+    "        v = cm3.mv(arg, 'vector')     # General conformal vector \n",
+    "        v = v + (v < eoo)*eo + (v < eo)*eoo  # 3D part \n",
+    "        v = eo + v + (v<v)*eoo/2\n",
+    "    elif arg == 0:\n",
+    "        v = eo\n",
+    "    elif (arg < eoo) == 0:    # Return point for 3D vector in arg\n",
+    "        v = eo + arg + (arg<arg)*eoo/2\n",
+    "    else: v = arg             # arg already in conformal representation   \n",
+    "    return(v)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def tp(arg): # conformal point --> R^3 vector\n",
+    "    if isinstance(arg,str):   # Return general 3D vector\n",
+    "        v = cm3.mv(arg, 'vector')\n",
+    "    else:                     # Return 3D vector part of arg\n",
+    "        v = arg\n",
+    "    v = v + (v < eoo)*eo + (v < eo)*eoo\n",
+    "    return(v)       "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def normalize(v): \n",
+    "    if (v < eoo) == 0: # Normalize 3D vector\n",
+    "        return(v/sqrt((v<v).scalar()))\n",
+    "    else:              # Normalize conformal vector: set eo coeff to 1.\n",
+    "        return(-v/(v<eoo))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def scalar(arg):\n",
+    "    return(cm3.mv(arg, 'scalar')) # Save user from typing all this"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<h4>* Create direct representations of geometric objects *</h4>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def round(*args):  # args are conformal points\n",
+    "    ans = args[0]\n",
+    "    for i in range(1,len(args)):\n",
+    "        ans = ans ^ args[i]\n",
+    "    return(ans)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def flat(*args):   # args are conformal points\n",
+    "    return(round(*args) ^ eoo)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def line(p,q):     # If q is 3D vector, line thru p parallel to q returned\n",
+    "    return(flat(p,q))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def plane(p,q,r):\n",
+    "    return(flat(p,q,r))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def circle(p,q,r):\n",
+    "    return(round(p,q,r))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def sphere(p,q,r,s):\n",
+    "    return(round(p,q,r,s))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<h4>* Create dual representations of geometric objects *</h4>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def dualLine(p, B):      # Thru point p, orthogonal to 3D bivector B\n",
+    "    return(p < (B*eoo))  # A vector"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def dualPlane(p,n):           # n: GA^3 normal vector    \n",
+    "    m = normalize(n)\n",
+    "    if isinstance(p,(int, long, float)):\n",
+    "        p = scalar(p)         # Python scalar -> GAlgebra scalar\n",
+    "    if (p!=0) and ((p<p)==0): # p: point on plane. \n",
+    "        return(p < (m^eoo))   # a vector\n",
+    "    else:                     # p: distance to origin.\n",
+    "        return(m + (p*eoo))   # a vector"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def dualSphere(c,rho):               # c:center. \n",
+    "    if isinstance(rho,(int, long, float)):\n",
+    "        rho = scalar(rho)            # Python scalar -> GAlgebra scalar\n",
+    "    if (rho!=0) and ((rho<rho)==0):  # rho: point on sphere \n",
+    "        return(rho < (c ^ eoo))  \n",
+    "    else:                            # rho: radius. \n",
+    "        return(c - (rho*rho*eoo)/2)  # A vector     "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def dualCircle(c,rho,n): # c:center. rho:radius. n:normal vector\n",
+    "    ds = dualSphere(c,rho)\n",
+    "    dp = dualPlane(c,n)    \n",
+    "    return(ds^dp)          # A Bivector "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<h4>*  Geometric operations *</h4>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def translate(object,a3): # a3: 3D vector\n",
+    "    return(1 - a3*eoo/2)*object*(1 + a3*eoo/2)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def rotate(object,itheta):\n",
+    "    return(exp(-itheta/2)*object*exp(itheta/2))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def invert(p, norm=False):   # GACS 513\n",
+    "    ans = -(eo - eoo/2)*p*(eo - eoo/2) \n",
+    "    if norm:\n",
+    "        ans = normalize(ans)\n",
+    "    return(ans)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Reflect point p in hyperplane with normal 3D vector n.\n",
+    "def reflect(p,n):\n",
+    "    return(-n*p*(n/norm2(n)))  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Can be considerably simplified: A Covariant Approach ..., 16 \n",
+    "def dilate(p, alpha, norm = False):  # Dilate by alpha (> 0)\n",
+    "    ans = exp(E*ln(alpha)/2)*p*exp(-E*ln(alpha)/2)\n",
+    "    if norm:\n",
+    "        ans = normalize(ans)\n",
+    "    return(ans)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<h4>* Play *</h4>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "celltoolbar": "Raw Cell Format",
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/g2.ipynb b/examples/primer/g2.ipynb
new file mode 100644
index 00000000..29ded884
--- /dev/null
+++ b/examples/primer/g2.ipynb
@@ -0,0 +1,108 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# G2: Standard 2D Model\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# g2: The geometric algebra G^2.\n",
+    "g2coords = (x,y) = symbols('x y', real=True)\n",
+    "g2 = Ga('e', g=[1,1,1], coords=g2coords)\n",
+    "(ex, ey) = g2.mv()\n",
+    "\n",
+    "grad = g2.grad\n",
+    "from galgebra.dop import *\n",
+    "pdx = Pdop(x)\n",
+    "pdy = Pdop(y)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/g3.ipynb b/examples/primer/g3.ipynb
new file mode 100644
index 00000000..bc9923e2
--- /dev/null
+++ b/examples/primer/g3.ipynb
@@ -0,0 +1,109 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# G3: Standard 3D Model\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# g3: The geometric algebra G^3.\n",
+    "g3coords = (x,y,z) = symbols('x y z', real=True)\n",
+    "g3 = Ga('e', g=[1,1,1], coords=g3coords)\n",
+    "(ex, ey, ez) = g3.mv()\n",
+    "\n",
+    "grad = g3.grad\n",
+    "from galgebra.dop import *\n",
+    "pdx = Pdop(x)\n",
+    "pdy = Pdop(y)\n",
+    "pdz = Pdop(z)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/g4.ipynb b/examples/primer/g4.ipynb
new file mode 100644
index 00000000..0c35e51c
--- /dev/null
+++ b/examples/primer/g4.ipynb
@@ -0,0 +1,103 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# G4: Standard 4D Model\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# g4: R^4 using cartesian coordiantes\n",
+    "g4coords = (x,y,z,w) = symbols('x y z w', real=True)\n",
+    "g4 = Ga('e', g=[1,1,1,1], coords=g4coords)\n",
+    "(ex, ey, ez, ew) = g4.mv()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/gprint.ipynb b/examples/primer/gprint.ipynb
new file mode 100644
index 00000000..63ff1b24
--- /dev/null
+++ b/examples/primer/gprint.ipynb
@@ -0,0 +1,347 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Demo of `gprint`\n",
+    "\n",
+    "Author: Alan Bromborsky\n",
+    "\n",
+    "Last updated: 2020-09-22\n",
+    "\n",
+    "Original name: `GAlgebraOutput.ipynb` in http://www.faculty.luther.edu/~macdonal/GAfiles.zip"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Set up standard G^3 geometric algebra\n",
+    "g3coords = (x,y,z) = symbols('x y z', real=True) # Without real=True, symbols are complex\n",
+    "g3 = Ga('\\mathbf{e}', g=[1,1,1], coords=g3coords)\n",
+    "(ex, ey, ez) = g3.mv()     # Program names of basis vectors.\n",
+    "(exr, eyr, ezr) = g3.mvr() # Program names of reciprocal basis vectors."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} word word\\ word \\cdot ex<ey\\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*}  \\text{word word\\ word \\cdot ex<ey)}\\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'word word\\ word \\cdot ex<ey', r'\\\\ \\text{word word\\ word \\cdot ex<ey)}')\n",
+    "    # \\\\ gives a new line. The second string encloses the first in \\text{}."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B} = B^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + B^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + B^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B} =  \\begin{aligned}[t]  & B^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} \\\\  &  + B^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + B^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B} = B^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + B^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + B^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "B = g3.mv('B', 'bivector')\n",
+    "Fmt(1)  # Set Fmt globally\n",
+    "gprint(r'\\mathbf{B} =', B)         # B will be bold.\n",
+    "gprint(r'\\mathbf{B} =', B.Fmt(3))  # Fmt(3) here only.\n",
+    "gprint(r'\\mathbf{B} =', B)         # Global Fmt remembered."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B}^2 = - {\\left ( B^{xy} \\right )}^{2} - {\\left ( B^{xz} \\right )}^{2} - {\\left ( B^{yz} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'\\mathbf{B}^2 =', B*B)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\langle \\mathbf{M} \\rangle_2 = M^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + M^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + M^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "M = g3.mv('M', 'mv')\n",
+    "gprint(r'\\langle \\mathbf{M} \\rangle_2 =', M.grade(2)) \n",
+    "   # grade(2) could be replaced by, e.g., odd(), or omitted altogether. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\alpha_1\\mathbf{X}/\\gamma_r^3$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'\\alpha_1\\mathbf{X}/\\gamma_r^3')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\theta  ,  \\theta  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Program name and output are different\n",
+    "theta = symbols('theta', real = True) \n",
+    "th = symbols('theta', real = True)  # This will save typing if theta is used a lot.\n",
+    "gprint(theta, ', ', th)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "grad = g3.grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{\\mathbf{e}_x} \\frac{\\partial}{\\partial x} + \\mathbf{\\mathbf{e}_y} \\frac{\\partial}{\\partial y} + \\mathbf{\\mathbf{e}_z} \\frac{\\partial}{\\partial z}$"
+      ],
+      "text/plain": [
+       "\\mathbf{e}_x*D{x} + \\mathbf{e}_y*D{y} + \\mathbf{e}_z*D{z}"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle {\\nabla}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'{\\nabla}')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/h3.ipynb b/examples/primer/h3.ipynb
new file mode 100644
index 00000000..ccf942db
--- /dev/null
+++ b/examples/primer/h3.ipynb
@@ -0,0 +1,107 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# h3: 3D Homogeneous Coordinates\n",
+    "\n",
+    "import sympy \n",
+    "from sympy import *    # Make SymPy available to this program\n",
+    "sympy.init_printing()  \n",
+    "\n",
+    "from galgebra.printer import Fmt, GaPrinter # Need all these parameters\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()\n",
+    "\n",
+    "from galgebra.ga import *  # Make GAlgebra available to this program.\n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "h3coords = (x,y,z,e) = symbols('x y z e', real=True)\n",
+    "# h3 = Ga('e_x e_y e_z e_e', g=[1,1,1,1], coords=h3coords)\n",
+    "h3 = Ga('\\mathbf{e}', g=[1,1,1,1], coords=h3coords)\n",
+    "(ex,ey,ez,ee) = h3.mv()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/primer.ipynb b/examples/primer/primer.ipynb
new file mode 100644
index 00000000..cdc466b6
--- /dev/null
+++ b/examples/primer/primer.ipynb
@@ -0,0 +1,534 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# G3: Standard 3D Model\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "# from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# g3: The geometric algebra G^3.\n",
+    "g3coords = (x,y,z) = symbols('x y z', real=True)\n",
+    "g3 = Ga('e', g=[1,1,1], coords=g3coords)\n",
+    "(ex, ey, ez) = g3.mv()\n",
+    "\n",
+    "grad = g3.grad\n",
+    "# from galgebra.dop import *\n",
+    "# pdx = Pdop(x)\n",
+    "# pdy = Pdop(y)\n",
+    "# pdz = Pdop(z)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle   \\boldsymbol{e}_{x} \\cdot  \\boldsymbol{e}_{y} =  0  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(ex,r'\\cdot',ey,'=',ex < ey)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B}^{\\dagger}=  \\begin{aligned}[t]  & - B^{xy}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\  &  - B^{xz}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\  &  - B^{yz}  \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{I can do three lines output from one cell}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\mathbf{B} =  \\begin{aligned}[t]  & B^{xy}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\  &  + B^{xz}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\  &  + B^{yz}  \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*}  \\mathbf{B}^2 = - {B^{xy} }^{2} - {B^{xz} }^{2} - {B^{yz} }^{2} \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*}  \\mathbf{B}^{\\dagger}=  \\begin{aligned}[t]  & - B^{xy}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\  &  - B^{xz}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\  &  - B^{yz}  \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*}  \\mathbf{\\nabla B} =  \\begin{aligned}[t]  & \\left ( - \\partial_{y} B^{xy}  - \\partial_{z} B^{xz} \\right ) \\boldsymbol{e}_{x} \\\\  &  + \\left ( \\partial_{x} B^{xy}  - \\partial_{z} B^{yz} \\right ) \\boldsymbol{e}_{y} \\\\  &  + \\left ( \\partial_{x} B^{xz}  + \\partial_{y} B^{yz} \\right ) \\boldsymbol{e}_{z} \\\\  &  + \\left ( \\partial_{z} B^{xy}  - \\partial_{y} B^{xz}  + \\partial_{x} B^{yz} \\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "Fmt(3)\n",
+    "B = g3.mv('B', 'bivector',f=True)\n",
+    "gprint(r'\\mathbf{B}^{\\dagger}=',B.rev())\n",
+    "gprint(r'\\text{I can do three lines output from one cell}')\n",
+    "gprint(r'\\mathbf{B} =',B,r'\\\\ \\mathbf{B}^2 =',B*B,r'\\\\ \\mathbf{B}^{\\dagger}=',B.rev(),r'\\\\ \\mathbf{\\nabla B} =',grad*B)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\frac{\\partial^{3}}{\\partial x^{2}\\partial y} \\bs{B}=  \\begin{aligned}[t]  & \\frac{\\partial^{3}}{\\partial^{2} x\\partial y}  B^{xy} {\\left (x,y,z \\right )} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\  &  + \\frac{\\partial^{3}}{\\partial^{2} x\\partial y}  B^{xz} {\\left (x,y,z \\right )} \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\  &  + \\frac{\\partial^{3}}{\\partial^{2} x\\partial y}  B^{yz} {\\left (x,y,z \\right )} \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "from galgebra.dop import *\n",
+    "gFormat(Fmode=False, Dmode=False)\n",
+    "pdx2y = Pdop({x:2,y:1})\n",
+    "gprint(pdx2y,r'\\bs{B}=',pdx2y*B)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\frac{\\partial^{3}}{\\partial x^{2}\\partial y} \\bs{B}=  \\begin{aligned}[t]  & \\partial^{2}_{x}\\partial_{y} B^{xy}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\  &  + \\partial^{2}_{x}\\partial_{y} B^{xz}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\  &  + \\partial^{2}_{x}\\partial_{y} B^{yz}  \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "pdx2y = Pdop({x:2,y:1})\n",
+    "gFormat(Fmode=True, Dmode=True)\n",
+    "gprint(pdx2y,r'\\bs{B}=',pdx2y*B)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B} = B^{xy}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} + B^{xz}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} + B^{yz}  \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "Fmt(1)\n",
+    "gprint(r'\\mathbf{B} =',B)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\grade{B}{2} =  \\begin{aligned}[t]  & B^{xy}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} \\\\  &  + B^{xz}  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{z} \\\\  &  + B^{yz}  \\boldsymbol{e}_{y}\\wedge \\boldsymbol{e}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "Fmt(3)\n",
+    "gprint(r'\\grade{B}{2} =',B.grade(2))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle type(\\bs{B}) =<class 'galgebra.mv.Mv'>$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'type(\\bs{B}) =',str(type(B)))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\grad =  \\boldsymbol{e}_{x} \\frac{\\partial}{\\partial x} + \\boldsymbol{e}_{y} \\frac{\\partial}{\\partial y} + \\boldsymbol{e}_{z} \\frac{\\partial}{\\partial z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint('\\grad = ', grad)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle *  \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(r'*', ex*ey)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle V = V^{x} \\mathbf{e}_{x} + V^{y} \\mathbf{e}_{y} + V^{z} \\mathbf{e}_{z}$"
+      ],
+      "text/plain": [
+       "V__x*e_x + V__y*e_y + V__z*e_z"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "Fmt(1)\n",
+    "V = g3.mv('V', 'vector')\n",
+    "V"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# G2: Standard 3D Model\n",
+    "\n",
+    "g2coords = (x,y) = symbols('x y', real=True)\n",
+    "g2 = Ga('e', g=[1,1], coords=g2coords)\n",
+    "(ex, ey) = g2.mv()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\left ( {L^{x}}_{x} {L^{y}}_{y} - {L^{x}}_{y} {L^{y}}_{x}\\right ) \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "L = g2.lt('L')\n",
+    "gprint(L(ex^ey))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto 2 \\boldsymbol{e}_{y} \\\\ \\boldsymbol{e}_{y} &\\mapsto  \\boldsymbol{e}_{x} + \\boldsymbol{e}_{y} \\end{aligned} \\right\\} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "L = g2.lt([ [0,2], [1,1] ])\n",
+    "gprint(L)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  -2 \\boldsymbol{e}_{x}\\wedge \\boldsymbol{e}_{y} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(L(ex^ey))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left[\\begin{matrix}0 & 1\\\\2 & 1\\end{matrix}\\right]$"
+      ],
+      "text/plain": [
+       "⎡0  1⎤\n",
+       "⎢    ⎥\n",
+       "⎣2  1⎦"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "L.matrix()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left[\\begin{matrix}0 & 2\\\\1 & 1\\end{matrix}\\right]$"
+      ],
+      "text/plain": [
+       "⎡0  2⎤\n",
+       "⎢    ⎥\n",
+       "⎣1  1⎦"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "L.adj().matrix()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/sp2.ipynb b/examples/primer/sp2.ipynb
new file mode 100644
index 00000000..15ef6677
--- /dev/null
+++ b/examples/primer/sp2.ipynb
@@ -0,0 +1,91 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# sp2: Geometric algebra for unit sphere in R^3 using spherical coordinates.\n",
+    "# Mathematics coordinate order: (phi,theta)\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# sp2: Geometric algebra for unit sphere in R^3 using spherical coordinates.\n",
+    "# Mathematics coordinate order: (phi,theta)\n",
+    "\n",
+    "# Set up sp2\n",
+    "basis = 'e_phi e_theta'\n",
+    "coords = (phi,th) = symbols('phi theta', real=True)\n",
+    "g = [sin(phi)*cos(th), sin(phi)*sin(th), cos(phi)]\n",
+    "sp2 = Ga('\\mathbf{e}', g=None , coords=coords, \\\n",
+    "        X=[sin(phi)*cos(th), sin(phi)*sin(th), cos(phi)], norm=True)\n",
+    "\n",
+    "(ephi, eth) = sp2.mv()\n",
+    "(rphi, rth) = sp2.mvr()\n",
+    "\n",
+    "# Derivatives\n",
+    "grad    = sp2.grad\n",
+    "# from galgebra.dop import *\n",
+    "# pdphi = Pdop(phi)\n",
+    "# pdth  = Pdop(th)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/sp2g3.ipynb b/examples/primer/sp2g3.ipynb
new file mode 100644
index 00000000..d89f2f81
--- /dev/null
+++ b/examples/primer/sp2g3.ipynb
@@ -0,0 +1,145 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Unit sphere R^3 as a submanifold of g3 in cartesian coordinates\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Unit sphere R^3 as a submanifold of g3 in cartesian coordinates\n",
+    "\n",
+    "# g3: Base manifold.\n",
+    "g3coords = (x,y,z) = symbols('x y z', real=True) \n",
+    "g3 = Ga('\\mathbf{e}', g=[1,1,1], coords=g3coords)\n",
+    "(ex, ey, ez) = g3.mv()\n",
+    "\n",
+    "# sp2: Submanifold\n",
+    "sp2coords = (phi,th) = symbols('phi theta', real=True)\n",
+    "  # Parameterize unit sphere using the coordinates of g3:\n",
+    "sp2param = [sin(phi)*cos(th), sin(phi)*sin(th), cos(phi)] \n",
+    "  # Map the g3 coordinates of the sphere to its sp2 coordinates:\n",
+    "sp2 = g3.sm(sp2param, sp2coords, norm=True)  # \"sm\" is submanifold\n",
+    "\n",
+    "(ephi, eth) = sp2.mv()\n",
+    "(rphi, rth) = sp2.mvr()\n",
+    "\n",
+    "# Derivatives\n",
+    "grad  = sp2.grad\n",
+    "from galgebra.dop import *\n",
+    "pdphi = Pdop(phi)\n",
+    "pdth  = Pdop(th)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{e}_{\\phi} \\frac{\\partial}{\\partial \\phi} + \\mathbf{e}_{\\theta} \\frac{1}{\\sin{\\left(\\phi \\right)}} \\frac{\\partial}{\\partial \\theta}$"
+      ],
+      "text/plain": [
+       "e_phi*D{phi} + e_theta*1/sin(phi)*D{theta}"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "grad = sp2.grad\n",
+    "grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/sp2sp3.ipynb b/examples/primer/sp2sp3.ipynb
new file mode 100644
index 00000000..e2fece30
--- /dev/null
+++ b/examples/primer/sp2sp3.ipynb
@@ -0,0 +1,146 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# A geometric algebra for the unit sphere in R^3 \n",
+    "# as a submanifold of R^3 with spherical coordintes.\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# A geometric algebra for the unit sphere in R^3 \n",
+    "# as a submanifold of R^3 with spherical coordintes.\n",
+    "\n",
+    "# sp3: Base manifold\n",
+    "sp3coords = (r, phi, theta) = symbols('r phi theta', real=True) \n",
+    "sp3 = Ga('e', g=None, coords=sp3coords, \\\n",
+    "    X=[r*sin(phi)*cos(theta), r*sin(phi)*sin(theta), r*cos(phi)], norm=True)\n",
+    "(er, ephi, etheta) = sp3.mv()\n",
+    "\n",
+    "# sp2: Submanifold\n",
+    "sp2coords = (p,t) = symbols('phi theta', real=True) # they output as Greek phi and theta\n",
+    "  # Parameterize the unit sphere using the spherical coordinates of sp3:\n",
+    "sp2param = [1, p, t] \n",
+    "  # Map the sp3 coordinates of the sphere to its sp2 coordinates:\n",
+    "sp2 = sp3.sm(sp2param, sp2coords, norm=True)\n",
+    "\n",
+    "(ep, et) = sp2.mv()\n",
+    "(rp, rt) = sp2.mvr()\n",
+    "\n",
+    "# Derivatives\n",
+    "grad  = sp2.grad\n",
+    "from galgebra.dop import *\n",
+    "pdph = Pdop(p)\n",
+    "pdth = Pdop(t)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle  \\boldsymbol{e}_{\\phi } \\frac{\\partial}{\\partial \\phi } + \\boldsymbol{e}_{\\theta } \\frac{1}{\\sin{\\left (\\phi  \\right )}} \\frac{\\partial}{\\partial \\theta } $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "gprint(grad) "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/sp3.ipynb b/examples/primer/sp3.ipynb
new file mode 100644
index 00000000..d5112704
--- /dev/null
+++ b/examples/primer/sp3.ipynb
@@ -0,0 +1,137 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# sp3: Spherical Coordinates in R^3\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# sp3: Geometric algebra for R^3 using spherical coordinates \n",
+    "# Mathematics convention: r, phi (colatitude), theta (longitude), in right handed order. \n",
+    "\n",
+    "sp3coords = (r, phi, theta) = symbols('r phi theta') \n",
+    "sp3 = Ga('e', g=None, coords=sp3coords, \\\n",
+    "    X=[r*sin(phi)*cos(theta), r*sin(phi)*sin(theta),r*cos(phi)], norm=True)\n",
+    "       # g = None. Instead, the spherical coordinate parameterization X is used.\n",
+    "       # \"\\\" is Python's line continuation character\n",
+    "(er, ephi, etheta) = sp3.mv()\n",
+    "\n",
+    "grad = sp3.grad\n",
+    "from galgebra.dop import *\n",
+    "pdr     = Pdop(r)\n",
+    "pdphi   = Pdop(phi)\n",
+    "pdtheta = Pdop(theta)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{e}_{r} \\frac{\\partial}{\\partial r} + \\mathbf{e}_{\\phi} \\frac{1}{r} \\frac{\\partial}{\\partial \\phi} + \\mathbf{e}_{\\theta} \\frac{1}{r \\sin{\\left(\\phi \\right)}} \\frac{\\partial}{\\partial \\theta}$"
+      ],
+      "text/plain": [
+       "e_r*D{r} + e_phi*1/r*D{phi} + e_theta*1/(r*sin(phi))*D{theta}"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sp3.grad"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/primer/spacetime.ipynb b/examples/primer/spacetime.ipynb
new file mode 100644
index 00000000..6cd9bcc2
--- /dev/null
+++ b/examples/primer/spacetime.ipynb
@@ -0,0 +1,126 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Spacetime algebra. [1, -1, -1, -1] signature\n",
+    "\n",
+    "# Make SymPy available to this program:\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "\n",
+    "# Make GAlgebra available to this program:\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:       sets the way that a multivector's basis expansion is output.\n",
+    "    # GaPrinter: makes GA output a little more readable.\n",
+    "    # Format:    turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "gFormat()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "txyz = (t, x, y, z) = symbols('t x y z', real=True)\n",
+    "stcoords = (t,x,y,z) = symbols('t x y z')\n",
+    "st = Ga('\\mathbf{e}', g=[1, -1, -1, -1], coords=stcoords)\n",
+    "(et, ex, ey, ez) = st.mv()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle X = X^{t} \\mathbf{\\mathbf{e}_t} + X^{x} \\mathbf{\\mathbf{e}_x} + X^{y} \\mathbf{\\mathbf{e}_y} + X^{z} \\mathbf{\\mathbf{e}_z}$"
+      ],
+      "text/plain": [
+       "X__t*\\mathbf{e}_t + X__x*\\mathbf{e}_x + X__y*\\mathbf{e}_y + X__z*\\mathbf{e}_z"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "st.mv('X', 'vector')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/galgebra/__init__.py b/galgebra/__init__.py
index 833af113..405c5fae 100644
--- a/galgebra/__init__.py
+++ b/galgebra/__init__.py
@@ -20,6 +20,7 @@
     dop
     atoms
     printer
+    gprinter
     utils
 
 .. note::
diff --git a/galgebra/_version.py b/galgebra/_version.py
index 5aa7f5f3..a3aa75ae 100644
--- a/galgebra/_version.py
+++ b/galgebra/_version.py
@@ -5,4 +5,4 @@
 # 1) we don't load dependencies by storing it in __init__.py
 # 2) we can import it in setup.py for the same reason
 # 3) we can import it into your module
-__version__ = '0.5.2'
+__version__ = '0.6.0-dev'
diff --git a/galgebra/ga.py b/galgebra/ga.py
index c88a0d35..feb8b40e 100644
--- a/galgebra/ga.py
+++ b/galgebra/ga.py
@@ -2049,7 +2049,7 @@ def pDiff(self, A: _mv.Mv, coord: Union[List, Symbol]) -> _mv.Mv:
         # Simple partial differentiation, once with respect to a single
         # variable, but including case of non-constant basis vectors
 
-        dA = self.mv(expand(diff(A.obj, coord)))
+        dA = self.mv(expand(diff(A if isinstance(A, Expr) else A.obj, coord)))
 
         if self.connect_flg and self.dslot == -1 and not A.is_scalar():  # Basis blades are function of coordinates
             B = self.remove_scalar_part(A)
diff --git a/galgebra/gprinter.py b/galgebra/gprinter.py
new file mode 100644
index 00000000..d26aff65
--- /dev/null
+++ b/galgebra/gprinter.py
@@ -0,0 +1,300 @@
+from .printer import GaLatexPrinter, isinteractive, Format_cnt, latex
+import subprocess
+import sys
+import shutil
+import inspect
+
+from sympy import init_printing
+
+try:
+    from IPython.display import display, Math
+except ImportError:
+    pass
+
+SYS_CMD = {'linux2': {'rm': 'rm', 'evince': 'evince', 'null': ' > /dev/null', '&': '&'},
+           'linux': {'rm': 'rm', 'evince': 'evince', 'null': ' > /dev/null', '&': '&'},
+           'win32': {'rm': 'del', 'evince': 'start', 'null': ' > NUL', '&': ''},
+           'darwin': {'rm': 'rm', 'evince': 'open', 'null': ' > /dev/null', '&': '&'}}
+
+
+class LaTeX:
+    # LaTeX data
+    line_sep = """
+************************************************************************
+"""
+    latex_flg = False
+    latex_str = ''
+
+    latex_preamble = """
+\\pagestyle{empty}
+\\usepackage[utf8]{inputenc}
+\\usepackage{amsmath}
+\\usepackage{amsfonts}
+\\usepackage{amssymb}
+\\usepackage{amsbsy}
+\\usepackage{tensor}
+\\usepackage{listings}
+\\usepackage{color}
+\\usepackage{xcolor}
+\\usepackage{bm}
+\\usepackage{breqn}
+\\definecolor{gray}{rgb}{0.95,0.95,0.95}
+\\setlength{\\parindent}{0pt}
+\\DeclareMathOperator{\\Tr}{Tr}
+\\DeclareMathOperator{\\Adj}{Adj}
+\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}
+\\newcommand{\\lp}{\\left (}
+\\newcommand{\\rp}{\\right )}
+\\newcommand{\\paren}[1]{\\lp {#1} \\rp}
+\\newcommand{\\half}{\\frac{1}{2}}
+\\newcommand{\\llt}{\\left <}
+\\newcommand{\\rgt}{\\right >}
+\\newcommand{\\abs}[1]{\\left |{#1}\\right | }
+\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}
+\\newcommand{\\lbrc}{\\left \\{}
+\\newcommand{\\rbrc}{\\right \\}}
+\\newcommand{\\W}{\\wedge}
+\\newcommand{\\prm}[1]{{#1}^{\\prime}}
+\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}
+\\newcommand{\\R}{\\dagger}
+\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}
+\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}
+\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp}
+\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}
+\\newcommand{\\Nabla}{\\boldsymbol{\\nabla}}
+\\newcommand{\\eb}{\\boldsymbol{e}}
+\\newcommand{\\bs}[1]{\\boldsymbol{#1}}
+\\newcommand{\\grad}{\\bs{\\nabla}}
+\\usepackage{float}
+\\floatstyle{plain} % optionally change the style of the new float
+\\newfloat{Code}{H}{myc}
+\\lstloadlanguages{Python}
+\\begin{document}
+
+"""
+
+    ip_cmds = \
+        [r'$$\DeclareMathOperator{\Tr}{Tr}$$',
+            r'$$\DeclareMathOperator{\Adj}{Adj}$$',
+            r'$$\newcommand{\bfrac}[2]{\displaystyle\frac{#1}{#2}}$$',
+            r'$$\newcommand{\lp}{\left (}$$',
+            r'$$\newcommand{\rp}{\right )}$$',
+            r'$$\newcommand{\paren}[1]{\lp {#1} \rp}$$',
+            r'$$\newcommand{\half}{\frac{1}{2}}$$',
+            r'$$\newcommand{\llt}{\left <}$$',
+            r'$$\newcommand{\rgt}{\right >}$$',
+            r'$$\newcommand{\abs}[1]{\left |{#1}\right | }$$',
+            r'$$\newcommand{\pdiff}[2]{\bfrac{\partial {#1}}{\partial {#2}}}$$',
+            r'$$\newcommand{\npdiff}[3]{\bfrac{\partial^{#3} {#1}}{\partial {#2}^{#3}}}$$',
+            r'$$\newcommand{\lbrc}{\left \{}$$',
+            r'$$\newcommand{\rbrc}{\right \}}$$',
+            r'$$\newcommand{\W}{\wedge}$$',
+            r'$$\newcommand{\prm}[1]{{#1}^{\prime}}$$',
+            r'$$\newcommand{\ddt}[1]{\bfrac{d{#1}}{dt}}$$',
+            r'$$\newcommand{\R}{\dagger}$$',
+            r'$$\newcommand{\deriv}[3]{\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$',
+            r'$$\newcommand{\grade}[2]{\left < {#1} \right >_{#2}}$$',
+            r'$$\newcommand{\f}[2]{{#1}\lp {#2} \rp}$$',
+            r'$$\newcommand{\eval}[2]{\left . {#1} \right |_{#2}}$$',
+            r'$$\newcommand{\bs}[1]{\boldsymbol{#1}}$$',
+            r'$$\newcommand{\grad}{\bs{\nabla}}$$']
+
+# ***********************************************************************
+
+
+def gFormat(Fmode: bool = True, Dmode: bool = True, inverse='full'):
+    r"""
+    Turns on latex printing with configurable options.
+
+    This redirects printer output so that latex compiler can capture it.
+
+    ``Format()`` is also required for printing from *ipython notebook* (note that ``xpdf()`` is not needed to print from *ipython notebook*).
+
+    Parameters
+    ----------
+    Fmode:
+        Value for the ``omit_function_args`` setting of
+        :class:`GaLatexPrinter`.
+    Dmode:
+        Value for the ``omit_partial_derivative_fraction`` setting of
+        :class:`GaLatexPrinter`.
+    """
+    global Format_cnt
+
+    GaLatexPrinter.set_global_settings(
+        omit_partial_derivative_fraction=Dmode,
+        omit_function_args=Fmode,
+        inv_trig_style=inverse,
+    )
+
+    if Format_cnt == 0:
+        Format_cnt += 1
+
+        LaTeX.latex_flg = True
+
+        if isinteractive():
+            init_printing(use_latex='mathjax')
+            from IPython.display import Math, display
+            cmds = '\n'.join(LaTeX.ip_cmds)
+            display(Math(cmds))
+
+    return
+
+
+def gprint(*xargs):
+    """
+    Print latex or text from python script or latex from Jupyter Notebook/Lab
+
+    """
+    x = []
+    fstr = ''
+    new_eq_flg = False
+    i = 0
+    for xi in xargs:
+        if isinstance(xi, str):
+            if r'\\' in xi and i > 0:
+                if isinteractive():
+                    xi_rep = xi.replace(r'\\', r'\end{equation*}@\begin{equation*} ')
+                else:
+                    xi_rep = xi.replace(r'\\', r'\end{equation*}'+'\n'+r'\begin{equation*} ')
+                new_eq_flg = True
+                fstr += xi_rep
+            else:
+                fstr += xi
+        elif isinstance(xi, type):
+            if LaTeX.latex_flg:
+                fstr += r' \text{'+str(xi)+'} '
+            else:
+                fstr += str(xi)
+        else:
+            if LaTeX.latex_flg:
+                x.append(latex(xi))
+                if new_eq_flg:
+                    new_eq_flg = False
+                fstr += r' %s '
+            else:
+                x.append(str(xi))
+                fstr += r' %s '
+
+        i += 1
+
+    if LaTeX.latex_flg:
+        if isinteractive():
+            lstr = fstr % tuple(x)
+            if '@' in lstr:
+                lines = lstr.split('@')
+                lines[0] = r'\begin{equation*} '+lines[0]
+                lines[-1] += r'\end{equation*}'
+                for line in lines:
+                    display(Math(line))
+            else:
+                display(Math(lstr))
+        else:
+            LaTeX.latex_str += r'\begin{equation*} ' + (fstr % tuple(x)) + r'\end{equation*} '+'\n'
+    else:
+        print(fstr % tuple(x))
+
+    return
+
+
+def gxpdf(filename=None, paper=(14, 11), crop=False, png=False, prog=False, debug=False, pt='10pt', pdfprog='pdflatex', evince=True, rm=True, null=True, documentclass='book'):
+
+    """
+    Post processes LaTeX output (see comments below), adds preamble and
+    postscript, generates tex file, inputs file to latex, displays resulting
+    pdf file.
+
+    Arg         Value       Result
+    pdfprog    'pdflatex'   Use pdfprog to generate pdf output, only generate tex if pdfprog is None
+    crop        True        Use "pdfcrop" to crop output file (pdfcrop must be installed, linux only)
+    png         True        Use "convert" to produce png output (imagemagick must be installed, linux only)
+
+    We assume that if gxpdf() is called then gFormat() has been called at the beginning of the program.
+    """
+
+    latex_str = paper_format(paper, pt, documentclass)+LaTeX.latex_preamble+LaTeX.latex_str+r'\end{document}'
+    # Clean up the latex string after printing
+    LaTeX.latex_str = ''
+
+    if filename is None:
+        pyfilename = sys.argv[0]
+        rootfilename = pyfilename.replace('.py', '')
+        tex_filename = rootfilename + '.tex'
+        pdf_filename = rootfilename + '.pdf'
+    else:
+        tex_filename = filename
+        pdf_filename = tex_filename.replace('.tex', '.pdf')
+        rootfilename = tex_filename.replace('.tex', '')
+
+    if debug:
+        print('latex file =', filename)
+
+    with open(tex_filename, 'w') as latex_file:
+        latex_file.write(latex_str)
+
+    if pdfprog is not None:
+        sys_cmd = SYS_CMD[sys.platform]
+        pdflatex = shutil.which(pdfprog)
+
+        if debug:  # Display latex excution output for debugging purposes
+            print('pdflatex path =', pdflatex)
+            # os.system(pdfprog + ' ' + filename[:-4])
+        else:  # Works for Linux don't know about Windows
+            if null:
+                subprocess.call([pdfprog, tex_filename, sys_cmd['null']])
+            else:
+                subprocess.call([pdfprog, tex_filename])
+            # os.system(pdfprog + ' ' + filename[:-4] + sys_cmd['null'])
+
+        if evince:
+            subprocess.call([sys_cmd['evince'], pdf_filename])
+
+        # eval(input('!!!!Return to continue!!!!\n'))
+
+        if rm:
+            if debug:
+                subprocess.call([sys_cmd['rm'], rootfilename+'.aux ', rootfilename+'.log'])
+            else:
+                subprocess.call([sys_cmd['rm'], rootfilename+'.aux ', rootfilename+'.log ', rootfilename+'.tex'])
+        if crop:
+            subprocess.call(['pdfcrop', pdf_filename])
+            subprocess.call(['rm', pdf_filename])
+            subprocess.call(['mv', rootfilename+'-crop.pdf', pdf_filename])
+        if png:
+            subprocess.call(['Pdf2Png', rootfilename])
+    return
+
+
+def paper_format(paper, pt, documentclass='book'):  # Set size of paper and font size
+
+    if paper == 'letter':
+        paper_size = """
+\\documentclass[@10pt@,fleqn]{%s}
+""" % documentclass
+    else:
+        paper_size = """
+\\documentclass[@10pt@,fleqn]{%s}
+\\usepackage[vcentering]{geometry}
+""" % documentclass
+        if paper == 'landscape':
+            paper = [11, 8.5]
+        paper_size += '\\geometry{papersize={' + str(paper[0]) + \
+                      'in,' + str(paper[1]) + 'in},total={' + str(paper[0] - 1) + \
+                      'in,' + str(paper[1] - 1) + 'in}}\n'
+
+    paper_size = paper_size.replace('@10pt@', pt)
+
+    return paper_size
+
+
+def gPrint_Function():
+    """ Print out the source of the current function """
+
+    tmp_str = inspect.getsource(inspect.currentframe().f_back)
+    if LaTeX.latex_flg:
+        LaTeX.latex_str += r'\begin{lstlisting}[language=Python,showspaces=false,showstringspaces=false,backgroundcolor=\color{gray},frame=single]%s\end{lstlisting}\text{Code Output:}' % tmp_str
+    else:
+        print('\n' + 80 * '*')
+        print(tmp_str)
+        print('Code output:\n')
+    return
diff --git a/galgebra/lt.py b/galgebra/lt.py
index 936b7933..857bf9fc 100644
--- a/galgebra/lt.py
+++ b/galgebra/lt.py
@@ -11,7 +11,7 @@
 from typing import Mapping
 
 from sympy import (
-    expand, symbols, Matrix, Transpose, zeros, Symbol, Function, S, Add, Expr
+    expand, symbols, Matrix, Transpose, zeros, Symbol, Function, S, Add, Expr, simplify
 )
 from sympy.printing.latex import LatexPrinter as _LatexPrinter
 from sympy.printing.str import StrPrinter as _StrPrinter
@@ -32,62 +32,93 @@
 })
 
 
-def Symbolic_Matrix(root, coords=None, mode='g', f=False, sub=True):
-    if sub:
-        pos = '_'
-    else:
-        pos = '__'
-    if isinstance(coords, (list, tuple)):
+# ## GSG code starts ###
+def Symbolic_Matrix(kernel, coords=None, f=False, mode='g'):
+    """
+    Returns a square real matrix the entries of which are symbolic
+    constants or symbolic functions of the coordinates.
+    - `kernel` is a one-letter string.  It specifies the kernel letter of
+      indexed symbols or functions used to specify the matrix's entries
+    - `coords` is a list or tuple.  Its entries are used to label the
+      components of a vector.
+    - `f`, a boolean, specifies that matrix entries are symbolic functions
+      of the coordinates or are symbolic constants, according to whether
+      `f` is True or False.
+    - `mode` is a one-letter string.  When`mode` is 'g', 's', or 'a' the
+      matrix will be general, symmetric, or antisymmetric.
+    """
+
+    def general_matrix(kernel, coords=None, f=False):
+        """Returns a general square matrix.  The row index of each entry
+        appears as a superscript, while the column index appears as a
+        subscript."""
         n = len(coords)
-        n_range = range(n)
-        mat = zeros(n)
-        if mode == 'g':  # General symbolic matrix
-            for row in n_range:
-                row_index = str(coords[row])
-                for col in n_range:
-                    col_index = str(coords[col])
-                    element = root + pos + row_index + col_index
-                    if not f:
-                        mat[row, col] = Symbol(element, real=True)
-                    else:
-                        mat[row, col] = Function(element)(*coords)
-
-        elif mode == 's':  # Symmetric symbolic matrix
-            for row in n_range:
-                row_index = str(coords[row])
-                for col in n_range:
-                    col_index = str(coords[col])
-                    if row <= col:
-                        element = root + pos + row_index + col_index
-                    else:
-                        element = root + pos + col_index + row_index
-                    if not f:
-                        mat[row, col] = Symbol(element, real=True)
-                    else:
-                        mat[row, col] = Function(element)(*coords)
-
-        elif mode == 'a':  # Asymmetric symbolic matrix
-            for row in n_range:
-                row_index = str(coords[row])
-                for col in n_range:
-                    col_index = str(coords[col])
-                    if row <= col:
-                        sign = S.One
-                        element = root + pos + row_index + col_index
-                    else:
-                        sign = -S.One
-                        element = root + pos + col_index + row_index
-                    if row == col:
-                        sign = S.Zero
-                    if not f:
-                        mat[row, col] = sign * Symbol(element, real=True)
-                    else:
-                        mat[row, col] = sign * Function(element)(*coords)
-        else:
-            raise ValueError('In Symbolic_Matrix mode = ' + str(mode))
-    else:
-        raise ValueError('In Symbolic_Matrix coords = ' + str(coords))
-    return mat
+        # Create matrix entries and store in appropriate locations in `G`:
+        G = zeros(n, n)
+        if f:        # entries are symbolic functions
+            for i in range(n):
+                for j in range(n):
+                    entry = '{' + kernel + '__' + str(coords[i]) + '}_' + str(coords[j])
+                    G[i, j] = Function(entry)(*coords)
+        else:        # entries are symbolic constants
+            for i in range(n):
+                for j in range(n):
+                    entry = '{' + kernel + '__' + str(coords[i]) + '}_' + str(coords[j])
+                    G[i, j] = Symbol(entry, real=True)
+        return G
+
+    def symmetric_matrix(kernel, coords=None, f=False):
+        """Returns a symmetric matrix.  Entries have a single index, which
+        appears as a subscript."""
+        n = len(coords)
+        # Create and temporarily store matrix entries in `parameters`
+        parameters = []
+        if f:        # entries are symbolic functions
+            for i in range((n*(n+1)//2), 0, -1):
+                parameters.append(Function(kernel + '_' + str(i))(*coords))
+        else:        # entries are symbolic constants
+            for i in range((n*(n+1)//2), 0, -1):
+                parameters.append(Symbol(kernel + '_' + str(i), real=True))
+        # Transfer entries to symmetric matrix `S`.
+        S = zeros(n, n)
+        for i in range(n):
+            for j in range(i, n):
+                S[i, j] = parameters.pop()
+                S[j, i] = S[i, j]
+        return S
+
+    def antisymmetric_matrix(kernel, coords=None, f=False):
+        """Returns an antisymmetric matrix.  Entries have a a single index,
+        which appears as a subscript."""
+        n = len(coords)
+        # Create and temporarily store matrix entries in `parameters`
+        parameters = []
+        if f:        # entries are symbolic functions
+            for i in range((n*(n-1)//2), 0, -1):
+                parameters.append(Function(kernel + '_' + str(i))(*coords))
+        else:        # entries are symbolic constants
+            for i in range((n*(n-1)//2), 0, -1):  # each parameter is a symbol
+                parameters.append(Symbol(kernel + '_' + str(i), real=True))
+        # Transfer entries to antisymmetric matrix `A`.
+        A = zeros(n, n)
+        for i in range(n):
+            for j in range(i+1, n):
+                A[i, j] = parameters.pop()
+                A[j, i] = - A[i, j]
+        return A
+
+    # Check legitimacy of parameter values:
+    if not isinstance(coords, (list, tuple)):
+        raise ValueError('coords = ' + str(coords) + ' in Symbolic_Matrix')
+    if mode not in ['g', 's', 'a']:
+        raise ValueError('mode = ' + str(mode) + ' in Symbolic_Matrix')
+    if mode == 'g':
+        return general_matrix(kernel, coords, f)
+    if mode == 's':
+        return symmetric_matrix(kernel, coords, f)
+    if mode == 'a':
+        return antisymmetric_matrix(kernel, coords, f)
+# ## GSG code ends ###
 
 
 def Matrix_to_dictionary(mat_rep, basis):
@@ -104,29 +135,33 @@ def Matrix_to_dictionary(mat_rep, basis):
     }
 
 
+# ## GSG code starts ###
 def Dictionary_to_Matrix(dict_rep, ga):
-    """ Convert dictionary representation of linear transformation to matrix """
-    lst_mat = []  # list representation of sympy matrix
-    for e_row in ga.basis:
-        lst_mat_row = len(ga.basis) * [S.Zero]
-
-        element = dict_rep.get(e_row, S.Zero)
-        if isinstance(element, mv.Mv):
-            element = element.obj
-        for coef, base in metric.linear_expand_terms(element):
-            index = ga.basis.index(base)
-            lst_mat_row[index] = coef
-
-        lst_mat.append(lst_mat_row)
-    # expand the transpose
-    return Transpose(Matrix(lst_mat)).doit()
+    """Returns the matrix representation of that linear transformation on
+    geometric algebra ga which has dictionary representation dict_rep."""
+    # columns[j] is a list of the entries in the matrix's jth column.
+    # columns[j][i] is the (i,j)th entry in the matrix.
+    # Matrix[columns] instantiates the transpose of the desired matrix.
+    columns = []
+    for b in ga.basis:             # b is a basis symbol for ga.
+        column = ga.n * [S.Zero]   # Initialize column for dict_rep value at b.
+        dict_value = dict_rep[b]   # dict_rep's value at b
+        if isinstance(dict_value, mv.Mv):
+            dict_value = dict_value.obj
+        if dict_value is not S.Zero:
+            for coef, base in metric.linear_expand_terms(dict_value):
+                row_index = ga.basis.index(base)
+                column[row_index] = coef
+        columns.append(column)
+    return Transpose(Matrix(columns)).doit()
+# ## GSG code ends ###
 
 
 class Lt(printer.GaPrintable):
     r"""
     A Linear Transformation
 
-    Except for the spinor representation the linear transformation
+    Except for the versor representation, the linear transformation
     is stored as a dictionary with basis vector keys and vector
     values ``self.lt_dict`` so that a is a vector :math:`a = a^{i}e_{i}` then
 
@@ -134,11 +169,13 @@ class Lt(printer.GaPrintable):
         \mathtt{self(}a\mathtt{)}
             = a^{i} * \mathtt{self.lt\_dict[}e_{i}\mathtt{]}.
 
-    For the spinor representation the linear transformation is
-    stored as the even multivector ``self.R`` so that if a is a
+    For the versor representation, the linear transformation is
+    stored as a versor ``self.V`` so that if a is a
     vector::
 
-        self(a) = self.R * a * self.R.rev().
+        self(a) = self.V.g_invol() * a * self.V.inv()
+
+    where ``self.V.g_invol()`` is the grade involute of ``self.V``.
 
     Attributes
     ----------
@@ -220,10 +257,18 @@ def __init__(self, *args, ga, f=False, mode='g'):
 
             Construct from the operation of matrix pre-multiplication.
 
-        .. class:: Lt(spinor: mv.Mv, /, *, ga)
+        # ## GSG code starts ###
+        .. class:: Lt(lt_list: list, /, *, ga)
+            :noindex:
+
+            Construct from a list of lists, the j_th list of which contains
+            the coefficients of j_th image vector's basis expansion.
+        # ## GSG code ends ###
+
+        .. class:: Lt(versor: mv.Mv, /, *, ga)
             :noindex:
 
-            Construct from a spinor / rotor, which need not square to one.
+            Construct from a not-necessarily-normalized versor.
 
         .. class:: Lt(func: Callable[[mv.Mv], mv.Mv], /, *, ga)
             :noindex:
@@ -239,7 +284,7 @@ def __init__(self, *args, ga, f=False, mode='g'):
         Parameters
         ----------
         ga : Ga
-            Geometric algebra which is the domain and codomain of this transform
+            Geometric algebra which is both domain and codomain of this transformation
         f : bool
             True if Lt if function of coordinates. Only supported in the string
             constructor
@@ -247,22 +292,28 @@ def __init__(self, *args, ga, f=False, mode='g'):
             g:general, s:symmetric, a:antisymmetric transformation.
             Only supported in the string constructor.
         """
+
         mat_rep = args[0]
         self.Ga = ga
-        self.spinor = False
-        self.rho_sq = None
-
         self.lt_dict = {}
         self.mat = None
-
-        if isinstance(mat_rep, dict):  # Dictionary input
+        self.versor = False
+        # self.V, self.Vrev, and self.Vqform are never actually used in the current
+        # implementation of orthogonal outermorphisms created by a versor input.
+        self.V = None
+        self.Vrev = None
+        self.Vqform = None
+
+        if isinstance(mat_rep, dict):         # Dictionary input
             for key in mat_rep:
                 self.lt_dict[key] = mat_rep[key]
 
-        elif isinstance(mat_rep, list):  # List of lists input
+        elif isinstance(mat_rep, list):       # List input
             if not isinstance(mat_rep[0], list):
+                # At this point mat_rep[i] is the desired image vector for the
+                # i_th basis image vectors.
                 for lt_i, base in zip(mat_rep, self.Ga.basis):
-                    self.lt_dict[base] = lt_i
+                    self.lt_dict[base] = sym(lt_i)
             else:
                 # mat_rep = map(list, zip(*mat_rep))  # Transpose list of lists
                 for row, base1 in zip(mat_rep, self.Ga.basis):
@@ -271,29 +322,38 @@ def __init__(self, *args, ga, f=False, mode='g'):
                         tmp += col * base2
                     self.lt_dict[base1] = tmp
 
-        elif isinstance(mat_rep, Matrix):  # Matrix input
-            self.mat = mat_rep
-            mat_rep = self.mat * self.Ga.g_inv
+        # ## GSG code starts ###
+        elif isinstance(mat_rep, Matrix):    # Matrix input
             self.lt_dict = Matrix_to_dictionary(mat_rep, self.Ga.basis)
-
-        elif isinstance(mat_rep, mv.Mv):  # Spinor input
-            self.spinor = True
-            self.R = mat_rep
-            self.Rrev = mat_rep.rev()
-            self.rho_sq = self.R * self.Rrev
-            if self.rho_sq.is_scalar():
-                self.rho_sq = self.rho_sq.scalar()
-                if self.rho_sq == S.One:
-                    self.rho_sq = None
-            else:
-                raise ValueError('In Spinor input for Lt, S*S.rev() not a scalar!\n')
-
-        elif isinstance(mat_rep, str):  # String input
-            Amat = Symbolic_Matrix(mat_rep, coords=self.Ga.coords, mode=mode, f=f)
-            self.__init__(Amat, ga=self.Ga)
-
-        elif callable(mat_rep):  # Linear multivector function input
-            # F is a multivector function to be tested for linearity
+        # ## GSG code ends ###
+
+        # ## GSG code starts. ###
+        # This code segment uses versor `mat_rep` and a sandwich product only to
+        # create a linear vector-valued function of vector. That function is then
+        # used to create a dictionary-based outermorphism. Evaluation of the
+        # outermorphism on a multivector is by dictionary lookup, not by a
+        # sandwich product of the multivector with the versor.
+        elif isinstance(mat_rep, mv.Mv):     # Versor input
+            if not mat_rep.is_versor:
+                raise ValueError(mat_rep, 'is not a versor in Versor input for Lt!\n')
+            V = mat_rep
+            Vg_invol = V.g_invol()
+            Vinv = V.inv()
+            outermorphism = ga.lt(lambda x: Vg_invol * x * Vinv)
+            self.lt_dict = simplify(outermorphism.lt_dict)
+        # ## GSG code ends ###
+
+        # ## GSG code starts ###
+        elif isinstance(mat_rep, str):    # (One-letter) string input
+            Amat = Symbolic_Matrix(mat_rep, coords=self.Ga.coords, f=f, mode=mode)
+            if mode == 'g':
+                self.__init__(Amat, ga=self.Ga)
+            elif mode in ['s', 'a']:
+                self.__init__(self.Ga.g_inv * Amat, ga=self.Ga)
+        # ## GSG code ends ###
+
+        elif callable(mat_rep):    # Linear multivector function input
+            # Function is tested for linearity before use.
             F = mat_rep
             a = mv.Mv('a', 'vector', ga=self.Ga)
             b = mv.Mv('b', 'vector', ga=self.Ga)
@@ -311,10 +371,9 @@ def __init__(self, *args, ga, f=False, mode='g'):
     @_cached_property
     def mv_dict(self) -> Mapping[Expr, Expr]:
         # dict for linear transformation of multivector
-        if self.spinor:
+        if self.versor:
             # no lt_dict
             return None
-
         return {
             blade: reduce(
                 self.Ga.wedge,
@@ -324,47 +383,58 @@ def mv_dict(self) -> Mapping[Expr, Expr]:
             for index, blade in self.Ga.indexes_to_blades_dict.items()
         }
 
-    def __call__(self, v, obj=False):
+    def __call__(self, M, obj=False):
         r"""
-        Returns the image of the multivector :math:`A` under the linear transformation :math:`L`.
-
-        :math:`{{L}\lp {A} \rp }` is defined by the linearity of :math:`L`, the
-        vector values :math:`{{L}\lp {{{\eb}}_{i}} \rp }`, and the definition
-        :math:`{{L}\lp {{{\eb}}_{i_{1}}{\wedge}\dots{\wedge}{{\eb}}_{i_{r}}} \rp } = {{L}\lp {{{\eb}}_{i_{1}}} \rp }{\wedge}\dots{\wedge}{{L}\lp {{{\eb}}_{i_{r}}} \rp }`.
+        Returns the image of multivector :math:`M` under the linear transformation
+        :math:`L`. :math:`{{L}\lp{M}\rp}` is defined by
+        the linearity of :math:`L`,
+        the vector values :math:`{{L}\lp{{{\eb}}_{j}}\rp }`, and the definition
+        :math:`{{L}\lp{{{\eb}}_{j_{1}}{\wedge}\dots{\wedge}{{\eb}}_{j_{r}}}\rp}={{L}\lp{{{\eb}}_{j_{1}}}\rp}{\wedge}\dots{\wedge}{{L}\lp{{{\eb}}_{j_{r}}}\rp}`.
         """
 
-        if isinstance(v, mv.Mv) and self.Ga != v.Ga:
+        if isinstance(M, mv.Mv) and self.Ga != M.Ga:
             raise ValueError('In Lt call Lt and argument refer to different vector spaces')
 
-        if self.spinor:
-            if not isinstance(v, mv.Mv):
-                v = mv.Mv(v, ga=self.Ga)
-            if self.rho_sq is None:
-                R_v_Rrev = self.R * v * self.Rrev
+        # ## GSG code starts ###
+        # Given the current way an outermorphism is created from a versor input,
+        # self.versor will always be false; hence the following code fragment will
+        # never execute.
+        if self.versor:
+            # Sandwich M or M's grade involute depending on whether versor self.V
+            # is even or odd.
+            if self.V == self.V.odd():
+                V_M_Vrev = self.V * M.g_invol() * self.Vrev
+            elif self.V == self.V.even():
+                V_M_Vrev = self.V * M * self.Vrev
             else:
-                R_v_Rrev = self.rho_sq * self.R * v * self.Rrev
+                raise ValueError('self.V is not a versor in  __call__')
+            # Divide by normalization factor self.Vqform to convert sandwiching
+            # between self.V and its reverse to sandwiching between self.V and
+            # its inverse.
+            V_M_Vinv = 1/(self.Vqform) * V_M_Vrev
             if obj:
-                return R_v_Rrev.obj
+                return V_M_Vinv.obj
             else:
-                return R_v_Rrev
+                return V_M_Vinv
+        # ## GSG code ends ###
 
-        if isinstance(v, mv.Mv):
-            if v.is_vector():
-                lt_v = v.obj.xreplace(self.lt_dict)
+        if isinstance(M, mv.Mv):
+            if M.is_vector():
+                lt_M = M.obj.xreplace(self.lt_dict)
                 if obj:
-                    return lt_v
+                    return lt_M
                 else:
-                    return mv.Mv(lt_v, ga=self.Ga)
+                    return mv.Mv(lt_M, ga=self.Ga)
             else:
-                mv_obj = v.obj
+                mv_obj = M.obj
         else:
-            mv_obj = mv.Mv(v, ga=self.Ga).obj
+            mv_obj = mv.Mv(M, ga=self.Ga).obj
 
-        lt_v = mv_obj.xreplace(self.mv_dict)
+        lt_M = mv_obj.xreplace(self.mv_dict)
         if obj:
-            return lt_v
+            return lt_M
         else:
-            return mv.Mv(lt_v, ga=self.Ga)
+            return mv.Mv(lt_M, ga=self.Ga)
 
     def __add__(self, LT):
 
@@ -420,15 +490,16 @@ def __rmul__(self, LT):
         else:
             raise TypeError('Cannot have LT as left argument in Lt __rmul__\n')
 
-    def det(self) -> Expr:  # det(L) defined by L(I) = det(L)I
+    # ## GSG code starts ###
+    def det(self) -> Expr:    # det(L) defined by L(E) = det(L)E
         r"""
-        Returns the determinant (a scalar) of the linear transformation,
-        :math:`L`, defined by :math:`{{\det}\lp {L} \rp }I = {{L}\lp {I} \rp }`.
+        - Returns the determinant of the linear transformation :math:`L`,
+          defined by :math:`\det(L) = L(E) E^{-1}`, where :math:`E` is the
+          basis blade for the pseudoscalar grade space.
+        - Expression returned is a real SymPy scalar, not a GAlgebra 0-vector.
         """
-
-        lt_I = self(self.Ga.i, obj=True)
-        det_lt_I = lt_I.subs(self.Ga.i.obj, S.One)
-        return det_lt_I
+        return (self(self.Ga.e) * self.Ga.e.inv()).scalar()
+    # ## GSG code ends ###
 
     def tr(self) -> Expr:  # tr(L) defined by tr(L) = grad|L(x)
         r"""
@@ -436,23 +507,21 @@ def tr(self) -> Expr:  # tr(L) defined by tr(L) = grad|L(x)
         :math:`L`, defined by :math:`{{\operatorname{tr}}\lp {L} \rp }=\nabla_{a}\cdot{{L}\lp {a} \rp }`
         where :math:`a` is a vector in the tangent space.
         """
-
         connect_flg = self.Ga.connect_flg
         self.Ga.connect_flg = False
-
         F_x = mv.Mv(self(self.Ga.coord_vec, obj=True), ga=self.Ga)
         tr_F = (self.Ga.grad | F_x).scalar()
         self.Ga.connect_flg = connect_flg
         return tr_F
 
+    r'''
     def adj(self) -> 'Lt':
         r"""
-        Returns the adjoint (a linear transformation) of the linear
-        transformation, :math:`L`, defined by :math:`a\cdot{{L}\lp {b} \rp } = b\cdot{{\bar{L}}\lp {a} \rp }`
-        where :math:`a` and :math:`b` are any two vectors in the tangent space
-        and :math:`\bar{L}` is the adjoint of :math:`L`.
+        Returns the adjoint :math:`{\bar{L}}`(a linear transformation) of linear
+        transformation :math:`L`, defined by
+        :math:`a\cdot{{L}\lp {b} \rp } = b\cdot{{\bar{L}}\lp {a} \rp }`
+        where :math:`a` and :math:`b` are any two vectors in the tangent space.
         """
-
         self_adj = []
         for e_j in self.Ga.basis:
             s = S.Zero
@@ -463,19 +532,44 @@ def adj(self) -> 'Lt':
             else:
                 self_adj.append(expand(s) / self.Ga.e_sq)
         return Lt(self_adj, ga=self.Ga)
+    '''
 
+    # ## GSG code starts ###
+    def adj(self) -> 'Lt':
+        r"""
+        Returns the adjoint transformation :math:`{\bar{L}}` of linear
+        transformation :math:`L`, defined by
+        :math:`a\cdot{{L}\lp {b} \rp } = b\cdot{{\bar{L}}\lp {a} \rp }`,
+        where :math:`a` and :math:`b` are any two vectors in the tangent space.
+        """
+        matrix_of_adjoint = self.Ga.g_inv * self.matrix().T * self.Ga.g
+        return self.Ga.lt(matrix_of_adjoint)
+    # ## GSG code ends ###
+
+    # ## GSG code starts ###
+    def is_singular(self):
+        """Returns `True` if and only if  linear transformation `self` is singular."""
+        E = self.Ga.E()
+        return simplify((self(E) < E.inv()).scalar()) == S.Zero
+    # ## GSG code ends
+
+    # ## GSG code starts ###
     def inv(self):
-        if self.spinor:
-            Lt_inv = Lt(self.Rrev, ga=self.Ga)
-            Lt_inv.rho_sq = S.One/(self.rho_sq**2)
+        """Returns compositional inverse of linear transformation`self`.
+        Assumes transformation is nonsingular.  If `self` is a versor based
+        transformation, its inverse will also be versor based."""
+        if self.versor:
+            return self.Ga.lt(self.V.rev())
+        if not self.is_singular():
+            return self.Ga.lt(Matrix(self.matrix().inv()))
         else:
-            raise ValueError('Lt inverse currently implemented only for spinor!\n')
-        return Lt_inv
+            raise ValueError('transformation in inv() is non-invertible')
+    # ## GSG code ends ###
 
     def _sympystr(self, print_obj):
 
-        if self.spinor:
-            return 'R = ' + print_obj._print(self.R)
+        if self.versor:    # ## GSG: changed `self.spinor` to `self.versor` ###
+            return 'R = ' + print_obj._print(self.V)
         else:
             pre = 'Lt('
             s = ''
@@ -486,59 +580,48 @@ def _sympystr(self, print_obj):
                     s += pre + print_obj._print(base) + ') = 0\n'
             return s[:-1]
 
+    # ## GSG code starts ###
     def _latex(self, print_obj):
         parts = []
-        for base in self.Ga.basis:
-            if self.spinor:
-                val = self.R * mv.Mv(base, ga=self.Ga) * self.Rrev
+        for base in self.Ga.basis:           # base is a basis symbol
+            if self.versor:
+                b = mv.Mv(base, ga=self.Ga)  # b is the corresponding basis vector
+                if self.V == self.V.odd():
+                    unnormalized_image = self.V * (b.g_invol()) * self.Vrev
+                elif self.V == self.V.even():
+                    unnormalized_image = self.V * b * self.Vrev
+                else:
+                    raise ValueError('self.V is not a versor in  _latex')
+                image = 1/(self.Vqform) * unnormalized_image
             else:
-                val = mv.Mv(self.lt_dict.get(base, S.Zero), ga=self.Ga)
-            parts.append(print_obj._print(base) + ' &\\mapsto ' + print_obj._print(val))
+                image = mv.Mv(self.lt_dict.get(base, S.Zero), ga=self.Ga)
+            parts.append(print_obj._print(base) + ' &\\mapsto ' + print_obj._print(image))
         return '\\left\\{ \\begin{aligned} ' + ' \\\\ '.join(parts) + ' \\end{aligned} \\right\\}'
+    # ## GSG code ends ###
 
     def Fmt(self, fmt=1, title=None) -> printer.GaPrintable:
         return printer._FmtResult(self, title)
 
+    # ## GSG code starts ###
     def matrix(self) -> Matrix:
         r"""
-        Returns the matrix representation of the linear transformation,
-        :math:`L`, defined by :math:`{{L}\lp {{{\eb}}_{i}} \rp } = L_{ij}{{\eb}}_{j}`
-        where :math:`L_{ij}` is the matrix representation.
+        Returns the matrix :math:`[{L__i}_j]` defined for linear transformation
+        :math:`L` by :math:`L({\eb}_j)=\sum_i {L__i}_j \eb}_i`.
         """
         if self.mat is not None:
             return self.mat
+        elif self.versor:
+            self.lt_dict = {}
+            for base in self.Ga.basis:
+                self.lt_dict[base] = self(base).simplify()
+            self.versor = False    # temporary change of self.versor
+            mat = self.matrix()
+            self.versor = True     # reverse change to self.versor
+            return mat
         else:
-            if self.spinor:
-                self.lt_dict = {}
-                for base in self.Ga.basis:
-                    self.lt_dict[base] = self(base).simplify()
-                self.spinor = False
-                mat = self.matrix()
-                self.spinor = True
-                return mat
-            else:
-                """
-                mat_rep = []
-                for base in self.Ga.basis:
-                    if base in self.lt_dict:
-                        row = []
-                        image = (self.lt_dict[base])
-                        if isinstance(image, mv.Mv):
-                            image = image.obj
-                        coefs, bases = metric.linear_expand(image)
-                        for base in self.Ga.basis:
-                            try:
-                                i = bases.index(base)
-                                row.append(coefs[i])
-                            except:
-                                row.append(0)
-                        mat_rep.append(row)
-                    else:
-                        mat_rep.append(self.Ga.n * [0])
-                return Matrix(mat_rep).transpose()
-                """
-                self.mat = Dictionary_to_Matrix(self.lt_dict, self.Ga) * self.Ga.g
-                return self.mat
+            self.mat = Dictionary_to_Matrix(self.lt_dict, self.Ga)
+            return self.mat.doit()
+    # ## GSG code ends ###
 
 
 class Mlt(printer.GaPrintable):
@@ -913,3 +996,28 @@ def comps(self):
                 print('')
             output += str(i)+':'+str(i_index)+':'+str(self(*e)) + '\n'
         return output
+
+
+# ## GSG code starts ###
+def det(L: Lt) -> Expr:    # det(L) defined by L(E) = det(L)E
+    r"""
+    - Returns the determinant of the linear transformation :math:`L`,
+      defined by :math:`\det(L) = L(E) E^{-1}`, where :math:`E` is the
+      basis blade for the pseudoscalar grade space.
+    - Expression returned is a real SymPy scalar, not a GAlgebra 0-vector.
+    """
+    return L.det()
+# ## GSG code ends ###
+
+
+# ## GSG code starts ###
+def sym(v):
+    """
+    Returns that linear combination of basis vector symbols which corresponds
+    to vector v, itself a linear combination of basis vectors.
+    """
+    # Obtain the coefficients in basis vector expansion of `v`.
+    # Then construct and return corresponding basis vector symbol expansion.
+    coefs = v.blade_coefs(v.Ga.mv())
+    return sum(coefs[j]*v.Ga.basis[j] for j in range(v.Ga.n))
+# ## GSG code ends ###
diff --git a/galgebra/metric.py b/galgebra/metric.py
index 757ebdc1..aa26f91b 100644
--- a/galgebra/metric.py
+++ b/galgebra/metric.py
@@ -9,7 +9,7 @@
 from sympy import (
     diff, trigsimp, Matrix, Rational,
     sqf_list, sqrt, eye, S, expand, Mul,
-    Add, simplify, Expr, Function, MatrixSymbol
+    Add, simplify, Expr, Abs, Function, MatrixSymbol
 )
 
 from . import printer
@@ -114,19 +114,49 @@ def collect(A, nc_list):
     return C
 
 
-def square_root_of_expr(expr):
+def abs_with_hint(expr, hint: str = '0') -> Expr:
     """
-    If expression is product of even powers then every power is divided
-    by two and the product is returned.  If some terms in product are
-    not even powers the sqrt of the absolute value of the expression is
-    returned.  If the expression is a number the sqrt of the absolute
-    value of the number is returned.
+    Heuristics for simplifying the absolute value of an expression with hints.
     """
+
+    # Case1: expr is numeric
     if expr.is_number:
-        if expr > 0:
-            return sqrt(expr)
-        else:
-            return sqrt(-expr)
+        return Abs(expr)
+    # # Case2: metric is positive definite
+    # if self.Ga.g.is_positive_definite:
+    #     return expr
+    # Case3: expr is nonnegative
+    if (expr >= 0) == True:         # noqa: E712
+        return +expr
+    # Case4: expr is nonpositive
+    if (expr <= 0) == True:         # noqa: E712
+        return -expr
+
+    # Case5: expr's sign is unknown, so use `hint`.
+    if hint == '0':
+        return Abs(expr)
+    elif hint == '+':
+        return +expr
+    elif hint == '-':
+        return -expr
+    else:
+        raise ValueError("hint must be '0', '+', or '-'.")
+
+
+def square_root_of_expr(expr, hint='0'):
+    """
+    If expression is product of even powers then every power is divided by two
+    and the absolute value of product is returned.
+    If some terms in product are not even powers the sqrt of the absolute value of
+    the expression is returned.
+    If the expression is a number the sqrt of the absolute value of the number is returned.
+
+    String values '+', '-', or '0' of hint respectively determine
+    whether expr should be regarded as nonnegative, nonpositive,
+    or of unknown sign.
+    """
+    if expr.is_number:
+        return sqrt(abs_with_hint(expr, hint))
     else:
         expr = trigsimp(expr)
         coef, pow_lst = sqf_list(expr)
@@ -137,8 +167,9 @@ def square_root_of_expr(expr):
                 coef = sqrt(abs(coef))  # Product coefficient not a number
         for p in pow_lst:
             f, n = p
+            # Product not all even powers
             if n % 2 != 0:
-                return sqrt(abs(expr))  # Product not all even powers
+                return sqrt(abs_with_hint(expr, hint))
             else:
                 coef *= f ** (n / S(2))  # Positive sqrt of the square of an expression
         return coef
diff --git a/galgebra/mv.py b/galgebra/mv.py
index ecec5dc3..e18c4ed4 100644
--- a/galgebra/mv.py
+++ b/galgebra/mv.py
@@ -246,15 +246,17 @@ def _make_mv(ga: 'Ga', __name: str, **kwargs) -> Expr:
             for grade in range(ga.n + 1)
         ))
 
+    # ## GSG code starts ###
     @staticmethod
-    def _make_spinor(ga: 'Ga', __name: str, **kwargs) -> Expr:
-        """ Make a general even (spinor) multivector """
+    def _make_even(ga: 'Ga', __name: str, **kwargs) -> Expr:
+        """ Make a general even multivector """
         if not isinstance(__name, str):
             raise TypeError("Must be a string")
         return reduce(operator.add, (
             Mv._make_grade(ga, __name, grade, **kwargs)
             for grade in range(0, ga.n + 1, 2)
         ))
+    # ## GSG code ends ###
 
     @staticmethod
     def _make_odd(ga: 'Ga', __name: str, **kwargs) -> Expr:
@@ -268,9 +270,10 @@ def _make_odd(ga: 'Ga', __name: str, **kwargs) -> Expr:
 
     # aliases
     _make_grade2 = _make_bivector
-    _make_even = _make_spinor
+    _make_spinor = _make_even  # alias for compatibility with old code
+    ### GSSG: removed alias `_make_even = _make_spinor_`
 
-    def __init__(self, *args, ga, recp=None, coords=None, **kwargs):
+    def __init__(self, *args, ga: 'Ga', recp=None, coords=None, **kwargs):
         """
         __init__(self, *args, ga, recp=None, **kwargs)
 
@@ -341,6 +344,7 @@ def __init__(self, *args, ga, recp=None, coords=None, **kwargs):
             It is incorrectly described internally as the coordinates to be
             used with multivector functions.
         """
+        ### GSG: removed mention of "spinor" under `category : str` in above docstring
         kw = _KwargParser('__init__', kwargs)
         self.Ga = ga
         self.recp = recp  # not used
@@ -364,6 +368,7 @@ def __init__(self, *args, ga, recp=None, coords=None, **kwargs):
                 self.obj = x.obj
                 self.is_blade_rep = x.is_blade_rep
                 self.i_grade = x.i_grade
+                self.characterise_Mv()
             else:
                 if isinstance(x, Expr):  # copy constructor for obj expression
                     self.obj = x
@@ -411,7 +416,7 @@ def reflect_in_blade(self, blade: 'Mv') -> 'Mv':  # Reflect mv in blade
         if blade.is_blade():
             self.characterise_Mv()
             blade.characterise_Mv()
-            blade_inv = blade.rev() / blade.norm2()
+            blade_inv = blade.rev() / blade.qform()  # ### GSG replaced .norm2() by .qform()
             grade_dict = self.Ga.grade_decomposition(self)
             blade_grade = blade.i_grade
             reflect = Mv(0, 'scalar', ga=self.Ga)
@@ -428,7 +433,7 @@ def project_in_blade(self, blade: 'Mv') -> 'Mv':
         # See Mv class functions documentation
         if blade.is_blade():
             blade.characterise_Mv()
-            blade_inv = blade.rev() / blade.norm2()
+            blade_inv = blade.rev() / blade.qform()  # ### GSG replaced .norm2() by .qform()
             return (self < blade) * blade_inv  # < is left contraction
         else:
             raise ValueError(str(blade) + 'is not a blade in project_in_blade(self, blade)')
@@ -811,7 +816,10 @@ def __pow__(self, n):  # Integer power operator
         if not isinstance(n, int):
             raise ValueError('!!!!Multivector power can only be to integer power!!!!')
 
-        result = S.One
+        if n < 0:
+            return (self**(-n)).inv()
+
+        result = Mv(S.One, 'scalar', ga=self.Ga)
         for x in range(n):
             result *= self
         return result
@@ -948,6 +956,20 @@ def is_versor(self) -> bool:
         self.versor_flg = test.is_vector()
         return self.versor_flg
 
+    r'''
+    ### GSG start code ###
+    def is_versor(self) -> bool:
+        """
+        Presumes `self` is an invertible multivector.
+        Returns True if `self` is a versor (geometric product of invertible
+        vectors).
+        This test follows results in lecture notes by Lundholm and Svensson.
+        """
+        x = self.Ga.mv('', 1)    # generic 1-vector
+        return (self.g_invol() * x * self.inv()).is_vector()
+    ### GSG end code ###
+    '''
+
     def is_zero(self) -> bool:
         return self.obj == 0
 
@@ -1038,6 +1060,15 @@ def dual(self) -> 'Mv':
         else:
             return sign * self * I
 
+    # ## GSG code starts ###
+    def undual(self) -> 'Mv':
+        """
+        Inverse method to multivector method `.dual()`, so both
+        `A.dual().undual()` and `A.undual().dual` return `A`.
+        """
+        return self.Ga.I()**2 * self.dual()
+    # ## GSG code ends ###
+
     def even(self) -> 'Mv':
         """ return even parts of multivector """
         return Mv(self.Ga.even_odd(self.obj, True), ga=self.Ga)
@@ -1046,12 +1077,51 @@ def odd(self) -> 'Mv':
         """ return odd parts of multivector """
         return Mv(self.Ga.even_odd(self.obj, False), ga=self.Ga)
 
+    # ## GSG code starts ###
+    def g_invol(self) -> 'Mv':
+        """
+        - Returns grade involute of multivector `self`; negates
+          `self`'s odd grade part but preserves its even grade part.
+        - Grade involution is its own inverse operation.
+        """
+        return self.even() - self.odd()
+    # ## GSG code ends ###
+
     def rev(self) -> 'Mv':
         self = self.blade_rep()
         return Mv(self.Ga.reverse(self.obj), ga=self.Ga)
 
     __invert__ = rev  # allow `~x` to call x.rev()
 
+    # ## GSG code starts ###
+    def ccon(self) -> 'Mv':
+        """
+        - Returns Clifford conjugate of multivector `self`, i.e.
+          returns the reverse of self's grade involute.
+        - Clifford conjugation is its own inverse operation.
+        """
+        return self.g_invol().rev()
+    # ## GSG code ends ###
+
+    # ## GSG code starts ###
+    def sp(self, B, switch='') -> Expr:    # scalar product
+        """
+        - Returns scalar product of multivectors self and B.
+        - Object returned is a real expression, not a 0-vector.
+        - switch can be either '' (the empty string) or 'rev'.  The
+          latter causes left factor self to be reversed before its
+          product with B is taken.
+        """
+        if not isinstance(B, Mv):
+            raise ValueError("Right factor of sp must be a multivector")
+        if switch not in ['', 'rev']:
+            raise ValueError("switch must be '' or 'rev'.")
+        if switch == '':
+            return (self * B).scalar()
+        if switch == 'rev':
+            return (self.rev() * B).scalar()
+    # ## GSG code ends ###
+
     def diff(self, coord) -> 'Mv':
         if self.Ga.coords is None:
             obj = diff(self.obj, coord)
@@ -1187,59 +1257,88 @@ def _repr_latex_(self) -> str:
             return printer._FmtResult(self, self.title)._repr_latex_()
         return super()._repr_latex_()
 
-    def norm2(self) -> Expr:
-        reverse = self.rev()
-        product = self * reverse
-        if product.is_scalar():
-            return product.scalar()
-        else:
-            raise TypeError('"(' + str(product) + ')**2" is not a scalar in norm2.')
+    # ## GSG code starts ###
+    def qform(self) -> Expr:
+        """
+        - Returns the quadratic form of multivector self.
+        - Return value is a real SymPy expression, NOT a GAlgebra 0-vector.
+        - Expression necessarily represents a nonnegative number
+          only when self's geometric algebra has a Euclidean metric.
+        """
+        return simplify((self.rev()*self).scalar())
+    # ## GSG code ends ###
 
-    def norm(self, hint: str = '+') -> Expr:
+    # ## GSG code starts ###
+    def norm2(self, hint: str = '0') -> Expr:
         """
-        If A is a multivector and A*A.rev() is a scalar then::
-
-            A.norm() == sqrt(Abs(A*A.rev()))
-
-        The problem in simplifying the norm is that if ``A`` is symbolic
-        you don't know if ``A*A.rev()`` is positive or negative. The use
-        of the hint argument is as follows:
-
-        =======  ========================
-        hint     ``A.norm()``
-        =======  ========================
-        ``'+'``  ``sqrt(A*A.rev())``
-        ``'-'``  ``sqrt(-A*A.rev())``
-        ``'0'``  ``sqrt(Abs(A*A.rev()))``
-        =======  ========================
-
-        The default ``hint='+'`` is correct for vectors in a Euclidean vector
-        space.  For bivectors in a Euclidean vector space use ``hint='-'``. In
-        a mixed signature space all bets are off for the norms of symbolic
-        expressions.
+        - Returns the normsquared of multivector self, defined as the
+          absolute value of the quadratic form at self.
+        - Return value is a real SymPy expression, NOT a GAlgebra 0-vector.
+          Whether numeric or symbolic, A.norm2() always represents a
+          nonnegative number.
+        - String values '+', '-', or '0' of hint respectively determine
+          whether the quadratic form, the absolute value of which is the
+          norm squared, should be regarded as nonnegative, nonpositive,
+          or of unknown sign, except when that quantity's sign can be
+          determined by other considerations, such as the metric being
+          Euclidean.
         """
-        reverse = self.rev()
-        product = self * reverse
-
-        if product.is_scalar():
-            product = product.scalar()
-            if product.is_number:
-                if product >= S.Zero:
-                    return sqrt(product)
-                else:
-                    return sqrt(-product)
-            else:
-                if hint == '+':
-                    return metric.square_root_of_expr(product)
-                elif hint == '-':
-                    return metric.square_root_of_expr(-product)
-                else:
-                    return sqrt(Abs(product))
-        else:
-            raise TypeError('"(' + str(product) + ')" is not a scalar in norm.')
+        quadform = self.qform()             # the quadratic form at `self`
+
+        # Case2: metric is positive definite
+        if self.Ga.g.is_positive_definite:
+            return quadform
+
+        return metric.abs_with_hint(quadform, hint)
+    # ## GSG code ends ###
+
+    # ## GSG code starts ###
+    def norm(self, hint='0') -> Expr:
+        """
+        - Returns the norm of multivector self, defined as the square
+          root of self's norm squared.
+        - Whether numeric or symbolic, returned value is a real SymPy
+          expression that necessarily represents a nonnegative number.
+          Returned value is NOT a GAlgebra 0-vector.
+        - String values '+', '-', or '0' of hint respectively determine
+          whether the quadratic form from which the norm ultimately derives
+          should be regarded as nonnegative, nonpositive, or of unknown
+          sign, except when the quadratic form's sign can be determined
+          by other considerations, such as the metric being Euclidean.
+        """
+        return simplify(metric.square_root_of_expr(self.norm2(hint), hint='+'))
+    # ## GSG code ends ###
 
     __abs__ = norm  # allow `abs(x)` to call z.norm()
 
+    # ## GSG code starts ###
+    def mag2(self) -> Expr:
+        """
+        - Returns the magnitude squared of multivector self, defined as
+          the sum of the absolute values of the norm squareds of self's
+          grade parts.
+        - Returned value is a real SymPy expression, NOT a GAlgebra 0-vector.
+          Expression necesssarily represents a nonnegative number.
+        - The magnitude squared differs from the norm squared of `self`
+          when the metric is non-Euclidean.
+        """
+        total = 0
+        for k in range(self.Ga.n + 1):
+            total += Abs(self.grade(k).norm2())
+        return total
+    # ## GSG code ends ###
+
+    # ## GSG code starts ###
+    def mag(self) -> Expr:
+        """
+        - Returns the magnitude of multivector self, defined as the square root
+          of the magnitude squared.
+        - The magnitude necessarily agrees with the norm only when the metric is
+          Euclidean.  Otherwise the magnitude is greater than or equal to the norm.
+        """
+        return simplify(sqrt(self.mag2()))
+    # ## GSG code ends ###
+
     def inv(self) -> 'Mv':
         if self.is_scalar():  # self is a scalar
             return self.Ga.mv(S.One/self.obj)
@@ -1274,7 +1373,6 @@ def trigsimp(self) -> 'Mv':
     def simplify(self, modes=simplify) -> 'Mv':
         """
         Simplify a multivector by scalar (sympy) simplifications.
-
         `modes` is an operation or sequence of operations to apply to the the
         coefficients of a multivector expansion.
         """
@@ -1860,6 +1958,20 @@ def dual(A: Mv) -> Mv:
         raise ValueError('A not a multivector in dual(A)')
 
 
+# ## GSG code starts ###
+def undual(A: Mv) -> Mv:
+    """
+    Equivalent to :meth: `Mv.undual`.
+    Inverse function to multivector function `dual`, so both
+    `undual(dual(A))` and `dual(undual(A))` return `A`.
+    """
+    if isinstance(A, Mv):
+        return A.undual()
+    else:
+        raise ValueError('A not a multivector in undual(A).')
+# ## GSG code ends ###
+
+
 def even(A: Mv) -> Mv:
     """ Equivalent to :meth:`Mv.even` """
     if not isinstance(A, Mv):
@@ -1874,10 +1986,25 @@ def odd(A: Mv) -> Mv:
     return A.odd()
 
 
+# ## GSG code starts ###
+def g_invol(A: Mv) -> Mv:
+    """
+    Equivalent to :meth: `Mv.g_invol`.
+    - Returns grade involute of multivector `A`; negates `A`'s odd grade
+    part but preserves its even grade part.
+    - Grade involution is its own inverse operation.
+    """
+    if not isinstance(A, Mv):
+        raise ValueError('A not a multivector in g_invol(A)')
+    return A.g_invol()
+# ## GSG code ends ###
+
+
 def exp(A: Union[Mv, Expr], hint: str = '-') -> Union[Mv, Expr]:
     """
     If ``A`` is a multivector then ``A.exp(hint)`` is returned.
-    If ``A`` is a *sympy* expression the *sympy* expression :math:`e^{A}` is returned (see :func:`sympy.exp`).
+    If ``A`` is a *sympy* expression the *sympy* expression :math:`e^{A}`
+    is returned (see :func:`sympy.exp`).
     """
     if isinstance(A, Mv):
         return A.exp(hint)
@@ -1900,20 +2027,92 @@ def inv(A: Mv) -> Mv:
     return A.inv()
 
 
-def norm(A: Mv, hint: str = '+') -> Expr:
-    """ Equivalent to :meth:`Mv.norm` """
-    if isinstance(A, Mv):
-        return A.norm(hint=hint)
-    else:
-        raise ValueError('A not a multivector in norm(A)')
+# ## GSG code starts ###
+def qform(A: Mv) -> Expr:
+    """
+    - Equivalent to :meth:`Mv.qform`.
+    - qform(A) returns the quadratic form at multivector A.
+    - Returned value is a real SymPy expression, NOT a GAlgebra 0-vector.
+    - Expression necessarily represents a nonnegative number only
+      when A's geometric algebra has a Euclidean metric.
+    """
+    if not isinstance(A, Mv):
+        raise TypeError('A not a multivector in qform(A)')
+    return A.qform()
+# ## GSG code ends ###
 
 
-def norm2(A: Mv) -> Expr:
-    """ Equivalent to :meth:`Mv.norm2` """
-    if isinstance(A, Mv):
-        return A.norm2()
-    else:
-        raise ValueError('A not a multivector in norm(A)')
+# ## GSG code starts ###
+def norm2(A: Mv, hint: str = '0') -> Expr:
+    """
+    - Equivalent to :meth:`Mv.norm2`
+    - Returns the normsquared of multivector self, defined as the
+      absolute value of the quadratic form at self.
+    - norm2(A() returns a real SymPy expression, NOT a GAlgebra 0-vector.
+      Whether numeric or symbolic, norm2(A) always represents a
+      nonnegative number.
+    - String values '+', '-', or '0' of hint respectively determine
+      whether the quadratic form, the absolute value of which is the
+      norm squared, should be regarded as nonnegative, nonpositive,
+      or of unknown sign, except when that quantity's sign can be
+      determined by other considerations, such as the metric being
+      Euclidean.
+      """
+    if not isinstance(A, Mv):
+        raise TypeError('A not a multivector in norm2(A)')
+    return A.norm2(hint)
+# ## GSG code ends ###
+
+
+# ## GSG code starts ###
+def norm(A: Mv, hint: str = '0') -> Expr:
+    """
+    - Equivalent to :meth:`Mv.norm`
+    - Whether numeric nor symbolic, returned value is a real SymPy
+      expression that necessarily represents a nonnegative number.
+      Returned value is NOT a GAlgebra 0-vector.
+    - String values '+', '-', or '0' of hint respectively
+      determine whether a symbolic self.norm2() expression
+      should be regarded as nonnegative, nonpositive, or of
+      unknown sign.
+    """
+    if not isinstance(A, Mv):
+        raise TypeError('A not a multivector in norm(A)')
+    return A.norm(hint=hint)
+# ## GSG code ends ###
+
+
+# ## GSG code starts ###
+def mag2(A: Mv) -> Expr:
+    """
+    - Equivalent to :meth:`Mv.mag2`
+    - Returns the magnitude squared of multivector self, defined as
+      the sum of the absolute values of the norm squareds of self's
+      grade parts.
+    - Returned value is a real SymPy expression, NOT a GAlgebra 0-vector.
+      Expression necesssarily represents a nonnegative number.
+    - The magnitude squared differs from the normsquared of `self`
+      when the metric is non-Euclidean.
+    """
+    if not isinstance(A, Mv):
+        raise TypeError('A not a multivector in mag2(A)')
+    return A.mag2()
+# ## GSG code ends ###
+
+
+# ## GSG code starts ###
+def mag(A: Mv) -> Expr:
+    """
+    - Equivalent to :meth:`Mv.mag`
+    - Returns the magnitude of multivector self, defined as the square root
+      of the magnitude squared.
+    - The magnitude necessarily agrees with the norm only when the metric is
+      Euclidean.  Otherwise the magnitude is greater than or equal to the norm.
+    """
+    if not isinstance(A, Mv):
+        raise TypeError('A not a multivector in mag(A)')
+    return A.mag()
+# ## GSG code ends ###
 
 
 def proj(B: Mv, A: Mv) -> Mv:
@@ -1938,9 +2137,11 @@ def rot(itheta: Mv, A: Mv, hint: str = '-') -> Mv:
 def refl(B: Mv, A: Mv) -> Mv:
     r"""
     Reflect multivector :math:`A` in blade :math:`B`.
+    Returns
 
-    If :math:`s` is grade of :math:`B` returns :math:`\sum_{r}(-1)^{s(r+1)}B{\left < {A} \right >}_{r}B^{-1}`.
+    :math:`\sum_{r}(-1)^{s(r+1)}B{\left < {A} \right >}_{r}B^{-1}`.
 
+    if :math:`B` has grade :math:`s`.
     Equivalent to :meth:`Mv.reflect_in_blade`
     """
     if isinstance(A, Mv):
@@ -1957,8 +2158,38 @@ def rev(A: Mv) -> Mv:
         raise ValueError('A not a multivector in rev(A)')
 
 
+# ## GSG code starts ###
+def ccon(A: Mv) -> Mv:
+    """
+    - Equivalent to :meth: `Mv.ccon`.
+    - Returns Clifford conjugate of multivector `self`, i.e.
+      returns the reverse of self's grade involute.
+    - Clifford conjugation is its own inverse operation.
+    """
+    if not isinstance(A, Mv):
+        raise ValueError('A not a multivector in ccon(A)')
+    return A.ccon()
+# ## GSG code ends ###
+
+
 def scalar(A: Mv) -> Expr:
     """ Equivalent to :meth:`Mv.scalar` """
     if not isinstance(A, Mv):
-        raise ValueError('A = ' + str(A) + ' not a multivector in inv(A).')
+        raise ValueError('A = ' + str(A) + ' not a multivector in scalar(A).')
     return A.scalar()
+
+
+# ## GSG code starts ###
+def sp(A: Mv, B: Mv, switch='') -> Expr:
+    """
+    - Equivalent to :meth: `Mv.sp`.
+    - Returns scalar product of multivectors A and B.
+    - Returns a real SymPy expression, not a GAlgebra 0-vector.
+    - switch can be either '' (the empty string) or 'rev'.  The
+      latter causes left factor A to be reversed before its
+      product with B is taken.
+    """
+    if not isinstance(A, Mv):
+        raise ValueError("Left factor of sp must be a multivector")
+    return A.sp(B, switch)
+# ## GSG code ends ###
diff --git a/galgebra/primer.py b/galgebra/primer.py
new file mode 100644
index 00000000..30305457
--- /dev/null
+++ b/galgebra/primer.py
@@ -0,0 +1,60 @@
+# Author:  Greg Grunberg
+# Last revised:  2023-01-13
+
+# INSTRUCTIONS:
+#
+# To invoke this program within a Jupyter notebok, write the command
+#
+#     from galgebra.primer import *
+#
+# in the notebook's first In[ ] cell and execute that cell.
+
+from sys import version
+
+# Make SymPy available to this program
+import sympy
+from sympy import *
+
+# Make GAlgebra available to this program.
+import galgebra
+from galgebra.ga import *
+from galgebra.mv import *
+from galgebra.lt import *
+from galgebra.dop import *
+from galgebra.printer import Fmt, GaPrinter, Format
+# Fmt:  sets display mode of a multivector's basis expansion.
+# GaPrinter:  makes GA output a little more readable.
+# Format:  turns on latex printer.
+from galgebra.gprinter import gFormat, gprint
+gFormat()
+# Default `Fmode=True` suppresses display of the arguments of
+#   multivector fields.
+# Default `Dmode=True` causes partial differentiation
+#   operators to be displayed in shortened form.
+Ga.dual_mode('Iinv+')
+# Sets multivector dualization to be right multiplication by the
+# by the inverse unit pseudoscalar (the convention used in the
+# textbooks LAGA, VAGC, and GACS).
+initializations_list = r"""\textsf{The following initialization commands were executed:}\\
+\quad\texttt{from sys import version}\\
+\quad\texttt{import sympy}\\
+\quad\texttt{from sympy import *}\\
+\quad\texttt{import galgebra}\\
+\quad\texttt{from galgebra.ga import *}\\
+\quad\texttt{from galgebra.mv import *}\\
+\quad\texttt{from galgebra.lt import *}\\
+\quad\texttt{from galgebra.dop import *}\\
+\quad\texttt{from galgebra.printer import Fmt, GaPrinter, Format}\\
+\quad\texttt{from galgebra.gprinter import gFormat, gprint}\\
+\quad\texttt{gFormat()}\\~~~~~~~~
+\quad\textsf{# default 'Fmode=True' suppresses arguments of multivector fields}\\~~~~~~~~
+\quad\textsf{# default 'Dmode=True' displays partial differentiation operators in short form}\\
+\quad\texttt{Ga.dual_mode('Iinv+')}\\~~~~~~~~
+\quad\textsf{# dual and undual defined by }\mathbf{M}^\star = \mathbf{M}\mathbf{I}^{-1} \textsf{ and } \mathbf{M}^{-\star} = \mathbf{M}\mathbf{I}\\"""
+gprint(initializations_list)
+
+# Display versions of softwared being used.
+gprint(r'\textsf{This notebook is now using} \\',
+       r'\qquad\bullet~ \textsf{Python }', version[:5],
+       r'\qquad\bullet~ \textsf{SymPy }', sympy.__version__[:8],
+       r'\qquad\bullet~ \textsf{GAlgebra }', galgebra.__version__[:], r'.')
diff --git a/setup.cfg b/setup.cfg
index 7220a6b2..bcac97f2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -63,3 +63,17 @@ per-file-ignores =
     # F401 'sympy.core.function._coeff_isneg' imported but unused
     # F401 'sympy.interactive.printing' imported but unused
     ./galgebra/printer.py: E122,E127,E128,E262,E265,E402,F401
+
+    # F401 '.printer.Format_cnt' imported but unused
+    ./galgebra/gprinter.py: F401
+
+    # F403 'from sympy import *' used; unable to detect undefined names
+    # F403 'from galgebra.ga import *' used; unable to detect undefined names
+    # F403 'from galgebra.mv import *' used; unable to detect undefined names
+    # F403 'from galgebra.lt import *' used; unable to detect undefined names
+    # F403 'from galgebra.dop import *' used; unable to detect undefined names
+    # F401 'galgebra.printer.Fmt' imported but unused
+    # F401 'galgebra.printer.GaPrinter' imported but unused
+    # F401 'galgebra.printer.Format' imported but unused
+    # F405 'Ga' may be undefined, or defined from star imports: galgebra.dop, galgebra.ga, galgebra.lt, galgebra.mv, sympy
+    ./galgebra/primer.py: F403,F401,F405


Test patch:
diff --git a/examples/ipython/simple_ga_test.ipynb b/examples/ipython/simple_ga_test.ipynb
index 3567610c..9891d78b 100644
--- a/examples/ipython/simple_ga_test.ipynb
+++ b/examples/ipython/simple_ga_test.ipynb
@@ -284,9 +284,7 @@
   {
    "cell_type": "code",
    "execution_count": 17,
-   "metadata": {
-    "scrolled": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "A = o3d.lt('A')"
@@ -300,12 +298,12 @@
     {
      "data": {
       "text/latex": [
-       "\\begin{equation*}\\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{xx} \\boldsymbol{e}_{x} + A_{yx} \\boldsymbol{e}_{y} + A_{zx} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{xy} \\boldsymbol{e}_{x} + A_{yy} \\boldsymbol{e}_{y} + A_{zy} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{xz} \\boldsymbol{e}_{x} + A_{yz} \\boldsymbol{e}_{y} + A_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\}\\end{equation*}"
+       "\\begin{equation*}\\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto {A^{x}}_{x} \\boldsymbol{e}_{x} + {A^{y}}_{x} \\boldsymbol{e}_{y} + {A^{z}}_{x} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto {A^{x}}_{y} \\boldsymbol{e}_{x} + {A^{y}}_{y} \\boldsymbol{e}_{y} + {A^{z}}_{y} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto {A^{x}}_{z} \\boldsymbol{e}_{x} + {A^{y}}_{z} \\boldsymbol{e}_{y} + {A^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\}\\end{equation*}"
       ],
       "text/plain": [
-       "Lt(e_x) = A_xx*e_x + A_yx*e_y + A_zx*e_z\n",
-       "Lt(e_y) = A_xy*e_x + A_yy*e_y + A_zy*e_z\n",
-       "Lt(e_z) = A_xz*e_x + A_yz*e_y + A_zz*e_z"
+       "Lt(e_x) = {A__x}_x*e_x + {A__y}_x*e_y + {A__z}_x*e_z\n",
+       "Lt(e_y) = {A__x}_y*e_x + {A__y}_y*e_y + {A__z}_y*e_z\n",
+       "Lt(e_z) = {A__x}_z*e_x + {A__y}_z*e_y + {A__z}_z*e_z"
       ]
      },
      "execution_count": 18,
@@ -327,12 +325,12 @@
     {
      "data": {
       "text/latex": [
-       "\\begin{equation*}A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{xx} \\boldsymbol{e}_{x} + A_{yx} \\boldsymbol{e}_{y} + A_{zx} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{xy} \\boldsymbol{e}_{x} + A_{yy} \\boldsymbol{e}_{y} + A_{zy} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{xz} \\boldsymbol{e}_{x} + A_{yz} \\boldsymbol{e}_{y} + A_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\}\\end{equation*}"
+       "\\begin{equation*}A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto {A^{x}}_{x} \\boldsymbol{e}_{x} + {A^{y}}_{x} \\boldsymbol{e}_{y} + {A^{z}}_{x} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto {A^{x}}_{y} \\boldsymbol{e}_{x} + {A^{y}}_{y} \\boldsymbol{e}_{y} + {A^{z}}_{y} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto {A^{x}}_{z} \\boldsymbol{e}_{x} + {A^{y}}_{z} \\boldsymbol{e}_{y} + {A^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\}\\end{equation*}"
       ],
       "text/plain": [
-       "A = Lt(e_x) = A_xx*e_x + A_yx*e_y + A_zx*e_z\n",
-       "Lt(e_y) = A_xy*e_x + A_yy*e_y + A_zy*e_z\n",
-       "Lt(e_z) = A_xz*e_x + A_yz*e_y + A_zz*e_z"
+       "A = Lt(e_x) = {A__x}_x*e_x + {A__y}_x*e_y + {A__z}_x*e_z\n",
+       "Lt(e_y) = {A__x}_y*e_x + {A__y}_y*e_y + {A__z}_y*e_z\n",
+       "Lt(e_z) = {A__x}_z*e_x + {A__y}_z*e_y + {A__z}_z*e_z"
       ]
      },
      "execution_count": 19,
@@ -352,12 +350,12 @@
     {
      "data": {
       "text/latex": [
-       "\\begin{equation*}A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto A_{xx} \\boldsymbol{e}_{x} + A_{yx} \\boldsymbol{e}_{y} + A_{zx} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto A_{xy} \\boldsymbol{e}_{x} + A_{yy} \\boldsymbol{e}_{y} + A_{zy} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto A_{xz} \\boldsymbol{e}_{x} + A_{yz} \\boldsymbol{e}_{y} + A_{zz} \\boldsymbol{e}_{z} \\end{aligned} \\right\\}\\end{equation*}"
+       "\\begin{equation*}A = \\left\\{ \\begin{aligned} \\boldsymbol{e}_{x} &\\mapsto {A^{x}}_{x} \\boldsymbol{e}_{x} + {A^{y}}_{x} \\boldsymbol{e}_{y} + {A^{z}}_{x} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{y} &\\mapsto {A^{x}}_{y} \\boldsymbol{e}_{x} + {A^{y}}_{y} \\boldsymbol{e}_{y} + {A^{z}}_{y} \\boldsymbol{e}_{z} \\\\ \\boldsymbol{e}_{z} &\\mapsto {A^{x}}_{z} \\boldsymbol{e}_{x} + {A^{y}}_{z} \\boldsymbol{e}_{y} + {A^{z}}_{z} \\boldsymbol{e}_{z} \\end{aligned} \\right\\}\\end{equation*}"
       ],
       "text/plain": [
-       "A = Lt(e_x) = A_xx*e_x + A_yx*e_y + A_zx*e_z\n",
-       "Lt(e_y) = A_xy*e_x + A_yy*e_y + A_zy*e_z\n",
-       "Lt(e_z) = A_xz*e_x + A_yz*e_y + A_zz*e_z"
+       "A = Lt(e_x) = {A__x}_x*e_x + {A__y}_x*e_y + {A__z}_x*e_z\n",
+       "Lt(e_y) = {A__x}_y*e_x + {A__y}_y*e_y + {A__z}_y*e_z\n",
+       "Lt(e_z) = {A__x}_z*e_x + {A__y}_z*e_y + {A__z}_z*e_z"
       ]
      },
      "execution_count": 20,
@@ -379,7 +377,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
    "name": "python3"
   },
@@ -393,9 +391,9 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.0"
+   "version": "3.11.8"
   }
  },
  "nbformat": 4,
- "nbformat_minor": 1
+ "nbformat_minor": 4
 }
diff --git a/examples/ipython/test_gsg_undual_etc.ipynb b/examples/ipython/test_gsg_undual_etc.ipynb
new file mode 100644
index 00000000..85d4b49d
--- /dev/null
+++ b/examples/ipython/test_gsg_undual_etc.ipynb
@@ -0,0 +1,4106 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "6f55977f",
+   "metadata": {},
+   "source": [
+    "# Testing of undualization, grade involution, Clifford conjugation, scalar product, normsquared, and norm\n",
+    "\n",
+    "Author:  Greg Grunberg  \n",
+    "Last updated 2021-03-31  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ebee586e",
+   "metadata": {},
+   "source": [
+    "In this notebook I test the operations `undual`, `g_invol`, `ccon`, `sp`, `norm2`, and `norm`, code for which was described in the notebook **Code for undualization, grade involution, Clifford conjugation, scalar product, normsquared, and norm**.  The first four of these are operations new to GAlgebra, while the last two are old operations I have newly recoded. \n",
+    "\n",
+    "Herein I use SymPy and the latest development version, as of this writing, of PyGAE Galgebra.  PyGAE GAlgebra is available from https://github.com/pygae/galgebra, with the latest development version uploaded on 2020-10-18.  *The GAlgebra module* **mv.py** *being used by this notebook is the 2020-10-18 version except modified so as to incoporate the `Mv` class methods testing of which is the purpose of this notebook.*\n",
+    "\n",
+    "I also use the GAlgebra module **gprinter.py**.  That module, not yet available from the GitHub website, was provided directly to me by Alan Bromborsky, the original author of GAlgebra.  The functions **`gprint`** and **`gFormat`** used herein come from **gprinter.py**.\n",
+    "\n",
+    "\n",
+    "**References**  \n",
+    "[CAGC]: *Clifford Algebra to Geometric Calculus*, by Hestenes & Sobczyk   \n",
+    "[GACS]: *Geometric Algebra for Computer Science*, by Dorst, Fontijne, & Mann   \n",
+    "[LAGA]: *Linear and Geometric Algebra*, by Alan Macdonald\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b8077836",
+   "metadata": {},
+   "source": [
+    "## 1. Notebook initialization\n",
+    "\n",
+    "Execution of the next cell makes SymPy and GAlgebra available within this notebook."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "c59e2276",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\DeclareMathOperator{\\Tr}{Tr}$$\n",
+       "$$\\DeclareMathOperator{\\Adj}{Adj}$$\n",
+       "$$\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}$$\n",
+       "$$\\newcommand{\\lp}{\\left (}$$\n",
+       "$$\\newcommand{\\rp}{\\right )}$$\n",
+       "$$\\newcommand{\\paren}[1]{\\lp {#1} \\rp}$$\n",
+       "$$\\newcommand{\\half}{\\frac{1}{2}}$$\n",
+       "$$\\newcommand{\\llt}{\\left <}$$\n",
+       "$$\\newcommand{\\rgt}{\\right >}$$\n",
+       "$$\\newcommand{\\abs}[1]{\\left |{#1}\\right | }$$\n",
+       "$$\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}$$\n",
+       "$$\\newcommand{\\npdiff}[3]{\\bfrac{\\partial^{#3} {#1}}{\\partial {#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\lbrc}{\\left \\{}$$\n",
+       "$$\\newcommand{\\rbrc}{\\right \\}}$$\n",
+       "$$\\newcommand{\\W}{\\wedge}$$\n",
+       "$$\\newcommand{\\prm}[1]{{#1}^{\\prime}}$$\n",
+       "$$\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}$$\n",
+       "$$\\newcommand{\\R}{\\dagger}$$\n",
+       "$$\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}}$$\n",
+       "$$\\newcommand{\\grade}[2]{\\left < {#1} \\right >_{#2}}$$\n",
+       "$$\\newcommand{\\f}[2]{{#1}\\lp {#2} \\rp}$$\n",
+       "$$\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}}$$\n",
+       "$$\\newcommand{\\bs}[1]{\\boldsymbol{#1}}$$\n",
+       "$$\\newcommand{\\grad}{\\bs{\\nabla}}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{This notebook is now using} \\\\\\qquad\\bullet~ \\text{Python }3.9.18\\qquad\\bullet~ \\text{SymPy }1.12\\qquad\\bullet~ \\text{GAlgebra }0.6.0-dev.$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "import platform\n",
+    "\n",
+    "import sympy \n",
+    "from sympy import *\n",
+    "import galgebra\n",
+    "from galgebra.ga import *  \n",
+    "from galgebra.mv import *\n",
+    "from galgebra.printer import Fmt, GaPrinter, Format\n",
+    "    # Fmt:  sets display mode of a multivector's basis expansion.\n",
+    "    # GaPrinter:  makes GA output a little more readable.\n",
+    "    # Format:  turns on latex printer.\n",
+    "from galgebra.gprinter import gFormat, gprint\n",
+    "\n",
+    "gFormat()\n",
+    "\t# Default `Fmode=True` suppresses display of a multivector fields'\n",
+    "\t# arguments.  Default `Dmode=True` causes partial differentiation\n",
+    "\t# operators to be displayed in shortened form.\n",
+    "Ga.dual_mode('Iinv+')  \n",
+    "    # Sets dual of a multivector to be the multivector multiplied on \n",
+    "    # its right by the inverse unit pseudoscalar, the convention used\n",
+    "    # in LAGA, VAGC, and GACS.\n",
+    "gprint(r'\\text{This notebook is now using} \\\\',\n",
+    "       r'\\qquad\\bullet~ \\text{Python }', platform.python_version(),\n",
+    "       r'\\qquad\\bullet~ \\text{SymPy }', sympy.__version__[:],\n",
+    "       r'\\qquad\\bullet~ \\text{GAlgebra }', \n",
+    "       galgebra.__version__[:], r'.')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d5005640",
+   "metadata": {},
+   "source": [
+    "## 2. Testing environment `g3` for operations `undual`, `g_invol`, `ccon`, and `sp`\n",
+    "\n",
+    "For testing purposes we will use the geometric algebra $\\mathbb{G}^{3,0}$ of Euclidean 3-space, which we implement in the next cell as `g3`.  Cartesian coordinates $(x,y,z)$ are used.\n",
+    "\n",
+    "**Notation:**  Unlike [GACS] and [LAGA], where boldface is used to signal that a multivector is a blade, this notebook uses boldface to denote non-scalar multivectors. Doing so allows establishes a visual distinction between a multivector $\\mathbf{A}$ and its grade 0 part, $A = \\left< \\mathbf{A} \\right>_0$.  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "e053271f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{model } \\textsf{g3} \\textbf{ of } \\mathbb{R}^{3}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{coordinates}~~ \\left[ x, \\  y, \\  z\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{basis}~~ \\left(  \\boldsymbol{\\mathbf{e}}_{x}, \\   \\boldsymbol{\\mathbf{e}}_{y}, \\   \\boldsymbol{\\mathbf{e}}_{z}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{metric tensor}~~ [g_{ij}] = \\left[\\begin{array}{ccc}1 & 0 & 0\\\\0 & 1 & 0\\\\0 & 0 & 1\\end{array}\\right] \\text{ is Euclidean}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{unit pseudoscalar}~~ \\mathbf{I} =  \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\quad\\text{generic multivectors} \\\\ \\quad\\quad\\mathbf{A} =  \\begin{aligned}[t]  & A  \\\\  &  + A^{x} \\boldsymbol{\\mathbf{e}}_{x} + A^{y} \\boldsymbol{\\mathbf{e}}_{y} + A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\begin{equation*} \\quad \\quad \\mathbf{B} =  \\begin{aligned}[t]  & B  \\\\  &  + B^{x} \\boldsymbol{\\mathbf{e}}_{x} + B^{y} \\boldsymbol{\\mathbf{e}}_{y} + B^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + B^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + B^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + B^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + B^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  \\end{equation*}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Instantiate Cartesian coordinates.\n",
+    "# Instantiate the geometric algebra `g3` of Euclidean 3-space:\n",
+    "x, y, z = symbols('x y z', real=True)    \n",
+    "g3 = Ga('\\mathbf{e}', \n",
+    "        g=[[1,0,0], [0,1,0], [0,0,1]],\n",
+    "        coords=(x,y,z))\n",
+    "\n",
+    "# Show various attributes and functions of `g3`:\n",
+    "gprint(r\"\\textbf{model } \\textsf{g3} \\textbf{ of } \\mathbb{R}^{3}:\")\n",
+    "gprint(r'\\quad \\text{coordinates}~~', g3.coords)\n",
+    "gprint(r'\\quad \\text{basis}~~', g3.mv())\n",
+    "gprint(r'\\quad \\text{metric tensor}~~ [g_{ij}] =', g3.g,\n",
+    "       r'\\text{ is Euclidean}')\n",
+    "gprint(r'\\quad \\text{unit pseudoscalar}~~ \\mathbf{I} =', g3.I())\n",
+    "\n",
+    "# Instantiate and show generic multivectors `A` and `B` of `g3`:\n",
+    "A = g3.mv('A', 'mv')\n",
+    "B = g3.mv('B', 'mv')\n",
+    "gprint(r'\\quad\\text{generic multivectors} \\\\ \\quad\\quad\\mathbf{A} =',\n",
+    "       A.Fmt(2), r'\\\\\\quad \\quad \\mathbf{B} =', B.Fmt(2))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "99253ba6",
+   "metadata": {},
+   "source": [
+    "## 3 Tests of `undual`, `g_invol`, `ccon`, and `sp`"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "10353087",
+   "metadata": {},
+   "source": [
+    "### 3.1. `undual` (undualization)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "c7fa60e5",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} =  \\begin{aligned}[t]  & A  \\\\  &  + A^{x} \\boldsymbol{\\mathbf{e}}_{x} + A^{y} \\boldsymbol{\\mathbf{e}}_{y} + A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A}^{\\star} =  \\begin{aligned}[t]  & A^{xyz}  \\\\  &  + A^{yz} \\boldsymbol{\\mathbf{e}}_{x} - A^{xz} \\boldsymbol{\\mathbf{e}}_{y} + A^{xy} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  - A^{z} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{y} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} - A^{x} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  - A \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A}^{-\\star} =  \\begin{aligned}[t]  & - A^{xyz}  \\\\  &  - A^{yz} \\boldsymbol{\\mathbf{e}}_{x} + A^{xz} \\boldsymbol{\\mathbf{e}}_{y} - A^{xy} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{z} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} - A^{y} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{x} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A}^{-\\star} = -\\mathbf{A}^{\\star}:~~ \\text{True} \\qquad \\text {(Note: }\\mathbf{I}^2 = -1 )$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle (\\mathbf{A}^{\\star})^{-\\star} = \\mathbf{A}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle (\\mathbf{A}^{-\\star})^{\\star} = \\mathbf{A}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Show `A`, its method dual, and its method undual.  \n",
+    "gprint(r'\\mathbf{A} =', A.Fmt(2))\n",
+    "gprint(r'\\mathbf{A}^{\\star} =', A.dual().Fmt(2))\n",
+    "gprint(r'\\mathbf{A}^{-\\star} =', A.undual().Fmt(2))\n",
+    "gprint(r'\\mathbf{A}^{-\\star} = -\\mathbf{A}^{\\star}:~~',\n",
+    "       A.undual() == - A.dual(), \n",
+    "       r'\\qquad \\text {(Note: }\\mathbf{I}^2 =', I**2, ')')\n",
+    "\n",
+    "# Check whether method `dual` and method `undual` have inverse actions:\n",
+    "gprint(r'(\\mathbf{A}^{\\star})^{-\\star} = \\mathbf{A}:~~',\n",
+    "       A.dual().undual() == A)\n",
+    "gprint(r'(\\mathbf{A}^{-\\star})^{\\star} = \\mathbf{A}:~~',\n",
+    "       A.undual().dual() == A)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3dd6433b",
+   "metadata": {},
+   "source": [
+    "### 3.2. `g_invol` (grade involution)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "a8c28609",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} =  \\begin{aligned}[t]  & A  \\\\  &  + A^{x} \\boldsymbol{\\mathbf{e}}_{x} + A^{y} \\boldsymbol{\\mathbf{e}}_{y} + A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\widehat{\\mathbf{A}} =  \\begin{aligned}[t]  & A  \\\\  &  - A^{x} \\boldsymbol{\\mathbf{e}}_{x} - A^{y} \\boldsymbol{\\mathbf{e}}_{y} - A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  - A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\widehat{\\left< \\mathbf{A} \\right>_+} = +\\left< \\mathbf{A} \\right>_+:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\widehat{\\left< \\mathbf{A} \\right>_-} = -\\left< \\mathbf{A} \\right>_-:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\widehat{\\widehat{\\mathbf{A}}} = \\mathbf{A}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Show `A` and its grade involute.  \n",
+    "# Check whether `g_invol` is self-inverse:\n",
+    "gprint(r'\\mathbf{A} =', A.Fmt(2))\n",
+    "gprint(r'\\widehat{\\mathbf{A}} =', A.g_invol().Fmt(2))\n",
+    "gprint(r'\\widehat{\\left< \\mathbf{A} \\right>_+} = '\n",
+    "       + r'+\\left< \\mathbf{A} \\right>_+:~~',\n",
+    "       A.even().g_invol() == A.even())\n",
+    "gprint(r'\\widehat{\\left< \\mathbf{A} \\right>_-} = '\n",
+    "       + r'-\\left< \\mathbf{A} \\right>_-:~~',\n",
+    "       A.odd().g_invol() == - A.odd())\n",
+    "gprint(r'\\widehat{\\widehat{\\mathbf{A}}} = \\mathbf{A}:~~',\n",
+    "       A.g_invol().g_invol() == A)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "24492ec8",
+   "metadata": {},
+   "source": [
+    "### 3.3. `ccon` (Clifford conjugation)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "aa255f32",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} =  \\begin{aligned}[t]  & A  \\\\  &  + A^{x} \\boldsymbol{\\mathbf{e}}_{x} + A^{y} \\boldsymbol{\\mathbf{e}}_{y} + A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\overline{\\mathbf{A}} =  \\begin{aligned}[t]  & A  \\\\  &  - A^{x} \\boldsymbol{\\mathbf{e}}_{x} - A^{y} \\boldsymbol{\\mathbf{e}}_{y} - A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  - A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} - A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} - A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\overline{\\overline{\\mathbf{A}}} = \\mathbf{A}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\overline{\\mathbf{A}} = \\widehat{\\mathbf{A}}^\\dagger:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\overline{\\mathbf{A}} = \\widehat{\\mathbf{A^\\dagger}}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Show `A` and its Clifford conjugate.  \n",
+    "# Check whether `ccon` is self-inverse:\n",
+    "gprint(r'\\mathbf{A} =', A.Fmt(2))\n",
+    "gprint(r'\\overline{\\mathbf{A}} =', A.ccon().Fmt(2))\n",
+    "gprint(r'\\overline{\\overline{\\mathbf{A}}} = \\mathbf{A}:~~',\n",
+    "       A.ccon().ccon() == A)\n",
+    "gprint(r'\\overline{\\mathbf{A}} = \\widehat{\\mathbf{A}}^\\dagger:~~',\n",
+    "       A.ccon() == (A.g_invol()).rev())\n",
+    "gprint(r'\\overline{\\mathbf{A}} = \\widehat{\\mathbf{A^\\dagger}}:~~',\n",
+    "       A.ccon() == (A.g_invol()).rev())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9f2f5dc1",
+   "metadata": {},
+   "source": [
+    "### 3.4. `sp` (scalar products)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "b89afded",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} =  \\begin{aligned}[t]  & A  \\\\  &  + A^{x} \\boldsymbol{\\mathbf{e}}_{x} + A^{y} \\boldsymbol{\\mathbf{e}}_{y} + A^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + A^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + A^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + A^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B} =  \\begin{aligned}[t]  & B  \\\\  &  + B^{x} \\boldsymbol{\\mathbf{e}}_{x} + B^{y} \\boldsymbol{\\mathbf{e}}_{y} + B^{z} \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + B^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} + B^{xz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{z} + B^{yz} \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z} \\\\  &  + B^{xyz} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y}\\wedge \\boldsymbol{\\mathbf{e}}_{z}  \\end{aligned}  $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} \\ast \\mathbf{B} = \\left< \\mathbf{A} \\mathbf{B} \\right>_0:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} \\ast \\mathbf{B} = A B + A^{x} B^{x} - A^{xy} B^{xy} - A^{xyz} B^{xyz} - A^{xz} B^{xz} + A^{y} B^{y} - A^{yz} B^{yz} + A^{z} B^{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\Vert \\mathbf{A} \\Vert^2 = A^\\dagger \\ast A:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} \\circledast \\mathbf{B} = A B + A^{x} B^{x} + A^{xy} B^{xy} + A^{xyz} B^{xyz} + A^{xz} B^{xz} + A^{y} B^{y} + A^{yz} B^{yz} + A^{z} B^{z} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\Vert\\mathbf{A}\\Vert^2 = \\mathbf{A}\\circledast\\mathbf{A}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} \\ast \\mathbf{B} =\\mathbf{A}^\\dagger \\circledast \\mathbf{B}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A} \\circledast \\mathbf{B} =\\mathbf{A}^\\dagger \\ast \\mathbf{B}:~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Show `A`, `B`, their scalar product, and their alternate \n",
+    "# scalar product.\n",
+    "gprint(r'\\mathbf{A} =', A.Fmt(2))\n",
+    "gprint(r'\\mathbf{B} =', B.Fmt(2))\n",
+    "gprint(r'\\mathbf{A} \\ast \\mathbf{B} = '\n",
+    "       + r'\\left< \\mathbf{A} \\mathbf{B} \\right>_0:~~',\n",
+    "       A.sp(B) == (A*B).scalar()) \n",
+    "gprint(r'\\mathbf{A} \\ast \\mathbf{B} =', A.sp(B))\n",
+    "gprint(r'\\Vert \\mathbf{A} \\Vert^2 = A^\\dagger \\ast A:~~',\n",
+    "       A.norm2() == A.rev().sp(A))\n",
+    "gprint(r'\\mathbf{A} \\circledast \\mathbf{B} =', A.sp(B,'rev'))\n",
+    "gprint(r'\\Vert\\mathbf{A}\\Vert^2 = \\mathbf{A}\\circledast\\mathbf{A}:~~',\n",
+    "       A.norm2() == A.sp(A,'rev'))\n",
+    "gprint(r'\\mathbf{A} \\ast \\mathbf{B} ='\n",
+    "       + r'\\mathbf{A}^\\dagger \\circledast \\mathbf{B}:~~',\n",
+    "       A.sp(B) == A.rev().sp(B, 'rev'))\n",
+    "gprint(r'\\mathbf{A} \\circledast \\mathbf{B} ='\n",
+    "       + r'\\mathbf{A}^\\dagger \\ast \\mathbf{B}:~~',\n",
+    "       A.sp(B,'rev') == A.rev().sp(B))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "090e0a7f",
+   "metadata": {},
+   "source": [
+    "The above tests include tests of the relationship of the (usual) scalar product $\\ast$ and the alternate scalar product $\\circledast$ to the normsquared operation $\\Vert \\cdot \\Vert^2$."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "343b9494",
+   "metadata": {},
+   "source": [
+    "### 3.5. Conclusions from testing the new operations\n",
+    "\n",
+    "The output from each of the executable In[ ] cells of subsections 3.1, 3.2, 3.3, and 3.4 was precisely what was expected from calculations done by hand.  That, together with the simplicity of the code, should give confidence that the operations were correctly coded."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a58386ba",
+   "metadata": {},
+   "source": [
+    "## 4.  Testing environments `e2`, `e2_polar`, `m2`, and `m2_polar` for `norm2` and `norm`\n",
+    "\n",
+    "We shall test `norm2` and `norm` in the context of four simple models:    \n",
+    "1. `e2` models $\\mathbb{E}^2$ (Euclidean 2-space) using Cartesian coordinates $(x,y)$.  The basis $(\\mathbf{e}_x,\\mathbf{e}_y)$ for the tangent space to $\\mathbb{E}^2$ at location $(x,y)$ is orthonormal.  Basis vectors are independent of location, which is to say every tangent space uses the same basis.   \n",
+    "\n",
+    "2. `e2_polar` models $\\mathbb{E}^2$ using polar coordinates $(r,\\theta)$, which are related to the Cartesian coordinates through the relationship $(x,y) = (r \\cos(\\theta), r \\sin(\\theta))$.  The basis $(\\mathbf{b}_r,\\mathbf{b}_\\theta)$ for the tangent space to $\\mathbb{E}^2$ at location $(r,\\theta)$ is orthogonal but not orthonormal.  The lengths and directions of the basis vectors depend on location ($(r,\\theta)$) (the point of tangency). The origin $r=0$ is excluded from the coordinate system, so we require $r>0$. \n",
+    "\n",
+    "3. `m2` models $\\mathbb{M}^{1,1}$ (Minkowski 2-space) using Cartesian coordinates $(s,t)$.  The basis $(\\mathbf{e}_s,\\mathbf{e}_t)$ for the tangent space to $\\mathbb{M}^{1,1}$ at $(s,t)$ is orthonormal.  Basis vectors are independent of location $(s,t)$.   \n",
+    "\n",
+    "4. `m2_polar` models $\\mathbb{M}^{1,1}$ using hyperbolic polar coordinates$^\\ast$ $(\\rho,\\phi)$, which are related to the Cartesian coordinates through the relationship $(s,t) = (\\rho \\cosh(\\phi), \\rho \\sinh(\\phi))$.  The basis $(\\mathbf{b}_\\rho,\\mathbf{b}_\\phi)$ for the tangent space to $\\mathbb{E}^2$ at $(\\rho,\\phi)$ is orthogonal but not orthonormal.  The lengths and directions of the basis vectors depend on location. Hyperbolic polar coordinates as defined here correspond to the region described in Cartesian coordinates $(s,t)$ as lying between the null lines $t=+s$ and $t=-s$, with $s>0$.  In particular this excludes the origin $\\rho=0$, so we require $\\rho>0$.  But even were the origin included, the polar coordinates describe only a quarter of the 2-space.  More specifically they should be called a coordinates *patch*.\n",
+    "\n",
+    "For each of the four models I define a symbolic multivector $\\mathbf{S}$ and a constant (location-independent) numeric multivector $\\mathbf{N}$.  The numeric multivector `N2` of model `e2_polar` has been so designed as to represent the same constant multivector $\\mathbf{N}$ as numeric multivector `N1` of model `e2`.  Similarly `N4` of `m2_polar`  represents the same constant numeric multivector as does `N3` of `m2`.  For models `m2` and `m2_polar` of the non-Euclidean space $\\mathbb{M}^{1,1}$, I also define multivector fields $\\mathbf{A}$ and $\\mathbf{B}$, respectively, for which $\\Vert\\mathbf{A}\\Vert^2 > 0$ and $\\Vert\\mathbf{B}\\Vert^2 < 0$.\n",
+    "\n",
+    "$^\\ast$ \"Hyperbolic polar\" is a name of my own invention, meant to indicate an analog on the *hyperbolic* plane (Minkowski 2-space) of *polar* coordinates $(r,\\theta)$ on the Euclidean plane.  If there's a standard name or standard symbols for these coordinates, I'm unaware of it."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "648220c5",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{model }\\textsf{e2}\\textbf{ of }\\mathbb{E}^2:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{coordinates}~~ \\left[ x, \\  y\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{basis}~~ \\left(  \\boldsymbol{\\mathbf{e}}_{x}, \\   \\boldsymbol{\\mathbf{e}}_{y}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{metric tensor}~~ [g_{ij}] = \\left[\\begin{array}{cc}1 & 0\\\\0 & 1\\end{array}\\right] \\text{ is Euclidean:}~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{multivectors}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{S} = S  + S^{x} \\boldsymbol{\\mathbf{e}}_{x} + S^{y} \\boldsymbol{\\mathbf{e}}_{y} + S^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{N} = - 2 \\sqrt{6}  - \\frac{3 \\sqrt{3}}{2} \\boldsymbol{\\mathbf{e}}_{x} + \\frac{3}{2} \\boldsymbol{\\mathbf{e}}_{y} -4 \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Model 1  \n",
+    "# Create Cartesian coordinates model `e2` of Euclidean 2-space.\n",
+    "x, y = symbols('x y', real=True)\n",
+    "e2  = Ga('\\mathbf{e}', g=[1,1], coords=(x,y))\n",
+    "ex, ey = e2.mv()\n",
+    "# Create symbolic mixed grade multivector `S1`.\n",
+    "S1 = e2.mv('S', 'mv')\n",
+    "# Create numeric mixed grade multivector `N1`.\n",
+    "N1 = -sqrt(24) - (sqrt(27)/2)*ex + Rational(3,2)*ey - 4*(ex^ey)\n",
+    "\n",
+    "# Show various attributes and functions of `e2`:\n",
+    "gprint(r\"\\textbf{model }\\textsf{e2}\\textbf{ of }\\mathbb{E}^2:\")\n",
+    "gprint(r'\\quad \\text{coordinates}~~', e2.coords)\n",
+    "gprint(r'\\quad \\text{basis}~~', e2.mv())\n",
+    "gprint(r'\\quad \\text{metric tensor}~~ [g_{ij}] =', e2.g,\n",
+    "       r'\\text{ is Euclidean:}~~', e2.g.is_positive_definite)\n",
+    "# Show multivectors `S1` and `N1` of `e2`:\n",
+    "gprint(r'\\quad \\text{multivectors}')\n",
+    "gprint(r'\\quad \\quad \\mathbf{S} =', S1)\n",
+    "gprint(r'\\quad \\quad \\mathbf{N} =', N1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "93020af9",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{model }\\textsf{e2_polar}\\textbf{ of }\\mathbb{E}^2:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{coordinates}~~ \\left[ r, \\  \\theta \\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{basis}~~ \\left(  \\boldsymbol{\\mathbf{b}}_{r}, \\   \\boldsymbol{\\mathbf{b}}_{\\theta }\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{metric tensor}~~ [g_{ij}] = \\left[\\begin{array}{cc}1 & 0\\\\0 & r^{2}\\end{array}\\right] \\text{ is Euclidean:}~~ \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{multivectors}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{S} = S  + S^{r} \\boldsymbol{\\mathbf{b}}_{r} + S^{\\theta } \\boldsymbol{\\mathbf{b}}_{\\theta } + S^{r\\theta } \\boldsymbol{\\mathbf{b}}_{r}\\wedge \\boldsymbol{\\mathbf{b}}_{\\theta } $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{N} = - 2 \\sqrt{6}  - 3 \\cos{\\left (\\theta  + \\frac{\\pi}{6} \\right )} \\boldsymbol{\\mathbf{b}}_{r} + \\frac{3 \\sin{\\left (\\theta  + \\frac{\\pi}{6} \\right )}}{r} \\boldsymbol{\\mathbf{b}}_{\\theta } - \\frac{4}{r} \\boldsymbol{\\mathbf{b}}_{r}\\wedge \\boldsymbol{\\mathbf{b}}_{\\theta } $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Model 2\n",
+    "# Create polar coordinates model `e2_polar` of Euclidean 2-space.  \n",
+    "r = symbols('r', positive=True)    # origin r=0 NOT included  \n",
+    "theta = symbols('theta', real=True)\n",
+    "p = (r*cos(theta), r*sin(theta))  \n",
+    "e2_polar = Ga('\\mathbf{b}', g=[1,r**2], coords=(r,theta))\n",
+    "br, btheta = e2_polar.mv()    # basis vectors NOT normalized\n",
+    "# Create symbolic mixed grade multivector `S2`.\n",
+    "# Create numeric mixed grade multivector `N2`.\n",
+    "S2 = e2_polar.mv('S', 'mv')\n",
+    "N2 = -sqrt(24) + ((3*sin(theta) - sqrt(27)*cos(theta))/2)*br \\\n",
+    "     + ((3*cos(theta) + sqrt(27)*sin(theta))/(2*r))*btheta \\\n",
+    "     - (4/r)*(br^btheta)\n",
+    "\n",
+    "# Show various attributes and functions of `e2_polar`:\n",
+    "gprint(r\"\\textbf{model }\\textsf{e2_polar}\\textbf{ of }\\mathbb{E}^2:\")\n",
+    "gprint(r'\\quad \\text{coordinates}~~', e2_polar.coords)\n",
+    "gprint(r'\\quad \\text{basis}~~', e2_polar.mv())\n",
+    "gprint(r'\\quad \\text{metric tensor}~~ [g_{ij}] =', e2_polar.g,\n",
+    "       r'\\text{ is Euclidean:}~~', e2_polar.g.is_positive_definite)\n",
+    "# Show multivectors `S2` and `N2` of `e2_polar`:\n",
+    "gprint(r'\\quad \\text{multivectors}')\n",
+    "gprint(r'\\quad \\quad \\mathbf{S} =', S2)\n",
+    "gprint(r'\\quad \\quad \\mathbf{N} =', N2)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "db8d0100",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{model }\\textsf{m2}\\textbf{ of }\\mathbb{M}^{1,1}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{coordinates}~~ \\left[ s, \\  t\\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{basis}~~ \\left(  \\boldsymbol{\\mathbf{e}}_{s}, \\   \\boldsymbol{\\mathbf{e}}_{t}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{metric tensor}~~ [g_{ij}] = \\left[\\begin{array}{cc}1 & 0\\\\0 & -1\\end{array}\\right] \\text{ is Euclidean:}~~ \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{multivectors}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{S} = S  + S^{s} \\boldsymbol{\\mathbf{e}}_{s} + S^{t} \\boldsymbol{\\mathbf{e}}_{t} + S^{st} \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{N} = - 2 \\sqrt{7}  + 3 \\boldsymbol{\\mathbf{e}}_{s} + 5 \\boldsymbol{\\mathbf{e}}_{t} - \\sqrt{37} \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{A} = 5  + \\left ( s^{2} + t^{2}\\right ) \\boldsymbol{\\mathbf{e}}_{s} + s^{2} \\boldsymbol{\\mathbf{e}}_{t} -5 \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Model 3  \n",
+    "# Create Cartesian coordinates model `m2` of Minkowski 2-space.  \n",
+    "s, t = symbols('s t', real=True)\n",
+    "m2  = Ga('\\mathbf{e}', g=[1,-1], coords=(s,t))\n",
+    "es, et = m2.mv()\n",
+    "# Create symbolic mixed grade multivector `S3`.\n",
+    "S3 = m2.mv('S', 'mv')\n",
+    "# Create numeric mixed grade multivector `N3`.\n",
+    "N3 = -sqrt(28) + 3*es + 5*et - sqrt(37)*(es^et)\n",
+    "# Create position-dependent multivector `A3` with a nonnegative\n",
+    "# normsquared.  `A3` will be used to test Case 3(a) in the execution\n",
+    "# of `norm`'s code.\n",
+    "A3 = 5 + (s**2 + t**2)*es + (s**2)*et - 5*(es^et) \n",
+    "\n",
+    "# Show various attributes and functions of `m2`:\n",
+    "gprint(r\"\\textbf{model }\\textsf{m2}\\textbf{ of }\\mathbb{M}^{1,1}:\")\n",
+    "gprint(r'\\quad \\text{coordinates}~~', m2.coords)\n",
+    "gprint(r'\\quad \\text{basis}~~', m2.mv())\n",
+    "gprint(r'\\quad \\text{metric tensor}~~ [g_{ij}] =', m2.g,\n",
+    "       r'\\text{ is Euclidean:}~~', m2.g.is_positive_definite)\n",
+    "# Show multivectors `S3`,`N3`, and `A3` of `m2`:\n",
+    "gprint(r'\\quad \\text{multivectors}')\n",
+    "gprint(r'\\quad \\quad \\mathbf{S} =', S3)\n",
+    "gprint(r'\\quad \\quad \\mathbf{N} =', N3)\n",
+    "gprint(r'\\quad \\quad \\mathbf{A} =', A3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "bb01bfee",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\textbf{model }\\textsf{m2_polar}\\textbf{ of }\\mathbb{M}^{1,1}:$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{coordinates}~~ \\left[ \\rho , \\  \\phi \\right] $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{basis}~~ \\left(  \\boldsymbol{\\mathbf{b}}_{\\rho }, \\   \\boldsymbol{\\mathbf{b}}_{\\phi }\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{metric tensor}~~ [g_{ij}] = \\left[\\begin{array}{cc}1 & 0\\\\0 & - \\rho ^{2}\\end{array}\\right] \\text{ is Euclidean:}~~ \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\text{multivectors}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{S} = S  + S^{\\rho } \\boldsymbol{\\mathbf{b}}_{\\rho } + S^{\\phi } \\boldsymbol{\\mathbf{b}}_{\\phi } + S^{\\rho \\phi } \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{N} = - 2 \\sqrt{7}  + \\left ( - e^{\\phi } + 4 e^{- \\phi }\\right ) \\boldsymbol{\\mathbf{b}}_{\\rho } + \\frac{e^{\\phi } + 4 e^{- \\phi }}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\phi } - \\frac{\\sqrt{37}}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\quad \\quad \\mathbf{B} = \\rho ^{2}  -16 \\boldsymbol{\\mathbf{b}}_{\\rho } + \\frac{16}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\phi } - \\sqrt{\\phi ^{2} + \\rho ^{2}} \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# Model 4  \n",
+    "# Create hyperbolic polar coordinates model `m2_polar` of\n",
+    "# Minkowski 2-space.\n",
+    "rho = symbols('rho', positive=True)  # origin rho=0 NOT included\n",
+    "phi = symbols('phi', real=True)\n",
+    "m2_polar = Ga('\\mathbf{b}', g=[1,-rho**2], coords=(rho, phi))\n",
+    "brho, bphi = m2_polar.mv()    # basis vectors NOT normalized\n",
+    "# Create symbolic mixed grade multivector `S4`.\n",
+    "S4 = m2_polar.mv('S', 'mv')\n",
+    "# Create numeric mixed grade multivector `N3`.\n",
+    "N4 = -sqrt(28) + (3*cosh(phi) - 5*sinh(phi))*brho \\\n",
+    "     + ((5*cosh(phi) - 3*sinh(phi))/rho)*bphi \\\n",
+    "     - (sqrt(37)/rho)*(brho^bphi)\n",
+    "# Create position-dependent multivector `B4` with a nonpositive\n",
+    "# normsquared.  `B4` will be used to test Case 3(b) in the execution\n",
+    "# of `norm`'s code.\n",
+    "B4 = rho**2 - 16*brho + (16/rho)*bphi - sqrt(rho**2 \\\n",
+    "     + phi**2)*(brho^bphi)\n",
+    "\n",
+    "# Show various attributes and functions of `m2_polar`:\n",
+    "gprint(r\"\\textbf{model }\\textsf{m2_polar}\\textbf{ of }\\mathbb{M}^{1,1}:\")\n",
+    "gprint(r'\\quad \\text{coordinates}~~', m2_polar.coords)\n",
+    "gprint(r'\\quad \\text{basis}~~', m2_polar.mv())\n",
+    "gprint(r'\\quad \\text{metric tensor}~~ [g_{ij}] =', m2_polar.g,\n",
+    "       r'\\text{ is Euclidean:}~~', m2_polar.g.is_positive_definite)\n",
+    "# Show multivectors `S4`,`N4`, and `B4` of `m2_polar`:\n",
+    "gprint(r'\\quad \\text{multivectors}')\n",
+    "gprint(r'\\quad \\quad \\mathbf{S} =', S4)\n",
+    "gprint(r'\\quad \\quad \\mathbf{N} =', N4)\n",
+    "gprint(r'\\quad \\quad \\mathbf{B} =', B4)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9f3876e9",
+   "metadata": {},
+   "source": [
+    "*Remark:*  The above reports as to whether the model's metric tensor `g` is Euclidean use the same test `g.is_positive_definite` as is used in Case 2 of `norm`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eeffef9c",
+   "metadata": {},
+   "source": [
+    "## 5. The test program for `norm2` and `norm`    \n",
+    "The next cell defines a program that displays the multivector `M` which is its argument, indicates the geometric algebra to which `M` belongs, shows the normsquared of `M`, and then shows for each of the three possible `hint` values the norm of `M`.\n",
+    "\n",
+    "Only multivectors defined in Section 4 (i.e. `S1`, `S2`, `S3`, `S4`, `N1`, `N2`, `N3`, `N4`, `A3`, and `B4`) are allowed values of `M`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "42cf5c27",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def test_with(M):\n",
+    "    \"\"\"\n",
+    "    Computes and displays the multivector `M`, its normsquared, \n",
+    "    and its norm as computed with each of the allowed hint values.\n",
+    "    Does the same for each grade part of `M`.\n",
+    "    \"\"\"\n",
+    "    ga_str = \\\n",
+    "        {e2:r'\\mathbb{G}(\\mathbb{E}^2)',\n",
+    "         e2_polar:r'\\mathbb{G}(\\mathbb{E}^2)',\n",
+    "         m2:r'\\mathbb{G}(\\mathbb{M}^{1,1})',\n",
+    "         m2_polar:r'\\mathbb{G}(\\mathbb{M}^{1,1})'}\n",
+    "    model_str = {e2:r'\\textsf{e2}', e2_polar:r'\\textsf{e2_polar}',\n",
+    "                 m2:r'\\testsf{m2}', m2_polar:r'\\textsf{m2_polar}'}\n",
+    "    M_str = {S1:r'\\mathbf{S}', S2:r'\\mathbf{S}',\n",
+    "             S3:r'\\mathbf{S}', S4:r'\\mathbf{S}',\n",
+    "             N1:r'\\mathbf{N}', N2:r'\\mathbf{N}',\n",
+    "             N3:r'\\mathbf{N}', N4:r'\\mathbf{N}',\n",
+    "             A3:r'\\mathbf{A}', B4:r'\\mathbf{B}'}\n",
+    "    # Display multivector `M`, its normsquared, and its norm:\n",
+    "    gprint(M_str[M] + r'=', M, r'\\in', ga_str[M.Ga])\n",
+    "    gprint(r'\\qquad \\implies \\Vert'+M_str[M]+r'\\Vert^2 =', \n",
+    "           M.norm2())\n",
+    "    gprint(r'\\qquad \\implies \\vert'+M_str[M]+r'\\vert =',\n",
+    "           M.norm('0'), r\"\\quad \\text{ when hint is '0'}\")\n",
+    "    gprint(r'\\qquad \\qquad\\; \\vert'+M_str[M]+r'\\vert =', \n",
+    "           M.norm('+'), r\"\\quad \\text{ when hint is '+'}\")\n",
+    "    gprint(r'\\qquad \\qquad\\; \\vert'+M_str[M]+r'\\vert =', \n",
+    "           M.norm('-'), r\"\\quad \\text{ when hint is '-'}\")\n",
+    "    # Display grade parts of multivector `M`, their normsquareds,\n",
+    "    # and their norms:\n",
+    "    for g in range(3):\n",
+    "        gprint(r'\\left<' +M_str[M]+r'\\right>_'+str(g)+r' =',\n",
+    "               M.grade(g), r'\\in ', ga_str[M.grade(g).Ga]) \n",
+    "        gprint(r'\\qquad \\implies \\Vert\\left<'+M_str[M]+r'\\right>_'\n",
+    "               +str(g)+r'\\Vert^2 =', M.grade(g).norm2())\n",
+    "        gprint(r'\\qquad \\implies \\vert\\left<'+M_str[M]+r'\\right>_'\n",
+    "               +str(g)+r'\\vert =', M.grade(g).norm('0'),\n",
+    "               r\"\\quad \\text{ when hint is '0'}\")\n",
+    "        gprint(r'\\qquad \\qquad\\; \\vert\\left<'+M_str[M]+r'\\right>_'\n",
+    "               +str(g)+r'\\vert =', M.grade(g).norm('+'),\n",
+    "               r\"\\quad \\text{ when hint is '+'}\")\n",
+    "        gprint(r'\\qquad \\qquad\\; \\vert\\left<'+M_str[M]+r'\\right>_'\n",
+    "               +str(g)+r'\\vert =', M.grade(g).norm('-'),\n",
+    "               r\"\\quad \\text{ when hint is '-'}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d1ddca69",
+   "metadata": {},
+   "source": [
+    "## 6.  Tests of `norm2` and `norm`"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "413f4854",
+   "metadata": {},
+   "source": [
+    "### 6.1 Testing within `e2`    \n",
+    "The next two In[ ] cells test `norm2` and `norm` with the multivectors `S1` and `N1` from `e2`.    \n",
+    "- Since `e2` has a Euclidean metric, the value of `S1.norm(hint)` should not and does not depend on the `hint` given.\n",
+    "- Since `N1` is a location-independent numeric multivector, the value of `N1.norm(hint)` should not and does not depend on the `hint` given.  Neither does it depend on location."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "id": "813ee65a",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{S}= S  + S^{x} \\boldsymbol{\\mathbf{e}}_{x} + S^{y} \\boldsymbol{\\mathbf{e}}_{y} + S^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} \\in\\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{S}\\Vert^2 = S^{2} + {\\left ( S^{x} \\right )}^{2} + {\\left ( S^{xy} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{x} \\right )}^{2} + {\\left ( S^{xy} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2}} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{x} \\right )}^{2} + {\\left ( S^{xy} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{x} \\right )}^{2} + {\\left ( S^{xy} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_0 = S \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_0\\Vert^2 = S^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_1 = S^{x} \\boldsymbol{\\mathbf{e}}_{x} + S^{y} \\boldsymbol{\\mathbf{e}}_{y} \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_1\\Vert^2 = {\\left ( S^{x} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{x} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2}} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{x} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{x} \\right )}^{2} + {\\left ( S^{y} \\right )}^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_2 = S^{xy} \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_2\\Vert^2 = {\\left ( S^{xy} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{xy} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{xy} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{xy} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(S1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "id": "c6748b4f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{N}= - 2 \\sqrt{6}  - \\frac{3 \\sqrt{3}}{2} \\boldsymbol{\\mathbf{e}}_{x} + \\frac{3}{2} \\boldsymbol{\\mathbf{e}}_{y} -4 \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} \\in\\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{N}\\Vert^2 = 49 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{N}\\vert = 7 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 7 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 7 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_0 = - 2 \\sqrt{6} \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_0\\Vert^2 = 24 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{6} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{6} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{6} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_1 = - \\frac{3 \\sqrt{3}}{2} \\boldsymbol{\\mathbf{e}}_{x} + \\frac{3}{2} \\boldsymbol{\\mathbf{e}}_{y} \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_1\\Vert^2 = 9 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_1\\vert = 3 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 3 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 3 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_2 = -4 \\boldsymbol{\\mathbf{e}}_{x}\\wedge \\boldsymbol{\\mathbf{e}}_{y} \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_2\\Vert^2 = 16 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_2\\vert = 4 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = 4 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = 4 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(N1)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a2422ab4",
+   "metadata": {},
+   "source": [
+    "### 6.2 Testing within `e2_polar`    \n",
+    "The next two In[ ] cells test `norm2` and `norm` with the multivectors `S2` and `N2` from `e2_polar`.    \n",
+    "- Since `e2_polar` has a Euclidean metric, the value of `S2.norm(hint)` should not and does not depend on the `hint` given.\n",
+    "- Since `N2` is a location-independent numeric multivector, the value of `N2.norm(hint)` should not and does not depend on the `hint` given.  Neither does it depend on location.\n",
+    "- Notice the results for `N2` should and do agree with those for `N1` above, which is as desired since `N1` and `N2` represent the same position-independent numeric multivector in $\\mathbb{G}(\\mathbb{E}^2)$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "id": "df49d984",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{S}= S  + S^{r} \\boldsymbol{\\mathbf{b}}_{r} + S^{\\theta } \\boldsymbol{\\mathbf{b}}_{\\theta } + S^{r\\theta } \\boldsymbol{\\mathbf{b}}_{r}\\wedge \\boldsymbol{\\mathbf{b}}_{\\theta } \\in\\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{S}\\Vert^2 = S^{2} + {\\left ( S^{r} \\right )}^{2} + {\\left ( S^{r\\theta } \\right )}^{2} r^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{r} \\right )}^{2} + {\\left ( S^{r\\theta } \\right )}^{2} r^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2}} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{r} \\right )}^{2} + {\\left ( S^{r\\theta } \\right )}^{2} r^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{r} \\right )}^{2} + {\\left ( S^{r\\theta } \\right )}^{2} r^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_0 = S \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_0\\Vert^2 = S^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_1 = S^{r} \\boldsymbol{\\mathbf{b}}_{r} + S^{\\theta } \\boldsymbol{\\mathbf{b}}_{\\theta } \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_1\\Vert^2 = {\\left ( S^{r} \\right )}^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{r} \\right )}^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2}} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{r} \\right )}^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{r} \\right )}^{2} + {\\left ( S^{\\theta } \\right )}^{2} r^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_2 = S^{r\\theta } \\boldsymbol{\\mathbf{b}}_{r}\\wedge \\boldsymbol{\\mathbf{b}}_{\\theta } \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_2\\Vert^2 = {\\left ( S^{r\\theta } \\right )}^{2} r^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{r\\theta } r \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{r\\theta } r \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{r\\theta } r \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(S2)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a051af1b",
+   "metadata": {},
+   "source": [
+    "*Remark:* SymPy was able to take a factor of $r^2$ out from under the radical as $r$ rather than $\\vert r \\vert$ because `r` was instantiated with condition `positive=True`.  SymPy does not know whether basis blade coefficients $S$ and $S^{r\\theta}$ are nonnegative, so it takes their squares out from under the radical as $\\vert S \\vert$ and $\\vert S^{r\\theta} \\vert$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "id": "71b635d8",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{N}= - 2 \\sqrt{6}  - 3 \\cos{\\left (\\theta  + \\frac{\\pi}{6} \\right )} \\boldsymbol{\\mathbf{b}}_{r} + \\frac{3 \\sin{\\left (\\theta  + \\frac{\\pi}{6} \\right )}}{r} \\boldsymbol{\\mathbf{b}}_{\\theta } - \\frac{4}{r} \\boldsymbol{\\mathbf{b}}_{r}\\wedge \\boldsymbol{\\mathbf{b}}_{\\theta } \\in\\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{N}\\Vert^2 = 49 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{N}\\vert = 7 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 7 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 7 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_0 = - 2 \\sqrt{6} \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_0\\Vert^2 = 24 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{6} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{6} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{6} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_1 = - 3 \\cos{\\left (\\theta  + \\frac{\\pi}{6} \\right )} \\boldsymbol{\\mathbf{b}}_{r} + \\frac{3 \\sin{\\left (\\theta  + \\frac{\\pi}{6} \\right )}}{r} \\boldsymbol{\\mathbf{b}}_{\\theta } \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_1\\Vert^2 = 9 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_1\\vert = 3 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 3 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 3 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_2 = - \\frac{4}{r} \\boldsymbol{\\mathbf{b}}_{r}\\wedge \\boldsymbol{\\mathbf{b}}_{\\theta } \\in \\mathbb{G}(\\mathbb{E}^2)$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_2\\Vert^2 = 16 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_2\\vert = 4 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = 4 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = 4 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(N2)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ee245858",
+   "metadata": {},
+   "source": [
+    "### 6.3 Testing within `m2`    \n",
+    "The next three In[ ] cells test `norm2` and `norm` using the `m2` multivectors `S3`, `N3`, and `A3.    \n",
+    "- Since `m2` has a non-Euclidean metric, the value of `S3.norm(hint)` should be expected to and does depend on the `hint` given.\n",
+    "- Since `N3` is a location-independent numeric multivector, the value of `N3.norm(hint)` should not and does not depend on the `hint` given.  Neither does it depend on location.\n",
+    "- `A3` has a nonnegative normsquared, discernible as such by SymPy, so it should and does trigger Case 3(a) of `norm`'s code, leading to a `A3.norm(hint)` which is `hint` independent."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "id": "7006e40f",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{S}= S  + S^{s} \\boldsymbol{\\mathbf{e}}_{s} + S^{t} \\boldsymbol{\\mathbf{e}}_{t} + S^{st} \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} \\in\\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{S}\\Vert^2 = \\left|{S^{2} + {\\left ( S^{s} \\right )}^{2} - {\\left ( S^{st} \\right )}^{2} - {\\left ( S^{t} \\right )}^{2}}\\right| $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{S}\\vert = \\sqrt{\\left|{S^{2} + {\\left ( S^{s} \\right )}^{2} - {\\left ( S^{st} \\right )}^{2} - {\\left ( S^{t} \\right )}^{2}}\\right|} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} + {\\left ( S^{s} \\right )}^{2} - {\\left ( S^{st} \\right )}^{2} - {\\left ( S^{t} \\right )}^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{- S^{2} - {\\left ( S^{s} \\right )}^{2} + {\\left ( S^{st} \\right )}^{2} + {\\left ( S^{t} \\right )}^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_0 = S \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_0\\Vert^2 = S^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_1 = S^{s} \\boldsymbol{\\mathbf{e}}_{s} + S^{t} \\boldsymbol{\\mathbf{e}}_{t} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_1\\Vert^2 = \\left|{{\\left ( S^{s} \\right )}^{2} - {\\left ( S^{t} \\right )}^{2}}\\right| $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{\\left|{{\\left ( S^{s} \\right )}^{2} - {\\left ( S^{t} \\right )}^{2}}\\right|} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{s} \\right )}^{2} - {\\left ( S^{t} \\right )}^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{- {\\left ( S^{s} \\right )}^{2} + {\\left ( S^{t} \\right )}^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_2 = S^{st} \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_2\\Vert^2 = {\\left ( S^{st} \\right )}^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{st} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{st} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{st} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(S3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "id": "9b32e234",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{N}= - 2 \\sqrt{7}  + 3 \\boldsymbol{\\mathbf{e}}_{s} + 5 \\boldsymbol{\\mathbf{e}}_{t} - \\sqrt{37} \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} \\in\\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{N}\\Vert^2 = 25 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{N}\\vert = 5 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 5 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 5 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_0 = - 2 \\sqrt{7} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_0\\Vert^2 = 28 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{7} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{7} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{7} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_1 = 3 \\boldsymbol{\\mathbf{e}}_{s} + 5 \\boldsymbol{\\mathbf{e}}_{t} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_1\\Vert^2 = 16 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_1\\vert = 4 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 4 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 4 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_2 = - \\sqrt{37} \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_2\\Vert^2 = 37 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_2\\vert = \\sqrt{37} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = \\sqrt{37} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = \\sqrt{37} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(N3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "id": "3862bdb9",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{A}= 5  + \\left ( s^{2} + t^{2}\\right ) \\boldsymbol{\\mathbf{e}}_{s} + s^{2} \\boldsymbol{\\mathbf{e}}_{t} -5 \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} \\in\\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{A}\\Vert^2 = t^{2} \\cdot \\left(2 s^{2} + t^{2}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{A}\\vert = \\sqrt{2 s^{2} + t^{2}} \\left|{t}\\right| \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{A}\\vert = \\sqrt{2 s^{2} + t^{2}} \\left|{t}\\right| \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{A}\\vert = \\sqrt{2 s^{2} + t^{2}} \\left|{t}\\right| \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{A}\\right>_0 = 5 \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{A}\\right>_0\\Vert^2 = 25 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{A}\\right>_0\\vert = 5 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{A}\\right>_0\\vert = 5 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{A}\\right>_0\\vert = 5 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{A}\\right>_1 = \\left ( s^{2} + t^{2}\\right ) \\boldsymbol{\\mathbf{e}}_{s} + s^{2} \\boldsymbol{\\mathbf{e}}_{t} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{A}\\right>_1\\Vert^2 = t^{2} \\cdot \\left(2 s^{2} + t^{2}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{A}\\right>_1\\vert = \\sqrt{2 s^{2} + t^{2}} \\left|{t}\\right| \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{A}\\right>_1\\vert = \\sqrt{2 s^{2} + t^{2}} \\left|{t}\\right| \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{A}\\right>_1\\vert = \\sqrt{2 s^{2} + t^{2}} \\left|{t}\\right| \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{A}\\right>_2 = -5 \\boldsymbol{\\mathbf{e}}_{s}\\wedge \\boldsymbol{\\mathbf{e}}_{t} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{A}\\right>_2\\Vert^2 = 25 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{A}\\right>_2\\vert = 5 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{A}\\right>_2\\vert = 5 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{A}\\right>_2\\vert = 5 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{A3.norm2() >= 0}: \\quad \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(A3.norm2() >= 0) == 0}: \\quad \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{A3.norm2() <= 0}: \\quad t^{2} \\cdot \\left(2 s^{2} + t^{2}\\right) \\leq 0 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(A3.norm2() <= 0) == 0}: \\quad \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{A3.norm2() < 0}: \\quad \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(A3.norm2() < 0) == 0}: \\quad \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(A3)\n",
+    "# Is `normsquared` nonnegative?  Show values of relational and \n",
+    "# conditional in Case 3(a) of `norm`'s code:\n",
+    "gprint(r'\\text{A3.norm2() >= 0}: \\quad', A3.norm2() >= 0)\n",
+    "gprint(r'\\text{(A3.norm2() >= 0) == 0}: \\quad',\n",
+    "       (A3.norm2() >= 0) == True)\n",
+    "# Is `normsquared` nonpositive?  Show values of relational and \n",
+    "# conditional in Case 3(b):\n",
+    "gprint(r'\\text{A3.norm2() <= 0}: \\quad', A3.norm2() <= 0)\n",
+    "gprint(r'\\text{(A3.norm2() <= 0) == 0}: \\quad',\n",
+    "       (A3.norm2() <= 0) == True)\n",
+    "# Is `normsquared` negative?  Show values of relational and \n",
+    "# conditional complementary to Case 3(a):\n",
+    "gprint(r'\\text{A3.norm2() < 0}: \\quad', A3.norm2() < 0)\n",
+    "gprint(r'\\text{(A3.norm2() < 0) == 0}: \\quad',\n",
+    "       (A3.norm2() < 0) == True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "183a219a",
+   "metadata": {},
+   "source": [
+    "So as to provide a test of Case 3(a) in the execution of `norm`, the multivector `A3` from `m2` was designed to have a nonnegative `normsquared`.  (The various cases are described in the notebook **Code for undualization, grade involution, Clifford conjugation, scalar product, normsquared, and norm**.) The relational `normsquared >= 0` within Case 3(a)'s conditional evaluates to `True`; hence the conditional itself, `(normsquared >= 0) == True`, also evaluates to `True`.  The consequent of the `if` statement then executes, returning the `hint`-independent expression `\\sqrt(+normsquared)`.\n",
+    "\n",
+    "As an experiment as to how SymPy would handle other relationals, the above cell also showed how SymPy would evaluate the nonpositivity relational `normsquared <= 0` of Case 3(b)'s conditional.  SymPy wasn't able to assign a `True` or `False` value to `normsquared <= 0`, possibly because some of the possible values of `normsquared` are positive.\n",
+    "\n",
+    "Not unexpectedly, the complementary relational `normsquared < 0` (negativity) to Case 3(a)'s `normsquared >= 0` relational returns `False` when the 3(a) relational (nonnegativity) returns `True`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c475ccd2",
+   "metadata": {},
+   "source": [
+    "### 6.4 Testing within `m2_polar`    \n",
+    "The next three In[ ] cells test `norm2` and `norm` with the multivectors `S4`, `N4`, and `B4` from `m2_polar`.    \n",
+    "- Since `m2_polar` has a non-Euclidean metric, the value of `S4.norm(hint)` should be expected to and does depend on the `hint` given.\n",
+    "- Since `N4` is a location-independent numeric multivector, the value of `N43.norm(hint)` should not and does not depend on the `hint` given.  Neither does it depend on location.\n",
+    "- Notice the results for `N4` should and do agree with those for `N3` above, which is as desired since `N3` and `N4` represent the same position-independent numeric multivector in $\\mathbb{G}(\\mathbb{M}^{1,1})$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "id": "cf7afd38",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{S}= S  + S^{\\rho } \\boldsymbol{\\mathbf{b}}_{\\rho } + S^{\\phi } \\boldsymbol{\\mathbf{b}}_{\\phi } + S^{\\rho \\phi } \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } \\in\\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{S}\\Vert^2 = \\left|{S^{2} - {\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} + {\\left ( S^{\\rho } \\right )}^{2} - {\\left ( S^{\\rho \\phi } \\right )}^{2} \\rho ^{2}}\\right| $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{S}\\vert = \\sqrt{\\left|{S^{2} - {\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} + {\\left ( S^{\\rho } \\right )}^{2} - {\\left ( S^{\\rho \\phi } \\right )}^{2} \\rho ^{2}}\\right|} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{S^{2} - {\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} + {\\left ( S^{\\rho } \\right )}^{2} - {\\left ( S^{\\rho \\phi } \\right )}^{2} \\rho ^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{S}\\vert = \\sqrt{- S^{2} + {\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} - {\\left ( S^{\\rho } \\right )}^{2} + {\\left ( S^{\\rho \\phi } \\right )}^{2} \\rho ^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_0 = S \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_0\\Vert^2 = S^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_0\\vert = S \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_1 = S^{\\rho } \\boldsymbol{\\mathbf{b}}_{\\rho } + S^{\\phi } \\boldsymbol{\\mathbf{b}}_{\\phi } \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_1\\Vert^2 = \\left|{{\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} - {\\left ( S^{\\rho } \\right )}^{2}}\\right| $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{\\left|{{\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} - {\\left ( S^{\\rho } \\right )}^{2}}\\right|} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{- {\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} + {\\left ( S^{\\rho } \\right )}^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_1\\vert = \\sqrt{{\\left ( S^{\\phi } \\right )}^{2} \\rho ^{2} - {\\left ( S^{\\rho } \\right )}^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{S}\\right>_2 = S^{\\rho \\phi } \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{S}\\right>_2\\Vert^2 = {\\left ( S^{\\rho \\phi } \\right )}^{2} \\rho ^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{\\rho \\phi } \\rho  \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{\\rho \\phi } \\rho  \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{S}\\right>_2\\vert = S^{\\rho \\phi } \\rho  \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(S4)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a7b3b632",
+   "metadata": {},
+   "source": [
+    "*Remark:* SymPy was able to take a factor of $\\rho^2$ out from under the radical as $\\rho$ rather than $\\vert \\rho \\vert$ because `rho` was instantiated with condition `positive=True`.  SymPy does not know whether basis blade coefficients $S$ and $S^{\\rho\\phi}$ are nonnegative, so it takes their squares out from under the radical as $\\vert S \\vert$ and $\\vert S^{\\rho\\phi} \\vert$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "id": "9374e530",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{N}= - 2 \\sqrt{7}  + \\left ( - e^{\\phi } + 4 e^{- \\phi }\\right ) \\boldsymbol{\\mathbf{b}}_{\\rho } + \\frac{e^{\\phi } + 4 e^{- \\phi }}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\phi } - \\frac{\\sqrt{37}}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } \\in\\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{N}\\Vert^2 = 25 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{N}\\vert = 5 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 5 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{N}\\vert = 5 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_0 = - 2 \\sqrt{7} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_0\\Vert^2 = 28 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{7} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{7} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_0\\vert = 2 \\sqrt{7} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_1 = \\left ( - e^{\\phi } + 4 e^{- \\phi }\\right ) \\boldsymbol{\\mathbf{b}}_{\\rho } + \\frac{e^{\\phi } + 4 e^{- \\phi }}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\phi } \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_1\\Vert^2 = 16 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_1\\vert = 4 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 4 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_1\\vert = 4 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{N}\\right>_2 = - \\frac{\\sqrt{37}}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{N}\\right>_2\\Vert^2 = 37 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{N}\\right>_2\\vert = \\sqrt{37} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = \\sqrt{37} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{N}\\right>_2\\vert = \\sqrt{37} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(N4)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "id": "43bb53cd",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\mathbf{B}= \\rho ^{2}  -16 \\boldsymbol{\\mathbf{b}}_{\\rho } + \\frac{16}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\phi } - \\sqrt{\\phi ^{2} + \\rho ^{2}} \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } \\in\\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\mathbf{B}\\Vert^2 = \\phi ^{2} \\rho ^{2} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\mathbf{B}\\vert = \\phi  \\rho  \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{B}\\vert = \\phi  \\rho  \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\mathbf{B}\\vert = \\phi  \\rho  \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{B}\\right>_0 = \\rho ^{2} \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{B}\\right>_0\\Vert^2 = \\rho ^{4} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{B}\\right>_0\\vert = \\rho ^{2} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{B}\\right>_0\\vert = \\rho ^{2} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{B}\\right>_0\\vert = \\rho ^{2} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{B}\\right>_1 = -16 \\boldsymbol{\\mathbf{b}}_{\\rho } + \\frac{16}{\\rho } \\boldsymbol{\\mathbf{b}}_{\\phi } \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{B}\\right>_1\\Vert^2 = 0 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{B}\\right>_1\\vert = 0 \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{B}\\right>_1\\vert = 0 \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{B}\\right>_1\\vert = 0 \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\left<\\mathbf{B}\\right>_2 = - \\sqrt{\\phi ^{2} + \\rho ^{2}} \\boldsymbol{\\mathbf{b}}_{\\rho }\\wedge \\boldsymbol{\\mathbf{b}}_{\\phi } \\in \\mathbb{G}(\\mathbb{M}^{1,1})$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\Vert\\left<\\mathbf{B}\\right>_2\\Vert^2 = - \\rho ^{2} \\left(- \\phi ^{2} - \\rho ^{2}\\right) $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\implies \\vert\\left<\\mathbf{B}\\right>_2\\vert = \\rho  \\sqrt{\\phi ^{2} + \\rho ^{2}} \\quad \\text{ when hint is '0'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{B}\\right>_2\\vert = \\rho  \\sqrt{\\phi ^{2} + \\rho ^{2}} \\quad \\text{ when hint is '+'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\qquad \\qquad\\; \\vert\\left<\\mathbf{B}\\right>_2\\vert = \\rho  \\sqrt{\\phi ^{2} + \\rho ^{2}} \\quad \\text{ when hint is '-'}$"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{B4.norm2() >= 0}: \\quad \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(B4.norm2() >= 0) == 0}: \\quad \\text{True} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{B4.norm2() <= 0}: \\quad \\phi ^{2} \\rho ^{2} \\leq 0 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(B4.norm2() <= 0) == 0}: \\quad \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{B4.norm2() > 0}: \\quad \\phi ^{2} \\rho ^{2} > 0 $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/latex": [
+       "$\\displaystyle \\text{(B4.norm2() > 0) == 0}: \\quad \\text{False} $"
+      ],
+      "text/plain": [
+       "<IPython.core.display.Math object>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "test_with(B4)\n",
+    "# Is `normsquared` nonnegative?  Show values of relational and \n",
+    "# conditional in Case 3(a) of `norm`'s code:\n",
+    "gprint(r'\\text{B4.norm2() >= 0}: \\quad', B4.norm2() >= 0)\n",
+    "gprint(r'\\text{(B4.norm2() >= 0) == 0}: \\quad',\n",
+    "       (B4.norm2() >= 0) == True)\n",
+    "# Is `normsquared` nonpositive?  Show values of relational and \n",
+    "# conditional in Case 3(b):\n",
+    "gprint(r'\\text{B4.norm2() <= 0}: \\quad', B4.norm2() <= 0)\n",
+    "gprint(r'\\text{(B4.norm2() <= 0) == 0}: \\quad',\n",
+    "       (B4.norm2() <= 0) == True)\n",
+    "# Is `normsquared` positive?  Show values of relational and \n",
+    "# conditional complementary to Case 3(b):\n",
+    "gprint(r'\\text{B4.norm2() > 0}: \\quad', B4.norm2() > 0)\n",
+    "gprint(r'\\text{(B4.norm2() > 0) == 0}: \\quad',\n",
+    "       (B4.norm2() > 0) == True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f18c50b4",
+   "metadata": {},
+   "source": [
+    "So as to provide a test of Case 3(b) in the execution of `norm`, the multivector `B4` from `m2_polar` was designed to have a nonpositive `normsquared`.  (The various cases are described in the notebook **Code for undualization, grade involution, Clifford conjugation, scalar product, normsquared, and norm**.) The relational `normsquared <= 0` within Case 3(b)'s conditional evaluates to `True`; hence the conditional itself, `(normsquared <= 0) == True`, also evaluates to `True`.  The consequent of the `if` statement then executes, returning the `hint`-independent expression `\\sqrt(-normsquared)`.\n",
+    "\n",
+    "As an experiment as to how SymPy would handle other relationals, the above cell also showed how SymPy would evaluate the nonnegativity relational `normsquared >= 0` of Case 3(a)'s conditional.  SymPy wasn't able to assign a `True` or `False` value to `normsquared >= 0`, possibly because some of the possible values of `normsquared` are negative.\n",
+    "\n",
+    "Not unexpectedly, the complementary relational `normsquared > 0` (positivity) to Case 3(b)'s `normsquared <= 0` relational returns `False` when the 3(b) relational (nonnegativity) returns `True`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "24afcbaa",
+   "metadata": {},
+   "source": [
+    "### 6.5. Conclusions from testing the recoded old operations\n",
+    "\n",
+    "The output from each of the executable In[ ] cells of subsections 6.1, 6.2, 6.3, and 6.4 was precisely as was desired and expected.  I am confident of the implementation of `norm2`.  But while the tests of `norm` gave correct results, I still suspect weaknesses in that method, weaknesses that were explicated at the end of Section 6 of the notebook **Code for undualization, grade involution, Clifford conjugation, scalar product, normsquared, and norm**.  Further testing, with more complex geometric algebras and multivectors therefrom, is needed."
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.18"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/test/.nbval_sanitize.cfg b/test/.nbval_sanitize.cfg
index 0bf1eee4..d4c3f945 100644
--- a/test/.nbval_sanitize.cfg
+++ b/test/.nbval_sanitize.cfg
@@ -45,3 +45,14 @@ replace: \\mathbf{\1}_{\2}
 # sympy master shows trailing commas on 1-tuples. This replacement is easier to do in reverse.
 regex: ,\\right\)
 replace: \\right)
+
+# ignore versions
+# \text{Python }3.8.5
+regex: \{Python \}(\d+\.\d+(\.\d+)?)
+replace: {Python }
+# \text{SymPy }1.8.dev
+regex: \{SymPy \}(\d+\.\d+(\.\d+)?(\.dev)?)
+replace: {SymPy }
+# \text{GAlgebra }0.5.0
+regex: \{GAlgebra \}(\d+\.\d+(\.\d+)?(-dev)?)
+replace: {GAlgebra }
diff --git a/test/fixtures/test_gprinter.pdf b/test/fixtures/test_gprinter.pdf
new file mode 100644
index 00000000..203f96ae
Binary files /dev/null and b/test/fixtures/test_gprinter.pdf differ
diff --git a/test/fixtures/test_lt_pdf.pdf b/test/fixtures/test_lt_pdf.pdf
new file mode 100644
index 00000000..0b4d011d
Binary files /dev/null and b/test/fixtures/test_lt_pdf.pdf differ
diff --git a/test/test_gprinter.py b/test/test_gprinter.py
new file mode 100644
index 00000000..73c9b69f
--- /dev/null
+++ b/test/test_gprinter.py
@@ -0,0 +1,188 @@
+from __future__ import annotations
+
+import os
+import unittest
+import pytest
+import subprocess
+from pathlib import Path
+
+# Make SymPy available to this program:
+import sympy 
+from sympy import symbols, simplify, sin, cos
+
+# Make GAlgebra available to this program:
+from galgebra.ga import Ga  
+# from galgebra.mv import *
+from galgebra.printer import Fmt, GaPrinter, Format
+# Fmt:       sets the way that a multivector's basis expansion is output.
+# GaPrinter: makes GA output a little more readable.
+# Format:    turns on latex gprinter.
+from galgebra.gprinter import gFormat, gprint, gxpdf, gPrint_Function
+
+ROOT = Path(__file__).parent.parent
+DIR_TEST = ROOT / 'test'
+DIR_GENERATED = DIR_TEST / 'generated'
+DIR_FIXTURES = DIR_TEST / 'fixtures'
+DIR_DIFF = DIR_TEST / 'diff'
+
+
+def lin_tran():
+    gFormat()
+    gPrint_Function()
+    (x, y, z) = xyz = symbols('x,y,z',real=True)
+    (o3d, ex, ey, ez) = Ga.build('e_x e_y e_z', g=[1, 1, 1], coords=xyz)
+    grad = o3d.grad
+    (u, v) = uv = symbols('u,v',real=True)
+    (g2d, eu, ev) = Ga.build('e_u e_v', coords=uv)
+    grad_uv = g2d.grad
+    v_xyz = o3d.mv('v','vector')
+    A_xyz = o3d.mv('A','vector',f=True)
+    A_uv = g2d.mv('A','vector',f=True)
+    # gprint(r'\text{3d orthogonal ($A$ is vector function)}')
+    gprint('A =', A_xyz)
+    gprint('A^{2} =', A_xyz * A_xyz)
+    gprint(r'\nabla \cdot A =', grad | A_xyz)
+    gprint(r'\nabla A =', grad * A_xyz)
+    gprint('v|(grad*A) =',v_xyz|(grad*A_xyz))
+    gprint(r'\text{2d general ($A$ is vector function)}')
+    gprint('A =', A_uv)
+    gprint('A^{2} =', A_uv * A_uv)
+    gprint(r'\nabla \cdot A =', grad_uv | A_uv)
+    gprint(r'\nabla A =', grad_uv * A_uv)
+    A = o3d.lt('A')
+    gprint(r'\text{3d orthogonal ($A$, $B$ are linear transformations)}')
+    gprint('A =', A)
+    gprint(r'\f{mat}{A} =', A.matrix())
+    gprint('\\f{\\det}{A} =', A.det())
+    gprint('\\overline{A} =', A.adj())
+    gprint('\\f{\\Tr}{A} =', A.tr())
+    gprint('\\f{A}{e_x \\wedge e_y} =', A(ex^ey))
+    gprint('\\f{A}{e_x} \\wedge \\f{A}{e_y} =', A(ex)^A(ey))
+    B = o3d.lt('B')
+    gprint('g =', o3d.g)
+    gprint('g^{-1} =', o3d.g_inv)
+
+    gprint('A + B =', A + B)
+    gprint('AB =', A * B)
+    gprint('A - B =', A - B)
+    gprint('General Symmetric Linear Transformation')
+    Asym = o3d.lt('A',mode='s')
+    gprint('A =', Asym)
+    gprint('General Antisymmetric Linear Transformation')
+    Aasym = o3d.lt('A',mode='a')
+    gprint('A =', Aasym)
+    gprint(r'\text{2d general ($A,\\;B$ are linear transformations)}')
+    A2d = g2d.lt('A')
+    gprint('g =', g2d.g)
+    gprint('g^{-1} =', g2d.g_inv)
+    gprint('gg^{-1} =', simplify(g2d.g * g2d.g_inv))
+    gprint('A =', A2d)
+    gprint(r'\f{mat}{A} =', A2d.matrix())
+    gprint('\\f{\\det}{A} =', A2d.det())
+    A2d_adj = A2d.adj()
+    gprint('\\overline{A} =', A2d_adj)
+    gprint('\\f{mat}{\\overline{A}} =', simplify(A2d_adj.matrix()))
+    gprint('\\f{\\Tr}{A} =', A2d.tr())
+    gprint('\\f{A}{e_u \\wedge e_v} =', A2d(eu^ev))
+    gprint('\\f{A}{e_u} \\wedge \\f{A}{e_v} =', A2d(eu)^A2d(ev))
+    B2d = g2d.lt('B')
+
+    gprint('B =', B2d)
+    gprint('A + B =', A2d + B2d)
+    gprint('A - B =', A2d - B2d)
+    gprint('AB =', A2d * B2d)
+    a = g2d.mv('a','vector')
+    b = g2d.mv('b','vector')
+    gprint(r'a|\f{\overline{A}}{b}-b|\f{\underline{A}}{a} =',((a|A2d.adj()(b))-(b|A2d(a))).simplify())
+    m4d = Ga('e_t e_x e_y e_z', g=[1, -1, -1, -1],coords=symbols('t,x,y,z',real=True))
+    T = m4d.lt('T')
+    gprint('g =', m4d.g)
+    gprint(r'\underline{T} =',T)
+    gprint(r'\overline{T} =',T.adj())
+    gprint(r'\f{\det}{\underline{T}} =',T.det())
+    gprint(r'\f{\mbox{tr}}{\underline{T}} =',T.tr())
+    a = m4d.mv('a','vector')
+    b = m4d.mv('b','vector')
+    gprint(r'a|\f{\overline{T}}{b}-b|\f{\underline{T}}{a} =',((a|T.adj()(b))-(b|T(a))).simplify())
+    coords = (r, th, phi) = symbols('r,theta,phi', real=True)
+    (sp3d, er, eth, ephi) = Ga.build('e_r e_th e_ph', g=[1, r**2, r**2*sin(th)**2], coords=coords)
+    grad = sp3d.grad
+    sm_coords = (u, v) = symbols('u,v', real=True)
+    smap = [1, u, v]  # Coordinate map for sphere of r = 1
+    sph2d = sp3d.sm(smap,sm_coords,norm=True)
+    (eu, ev) = sph2d.mv()
+    grad_uv = sph2d.grad
+    F = sph2d.mv('F','vector',f=True)
+    f = sph2d.mv('f','scalar',f=True)
+    gprint('f =',f)
+    gprint(r'\nabla f =',grad_uv * f)
+    gprint('F =',F)
+    gprint(r'\nabla F =',grad_uv * F)
+    tp = (th,phi) = symbols('theta,phi',real=True)
+    smap = [sin(th)*cos(phi),sin(th)*sin(phi),cos(th)]
+    sph2dr = o3d.sm(smap,tp,norm=True)
+    (eth, ephi) = sph2dr.mv()
+    grad_tp = sph2dr.grad
+    F = sph2dr.mv('F','vector',f=True)
+    f = sph2dr.mv('f','scalar',f=True)
+    gprint('f =',f)
+    gprint(r'\nabla f =',grad_tp * f)
+    gprint('F =',F)
+    gprint(r'\nabla F =',grad_tp * F)
+    return
+
+
+class TestGprinter(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(DIR_GENERATED, exist_ok=True)
+        os.makedirs(DIR_DIFF, exist_ok=True)
+        os.chdir(DIR_GENERATED)
+
+    def gen_pdf(self, name, documentclass):
+        gxpdf('%s.tex' % name, pdfprog='tectonic', rm=False, null=False, evince=False, documentclass=documentclass)
+
+    def check_pdf(self, name, expected_retcode=0):
+        retcode = subprocess.call([
+            'diff-pdf',
+            '--output-diff=%s' % (DIR_DIFF / ('%s-diff.pdf' % name)),
+            DIR_FIXTURES / ('%s.pdf' % name),
+            DIR_GENERATED / ('%s.pdf' % name)
+        ])
+        assert retcode == expected_retcode
+
+    @pytest.mark.skipif("TEST_GXPDF" not in os.environ, reason="Only run if TEST_GXPDF is set")
+    def test_gxpdf(self):
+        gFormat()
+        # Set up standard G^3 geometric algebra
+        g3coords = (x, y, z) = symbols('x y z', real=True)  # Without real=True, symbols are complex
+        g3 = Ga(r'\mathbf{e}', g=[1, 1, 1], coords=g3coords)
+        (ex, ey, ez) = g3.mv()      # Program names of basis vectors.
+        (exr, eyr, ezr) = g3.mvr()  # Program names of reciprocal basis vectors.
+
+        B = g3.mv('B', 'bivector')
+        Fmt(1)  # Set Fmt globally
+        gprint(r'\mathbf{B} =', B)         # B will be bold.
+        gprint(r'\mathbf{B} =', B.Fmt(3))  # Fmt(3) here only.
+        gprint(r'\mathbf{B} =', B)         # Global Fmt remembered.
+
+        gprint(r'\mathbf{B}^2 =', B*B)
+
+        M = g3.mv('M', 'mv')
+        gprint(r'\langle \mathbf{M} \rangle_2 =', M.grade(2)) 
+        # grade(2) could be replaced by, e.g., odd(), or omitted altogether.
+
+        gprint(r'\alpha_1\mathbf{X}/\gamma_r^3')
+
+        grad = g3.grad
+
+        gprint(r'{\nabla} = ', grad)
+
+        self.gen_pdf('test_gprinter', documentclass='book')
+        self.check_pdf('test_gprinter')
+
+    # This test is a POC, and should skip for now
+    @pytest.mark.skip
+    def test_lt_pdf(self):
+        lin_tran()
+        self.gen_pdf('test_lt_pdf', documentclass='report')
+        self.check_pdf('test_lt_pdf', expected_retcode=1)
\ No newline at end of file
diff --git a/test/test_mv.py b/test/test_mv.py
index 002dad49..4dbd3cee 100644
--- a/test/test_mv.py
+++ b/test/test_mv.py
@@ -2,7 +2,10 @@
 
 import pytest
 import sympy
+from sympy import symbols
 from galgebra.ga import Ga
+from galgebra.mv import proj, undual, g_invol, exp, norm, norm2, mag, mag2, ccon, rev, scalar, qform, sp
+
 
 class TestMv:
 
@@ -52,14 +55,13 @@ def test_get_coefs(self):
         with pytest.raises(ValueError):
             (e_1 ^ e_2).get_coefs(3)
 
-
     def test_blade_coefs(self):
         """
         Various tests on several multivectors.
         """
         _g3d, e_1, e_2, e_3 = Ga.build('e*1|2|3')
 
-        m0 =  2 * e_1 + e_2 - e_3 + 3 * (e_1 ^ e_3) + (e_1 ^ e_3) + (e_2 ^ (3 * e_3))
+        m0 = 2 * e_1 + e_2 - e_3 + 3 * (e_1 ^ e_3) + (e_1 ^ e_3) + (e_2 ^ (3 * e_3))
         assert m0.blade_coefs([e_1]) == [2]
         assert m0.blade_coefs([e_2]) == [1]
         assert m0.blade_coefs([e_1, e_2]) == [2, 1]
@@ -93,7 +95,7 @@ def test_rep_switching(self):
         # this ga has a non-diagonal metric
         _g3d, e_1, e_2, e_3 = Ga.build('e*1|2|3')
 
-        m0 =  2 * e_1 + e_2 - e_3 + 3 * (e_1 ^ e_3) + (e_1 ^ e_3) + (e_2 ^ (3 * e_3))
+        m0 = 2 * e_1 + e_2 - e_3 + 3 * (e_1 ^ e_3) + (e_1 ^ e_3) + (e_2 ^ (3 * e_3))
         m1 = (-4*(e_1 | e_3)-3*(e_2 | e_3))+2*e_1+e_2-e_3+4*e_1*e_3+3*e_2*e_3
         # m1 was chosen to make this true
         assert m0 == m1
@@ -148,6 +150,8 @@ def check(x, expected_grades):
             ga.mv('A', 'grade', not_an_argument=True)  # invalid kwarg
         with pytest.raises(TypeError):
             ga.mv([1, 2, 3], 'vector', f=True)  # can't pass f with coefficients
+        with pytest.raises(TypeError):
+            ga.mv(e_1, 'even')  # Must be a string
 
     def test_abs(self):
         ga, e_1, e_2, e_3 = Ga.build('e*1|2|3', g=[1, 1, 1])
@@ -214,3 +218,109 @@ def test_contraction(self, make_one):
         assert (e12 > one) == e12
         assert (e12 < one) == 0
         assert (one > e12) == 0
+
+    def test_proj(self):
+        g3coords = symbols('x y z', real=True)
+        V = Ga('e', g=[1, 1, 1], coords=g3coords)
+
+        u = V.mv("u", "vector")
+        v = V.mv("v", "vector")
+        w = V.mv("w", "vector")
+        B = V.mv("B", "mv")
+
+        assert proj(u, v) == v.project_in_blade(u)
+        assert proj(w, v) + proj(w, u) == proj(w, u + v)
+        assert proj(u, v) == (v | u) / u
+
+        Vr = u ^ v
+        assert proj(Vr, B) == ((B < Vr) * Vr.inv())
+        assert proj(Vr, B) == ((B < Vr) < Vr.inv())
+
+    def test_norm2(self):
+        g3coords = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+        A = g3.mv('A', 'mv')
+
+        assert A.norm2() == A.rev().sp(A)
+        assert A.norm2('+') == A.rev().sp(A)
+        assert A.norm2('-') == A.rev().sp(A)
+
+        assert norm2(A) == norm(A) * norm(A)
+        assert norm2(A, '+') == norm(A) * norm(A)
+        assert norm2(A, '-') == norm(A) * norm(A)
+
+    def test_mag2(self):
+        g3coords = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+        A = g3.mv('A', 'mv')
+
+        assert mag2(A) == mag(A) * mag(A)
+
+    def test_undual(self):
+        # A not a multivector in undual(A).
+        with pytest.raises(ValueError):
+            undual(1)
+
+    def test_g_invol(self):
+        g3coords = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+        A = g3.mv('A', 'mv')
+
+        assert g_invol(A.even()) == A.even()
+        assert g_invol(A.odd()) == -A.odd()
+
+        with pytest.raises(ValueError):
+            g_invol(1)
+
+    def test_exp(self):
+        g3coords = (x, y, z) = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+
+        u = g3.mv("u", "vector")
+        v = g3.mv("v", "vector")
+
+        assert exp(u ^ v) == exp(-v ^ u)
+        assert exp(x + y + z) == exp(z + y + x)
+
+    def test_ccon(self):
+        g3coords = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+
+        A = g3.mv('A', 'mv')
+
+        assert ccon(ccon(A)) == A
+        assert ccon(A) == g_invol(rev(A))
+        assert ccon(A) == rev(g_invol(A))
+
+        # not a multivector in ccon(A)
+        with pytest.raises(ValueError):
+            ccon(1)
+
+    def test_scalar(self):
+        g3coords = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+
+        A = g3.mv('A', 'mv')
+        u = g3.mv("u", "vector")
+
+        assert scalar(A) == A.scalar()
+        assert scalar(u) == 0
+        assert scalar(u * u) == qform(u)
+
+        # not a multivector in scalar(A)
+        with pytest.raises(ValueError):
+            scalar(1)
+
+    def test_sp(self):
+        g3coords = symbols('x y z', real=True)
+        g3 = Ga('e', g=[1, 1, 1], coords=g3coords)
+
+        A = g3.mv('A', 'mv')
+        B = g3.mv('B', 'mv')
+
+        assert sp(A, B) == A.sp(B)
+        assert sp(A, B) == (A*B).scalar()
+
+        # not a multivector in sp(A, B)
+        with pytest.raises(ValueError):
+            sp(1, 1)