Full Code of sahasatvik/typst-theorems for AI

main ebaa938c8911 cached
8 files
59.9 KB
19.4k tokens
1 requests
Download .txt
Repository: sahasatvik/typst-theorems
Branch: main
Commit: ebaa938c8911
Files: 8
Total size: 59.9 KB

Directory structure:
gitextract_xw9617op/

├── LICENSE
├── README.md
├── basic.typ
├── differential_calculus.typ
├── manual.typ
├── manual_examples.typ
├── manual_template.typ
└── theorems.typ

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2023 Satvik Saha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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


================================================
FILE: README.md
================================================
# typst-theorems

An implementation of numbered theorem environments in
[typst](https://github.com/typst/typst).
Available as [ctheorems](https://typst.app/universe/package/ctheorems) in the
official Typst Universe.
Import with

```typst
#import "@preview/ctheorems:1.1.3": *
#show: thmrules
```

Alternatively, copy and import the [theorems.typ](theorems.typ) file to use in
your own projects.

### Features
- Numbered theorem environments can be created and customized.
- Environments can share the same counter, via same `identifier`s.
- Environment counters can be _attached_ (just as subheadings are attached to headings) to other environments, headings, or keep a global count via `base`.
- The depth of a counter can be manually set, via `base_level`.
- Environments can be `<label>`'d and `@reference`'d.

## Manual and Examples
Get acquainted with `typst-theorems` by checking out the minimal example below!

You can read the [manual](manual.pdf) for a full walkthrough of functionality offered by this module; flick through [manual_examples](manual_examples.pdf) and its [typ file](manual_examples.typ) to just see the examples.

The [differential_calculus.typ](differential_calculus.typ) ([render](differential_calculus.pdf)) project provides a practical use case. _(Hastily translated from my notes written in LaTeX)_

![basic example](basic.png)

### Preamble
```typst
#import "theorems.typ": *
#show: thmrules.with(qed-symbol: $square$)

#set page(width: 16cm, height: auto, margin: 1.5cm)
#set text(font: "Libertinus Serif", lang: "en")
#set heading(numbering: "1.1.")

#let theorem = thmbox("theorem", "Theorem", fill: rgb("#eeffee"))
#let corollary = thmplain(
  "corollary",
  "Corollary",
  base: "theorem",
  titlefmt: strong
)
#let definition = thmbox("definition", "Definition", inset: (x: 1.2em, top: 1em))

#let example = thmplain("example", "Example").with(numbering: none)
#let proof = thmproof("proof", "Proof")
```

### Document
```typst
= Prime numbers

#definition[
  A natural number is called a #highlight[_prime number_] if it is greater
  than 1 and cannot be written as the product of two smaller natural numbers.
]
#example[
  The numbers $2$, $3$, and $17$ are prime.
  @cor_largest_prime shows that this list is not exhaustive!
]

#theorem("Euclid")[
  There are infinitely many primes.
]
#proof[
  Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration
  of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list,
  it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$.  Since
  $p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a
  contradiction.
]

#corollary[
  There is no largest prime number.
] <cor_largest_prime>
#corollary[
  There are infinitely many composite numbers.
]

#theorem[
  There are arbitrarily long stretches of composite numbers.
]
#proof[
  For any $n > 2$, consider $
    n! + 2, quad n! + 3, quad ..., quad n! + n #qedhere
  $
]
```


## Acknowledgements

Thanks to

- [MJHutchinson](https://github.com/MJHutchinson) for suggesting and
  implementing the `base_level` and `base: none` features,
- [rmolinari](https://github.com/rmolinari) for suggesting and
  implementing the `separator: ...` feature,
- [DVDTSB](https://github.com/DVDTSB) for contributing
  - the idea of passing named arguments from the theorem directly to the `fmt`
    function.
  - the `number: ...` override feature.
  - the `title: ...` override feature in `thmbox`.
- [PgBiel](https://github.com/PgBiel) for fixing breaking changes in version
  updates.
- The awesome devs of [typst.app](https://typst.app/) for their
  support.


================================================
FILE: basic.typ
================================================
#import "theorems.typ": *
#show: thmrules.with(qed-symbol: $square$)

#set page(width: 16cm, height: auto, margin: 1.5cm)
#set text(font: "Libertinus Serif", lang: "en")
#set heading(numbering: "1.1.")

#let theorem = thmbox("theorem", "Theorem", fill: rgb("#eeffee"))
#let corollary = thmplain(
  "corollary",
  "Corollary",
  base: "theorem",
  titlefmt: strong
)
#let definition = thmbox("definition", "Definition", inset: (x: 1.2em, top: 1em))

#let example = thmplain("example", "Example").with(numbering: none)
#let proof = thmproof("proof", "Proof")


= Prime numbers

#definition[
  A natural number is called a #highlight[_prime number_] if it is greater
  than 1 and cannot be written as the product of two smaller natural numbers.
]
#example[
  The numbers $2$, $3$, and $17$ are prime.
  @cor_largest_prime shows that this list is not exhaustive!
]

#theorem("Euclid")[
  There are infinitely many primes.
]
#proof[
  Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration
  of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list,
  it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$.  Since
  $p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a
  contradiction.
]

#corollary[
  There is no largest prime number.
] <cor_largest_prime>
#corollary[
  There are infinitely many composite numbers.
]

#theorem[
  There are arbitrarily long stretches of composite numbers.
]
#proof[
  For any $n > 2$, consider $
    n! + 2, quad n! + 3, quad ..., quad n! + n #qedhere
  $
]


================================================
FILE: differential_calculus.typ
================================================
#import "theorems.typ": *
#show: thmrules

// Define theorem environments

#let theorem = thmbox(
  "theorem",
  "Theorem",
  fill: rgb("#e8e8f8")
)
#let lemma = thmbox(
  "theorem",            // Lemmas use the same counter as Theorems
  "Lemma",
  fill: rgb("#efe6ff")
)
#let corollary = thmbox(
  "corollary",
  "Corollary",
  base: "theorem",      // Corollaries are 'attached' to Theorems
  fill: rgb("#f8e8e8")
)

#let definition = thmbox(
  "definition",         // Definitions use their own counter
  "Definition",
  fill: rgb("#e8f8e8")
)

#let exercise = thmbox(
  "exercise",
  "Exercise",
  stroke: rgb("#ffaaaa") + 1pt,
  base: none,           // Unattached: count globally
).with(numbering: "I")  // Use Roman numerals

// Examples and remarks are not numbered
#let example = thmplain("example", "Example").with(numbering: none)
#let remark = thmplain(
  "remark",
  "Remark",
  inset: 0em
).with(numbering: none)

// Proofs are attached to theorems, although they are not numbered
#let proof = thmproof(
  "proof",
  "Proof",
  base: "theorem",
)

#let solution = thmplain(
  "solution",
  "Solution",
  base: "exercise",
  inset: 0em,
).with(numbering: none)


// Template

#let project(title: "", authors: (), body) = {
  set document(author: authors, title: title)
  set text(font: "Libertinus Serif", lang: "en")
  set heading(numbering: "1.1.", )
  set par(justify: true)

  show heading: it => [
    #v(1em)
    #it
  ]

  align(center)[
    #block(text(weight: 700, 1.75em, title))
    #v(2em)
  ]

  outline(fill: none, indent: true)

  body
}

// Shorthand for vectors
#let va = $bold(a)$
#let vb = $bold(b)$
#let vx = $bold(x)$
#let vy = $bold(y)$
#let vz = $bold(z)$
#let vv = $bold(v)$
#let vw = $bold(w)$
#let ve = $bold(e)$

// Mapping arrow
#let mapsto = $arrow.r.bar$

// Operators
#let grad = (x) => $nabla #x$
#let ip = (x, y) => $angle.l #x, #y angle.r$
#let pp = (x, y) => $(diff #x) / (diff #y)$
#let dd = (x, y) => $(d #x) / (d #y)$
#let Df = $D f$
#let Dg = $D g$
#let DT = $D T$


// Document starts here

#show: project.with(
  title: "Notes on Differential Calculus"
)

= Differentiability

#definition[
    Let $f : (a, b) -> RR^n$, and let $f_i = pi_i compose  f$ be its
    components. Then, $f$ is differentiable at $t_0  in  (a, b)$ if the
    following limit exists. $ 
        f'(t_0) =  lim _(h -> 0)  frac(f(t_0 + h) - f(t_0), h)  .
    $
    #remark[
        The vector $f'(t_0)$ represents the tangent to the curve $f$ at the
        point $f(t_0)$. The full tangent line is the parametric curve $f(t) +
        f'(t_0)(t - t_0)$.
    ]
]

