Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/preview/peano/0.1.0/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 F.X.P.

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.
105 changes: 105 additions & 0 deletions packages/preview/peano/0.1.0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!-- This is a program-generated file. Do not edit it directly. -->

# Typst package: `peano`

`peano` is a math utility package that provides you with representations of specialized number types, mathematic special functions, number theory related operations and so on. The name of the package comes from [Peano axioms](https://en.wikipedia.org/wiki/Peano_axioms), which builds up the framework of [natural numbers](https://en.wikipedia.org/wiki/Natural_number) &#x2115;, one of the the most elementary concepts in mathematics, aiming to convey this package's orientation as a simple utility package.

## Number types

`peano` currently supports two number types and their arithmetics:

- `rational`: representation of [rational numbers](https://en.wikipedia.org/wiki/Rational_number) &#x211a; in the form of fractions
- `complex`: representation of[ complex numbers](https://en.wikipedia.org/wiki/Complex_number) &#x2102;

It is a pity that Typst doesn't currently support custom types and overloading operators, so actually these numbers are represented by Typst's `dictionary` type, and you have to invoke specialized methods in the corresponding sub-module to perform arithmetic operations over these numbers.

To use these number types you have to first import the corresponding sub-module:

```typ
#import "@preview/peano:0.1.0"
#import peano.number: rational as q, complex as c
```

Each sub-module contains a method called `from`, by which you can directly create a number instance from a string or a built-in Typst number type. Arithmetic methods in these modules will automatically convert all parameters to the expected number type by this `from` method, so you can simply input strings and built-in number types as parameters.

### Rational numbers

```typ
#import "@preview/peano:0.1.0"
#import peano.number: rational as q

#q.from("1/2") // from string
#q.from(2, 3) // from numerator and denominator
#q.add("1/2", "1/3", "-1/5") // addition
#q.sub("2/3", "1/4") // subtraction
#q.mul("3/4", "2/3", "4/5") // multiplication
#q.div("5/6", "3/2") // division
#q.limit-den(calc.pi, 10000) // limiting maximum denominator
#q.pow("3/2", 5) // raising to an integer power

#q.to-str(q.from(113, 355)) // convert to string
#q.to-math(q.from(113, 355)) // convert to formatted `math.equation` element
```

Currently, `rational.from` supports fraction notation and decimal notation with an optional set of [repeating digits](https://en.wikipedia.org/wiki/Repeating_decimal) enclosed in square brackets.

```typ
#import "@preview/peano:0.1.0"
#import peano.number: rational as q

// normal values

#q.from("1/2")
#q.from("-2/3") // sign before numerator
#q.from("5/-4") // sign before denominator

// infinity or indeterminate values

#q.from("1/0") // infinity
#q.from("-1/0") // negative infinity
#q.from("1/-0") // also negative infinity
#q.from("0/0") // NaN

// decimal notation

#q.from("0.25")
#q.from("0.[3]") // repeating part wrapped in bracket
#q.from("3.[142857]")
#q.from("1.14[514]")
#q.from("1[2.3]") // repeating part can cross decimal point
```

A rational number can be displayed in both string and math format by using the `rational.to-str` and `rational.to-math` methods.

```typ
#import "@preview/peano:0.1.0"
#import peano.number: rational as q

#let value = q.from(113, 355)

#q.to-str(value)
#q.to-math(value)
```

### Complex numbers

```typ
#import "@preview/peano:0.1.0"
#import peano.number: complex as c

#c.from("1+2i") // from string
#c.from(3, 4) // from real and imaginary parts
#c.add("1+2i", "3+4i", "-2+3i", "6-5i", "2i")
```

## Number theory

The number theory sub module `peano.ntheory` currently supports prime factorization and the extended Euclidean algorithm that gives out the coefficients $u$ and $v$ in Bézout's identity $\gcd (a, b) = u a + v b$.

## Mathematic functions

The `peano.func` sub-module provides a collection of basic mathematic functions. Some of them are also included in Typst's `calc` module, but extended to support complex number input. Other self-defined functions also support function input if they can be defined in the complex field &#x210;.

### Special functions

The `peano.func.special` sub-module include special functions such as the gamma function, zeta function, Gauss error function.
123 changes: 123 additions & 0 deletions packages/preview/peano/0.1.0/src/_impl/func/common.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// -> func/common.typ

#import "init.typ": extend-calc-func-to-complex, define-func-with-complex, call-wasm-real-func, call-wasm-complex-func
#import "../number/complex.typ" as c: complex, is-complex, make-complex
#let math-utils-wasm = plugin("../math-utils.wasm")

#let /*pub*/ exp = extend-calc-func-to-complex("exp")
#let /*pub*/ ln(x) = {
if is-complex(x) {
call-wasm-complex-func(math-utils-wasm.ln_complex, x)
} else if x < 0 {
call-wasm-complex-func(math-utils-wasm.ln_complex, complex(x))
} else if x == 0 {
-float.inf
} else {
calc.ln(x)
}
}

#let log-complex(x, base: 10.0) = {
if base == 2 {
call-wasm-complex-func(math-utils-wasm.log2_complex, x)
} else if base == 10 {
call-wasm-complex-func(math-utils-wasm.log10_complex, x)
} else {
c.div(
call-wasm-complex-func(math-utils-wasm.ln_complex, x),
ln(base),
)
}
}

#let /*pub*/ log(x, base: 10.0) = {
if is-complex(x) {
log-complex(x, base: base)
} else if x < 0 {
log-complex(complex(x), base: base)
} else if x == 0 {
if is-complex(base) or base <= 0 {
c.nan
} else if base < 1 {
float.inf
} else if base == 1 {
float.nan
} else {
-float.inf
}
} else {
calc.log(x, base: base)
}
}

#let /*pub*/ sin = extend-calc-func-to-complex("sin")
#let /*pub*/ cos = extend-calc-func-to-complex("cos")
#let /*pub*/ tan = extend-calc-func-to-complex("tan")
#let /*pub*/ sinh = extend-calc-func-to-complex("sinh")
#let /*pub*/ cosh = extend-calc-func-to-complex("cosh")
#let /*pub*/ tanh = extend-calc-func-to-complex("tanh")

#let /*pub*/ asin(x) = {
if is-complex(x) {
call-wasm-complex-func(math-utils-wasm.asin_complex, x)
} else if x < -1 or x > 1 {
call-wasm-complex-func(math-utils-wasm.asin_complex, complex(x))
} else {
calc.asin(x)
}
}

