# Ivory: Geometric Units

In this page we will leave the ordinary number line, and begin to
introduce the `geometric`

level of the Ivory tower. Our
starting point is the equation:

*a*^{2} + 1 = 0

Or, as an s-expression (where the
`sqr`

function multiplies a number by itself):

```
= (+ (sqr a) 1)
(0)
```

This looks simple enough, but let’s take a moment to consider its
broader meaning. In particular, the sum includes one quantity that’s
*squared* `(sqr a)`

and one
quantity that *isn’t squared* `1`

. This feels
“off”, for a couple of reasons:

- As a physicist: the variable
`a`

must have different units than the constants`1`

and`0`

in order to be dimensionally consistent. For example, if`a`

were a length then the constants must be areas. - As a computer scientist: this feels like an ill-typed expression, like we’re mixing up encodings of semantically-distinct quantities.

Sure, those fears *might* be unfounded; but we can put
ourselves at ease by re-stating the equation entirely with squared
terms. This requires introducing a couple of extra variables, which
we’ll call `b`

and `c`

:

```
= (+ (sqr a) (sqr b))
( (sqr c))
```

Now let’s solve this equation:

- We know that
`(= (sqr b) 1)`

and`(= (sqr c) 0)`

, by definition. - We can rearrange the equation to find that
`(= (sqr a) (- (sqr b)))`

and hence`(= (sqr a) -1)`

You may be tempted to “square root” these and say that `(= b 1)`

,
`(= c 0)`

and (if you’re familiar with complex numbers) `(= a i)`

;
however, those are just *some* of the possible solutions to these
equations. Not only are there negative solutions too, but Geometric
Algebra provides even more by extending our arithmetic to include
*extra numbers*! These come in three flavours, one for each of
our variables (apologies for the intimidating names, which actually
pre-date Geometric Algebra!):

- We’ll call solutions to
`(= (sqr b) 1)`

(other than`1`

and`-1`

) hyperbolic units and write them as`h₀`

,`h₁`

,`h₂`

, etc. - We’ll call solutions to
`(= (sqr c) 0)`

(other than`0`

) dual units and write them as`d₀`

,`d₁`

,`d₂`

, etc. - We’ll call solutions to
`(= (sqr a) -1)`

imaginary units and write them as`i₀`

,`i₁`

,`i₂`

, etc.

Practical applications of GA will only use a few of these units, but
I want my code to support arbitrarily-many. Each of these units is a
perfectly legitimate `number`

, but
they are *not* part of `rational`

; hence they must occur at a
higher level of our numerical tower. We’ll define a new level called
`geometric`

to contain all of them.
I’ll be referring to them as GA/`geometric`

/non-`rational`

units”. Note that we
*cannot* call them “irrational”, since that already
means something else!

These non-`rational`

numbers do
not appear on the familiar number line. We’ll
give their geometric interpretation later. For now we’ll just treat them
as symbolic constants, the same way we treat τ, 𝑒, ϕ, etc.

### Representing Geometric Units In Scheme

This is pretty simple, since each unit contains two pieces of
information: the flavour and the index. We’ll represent the flavour
using a symbol: either `h`

or `d`

or `i`

. The index will just be a `number`

(we’ll be sticking to `natural`

indexes, but won’t enforce it).
We’ll combine these into a *pair*, by either giving them as
inputs to the `cons`

operation,
like `(cons 'd 0)`

for `d₀`

; or with a quotation, like
`'(i . 2)`

for `i₂`

(where the `.`

makes this a pair, rather than a list).

It *looks like* we’re calling functions named `d`

, `h`

and `i`

with a `number`

as input; but for something to
be a name, there must be some underlying definition that it’s referring
to. In this case we have no definitions (or, if you prefer, symbols are
merely names for themselves). These are “uninterpreted
functions”, meaning Racket will just pass around these expressions
as-is.

It may feel like cheating to claim these values are “incorporated
deeply” into the language, compared to “usual” numbers. Admittedly the
`natural`

type is a special case
(due to its place-value notation), but it turns out that *all* of
Scheme’s standard numerical tower relies on this “uninterpreted
function” trick!

Consider `integer`

: this
includes both `natural`

numbers and
their negatives. The latter are represented by prefixing the former with
a `-`

symbol, representing negation, which is left uninterpreted. The higher
levels, `rational`

and `complex`

, use uninterpreted functions
with two inputs (numerator & denominator, for `rational`

; “real” & “imaginary” for
`complex`

).

In any case, here are some Scheme functions for manipulating these GA units:

```
;; Predicates for spotting if a value is a GA unit (i.e. an appropriate pair)
(define/match (unit-d? n)[((cons 'd index)) (number? index)]
[(_) #f])
(define/match (unit-h? n)[((cons 'h index)) (number? index)]
[(_) #f])
(define/match (unit-i? n)[((cons 'i index)) (number? index)]
[(_) #f])
define unit-ga? (disjoin unit-h? unit-d? unit-i?))
(
;; Functions to access the flavour and index of a GA unit. The 'car'/'cdr'
;; functions return the first/second element of a pair.
define unit-flavour car)
(define unit-index cdr)
(
;; Helper for sorting units alphabetically
define (unit<? x y)
(let ([fx (unit-flavour x)]
([fy (unit-flavour y)]
[ix (unit-index x)]
[iy (unit-index y)])
or (symbol<? fx fy)
(and (equal? fx fy) (< iy ix))))) (
```

#### Parsing Geometric Units

Now we have a *representation* for `geometric`

units, as a pair of flavour and index, we can define a function to read
this subscript syntax:

```
define (subscripts-to-digits s)
([digits s])
(for/fold ([i "0123456789"]
([char "₀₁₂₃₄₅₆₇₈₉"])
string char) (string i))))
(string-replace digits (
define (parse-unit in)
(;; Look for flavour and index, using regexp capture groups
(match (regexp-match;; Match flavour then index; disallow leading zeros
"^([dhi])((₀(?![₀₁₂₃₄₅₆₇₈₉]))|([₁₂₃₄₅₆₇₈₉][₀₁₂₃₄₅₆₇₈₉]*))"
#px
in);; No match
[#f #f]
;; Found a match: s is entire match, groups are captured substrings
[(cons s (cons flavour-group (cons index-group _)))
cons
(string->symbol (bytes->string/utf-8 flavour-group))
(string->number
(])) (subscripts-to-digits (bytes->string/utf-8 index-group))))
```

## Code

## Test results

```
raco test: (submod "geo-units.rkt" test)
29 tests passed
```