#definition[
    Let $U  subset.eq  RR^n$ be open, and let $f: U -> RR^m$. Then, $f$ is
    differentiable at $x  in  U$ if there exists a linear transformation $
    lambda : RR^n -> RR^m$ such that $ 
         lim _(h -> 0)   frac(f(x + h) - f(x) -  lambda  h,  norm(h))  = 0.
    $ The derivative of $f$ at $x$ is denoted by $lambda  = Df(x)$.
    #remark[
        In a neighbourhood of $x$, we may approximate $ 
            f(x + h)  approx  f(x) + Df(x)(h).
        $
    ]
    #remark[
        The statement that this quantity goes to zero means that each of the
        $m$ components must also go to zero. For each of these limits, there
        are $n$ axes along which we can let $h -> 0$. As a result, we obtain
        $m times n$ limits, which allow us to identify the $m times n$
        components of the matrix representing the linear transformation
        $lambda$ (in the standard basis). These are the partial derivatives of
        $f$, and the matrix of $lambda$ is the Jacobian matrix of $f$
        evaluated at $x$.
    ]
]

#example[
    Let $T: RR^n -> RR^m$ be a linear map. By choosing $ lambda = T$, we see
    that $T$ is differentiable everywhere, with $DT(x) = T$ for every choice
    of $x  in  RR^n$. This is made obvious by the fact that the best linear
    approximation of a linear map at some point is the map itself; indeed, the
    'approximation' is exact.
]

#lemma[
    If $f: RR^n -> RR^m$ is differentiable at $x  in  RR^n$, with derivative
    $Df(x)$, then
        + $f$ is continuous at $x$.
        + The linear transformation $Df(x)$ is unique.
]
#proof[
    We prove the second part. Suppose that $lambda$, $mu$ satisfy the
    requirements for $Df(x)$; it can be shown that $ lim _(h -> 0) ( lambda  -
    mu )h \/  norm(h)  = 0$. Now, if $ lambda  v  !=   mu  v$ for some
    non-zero vector $v  in  RR^n$, then $ 
         lambda  v -  mu  v =   frac(lambda (t v) - mu(t v), norm(t v))
         dot.c norm(v) -> 0,
    $ a contradiction.
]

= Chain rule

#exercise[
    Let $T: RR^n -> RR^m$ be a linear transformation. Then, there exists $M
    > 0$ such that for all $ vx   in  RR^n$, we have $ 
         norm(T vx )   <=  M norm( vx ) .
    $
    #solution[
        Set $ vv _i = T( ve _i)$ where $ ve _i$ are the standard unit basis
        vectors of $RR^n$. Then, $ 
             norm(T vx )
             =   norm( sum _(i) x_i vv _i)
             <=  sum _(i) norm(x_i vv _i) 
             <=  max  norm( vv _i)   sum _i |x_i|.
        $  Since each $|x_i|  <=   norm( vx ) $, set $M = n max  norm( vv _i)
        $ and write $ 
             norm(T vx )
             <=  max  norm( vv _i)  sum _i |x_i|
             <=  max  norm( vv _i)  dot.c  n norm( vx )
             = M norm( vx ) .
        $
    ]
]
#theorem("Chain Rule")[
    Let $f: RR^n -> RR^m$, $g: RR^m -> RR^k$ where $f$ is differentiable at $a
    in  RR^n$ and $g$ is differentiable at $f(a)  in  RR^m$.  Then, $g compose
    f$ is differentiable, with $D(g compose  f)(a) = Dg(f(a))  compose Df(a)$.
    Note that this means that the Jacobian matrices simply multiply.
]
#proof[
    Set $b = f(a)  in  RR^m$, $ lambda  = Df(a)$, $ mu  = Dg(f(a))$. Define 
    $
         phi : RR^n -> RR^m, quad   phi (x) &= f(x) - f(a) -  lambda (x
        - a), \
         psi : RR^m -> RR^k, quad   psi (y) &= g(y) - g(b) -  mu (y - b).
    $
    We claim that $ 
         lim _(x -> a) frac(g compose  f(x) - g compose  f(a) -  mu  compose   lambda (x
        - a),  norm(x - a) ) = 0.
    $  Write the numerator as $ 
        g compose  f(x) - g compose  f(a) -  mu  compose   lambda (x - a) =  psi (f(x)) +
         mu ( phi (x)).
    $  Note that $ 
         lim _(x -> a)   frac( phi (x),  norm(x - a)  )  = 0, quad
         lim _(y -> b)   frac( psi (y),  norm(y - b)  )  = 0.
    $  Thus, find $M > 0$ such that $ norm( mu ( phi (x)))   <= 
     norm( phi (x)) $ for all $x  in  RR^n$, hence $ 
         lim _(x -> a)   frac( norm( mu ( phi (x))) ,  norm(x - a)  )   <=
         lim _{x -> a}   frac(M norm( phi (x)) ,  norm(x - a)  )  = 0.
    $  Now write $ 
         lim _(f(x) -> b)   frac( psi (f(x)),  norm(f(x)  - b) )  = 0, 
    $  hence for any $ epsilon  > 0$, there is a neighbourhood of $b$ on which $ 
         norm( psi (f(x)))   <=   epsilon   norm(f(x) - b)  =  epsilon 
         norm( phi (x) +  lambda (x - a)) .
    $  Apply the triangle inequality and find $M' > 0$ such that $ 
         norm( psi (f(x)))   <= 
         epsilon  norm( phi (x))  +  epsilon  M' norm(x - a) .
    $  Thus, $ 
         lim _(x -> a)   frac( norm( psi (f(x))) ,  norm(x - a)  )   <= 
         lim _(x -> a)   frac( epsilon  norm( phi (x)) ,  norm(x - a)  )  +  epsilon 
        M' =  epsilon  M'.
    $  Since $ epsilon  > 0$ was arbitrary, this limit is zero, completing the proof.
] 


= Partial derivatives

#definition[
    Let $U  subset.eq  RR^n$ be open, and let $f: U -> RR$. The partial
    derivative of $f$ with respect to the coordinate $x_j$ at some $a  in  U$
    is defined by the following limit, if it exists. $ 
         pp(f, x_j) (a) =  lim _(h -> 0)   frac(f(a + h ve_j) - f(a), h)  .
    $  
]
#lemma[
    If $f: U -> RR$ is differentiable at a point $a  in  RR^n$, then $ 
        Df(a)(x_1,  dots , x_n) = x_1 \, pp(f, x_1) (a) +  dots  + x_n
        \, pp(f, x_n) (a).
    $  
]
#example[
    Consider $ 
        f: RR^2 -> RR,  quad  (x, y)  mapsto  cases(
             (x y) \/ (x^2 + y^2)\, & " if "  (x, y)  !=  (0, 0),
            0\, & " if "  (x, y) = (0, 0).
        )
    $  Note that $f$ is not differentiable at $(0, 0)$; it is not even continuous
    there. However, both partial derivatives of $f$ exist at $(0, 0)$.
]

#lemma[
    If $f: RR^n -> RR^m$ is differentiable at $a  in  RR^n$, then the matrix
    representation of $Df(a)$ in the standard basis is given by $ 
        [Df(a)] = [ pp(f_i, x_j) (a)]_(i j).
    $  
]

#lemma[
    Let $f: RR^n -> RR^m$ be differentiable at $a  in  RR^n$, and let $g: RR^m
    -> RR^k$ be differentiable at $f(a)  in  RR^m$. Then, the matrix
    representation of $D(g compose  f)(a)$ in the standard basis is the
    product $ 
        [D(g compose  f)(a)] = [Dg(f(a))][Df(a)] = [ sum _( ell  = 1)^m
         pp(g_i, y_ ell )  pp(f_ ell , x_j) ]_(i j).
    $  In other words, $ 
         pp(, x_j) (g compose  f)_i(a) =  sum _( ell  = 1)^m  pp(g_i, y_ ell ) (f(a))
         pp(f_ ell , x_j) (a).
    $  
]

#example[
    Let $f: RR^2-> RR$ be differentiable, and let $ Gamma (f) = {(x, y, f(x,
    y)): x, y  in  RR}$ be the graph of $f$. Now, let $gamma : [-1, 1] ->
    Gamma (f)$ be a differentiable curve, represented by $ 
         gamma (t) = (g(t), h(t), f(g(t), h(t))).
    $  Then, we can compute the derivative $ 
         gamma '(a) = (g'(a), h'(a), lr(g'(a) pp(f, x)  + h'(a) pp(f, y) 
            \|)_((g(a), h(a))))
    $  
]