#let /*pub*/ acos(x) = {
if is-complex(x) {
call-wasm-complex-func(math-utils-wasm.acos_complex, x)
} else if x < -1 or x > 1 {
call-wasm-complex-func(math-utils-wasm.acos_complex, complex(x))
} else {
calc.acos(x)
}
}

#let /*pub*/ atan = extend-calc-func-to-complex("atan")
#let /*pub*/ asinh = define-func-with-complex("asinh")

#let /*pub*/ acosh(x) = {
if x < 1 {
x = complex(x)
}
if is-complex(x) {
call-wasm-complex-func(math-utils-wasm.acosh_complex, x)
} else {
call-wasm-real-func(math-utils-wasm.atanh, x)
}
}

#let /*pub*/ atanh(x) = {
if x < -1 or x > 1 {
x = complex(x)
}
if is-complex(x) {
call-wasm-complex-func(math-utils-wasm.atanh_complex, x)
} else {
call-wasm-real-func(math-utils-wasm.atanh, x)
}
}

#let /*pub*/ abs(x) = {
if is-complex(x) {
let (re, im) = x
calc.norm(re, im)
} else {
calc.abs(x)
}
}

#let /*pub*/ arg(x) = {
if is-complex(x) {
let (re, im) = x
calc.atan2(re, im)
} else if x >= 0 {
0deg
} else {
180deg
}
}
81 changes: 81 additions & 0 deletions packages/preview/peano/0.1.0/src/_impl/func/init.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#import "../number/complex.typ" as c: complex, is-complex, make-complex
#let math-utils-wasm = plugin("../math-utils.wasm")

#let calc-funcs = dictionary(calc)
#let math-utils-funcs = dictionary(math-utils-wasm)

#let call-wasm-complex-func(complex-func, x) = {
let result-bytes = complex-func(
x.re.to-bytes(),
x.im.to-bytes(),
)
let re = float.from-bytes(result-bytes.slice(0, 8))
let im = float.from-bytes(result-bytes.slice(8))
make-complex(re, im)
}

#let call-wasm-real-func(real-func, x) = {
let x = if type(x) == angle {
x.rad()
} else {
float(x)
}
float.from-bytes(real-func(x.to-bytes()))
}

#let extend-calc-func-to-complex(func-name) = {
let real-func = calc-funcs.at(func-name)
let complex-func = math-utils-funcs.at(func-name + "_complex")
x => {
if is-complex(x) {
call-wasm-complex-func(complex-func, x)
} else {
let x = if type(x) == angle {
x.rad()
} else {
x
}
real-func(x)
}
}
}

