Skip to content

Commit 2be4a03

Browse files
authored
peano:0.1.0 (#3245)
1 parent fc2148b commit 2be4a03

File tree

19 files changed

+993
-0
lines changed

19 files changed

+993
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 F.X.P.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<!-- This is a program-generated file. Do not edit it directly. -->
2+
3+
# Typst package: `peano`
4+
5+
`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.
6+
7+
## Number types
8+
9+
`peano` currently supports two number types and their arithmetics:
10+
11+
- `rational`: representation of [rational numbers](https://en.wikipedia.org/wiki/Rational_number) &#x211a; in the form of fractions
12+
- `complex`: representation of[ complex numbers](https://en.wikipedia.org/wiki/Complex_number) &#x2102;
13+
14+
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.
15+
16+
To use these number types you have to first import the corresponding sub-module:
17+
18+
```typ
19+
#import "@preview/peano:0.1.0"
20+
#import peano.number: rational as q, complex as c
21+
```
22+
23+
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.
24+
25+
### Rational numbers
26+
27+
```typ
28+
#import "@preview/peano:0.1.0"
29+
#import peano.number: rational as q
30+
31+
#q.from("1/2") // from string
32+
#q.from(2, 3) // from numerator and denominator
33+
#q.add("1/2", "1/3", "-1/5") // addition
34+
#q.sub("2/3", "1/4") // subtraction
35+
#q.mul("3/4", "2/3", "4/5") // multiplication
36+
#q.div("5/6", "3/2") // division
37+
#q.limit-den(calc.pi, 10000) // limiting maximum denominator
38+
#q.pow("3/2", 5) // raising to an integer power
39+
40+
#q.to-str(q.from(113, 355)) // convert to string
41+
#q.to-math(q.from(113, 355)) // convert to formatted `math.equation` element
42+
```
43+
44+
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.
45+
46+
```typ
47+
#import "@preview/peano:0.1.0"
48+
#import peano.number: rational as q
49+
50+
// normal values
51+
52+
#q.from("1/2")
53+
#q.from("-2/3") // sign before numerator
54+
#q.from("5/-4") // sign before denominator
55+
56+
// infinity or indeterminate values
57+
58+
#q.from("1/0") // infinity
59+
#q.from("-1/0") // negative infinity
60+
#q.from("1/-0") // also negative infinity
61+
#q.from("0/0") // NaN
62+
63+
// decimal notation
64+
65+
#q.from("0.25")
66+
#q.from("0.[3]") // repeating part wrapped in bracket
67+
#q.from("3.[142857]")
68+
#q.from("1.14[514]")
69+
#q.from("1[2.3]") // repeating part can cross decimal point
70+
```
71+
72+
A rational number can be displayed in both string and math format by using the `rational.to-str` and `rational.to-math` methods.
73+
74+
```typ
75+
#import "@preview/peano:0.1.0"
76+
#import peano.number: rational as q
77+
78+
#let value = q.from(113, 355)
79+
80+
#q.to-str(value)
81+
#q.to-math(value)
82+
```
83+
84+
### Complex numbers
85+
86+
```typ
87+
#import "@preview/peano:0.1.0"
88+
#import peano.number: complex as c
89+
90+
#c.from("1+2i") // from string
91+
#c.from(3, 4) // from real and imaginary parts
92+
#c.add("1+2i", "3+4i", "-2+3i", "6-5i", "2i")
93+
```
94+
95+
## Number theory
96+
97+
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$.
98+
99+
## Mathematic functions
100+
101+
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;.
102+
103+
### Special functions
104+
105+
The `peano.func.special` sub-module include special functions such as the gamma function, zeta function, Gauss error function.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// -> func/common.typ
2+
3+
#import "init.typ": extend-calc-func-to-complex, define-func-with-complex, call-wasm-real-func, call-wasm-complex-func
4+
#import "../number/complex.typ" as c: complex, is-complex, make-complex
5+
#let math-utils-wasm = plugin("../math-utils.wasm")
6+
7+
#let /*pub*/ exp = extend-calc-func-to-complex("exp")
8+
#let /*pub*/ ln(x) = {
9+
if is-complex(x) {
10+
call-wasm-complex-func(math-utils-wasm.ln_complex, x)
11+
} else if x < 0 {
12+
call-wasm-complex-func(math-utils-wasm.ln_complex, complex(x))
13+
} else if x == 0 {
14+
-float.inf
15+
} else {
16+
calc.ln(x)
17+
}
18+
}
19+
20+
#let log-complex(x, base: 10.0) = {
21+
if base == 2 {
22+
call-wasm-complex-func(math-utils-wasm.log2_complex, x)
23+
} else if base == 10 {
24+
call-wasm-complex-func(math-utils-wasm.log10_complex, x)
25+
} else {
26+
c.div(
27+
call-wasm-complex-func(math-utils-wasm.ln_complex, x),
28+
ln(base),
29+
)
30+
}
31+
}
32+
33+
#let /*pub*/ log(x, base: 10.0) = {
34+
if is-complex(x) {
35+
log-complex(x, base: base)
36+
} else if x < 0 {
37+
log-complex(complex(x), base: base)
38+
} else if x == 0 {
39+
if is-complex(base) or base <= 0 {
40+
c.nan
41+
} else if base < 1 {
42+
float.inf
43+
} else if base == 1 {
44+
float.nan
45+
} else {
46+
-float.inf
47+
}
48+
} else {
49+
calc.log(x, base: base)
50+
}
51+
}
52+
53+
#let /*pub*/ sin = extend-calc-func-to-complex("sin")
54+
#let /*pub*/ cos = extend-calc-func-to-complex("cos")
55+
#let /*pub*/ tan = extend-calc-func-to-complex("tan")
56+
#let /*pub*/ sinh = extend-calc-func-to-complex("sinh")
57+
#let /*pub*/ cosh = extend-calc-func-to-complex("cosh")
58+
#let /*pub*/ tanh = extend-calc-func-to-complex("tanh")
59+
60+
#let /*pub*/ asin(x) = {
61+
if is-complex(x) {
62+
call-wasm-complex-func(math-utils-wasm.asin_complex, x)
63+
} else if x < -1 or x > 1 {
64+
call-wasm-complex-func(math-utils-wasm.asin_complex, complex(x))
65+
} else {
66+
calc.asin(x)
67+
}
68+
}
69+
70+
#let /*pub*/ acos(x) = {
71+
if is-complex(x) {
72+
call-wasm-complex-func(math-utils-wasm.acos_complex, x)
73+
} else if x < -1 or x > 1 {
74+
call-wasm-complex-func(math-utils-wasm.acos_complex, complex(x))
75+
} else {
76+
calc.acos(x)
77+
}
78+
}
79+
80+
#let /*pub*/ atan = extend-calc-func-to-complex("atan")
81+
#let /*pub*/ asinh = define-func-with-complex("asinh")
82+
83+
#let /*pub*/ acosh(x) = {
84+
if x < 1 {
85+
x = complex(x)
86+
}
87+
if is-complex(x) {
88+
call-wasm-complex-func(math-utils-wasm.acosh_complex, x)
89+
} else {
90+
call-wasm-real-func(math-utils-wasm.atanh, x)
91+
}
92+
}
93+
94+
#let /*pub*/ atanh(x) = {
95+
if x < -1 or x > 1 {
96+
x = complex(x)
97+
}
98+
if is-complex(x) {
99+
call-wasm-complex-func(math-utils-wasm.atanh_complex, x)
100+
} else {
101+
call-wasm-real-func(math-utils-wasm.atanh, x)
102+
}
103+
}
104+
105+
#let /*pub*/ abs(x) = {
106+
if is-complex(x) {
107+
let (re, im) = x
108+
calc.norm(re, im)
109+
} else {
110+
calc.abs(x)
111+
}
112+
}
113+
114+
#let /*pub*/ arg(x) = {
115+
if is-complex(x) {
116+
let (re, im) = x
117+
calc.atan2(re, im)
118+
} else if x >= 0 {
119+
0deg
120+
} else {
121+
180deg
122+
}
123+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#import "../number/complex.typ" as c: complex, is-complex, make-complex
2+
#let math-utils-wasm = plugin("../math-utils.wasm")
3+
4+
#let calc-funcs = dictionary(calc)
5+
#let math-utils-funcs = dictionary(math-utils-wasm)
6+
7+
#let call-wasm-complex-func(complex-func, x) = {
8+
let result-bytes = complex-func(
9+
x.re.to-bytes(),
10+
x.im.to-bytes(),
11+
)
12+
let re = float.from-bytes(result-bytes.slice(0, 8))
13+
let im = float.from-bytes(result-bytes.slice(8))
14+
make-complex(re, im)
15+
}
16+
17+
#let call-wasm-real-func(real-func, x) = {
18+
let x = if type(x) == angle {
19+
x.rad()
20+
} else {
21+
float(x)
22+
}
23+
float.from-bytes(real-func(x.to-bytes()))
24+
}
25+
26+
#let extend-calc-func-to-complex(func-name) = {
27+
let real-func = calc-funcs.at(func-name)
28+
let complex-func = math-utils-funcs.at(func-name + "_complex")
29+
x => {
30+
if is-complex(x) {
31+
call-wasm-complex-func(complex-func, x)
32+
} else {
33+
let x = if type(x) == angle {
34+
x.rad()
35+
} else {
36+
x
37+
}
38+
real-func(x)
39+
}
40+
}
41+
}
42+
43+
#let define-func-with-complex(func-name) = {
44+
let real-func = math-utils-funcs.at(func-name)
45+
let complex-func = math-utils-funcs.at(func-name + "_complex")
46+
x => {
47+
if is-complex(x) {
48+
call-wasm-complex-func(complex-func, x)
49+
} else {
50+
call-wasm-real-func(real-func, x)
51+
}
52+
}
53+
}
54+
55+
#let define-func-2-with-complex(func-name) = {
56+
let real-func = math-utils-funcs.at(func-name)
57+
let complex-func = math-utils-funcs.at(func-name + "_complex")
58+
(x1, x2) => {
59+
if is-complex(x1) or is-complex(x2) {
60+
let x1 = complex(x1)
61+
let x2 = complex(x2)
62+
let result-bytes = complex-func(
63+
x1.re.to-bytes(),
64+
x1.im.to-bytes(),
65+
x2.re.to-bytes(),
66+
x2.im.to-bytes(),
67+
)
68+
let re = float.from-bytes(result-bytes.slice(0, 8))
69+
let im = float.from-bytes(result-bytes.slice(8))
70+
make-complex(re, im)
71+
} else {
72+
let x1 = float(x1)
73+
let x2 = float(x2)
74+
float.from-bytes(real-func(x1.to-bytes(), x2.to-bytes()))
75+
}
76+
}
77+
}
78+
79+
#let calc-funcs-with-complex = calc-funcs.keys().filter(it => (it + "_complex") in math-utils-funcs)
80+
#let extended-calc-funcs = calc-funcs-with-complex.map(it => (it, extend-calc-func-to-complex(it))).to-dict()
81+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// -> func/special.typ
2+
/// Special mathematic functions.
3+
4+
#import "init.typ": define-func-with-complex, define-func-2-with-complex
5+
6+
/// The #link("https://en.wikipedia.org/wiki/Gamma_function")[$Gamma$ function],
7+
/// defined by $Gamma(z) = integral_0^oo t^(z - 1) upright(e)^(-t) dif t$.
8+
#let /*pub*/ gamma = define-func-with-complex("gamma")
9+
10+
/// The #link("https://en.wikipedia.org/wiki/Digamma_function")[digamma function],
11+
/// which is the derivative of the logarithm of $Gamma$ function
12+
/// $psi(z) = dif/(dif z) ln Gamma(z) = (Gamma'(z))/(Gamma(z))$.
13+
#let /*pub*/ digamma = define-func-with-complex("digamma")
14+
15+
/// same as `digamma`
16+
#let /*pub*/ psi = digamma
17+
18+
/// The #link("https://en.wikipedia.org/wiki/Error_function")[Gauss error function],
19+
/// defined by $erf z = 2/sqrt(pi) integral_0^z e^(-t^2) dif t$
20+
#let /*pub*/ erf = define-func-with-complex("erf")
21+
22+
/// #link("https://en.wikipedia.org/wiki/Riemann_zeta_function")[Riemann's $zeta$ function]
23+
/// defined by $zeta(s) = sum_(n = 1)^oo 1/(n^s)$ for $Re s > 1$ and its analytic continuation otherwise.
24+
#let /*pub*/ zeta = define-func-with-complex("zeta")
25+
26+
/// The #link("https://en.wikipedia.org/wiki/Beta_function")[$Beta$ function],
27+
/// defined by $Beta(z_1, z_2) = integral_0^1 t^(z_1 - 1) (1 - t)^(z_2 - 1) dif t$.
28+
/// Equals to $(Gamma(z_1) Gamma(z_2))/(Gamma(z_1 + z_2))$
29+
#let /*pub*/ beta = define-func-2-with-complex("beta")
30+
31+
// Euler's $gamma$ constant. Equals to $lim_(n -> oo) ((sum_(k = 1)^(n) 1/n) - ln n)$
32+
#let /*pub*/ euler-gamma = -digamma(1)
259 KB
Binary file not shown.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// -> ntheory.typ
2+
/// number theory operations
3+
4+
#let math-utils-wasm = plugin("math-utils.wasm")
5+
6+
#let /*pub*/ prime-fac(num) = {
7+
cbor(math-utils-wasm.prime_factors(num.to-bytes(endian: "little")))
8+
}
9+
10+
/// 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)$.
11+
#let /*pub*/ egcd(a, b) = {
12+
cbor(
13+
math-utils-wasm.extended_gcd(
14+
a.to-bytes(endian: "little"),
15+
b.to-bytes(endian: "little"),
16+
)
17+
)
18+
}

0 commit comments

Comments
 (0)