#exercise[
    Consider the inner product map, $ ip( dot.c ,  dot.c ) : RR^n  times RR^n
    -> RR$. What is its derivative?
    #solution[
        We treat the inner product as a map $g: RR^(2n) -> RR$, which acts
        as $ 
             ip( vx ,  vy )  colon.eq g(x_1,  dots , x_n, y_1,  dots , y_n) = x_1y_1 +
             dots  + x_n y_n.
        $  Now, note that $ 
             pp(g, x_i)  = y_i,  quad   pp(g, y_i)  = x_i.
        $  Thus, $
            Dg( va ,  vb )( vx ,  vy ) &=  sum _(i = 1)^n x_i  pp(g, x_i) ( va ,  vb ) +
             sum _(i = 1)^n y_i  pp(g, y_i) ( va ,  vb ) \
            &=  sum _(i = 1)^n x_i b_i +  sum _(i = 1)^n y_i a_i \
            &=  ip( vx ,  vb )  +  ip( vy ,  va ) .
        $
        In other words, the matrix representation of the derivative of the inner
        product map at the point $( va ,  vb )$ is given by $[ vb^top
         va^top]$.
    ]
]

#exercise[
    Let $gamma : RR -> RR^n$ be a differentiable curve. What is the
    derivative of the real map $t  mapsto   norm( gamma (t)) ^2$?
    #solution[
        We write this map as $t  mapsto   ip( gamma (t),  gamma (t)) $. Consider the
        scheme $
            RR -> RR^(2n) -> RR, quad
            t  mapsto  mat(
                 gamma (t) ;  gamma (t)
            )  mapsto   ip( gamma (t),  gamma (t)) .
        $  Pick a point $t in  RR$, whence the derivative of the map at $t$ is $ 
            mat(
                 gamma (t)^top ,  gamma (t)^top
            ) mat(
                 gamma '(t) ;  gamma '(t)
            ) = 2 ip( gamma (t),  gamma '(t)) .
        $
    ]
    #remark[
        Consider the surface $S^(n - 1)  subset  RR^n$, and pick an arbitrary
        differentiable curve $gamma : RR -> S^(n - 1)$. Now, the tangent
        vector $gamma '(t)$ is tangent to the sphere $S^(n - 1)$ at any point
        $gamma (t)$. We claim that this tangent drawn at $ gamma (t)$ is always
        perpendicular to the position vector $ gamma (t)$. This is made trivial by
        our exercise: the map $t  mapsto   norm( gamma (t)) ^2 = 1$ is a constant
        map since $gamma$ is a curve on the unit sphere. This means that it has
        zero derivative, forcing $ ip( gamma (t),  gamma '(t))  = 0$.
    ]
]

== Directional derivatives

#definition[
    Let $U  subset.eq  RR^n$ be open, and let $f: U -> RR$. The directional
    derivative of $f$ along a direction $ vv   in  RR^n$ at a point $a  in  U$ is
    defined by the following limit, if it exists. $ 
         nabla _v f(a) =  lim _(h -> 0)   frac(f(a + h vv ) - f(a), h)  .
    $  
]

#example[
    Consider $ 
        f: RR^2 -> RR,  quad  (x, y)  mapsto  cases(
            x^3\/(x^2 + y^2)\, & " if "  (x, y)  !=  (0, 0),
            0\, & " if "  (x, y) = (0, 0).
        )
    $  Note that $f$ is not differentiable at $(0, 0)$. However, all
    directional derivatives derivatives of $f$ exist at $(0, 0)$. Indeed,
    consider a direction $( cos  theta , sin theta )$, and examine the limit
    $ 
         lim_(t -> 0)   frac(1, t)  [f(t cos  theta , t sin theta ) - f(0,
        0)] =  cos ^3 theta .
    $  
]

#definition[
    Let $f: RR^n -> RR$ be differentiable. The gradient of $f$ is defined
    as the map $ 
         grad(f) : RR^n -> RR^n,  quad  x  mapsto 
        [ pp(f, x_i) (x)]_i.
    $  
    #remark[
        The gradient at a point $x  in  RR^n$ is thought of as a vector. In
        contrast, the derivative is thought of as a linear transformation.
        Otherwise, we see that $ grad(f) (x) = [Df(x)]$.
    ]
]

#definition[
    Let $C^1(RR^n)$ be the set of real-valued differentiable functions on $RR^n$.
    Fix a point $a  in  RR^n$, then fix a tangent vector $v  in  RR^n$. Then, the
    map $ 
         nabla _v: C^1(RR^n) -> RR,  quad  f mapsto  Df(a)(v)
    $  is a linear functional. The quantity $ nabla _v f$ is called the
    directional derivative of $f$ in the direction $v$ at the point $a$.
    #remark[
        We can represent $ nabla _v$ as the operator $ 
             nabla _v ( dot.c ) = D( dot.c )(a)(v) =  sum _i v_i  lr(pp(, x_i)\|)_a =
            v dot.c   nabla  ( dot.c ).
        $ 
    ]
]

#lemma[
    The directional derivatives $ nabla _v$ form a vector space called the tangent
    space, attached to the point $a  in  RR^n$. This can be identified with the
    vector space $RR^n$ by the natural map $ nabla _v  mapsto  v$. The standard
    basis can be informally denoted by the vectors $ 
         nabla_( ve _1) colon.eq  pp(, x_1) ,  quad dots quad,  nabla _( ve _n) colon.eq  pp(, x_n) .
    $  
]


== Differentiation on manifolds \*

#definition[
    A homeomorphism is a continuous, bijective map whose inverse is also continuous.
]

#lemma[
    Let $f: RR^n -> RR$ be continuous. Denote the graph of $f$ as $ 
         Gamma (f) = \{(x, f(x)): x  in  RR^n\}.
    $  Then, $ Gamma (f)$ is a smooth manifold.
]
#proof[
    Consider the homeomorphism $ 
         phi :  Gamma (f) -> RR^n,  quad  (x, f(x))  mapsto  x.
    $  This is clearly bijective, continuous (restriction of a projection map),
    with a continuous inverse (from the continuity of $f$). Call this
    homeomorphism $phi$ a coordinate map on $Gamma (f)$.
]

#definition[
    Let $f: M -> RR$ where $M$ is a smooth manifold, with a coordinate map
    $phi : M -> RR^n$. We say that $f$ is differentiable at a point $a
     in  M$ if $f compose   phi ^(-1): RR^n -> RR$ is differentiable at
    $ phi (a)$.
]

#definition[
    Let $f: M -> RR$ where $M$ is a smooth manifold, let $ phi : M
    -> RR^n$ be a coordinate map, and let $a  in  M$. Let $gamma : RR -> M$
    be a curve such that $ gamma (0) = a$, and further let $gamma$ be
    differentiable in the sense that $phi  compose   gamma : RR -> RR^n$ is
    differentiable. The directional derivative of $f$ at $a$ along $gamma$ is
    defined as $ 
         dd(, t)  f( gamma (t)) \|_ (t = 0) =  lim _(h -> 0) lr(frac(f( gamma (t +
        h)) - f( gamma (t)), h) \|)_ (t = 0).
    $  Note that we are taking the derivative of $f compose   gamma : RR -> RR$
    in the conventional sense.
]

#lemma[
    Let $ gamma _1$ and $gamma _2$ be two curves in $M$ such that $gamma_1(0) =
     gamma _2(0) = a$, and $ 
         dd(, t) lr( phi  compose  gamma _1(t) \|)_ (t = 0) 
         = dd(, t)  lr( phi  compose  gamma _2(t)  \|)_ (t = 0).
    $  In other words, $gamma _1$ and $ gamma _2$ pass through the same point $a$
    at $t = 0$, and have the same velocities there. Then, the directional
    derivatives of $f$ at $a$ along $gamma _1$ and $ gamma _2$ are the same.
]