#let define-func-with-complex(func-name) = {
let real-func = math-utils-funcs.at(func-name)
let complex-func = math-utils-funcs.at(func-name + "_complex")
x => {
if is-complex(x) {
call-wasm-complex-func(complex-func, x)
} else {
call-wasm-real-func(real-func, x)
}
}
}

#let define-func-2-with-complex(func-name) = {
let real-func = math-utils-funcs.at(func-name)
let complex-func = math-utils-funcs.at(func-name + "_complex")
(x1, x2) => {
if is-complex(x1) or is-complex(x2) {
let x1 = complex(x1)
let x2 = complex(x2)
let result-bytes = complex-func(
x1.re.to-bytes(),
x1.im.to-bytes(),
x2.re.to-bytes(),
x2.im.to-bytes(),
)
let re = float.from-bytes(result-bytes.slice(0, 8))
let im = float.from-bytes(result-bytes.slice(8))
make-complex(re, im)
} else {
let x1 = float(x1)
let x2 = float(x2)
float.from-bytes(real-func(x1.to-bytes(), x2.to-bytes()))
}
}
}

#let calc-funcs-with-complex = calc-funcs.keys().filter(it => (it + "_complex") in math-utils-funcs)
#let extended-calc-funcs = calc-funcs-with-complex.map(it => (it, extend-calc-func-to-complex(it))).to-dict()

32 changes: 32 additions & 0 deletions packages/preview/peano/0.1.0/src/_impl/func/special.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// -> func/special.typ
/// Special mathematic functions.

#import "init.typ": define-func-with-complex, define-func-2-with-complex

/// The #link("https://en.wikipedia.org/wiki/Gamma_function")[$Gamma$ function],
/// defined by $Gamma(z) = integral_0^oo t^(z - 1) upright(e)^(-t) dif t$.
#let /*pub*/ gamma = define-func-with-complex("gamma")

/// The #link("https://en.wikipedia.org/wiki/Digamma_function")[digamma function],
/// which is the derivative of the logarithm of $Gamma$ function
/// $psi(z) = dif/(dif z) ln Gamma(z) = (Gamma'(z))/(Gamma(z))$.
#let /*pub*/ digamma = define-func-with-complex("digamma")

/// same as `digamma`
#let /*pub*/ psi = digamma

/// The #link("https://en.wikipedia.org/wiki/Error_function")[Gauss error function],
/// defined by $erf z = 2/sqrt(pi) integral_0^z e^(-t^2) dif t$
#let /*pub*/ erf = define-func-with-complex("erf")

/// #link("https://en.wikipedia.org/wiki/Riemann_zeta_function")[Riemann's $zeta$ function]
/// defined by $zeta(s) = sum_(n = 1)^oo 1/(n^s)$ for $Re s > 1$ and its analytic continuation otherwise.
#let /*pub*/ zeta = define-func-with-complex("zeta")

/// The #link("https://en.wikipedia.org/wiki/Beta_function")[$Beta$ function],
/// defined by $Beta(z_1, z_2) = integral_0^1 t^(z_1 - 1) (1 - t)^(z_2 - 1) dif t$.
/// Equals to $(Gamma(z_1) Gamma(z_2))/(Gamma(z_1 + z_2))$
#let /*pub*/ beta = define-func-2-with-complex("beta")

// Euler's $gamma$ constant. Equals to $lim_(n -> oo) ((sum_(k = 1)^(n) 1/n) - ln n)$
#let /*pub*/ euler-gamma = -digamma(1)
Binary file not shown.
18 changes: 18 additions & 0 deletions packages/preview/peano/0.1.0/src/_impl/ntheory.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// -> ntheory.typ
/// number theory operations

#let math-utils-wasm = plugin("math-utils.wasm")

#let /*pub*/ prime-fac(num) = {
cbor(math-utils-wasm.prime_factors(num.to-bytes(endian: "little")))
}

/// Execute the extended Euclidean algorithm to find the greatest common devisor $d = gcd(a, b)$ for $a$ and $b$ with coefficients $u$, $v$ in Bézout's identity such that $d = u a + v b$. The return value is a triple $(d, u, v)$.
#let /*pub*/ egcd(a, b) = {
cbor(
math-utils-wasm.extended_gcd(
a.to-bytes(endian: "little"),
b.to-bytes(endian: "little"),
)
)
}
Loading