Skip to main content

typst_library/math/
frac.rs

1use typst_syntax::Spanned;
2
3use crate::diag::bail;
4use crate::foundations::{Cast, Content, Value, elem};
5use crate::layout::Em;
6use crate::math::Mathy;
7
8/// How much padding to add around each side of a fraction.
9pub const FRAC_PADDING: Em = Em::new(0.1);
10
11/// A mathematical fraction.
12///
13/// = Example <example>
14/// ```example
15/// $ 1/2 < (x+1)/2 $
16/// $ ((x+1)) / 2 = frac(a, b) $
17/// ```
18///
19/// = Syntax <syntax>
20/// This function also has dedicated syntax: Use a slash to turn neighbouring
21/// expressions into a fraction. Multiple atoms can be grouped into a single
22/// expression using round grouping parentheses. Such parentheses are removed
23/// from the output, but you can nest multiple to force them.
24#[elem(title = "Fraction", Mathy)]
25pub struct FracElem {
26    /// The fraction's numerator.
27    #[required]
28    pub num: Content,
29
30    /// The fraction's denominator.
31    #[required]
32    pub denom: Content,
33
34    /// How the fraction should be laid out.
35    ///
36    /// #example(
37    ///   title: "Styles",
38    ///   ```
39    ///   $ frac(x, y, style: "vertical") $
40    ///   $ frac(x, y, style: "skewed") $
41    ///   $ frac(x, y, style: "horizontal") $
42    ///   ```
43    /// )
44    ///
45    /// #example(
46    ///   title: "Setting the default",
47    ///   ```
48    ///   #set math.frac(style: "skewed")
49    ///   $ a / b $
50    ///   ```
51    /// )
52    ///
53    /// #example(
54    ///   title: "Handling of grouping parentheses",
55    ///   ```
56    ///   // Grouping parentheses are removed.
57    ///   #set math.frac(style: "vertical")
58    ///   $ (a + b) / b $
59    ///
60    ///   // Grouping parentheses are removed.
61    ///   #set math.frac(style: "skewed")
62    ///   $ (a + b) / b $
63    ///
64    ///   // Grouping parentheses are retained.
65    ///   #set math.frac(style: "horizontal")
66    ///   $ (a + b) / b $
67    ///   ```
68    /// )
69    ///
70    /// #example(
71    ///   title: "Different styles in inline vs block equations",
72    ///   ```
73    ///   // This changes the style for inline equations only.
74    ///   #show math.equation.where(block: false): set math.frac(style: "horizontal")
75    ///
76    ///   This $(x-y)/z = 3$ is inline math, and this is block math:
77    ///   $ (x-y)/z = 3 $
78    ///   ```
79    /// )
80    ///
81    /// #example(
82    ///   title: "Use LaTeX-like convention",
83    ///   ```
84    ///   // Change the default style.
85    ///   #set math.frac(style: "horizontal")
86    ///   // Define a shorthand with the original style.
87    ///   #let frac = math.frac.with(style: "vertical")
88    ///
89    ///   $ p/q = frac(p, q) $
90    ///
91    ///   // The shadowed definition can still be accessed.
92    ///   #assert.eq($p/q$, $std.math.frac(p, q)$)
93    ///   ```
94    /// )
95    #[default(FracStyle::Vertical)]
96    pub style: FracStyle,
97
98    /// Whether the numerator was originally surrounded by parentheses that were
99    /// stripped by the parser.
100    #[internal]
101    #[parse(None)]
102    #[default(false)]
103    pub num_deparenthesized: bool,
104
105    /// Whether the denominator was originally surrounded by parentheses that
106    /// were stripped by the parser.
107    #[internal]
108    #[parse(None)]
109    #[default(false)]
110    pub denom_deparenthesized: bool,
111}
112
113/// Fraction style
114#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
115pub enum FracStyle {
116    /// Stacked numerator and denominator with a bar.
117    #[default]
118    Vertical,
119    /// Numerator and denominator separated by a slash.
120    Skewed,
121    /// Numerator and denominator placed inline and parentheses are not
122    /// absorbed.
123    Horizontal,
124}
125
126/// A binomial expression.
127///
128/// = Example <example>
129/// ```example
130/// $ binom(n, k) $
131/// $ binom(n, k_1, k_2, k_3, ..., k_m) $
132/// ```
133#[elem(title = "Binomial", Mathy)]
134pub struct BinomElem {
135    /// The binomial's upper index.
136    #[required]
137    pub upper: Content,
138
139    /// The binomial's lower index.
140    #[required]
141    #[variadic]
142    #[parse(
143        let values = args.all::<Spanned<Value>>()?;
144        if values.is_empty() {
145            // Prevents one element binomials
146            bail!(args.span, "missing argument: lower");
147        }
148        values.into_iter().map(|spanned| spanned.v.display()).collect()
149    )]
150    pub lower: Vec<Content>,
151}