#definition[
    Let $M$ be a smooth manifold, and let $a  in  M$. Consider the following
    equivalence relation on the set of all curves $gamma$ in $M$ such that
    $gamma (0) = a$. $ 
         gamma_1 tilde.op gamma_2  quad arrow.r.double.long quad
         dd(, t) lr( phi  compose gamma _1(t)  \|)_ (t = 0) 
         = dd(, t) lr( phi  compose   gamma _2(t) \|)_ (t = 0).
    $ Each resultant equivalence class of curves is called a tangent vector at
    $a  in  M$. Note that all these curves in a particular equivalence class pass
    through $a$ with the same velocity vector.

    The collection of all such tangent vectors, i.e. the space of all curves
    through $a$ modulo the equivalence relation which identifies curves with the
    same velocity vector through $a$, is called the tangent space to $M$ at $a$,
    denoted $T_a M$.

    #remark[
        Each tangent vector $v in  T_a M$ acts on a differentiable function
        $f: M -> RR$ yielding a (well-defined) directional derivative at
        $a$. $ 
            v: C^1(M) -> RR,  quad  f mapsto   dd(, t) 
            lr(f( gamma _v(t)) \|)_ (t = 0).
        $ 
        Thus, the tangent space represents all the directions in which taking a
        derivative of $f$ makes sense.
    ]
    #remark[
        The tangent space $T_a M$ is a vector space. Upon fixing $f$, the map
        $Df(a): T_a M -> RR$, $v  mapsto  v f(a)$ is a linear functional on
        the tangent space.
    ]
    #remark[
        Given a tangent vector $v  in  T_a M$, it can be identified with its
        corresponding velocity vector in $RR^n$. Thus, the tangent space $T_a M$
        can be identified with the geometric tangent plane drawn to the manifold
        $M$ at the point $a$.
    ]
]


= Mean value theorem
Consider a differentiable function $f: RR^n -> RR$, and fix $a  in  RR^n$.
Define the functions $ 
    g_i: RR -> RR,  quad  g_i(x) = f(a_1,  dots , a_(i - 1), x, a_(i + 1),
     dots , a_n).
$  Then, each $g_i$ is differentiable, with $ 
    g_i'(x) =  pp(f, x_i) (a_1,  dots , a_(i - 1), x, a_(i + 1),  dots , a_n).
$  By applying the Mean Value Theorem on some interval $[c, d]$, we can find
$ alpha  in  (c, d)$ such that $g_i(d) - g_i(c) = g_i'( alpha )(d - c)$. In other
words,
$ 
    f( dots , d,  dots ) - f( dots , c,  dots ) =  pp(f, x_i) ( dots ,  alpha ,  dots )(d
    - c).
$

#theorem[
    Let $f: RR^n -> RR^m$ and $a  in  RR^n$. Then, $f$ is differentiable at
    $a$ if all the partial derivatives $diff  f\/diff  x_j$ exist in a
    neighbourhood of $a$ and are continuous at $a$.
]
#proof[
    Without loss of generality, let $m = 1$. We claim that $ 
         lim _(h -> 0)     frac(1,  norm(h)  )  norm(f(a + h) - f(a) -  sum _(i = 0) ^n
         pp(f, x_i) (a)h_i) = 0.
    $  Examine $
        f(a + h) - f(a) &= f(a_1 + h_1,  dots , a_n + h_n) - f(a_1,  dots ,
        a_n) \
        &= f(a_1 + h_1,  dots , a_n + h_n) - f(a_1 + h_1,  dots , a_(n - 1) +
        h_(n - 1), a_n) + \
        &   quad  f(a_1 + h_1,  dots , a_(n - 1) + h_(n - 1), a_n) - f(a_1 + h_1,
         dots , a_(n - 1), a_n) + \
        &   quad   dots.h  \
        &   quad  f(a_1 + h_1, a_2,  dots , a_n) - f(a_1,  dots , a_n) \
        &=  pp(f, x_n) (c_n)h_n +  dots  +  pp(f, x_1) (c_1)h_1.
    $
    The last step follows from the Mean Value Theorem. As $h -> 0$, each $c_i
    -> a$. Thus, $
          frac(1,  norm(h))  norm(f(a + h) - f(a) -  sum _(i = 0)^n
         pp(f, x_i) (a)h_i) 
        &=   frac(1,  norm(h))  norm( sum _(i = 0) ^n (pp(f, x_i)(c_i) -
         pp(f, x_i) (a))h_i) \
        & <=   sum _(i = 0)^n | pp(f, x_i) (c_i) -  pp(f, x_i) (a) |
          frac(|h_i|,  norm(h)  )  \
        & <=   sum _(i = 0)^n | pp(f, x_i) (c_i) -  pp(f, x_i) (a) |.
    $
    Taking the limit $h -> 0$, observe that $(diff  f\/diff  x_i) (c_i) ->
    (diff  f \/ diff  x_i) (a)$ by the continuity of the partial derivatives,
    completing the proof.
]

#corollary[
    All polynomial functions on $RR^n$ are differentiable.
]

#theorem[
    Let $f: RR^n -> RR$ be differentiable with continuous partial
    derivatives, and let $a  in  RR^n$ be a point of local maximum. Then, $Df(a) =
    0$.
]
#proof[
    We need only show that each $ 
         pp(f, x_i) (a) = 0.
    $  This must be true, since $a$ is also a local maximum of each of the
    restrictions $g_i$ as defined earlier.
]

= Inverse and implicit function theorems
#theorem([Inverse function theorem])[
    Let $f: RR^n -> RR^n$ be continuously differentiable on a neighbourhood
    of $a  in  RR^n$, and let $"det" (Df(a))  !=  0$. Then, there exist neighbourhoods
    $U$ of $a$ and $W$ of $f(a)$ such that the restriction $f: U -> W$ is
    invertible. Furthermore, $f^(-1)$ is continuous on $U$ and differentiable on
    $U$.
]

#lemma[
    Consider a continuously differentiable function $f: RR^n -> RR$, and
    let $M$ denote the surface defined by the zero set of $f$. Then, $M$ can be
    represented as the graph of a differentiable function $h: RR^(n - 1) ->
    RR$ at those points where $Df  !=  0$.
]
#proof[
    Without loss of generality, suppose that $diff  f \/ diff  x_n  !=  0$
    at some point $a  in  M$. It can be shown that the map $ 
        F: RR^n -> RR^n ,  quad  x  mapsto  (x_1, x_2,  dots , x_(n - 1),
        f(x))
    $  is invertible in a neighbourhood $W$ of $a$, with a continuous and
    differentiable inverse of the form
    $ 
        G: RR^n -> RR^n,  quad  u  mapsto  (u_1, u_2,  dots , u_(n - 1),
        g(u)).
    $
    Since $F compose G$ must be the identity map on $W$, we demand $ 
        (x_1, x_2,  dots , x_(n - 1), f(x_1, x_2,  dots , x_(n - 1), g(x))) = (x_1,
        x_2,  dots , x_(n - 1), x_n).
    $  Thus, the zero set of $f$ in this neighbourhood of $a$ satisfies $x_n =
    0$, hence $ 
        f(x_1, x_2,  dots , x_(n - 1), g(x_1, x_2,  dots , x_(n - 1), 0)) = 0.
    $  In other words, the part of the surface $M$ in the neighbourhood of $a$ is
    precisely the set of points $ 
        (x_1, x_2,  dots , x_(n - 1), g(x_1, x_2,  dots , x_(n - 1), 0)).
    $  Simply set $ 
        h: RR^(n - 1) -> RR,  quad  x  mapsto  g(x_1, x_2,  dots , x_(n - 1),
        0),
    $  whence the surface $M$ is locally represented by the graph of $h$.
]

#block(inset: 1em)[
#remark()[
    Note that by using $ 
        f(x_1,  dots , x_(n - 1), h(x_1,  dots , x_(n - 1))) = 0
    $  on the surface, we can use the chain rule to conclude that for all $1  <= 
    i < n$, we have $ 
         pp(f, x_i) (a) +  pp(f, x_n) (a) pp(h, x_i) (a_1,  dots , a_(n - 1)) = 0.
    $  
]
]

#theorem([Implicit function theorem])[
    Let $f: RR^n times RR^m -> RR^m$ be continuously differentiable in an open
    set containing $(a, b)$, with $f(a, b) = 0$. Let $"det" (diff  f^j \/ diff
    x_(n + k) (a, b))  !=  0$. Then, there exists an open set $U  subset RR^n$
    containing $a$, an open set $V  subset  RR^m$ containing $b$, and a
    differentiable function $g: U -> V$ such that $f(x, g(x)) = 0$.

    #remark[
        The condition on the determinant can be rephrased as
        $"rank" Df(a, b) = m$.
    ]
]

#theorem[
    Let $f: RR^n -> RR$ be continuously differentiable, and let $M$ be the
    surface defined by its zero set. Furthermore, let $ grad(f) (a)  !=  0$ for
    some $a  in  M$; thus, $M$ can be locally represented by a graph on $RR^(n
    - 1)$. Then, $ grad(f) (a)$ is normal to the tangent vectors drawn at $a$
    to $M$; in fact, the perpendicular space of $ grad(f) (a)$ is precisely
    the tangent space $T_a M$.
]
#proof[
    Consider a tangent vector drawn at $a$ to $M$, represented by the
    differentiable curve $gamma : RR -> M$, $ gamma (0) = a$; note that
    we use the identification $gamma '(0) = v  in  RR^n$. Then, calculate $ 
         dd(, t) lr(f( gamma (t))  \|)_ (t = 0) = Df( gamma (0))( gamma '(0))
         = Df(a)(v).
    $  On the other hand, we have $f( gamma (t)) = 0$ identically. Thus, $ 
        v dot.c   grad(f) (a) = Df(a)(v) = 0
    $ as claimed.
]

= Taylor's theorem

#theorem("Clairaut")[
    Let $f: RR^n -> RR$ have continuous second order partial derivatives.
    Then, $ 
          frac(diff ^2 f, diff  x_i diff  x_j)   = frac(diff ^2
        f, diff  x_j diff  x_i).
    $
]

#theorem("Taylor")[
    Let $f: RR^2 -> RR$ have continuous second order partial derivatives,
    and let $(x_0, y_0)  in  RR^2$. Then, there exists $ epsilon  > 0$ such that
    for all $norm((x - x_0, y - y_0))  <  epsilon$,
    $
        f(x, y)   =   &f(x_0, y_0) +  pp(f, x) (x - x_0) +  pp(f, y) (y - y_0) \
        & + space frac(1, 2)  frac(diff^2 f, diff x^2)(x - x_0)^2 + frac(1, 2)
        frac(diff^2 f, diff y^2)(y - y_0)^2 \
        & + space frac(diff ^2 f, diff  x diff  y)  (x - x_0)(y - x_0) + R(x,
        y),
    $
    where as $(x, y) -> (x_0, y_0)$, the remainder term vanishes as $ 
          frac(|R(x, y)|,  norm((x - x_0, y - y_0) ) ^2)  -> 0.
    $  All partial derivatives here are evaluated at $(x_0, y_0)$.
]
#proof[
    This follows from applying the Taylor's Theorem in one variable to the real
    function $g: RR -> RR$, $t  mapsto  f((1 - t)(x_0, y_0) + t(x, y))$.
]


= Critical points and extrema
#definition[
    We say that $a  in  RR^n$ is a critical point of $f: RR^n -> RR$ if all
    $diff  f \/ diff  x^j = 0$ there.
]

#lemma[
    All points of extrema of a differentiable function are critical points.
]
#proof[
    We already know that $Df(a) = 0$ where $a$ is either a point of maximum or
    minimum.
]

#example[
    In order to find a point of extrema of a $C^2$-smooth function $f: RR^2
    -> RR$, we first identify a critical point $(x_0, y_0)$. Next, we must find
    a neighbourhood of $(x_0, y_0)$ which contains no other critical points -- to
    do this, apply Taylor's Theorem. Indeed, we see that $ 
        f(x, y) = f(x_0, y_0) + A(x - x_0)^2 + 2B(x - x_0)(y - y_0) + C(y -
        y_0)^2 + R_2.
    $  For non-degeneracy of solutions, we demand $A C - B^2  !=  0$, i.e. at
    $(x_0, y_0)$, we want $ 
        [  frac(diff ^2 f, diff  x diff  y)  ]^2  != 
          frac(diff ^2 f, diff  x^2)  
          frac(diff ^2 f, diff  y^2)  .
    $  

    If $A C - B^2 > 0$ and $diff ^2f \/ diff  x^2 > 0$, then we have found a
    point of minima; if $diff ^2 f \/ diff  x^2 < 0$, then we have found a
    point of maximum. If $A C - B^2 < 0$, then we have found a saddle point.
]

#example[
    Suppose that we wish to maximize the function $f: RR^2 -> RR$, given an
    equation of constraint $g = 0$, where $g: RR^2 -> RR$. Using the method
    of Lagrange multipliers, we look for solutions of the system $ 
        cases(
            grad(f) (x, y) +  lambda   grad(&g) (x, y) &= 0,
            &g(x, y) &= 0.
        )
    $
]


================================================
FILE: manual.typ
================================================
#import "manual_template.typ": *
#import "theorems.typ": *
#show: thmrules

#show: project.with(
  title: "typst-theorems",
  authors: (
    "sahasatvik",
  ),
  url: "https://github.com/sahasatvik/typst-theorems"
)



= Introduction

The `typst-theorems` package provides `Typst` functions that help create
numbered `theorem` environments. This is heavily inspired by the `\newtheorem`
functionality of `LaTeX`.

A _theorem environment_ lets you wrap content together with automatically
updating _numbering_ information. Such environments use internal `state`
counters for this purpose. Environments can

- share the same counter (_Theorems_ and _Lemmas_ often do so)
- keep a global count, or be attached to
  - other environments (_Corollaries_ are often numbered based upon the parent _Theorem_)
  - headings
- have a numbering level depth fixed (for instance, use only top level heading
  numbers)
- be referenced elsewhere in the document, via `label`s


= Using `typst-theorems`

Import all functions provided by `typst-theorems` using
```typst
#import "theorems.typ": *
#show: thmrules
```
The second line is crucial for displaying `thmenv`s and references correctly!

The core of this module consists of `thmenv`.
The functions `thmbox`, `thmplain`, and `thmproof` provide some simple
defaults for the appearance of `thmenv`s.


= Feature demonstration <feat>

Create box-like _theorem environments_ using `thmbox`, a wrapper around
`thmenv` which provides some simple defaults.

```typst
#let theorem = thmbox(
  "theorem",              // identifier
  "Theorem",              // head
  fill: rgb("#e8e8f8")
)
```
#let theorem = thmbox(
  "theorem",
  "Theorem",
  fill: rgb("#e8e8f8")
)

Such definitions are convenient to place in the preamble or a template; use
the environment in your document via
```typst
#theorem("Euclid")[
  There are infinitely many primes.
] <euclid>
```

This produces the following.
#theorem("Euclid")[
  There are infinitely many primes.
] <euclid>

Note that the `name` is optional. This `theorem` environment will be numbered
based on its parent `heading` counter, with successive `theorem`s
automatically updating the final index.

The `<euclid>` label can be used to refer to this Theorem via the reference
`@euclid`. Go to @references to read more.

You can create another environment which uses the same counter, say for
_Lemmas_, as follows.

```typst
#let lemma = thmbox(
  "theorem",              // identifier - same as that of theorem
  "Lemma",                // head
  fill: rgb("#efe6ff")
)

#lemma[
  If $n$ divides both $x$ and $y$, it also divides $x - y$.
]
```
#let lemma = thmbox(
  "theorem",
  "Lemma",
  fill: rgb("#efe6ff")
)
#lemma[
  If $n$ divides both $x$ and $y$, it also divides $x - y$.
]

You can _attach_ other environments to ones defined earlier. For instance,
_Corollaries_ can be created as follows.

```typst
#let corollary = thmbox(
  "corollary",            // identifier
  "Corollary",            // head
  base: "theorem",        // base - use the theorem counter
  fill: rgb("#f8e8e8")
)

#corollary(numbering: "1.1")[
  If $n$ divides two consecutive natural numbers, then $n = 1$.
]
```
#let corollary = thmbox(
  "corollary",
  "Corollary",
  base: "theorem",
  fill: rgb("#f8e8e8")
)

#corollary(numbering: "1.1")[
  If $n$ divides two consecutive natural numbers, then $n = 1$.
]

Note that we have provided a `numbering` string; this can be any valid
numbering pattern as described in the
#link("https://typst.app/docs/reference/meta/numbering/")[numbering]
documentation.


== Proofs

The `thmproof` function gives nicer defaults for formatting proofs.
```typst
#let proof = thmproof("proof", "Proof")

#proof([of @euclid])[
  Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration
  of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list,
  it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$.  Since
  $p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a
  contradiction.
]
```
#let proof = thmproof("proof", "Proof")

#proof([of @euclid])[
  Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration
  of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list,
  it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$.  Since
  $p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a
  contradiction.
]

If your proof ends in a block equation, or a list/enum, you can place
`qedhere` to correctly position the qed symbol.
```typst
#theorem[
  There are arbitrarily long stretches of composite numbers.
]
#proof[
  For any $n > 2$, consider $
    n! + 2, quad n! + 3, quad ..., quad n! + n #qedhere
  $
]
```
#theorem[
  There are arbitrarily long stretches of composite numbers.
]
#proof[
  For any $n > 2$, consider $
    n! + 2, quad n! + 3, quad ..., quad n! + n #qedhere
  $
]

*Caution*: The `qedhere` symbol does not play well with numbered/multiline
equations!

You can set a custom qed symbol (say $square$) by setting the appropriate
option in `thmrules` as follows.
```typst
#show: thmrules.with(qed-symbol: $square$)
```

== Suppressing numbering
Supplying `numbering: none` to an environment suppresses numbering for that
block, and prevents it from updating its counter.

```typst
#let example = thmplain(
  "example",
  "Example"
).with(numbering: none)

#example[
  The numbers $2$, $3$, and $17$ are prime.
]
```
#let example = thmplain(
  "example",
  "Example"
).with(numbering: none)

#example[
  The numbers $2$, $3$, and $17$ are prime.
]

Here, we have used the `thmplain` function, which is identical to `thmbox` but
sets some plainer defaults. You can also write
```typst
#lemma(numbering: none)[
  The square of any even number is divisible by $4$.
]
#lemma[
  The square of any odd number is one more than a multiple of $4$.
]
```
#lemma(numbering: none)[
  The square of any even number is divisible by $4$.
]
#lemma[
  The square of any odd number is one more than a multiple of $4$.
]

Note that the last _Lemma_ is _not_ numbered 3.1.2!

You can also override the automatic numbering as follows.
```typst
#lemma(number: "42")[
  The square of any natural number cannot be two more than a multiple of 4.
]
```
#lemma(number: "42")[
  The square of any natural number cannot be two more than a multiple of 4.
]

Note that this does _not_ affect the counters either!


== Limiting depth

You can limit the number of levels of the `base` numbering used as follows.
```typst
#let definition = thmbox(
  "definition",
  "Definition",
  base_level: 1,          // take only the first level from the base
  stroke: rgb("#68ff68") + 1pt
)

#definition("Prime numbers")[
  A natural number is called a _prime number_ if it is greater than $1$ and
  cannot be written as the product of two smaller natural numbers. <prime>
]
```
#let definition = thmbox(
  "definition",
  "Definition",
  base_level: 1,
  stroke: rgb("#68ff68") + 1pt
)
#definition("Prime numbers")[
  A natural number is called a _prime number_ if it is greater than $1$ and
  cannot be written as the product of two smaller natural numbers. <prime>
]

Note that this environment is _not_ numbered 3.2.1!

```typst
#definition("Composite numbers")[
  A natural number is called a _composite number_ if it is greater than $1$
  and not prime.
]
```
#definition("Composite numbers")[
  A natural number is called a _composite number_ if it is greater than $1$
  and not prime.
]

Setting a `base_level` higher than what `base` provides will introduce padded
zeroes.

```typst
#example(base_level: 4, numbering: "1.1")[
  The numbers $4$, $6$, and $42$ are composite.
]
```
#example(base_level: 4, numbering: "1.1")[
  The numbers $4$, $6$, and $42$ are composite.
]


== Custom formatting
The `thmbox` function lets you specify rules for formatting the `title`, the
`name`, and the `body` individually. Here, the `title` refers to the `head`
and `number` together.

```typst
#let proof-custom = thmplain(
  "proof",
  "Proof",
  base: "theorem",
  titlefmt: smallcaps,
  bodyfmt: body => [
    #body #h(1fr) $square$    // float a QED symbol to the right
  ]
).with(numbering: none)

#lemma[
  All even natural numbers greater than 2 are composite.
]
#proof-custom[
  Every even natural number $n$ can be written as the product of the natural
  numbers $2$ and $n\/2$. When $n > 2$, both of these are smaller than $2$
  itself.
]
```
#let proof-custom = thmplain(
  "proof",
  "Proof",
  base: "theorem",
  titlefmt: smallcaps,
  bodyfmt: body => [
    #body #h(1fr) $square$
  ]
).with(numbering: none)

#lemma[
  All even natural numbers greater than 2 are composite.
]
#proof-custom[
  Every even natural number $n$ can be written as the product of the natural
  numbers $2$ and $n\/2$. When $n > 2$, both of these are smaller than $2$
  itself.
]

You can go even further and use the `thmenv` function directly. It accepts an
`identifier`, a `base`, a `base_level`, and a `fmt` function.
```typst
#let notation = thmenv(
  "notation",                 // identifier
  none,                       // base - do not attach, count globally
  none,                       // base_level - use the base as-is
  (name, number, body, color: black) => [
                              // fmt - format content using the environment
                              // name, number, body, and an optional color
    #text(color)[#h(1.2em) *Notation (#number) #name*]:
    #h(0.2em)
    #body
    #v(0.5em)
  ]
).with(numbering: "I")        // use Roman numerals

#notation[
  The variable $p$ is reserved for prime numbers.
]
#notation("for Reals", color: green)[
  The variable $x$ is reserved for real numbers.
]
```
#let notation = thmenv(
  "notation",                 // identifier
  none,                       // base - do not attach, count globally
  none,                       // base_level - use the base as-is
  (name, number, body, color: black) => [
                              // fmt - format content using the environment name, number, body, and an optional color
    #text(color)[#h(1.2em) *Notation (#number) #name*]:
    #h(0.2em)
    #body
    #v(0.5em)
  ]
).with(numbering: "I")        // use Roman numerals

#notation[
  The variable $p$ is reserved for prime numbers.
]
#notation("for Reals", color: green)[
  The variable $x$ is reserved for real numbers.
]

Note that the `color: green` named argument supplied to the theorem
environment gets passed to the `fmt` function. In general, all extra named
arguments supplied to the theorem will be passed to `fmt`.
On the other hand, the positional argument `"for Reals"` will always be
interpreted as the `name` argument in `fmt`.

```typst
#lemma(title: "Lem.", stroke: 1pt)[
  All multiples of 3 greater than 3 are composite.
]
```
#lemma(title: "Lem.", stroke: 1pt)[
  All multiples of 3 greater than 3 are composite.
]

Here, we override the `title` (which defaults to the `head`) as well as the
`stroke` in the `fmt` produced by `thmbox`. All `block` arguments can be
overridden in `thmbox` environments in this way.


== Labels and references <references>

You can place a `<label>` outside a theorem environment, and reference it
later via `@` references! For example, go back to @euclid.

```typst
Recall that there are infinitely many prime numbers via @euclid.
```
#pad(
  left: 1.2em,
  [
    Recall that there are infinitely many prime numbers via @euclid.
  ]
)


```typst
You can reference future environments too, like @oddprime[Cor.].
```
#pad(
  left: 1.2em,
  [
    You can reference future environments too, like @oddprime[Cor.].
  ]
)

```typst
#lemma(supplement: "Lem.", refnumbering: "(1.1)")[
  All primes apart from $2$ and $3$ are of the form $6k plus.minus 1$.
] <primeform>

You can modify the supplement and numbering to be used in references, like @primeform.
```
#lemma(supplement: "Lem.", refnumbering: "(1.1)")[
  All primes apart from $2$ and $3$ are of the form $6k plus.minus 1$.
] <primeform>

You can modify the supplement and numbering to be used in references, like @primeform.

*Caution*: Links created by references to `thmenv`s will be styled according
to `#show link:` rules.


== Overriding `base`

```typst
#let remark = thmplain("remark", "Remark", base: "heading")
#remark[
  There are infinitely many composite numbers.
]

#corollary[
  All primes greater than $2$ are odd.
] <oddprime>
#remark(base: "corollary")[
  Two is a _lone prime_.
]
```

#let remark = thmplain("remark", "Remark", base: "heading")
#remark[
  There are infinitely many composite numbers.
]
#corollary[
  All primes greater than $2$ are odd.
] <oddprime>
#remark(base: "corollary")[
  Two is a _lone prime_.
]

This `remark` environment, which would normally be attached to the current
_heading_, now uses the `corollary` as a base.


#v(4em)

= Function reference
== `thmenv`

The `thmenv` function produces a _theorem environment_.
```typst
#let thmenv(
  identifier,             // environment counter name
  base,                   // base counter name, can be "heading" or none
  base_level,             // number of base number levels to use
  fmt                     // formatting function of the form
                          // (name, number, body, ..args) -> content
) = { ... }
```

The `fmt` function must accept a theorem `name`, `number`, `body`, and produce
formatted content. It may also accept additional positional arguments, via
`args`.

A _theorem environment_ is itself a map of the following form.
```typst
(
  ..args,
  body,                   // body content
  number: auto,           // number, overrides numbering if present
  numbering: "1.1",       // numbering style, can be a function
  refnumbering: auto,     // numbering style used in references,
                          // defaults to "numbering"
  supplement: identifier, // supplement used in references
  base: base,             // base counter name override
  base_level: base_level  // base_level override
) -> content
```

Positional arguments in `args` are as follows
- `name`: The name of the theorem, typically displayed after the title.

All additional named arguments in `args` will be passed on to the associated
`fmt` function supplied in `thmenv`.


== `thmbox` and `thmplain`

The `thmbox` wraps `thmenv`, supplying a box-like `fmt` function.
```typst
#let thmbox(
  identifier,             // identifier
  head,                   // head - common name, used in the title
  ..blockargs,            // named arguments, passed to #block
  supplement: auto,       // supplement for references, defaults to "head"
  padding: (top: 0.5em, bottom: 0.5em),
                          // box padding, passed to #pad
  namefmt: x => [(#x)],   // formatting for name
  titlefmt: strong,       // formatting for title (head + number)
  bodyfmt: x => x,        // formatting for body
  separator: [#h(0.1em):#h(0.2em)],
                          // separator inserted between name and body
  base: "heading",        // base - defaults to using headings
  base_level: none,       // base_level - defaults to using base as-is
) = { ... }
```

The `thmbox` function sets the following defaults for the `block`.
```typst
(
  width: 100%,
  inset: 1.2em,
  radius: 0.3em,
  breakable: false,
)
```

The `thmplain` function is identical to `thmbox`, except with plainer
defaults.

```typst
#let thmplain = thmbox.with(
  padding: (top: 0em, bottom: 0em),
  breakable: true,
  inset: (top: 0em, left: 1.2em, right: 1.2em),
  namefmt: name => emph([(#name)]),
  titlefmt: emph,
)
```


== `thmproof`, `proof-bodyfmt` and `qedhere`

The `thmproof` function is identical to `thmplain`, except with defaults
appropriate for proofs.

```typst
#let thmproof(..args) = thmplain(
    ..args,
    namefmt: emph,
    bodyfmt: proof-bodyfmt,
    ..args.named()
).with(numbering: none)
```

The `proof-bodyfmt` function is a `bodyfmt` function that automatically places
a qed symbol at the end of the body.

You can place `#qedhere` inside a block equation, or at the end of a list/enum
item to place the qed symbol on the same line.


== `thmrules`

The `thmrules` show rule sets important styling rules for theorem
environments, references, and equations in proofs.

```typst
#let thmrules(
  qed-symbol: $qed$,      // QED symbol used in proofs
  doc
) = { ... }
```


= Acknowledgements

Thanks to
- #link("https://github.com/MJHutchinson")[MJHutchinson] for suggesting and
  implementing the `base_level` and `base: none` features,
- #link("https://github.com/rmolinari")[rmolinari] for suggesting and
  implementing the `separator: ...` feature,
- #link("https://github.com/DVDTSB")[DVDTSB] for contributing
  - the idea of passing named arguments from the theorem directly to the `fmt`
    function.
  - the `number: ...` override feature.
  - the `title: ...` override feature in `thmbox`.
- The awesome devs of #link("https://typst.app/")[typst.app] for their
  support.


================================================
FILE: manual_examples.typ
================================================
#import "manual_template.typ": *
#import "theorems.typ": *
#show: thmrules

#show: project.with(
  title: "typst-theorems",
  authors: (
    "sahasatvik",
  ),
  url: "https://github.com/sahasatvik/typst-theorems"
)



= Introduction

This document only includes the examples given in the manual; each one of
these has been explained in full detail there.


= Feature demonstration

#let theorem = thmbox(
  "theorem",
  "Theorem",
  fill: rgb("#e8e8f8")
)

#theorem("Euclid")[
  There are infinitely many primes.
] <euclid>


#let lemma = thmbox(
  "theorem",
  "Lemma",
  fill: rgb("#efe6ff")
)

#lemma[
  If $n$ divides both $x$ and $y$, it also divides $x - y$.
]

#let corollary = thmbox(
  "corollary",
  "Corollary",
  base: "theorem",
  fill: rgb("#f8e8e8")
)

#corollary(numbering: "1.1")[
  If $n$ divides two consecutive natural numbers, then $n = 1$.
]


== Proofs

#let proof = thmproof("proof", "Proof")

#proof([of @euclid])[
  Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration
  of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list,
  it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$.  Since
  $p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a
  contradiction.
]

#theorem[
  There are arbitrarily long stretches of composite numbers.
]
#proof[
  For any $n > 2$, consider $
    n! + 2, quad n! + 3, quad ..., quad n! + n #qedhere
  $
]


== Suppressing numbering

#let example = thmplain(
  "example",
  "Example"
).with(numbering: none)

#example[
  The numbers $2$, $3$, and $17$ are prime.
]

#lemma(numbering: none)[
  The square of any even number is divisible by $4$.
]
#lemma[
  The square of any odd number is one more than a multiple of $4$.
]

#lemma(number: "42")[
  The square of any natural number cannot be two more than a multiple of 4.
]


== Limiting depth

#let definition = thmbox(
  "definition",
  "Definition",
  base_level: 1,
  stroke: rgb("#68ff68") + 1pt
)
#definition("Prime numbers")[
  A natural number is called a _prime number_ if it is greater than $1$ and
  cannot be written as the product of two smaller natural numbers. <prime>
]

#definition("Composite numbers")[
  A natural number is called a _composite number_ if it is greater than $1$
  and not prime.
]

#example(base_level: 4, numbering: "1.1")[
  The numbers $4$, $6$, and $42$ are composite.
]


== Custom formatting

#let proof-custom = thmplain(
  "proof",
  "Proof",
  base: "theorem",
  titlefmt: smallcaps,
  bodyfmt: body => [
    #body #h(1fr) $square$
  ]
).with(numbering: none)

#lemma[
  All even natural numbers greater than 2 are composite.
]
#proof-custom[
  Every even natural number $n$ can be written as the product of the natural
  numbers $2$ and $n\/2$. When $n > 2$, both of these are smaller than $2$
  itself.
]

#let notation = thmenv(
  "notation",                 // identifier
  none,                       // base - do not attach, count globally
  none,                       // base_level - use the base as-is
  (name, number, body, color: black) => [
                              // fmt - format content using the environment name, number, body, and an optional color
    #text(color)[#h(1.2em) *Notation (#number) #name*]:
    #h(0.2em)
    #body
    #v(0.5em)
  ]
).with(numbering: "I")        // use Roman numerals

#notation[
  The variable $p$ is reserved for prime numbers.
]
#notation("for Reals", color: green)[
  The variable $x$ is reserved for real numbers.
]

#lemma(title: "Lem.", stroke: 1pt)[
  All multiples of 3 greater than 3 are composite.
]


== Labels and references <references>

#pad(
  left: 1.2em,
  [
    Recall that there are infinitely many prime numbers via @euclid.
  ]
)


#pad(
  left: 1.2em,
  [
    You can reference future environments too, like @oddprime[Cor.].
  ]
)

#lemma(supplement: "Lem.", refnumbering: "(1.1)")[
  All primes apart from $2$ and $3$ are of the form $6k plus.minus 1$.
] <primeform>

You can modify the supplement and numbering to be used in references, like @primeform.


== Overriding `base`


#let remark = thmplain("remark", "Remark", base: "heading")
#remark[
  There are infinitely many composite numbers.
]
#corollary[
  All primes greater than $2$ are odd.
] <oddprime>
#remark(base: "corollary")[
  Two is a _lone prime_.
]


================================================
FILE: manual_template.typ
================================================
#let project(title: "", authors: (), url: "", body) = {
  set page(paper: "a4", numbering: "1", number-align: center)
  set document(author: authors, title: title)
  set text(font: "Libertinus Serif", lang: "en")
  set heading(numbering: "1.1.")
  set par(justify: true)
  set list(marker: ([•], [--]))
  show heading: it => pad(bottom: 0.5em, it)
  show raw.where(block: true): it => pad(left: 4em, it)
  show link: it => underline(text(fill: blue, it))



  align(center)[
    #block(text(weight: 700, 1.75em, title))
  ]

  pad(
    top: 0.5em,
    bottom: 2em,
    x: 2em,
    grid(
      columns: (1fr,) * calc.min(3, authors.len()),
      gutter: 1em,
      ..authors.map(author => align(center)[
        #author \
        #link(url)
      ]),
    ),
  )

  outline(indent: true)

  v(2em)

  body
}


================================================
FILE: theorems.typ
================================================
// Store theorem environment numbering

#let thmcounters = state("thm",
  (
    "counters": ("heading": ()),
    "latest": ()
  )
)


#let thmenv(identifier, base, base_level, fmt) = {

  let global_numbering = numbering

  return (
    ..args,
    body,
    number: auto,
    numbering: "1.1",
    refnumbering: auto,
    supplement: identifier,
    base: base,
    base_level: base_level
  ) => {
    let name = none
    if args != none and args.pos().len() > 0 {
      name = args.pos().first()
    }
    if refnumbering == auto {
      refnumbering = numbering
    }
    let result = none
    if number == auto and numbering == none {
      number = none
    }
    if number == auto and numbering != none {
      result = context {
        let heading-counter = counter(heading).get()
        return thmcounters.update(thmpair => {
          let counters = thmpair.at("counters")
          // Manually update heading counter
          counters.at("heading") = heading-counter
          if not identifier in counters.keys() {
            counters.insert(identifier, (0, ))
          }

          let tc = counters.at(identifier)
          if base != none {
            let bc = counters.at(base)

            // Pad or chop the base count
            if base_level != none {
              if bc.len() < base_level {
                bc = bc + (0,) * (base_level - bc.len())
              } else if bc.len() > base_level{
                bc = bc.slice(0, base_level)
              }
            }

            // Reset counter if the base counter has updated
            if tc.slice(0, -1) == bc {
              counters.at(identifier) = (..bc, tc.last() + 1)
            } else {
              counters.at(identifier) = (..bc, 1)
            }
          } else {
            // If we have no base counter, just count one level
            counters.at(identifier) = (tc.last() + 1,)
            let latest = counters.at(identifier)
          }

          let latest = counters.at(identifier)
          return (
            "counters": counters,
            "latest": latest
          )
        })
      }

      number = context {
        global_numbering(numbering, ..thmcounters.get().at("latest"))
      }
    }

    return figure(
      result +  // hacky!
      fmt(name, number, body, ..args.named()) +
      [#metadata(identifier) <meta:thmenvcounter>],
      kind: "thmenv",
      outlined: false,
      caption: name,
      supplement: supplement,
      numbering: refnumbering,
    )
  }
}


#let thmbox(
  identifier,
  head,
  ..blockargs,
  supplement: auto,
  padding: (top: 0.5em, bottom: 0.5em),
  namefmt: x => [(#x)],
  titlefmt: strong,
  bodyfmt: x => x,
  separator: [#h(0.1em):#h(0.2em)],
  base: "heading",
  base_level: none,
) = {
  if supplement == auto {
    supplement = head
  }
  let boxfmt(name, number, body, title: auto, ..blockargs_individual) = {
    if not name == none {
      name = [ #namefmt(name)]
    } else {
      name = []
    }
    if title == auto {
      title = head
    }
    if not number == none {
      title += " " + number
    }
    title = titlefmt(title)
    body = bodyfmt(body)
    pad(
      ..padding,
      block(
        width: 100%,
        inset: 1.2em,
        radius: 0.3em,
        breakable: false,
        ..blockargs.named(),
        ..blockargs_individual.named(),
        [#title#name#separator#body]
      )
    )
  }
  return thmenv(
    identifier,
    base,
    base_level,
    boxfmt
  ).with(
    supplement: supplement,
  )
}


#let thmplain = thmbox.with(
  padding: (top: 0em, bottom: 0em),
  breakable: true,
  inset: (top: 0em, left: 1.2em, right: 1.2em),
  namefmt: name => emph([(#name)]),
  titlefmt: emph,
)


// Track whether the qed symbol has already been placed in a proof
#let thm-qed-done = state("thm-qed-done", false)

// The configured QED symbol
#let thm-qed-symbol = state("thm-qed-symbol", $qed$)

// Show the qed symbol, update state
#let thm-qed-show = {
  thm-qed-done.update(true)
  context [#thm-qed-symbol.get()]
}

// If placed in a block equation/enum/list, place a qed symbol to its right
#let qedhere = metadata("thm-qedhere")

// Checks if content x contains the qedhere tag
#let thm-has-qedhere(x) = {
  if x == "thm-qedhere" {
    return true
  }

  if type(x) == content {
    for (f, c) in x.fields() {
      if thm-has-qedhere(c) {
        return true
      }
    }
  }

  if type(x) == array {
    for c in x {
      if thm-has-qedhere(c) {
        return true
      }
    }
  }

  return false
}


// bodyfmt for proofs
#let proof-bodyfmt(body) = {
  thm-qed-done.update(false)
  body
  context {
    if thm-qed-done.at(here()) == false {
      h(1fr)
      thm-qed-show
    }
  }
}

#let thmproof(..args) = thmplain(
    ..args,
    namefmt: emph,
    bodyfmt: proof-bodyfmt,
    ..args.named()
).with(numbering: none)


#let thmrules(qed-symbol: $qed$, doc) = {

  show figure.where(kind: "thmenv"): set block(breakable: true)
  show figure.where(kind: "thmenv"): set align(start)
  show figure.where(kind: "thmenv"): it => it.body

  show ref: it => {
    if it.element == none {
      return it
    }
    if it.element.func() != figure {
      return it
    }
    if it.element.kind != "thmenv" {
      return it
    }

    let supplement = it.element.supplement
    if it.citation.supplement != none {
      supplement = it.citation.supplement
    }

    let loc = it.element.location()
    let thms = query(selector(<meta:thmenvcounter>).after(loc))
    let number = thmcounters.at(thms.first().location()).at("latest")
    return link(
      it.target,
      [#supplement~#numbering(it.element.numbering, ..number)]
    )
  }

  show math.equation.where(block: true): eq => {
    if thm-has-qedhere(eq) and thm-qed-done.at(eq.location()) == false {
      grid(
        columns: (1fr, auto, 1fr),
        [], eq, align(right + horizon)[#thm-qed-show]
      )
    } else {
      eq
    }
  }

  show enum.item: it => {
    show metadata.where(value: "thm-qedhere"): {
      h(1fr)
      thm-qed-show
    }
    it
  }

  show list.item: it => {
    show metadata.where(value: "thm-qedhere"): {
      h(1fr)
      thm-qed-show
    }
    it
  }

  thm-qed-symbol.update(qed-symbol)

  doc
}
Download .txt
gitextract_xw9617op/

├── LICENSE
├── README.md
├── basic.typ
├── differential_calculus.typ
├── manual.typ
├── manual_examples.typ
├── manual_template.typ
└── theorems.typ
Condensed preview — 8 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (64K chars).
[
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2023 Satvik Saha\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 3645,
    "preview": "# typst-theorems\n\nAn implementation of numbered theorem environments in\n[typst](https://github.com/typst/typst).\nAvailab"
  },
  {
    "path": "basic.typ",
    "chars": 1562,
    "preview": "#import \"theorems.typ\": *\n#show: thmrules.with(qed-symbol: $square$)\n\n#set page(width: 16cm, height: auto, margin: 1.5cm"
  },
  {
    "path": "differential_calculus.typ",
    "chars": 26747,
    "preview": "#import \"theorems.typ\": *\n#show: thmrules\n\n// Define theorem environments\n\n#let theorem = thmbox(\n  \"theorem\",\n  \"Theore"
  },
  {
    "path": "manual.typ",
    "chars": 16951,
    "preview": "#import \"manual_template.typ\": *\n#import \"theorems.typ\": *\n#show: thmrules\n\n#show: project.with(\n  title: \"typst-theorem"
  },
  {
    "path": "manual_examples.typ",
    "chars": 4325,
    "preview": "#import \"manual_template.typ\": *\n#import \"theorems.typ\": *\n#show: thmrules\n\n#show: project.with(\n  title: \"typst-theorem"
  },
  {
    "path": "manual_template.typ",
    "chars": 806,
    "preview": "#let project(title: \"\", authors: (), url: \"\", body) = {\n  set page(paper: \"a4\", numbering: \"1\", number-align: center)\n  "
  },
  {
    "path": "theorems.typ",
    "chars": 6212,
    "preview": "// Store theorem environment numbering\n\n#let thmcounters = state(\"thm\",\n  (\n    \"counters\": (\"heading\": ()),\n    \"latest"
  }
]

About this extraction

This page contains the full source code of the sahasatvik/typst-theorems GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 8 files (59.9 KB), approximately 19.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!