Skip to main content

tex_engine/tex/
numerics.rs

1/*! Numerical values used in TeX, such as [integers](TeXInt) and [dimensions](TeXDimen).*/
2
3use crate::engine::fontsystem::Font;
4use crate::engine::state::State;
5use crate::engine::{EngineReferences, EngineTypes};
6use std::cmp::Ordering;
7use std::fmt::{Debug, Display, Formatter};
8use std::ops::Sub;
9use std::ops::{Add, Div, Mul, Neg};
10
11/// Bundles the various numerical types used in some engine, and converts between them.
12pub trait NumSet: Clone + Debug {
13    /// The integer type (canonically `i32`)
14    type Int: TeXInt;
15    /// The dimension type (canonically `Dim32`)
16    type Dim: TeXDimen + Numeric<Self::Int>;
17    /// The mu dimension type (canonically `Mu`)
18    type MuDim: MuDim + Numeric<Self::Int>;
19    /// Converts a [`MuSkip`] to a [`Skip`], using the current font's `em`.
20    fn muskip_to_skip(muskip: MuSkip<Self::MuDim>, em: Self::Dim) -> Skip<Self::Dim> {
21        let base = Self::mudim_to_dim(muskip.base, em);
22        let stretch = muskip.stretch.map(|s| match s {
23            MuStretchShrink::Mu(i) => StretchShrink::Dim(Self::mudim_to_dim(i, em)),
24            MuStretchShrink::Fil(i) => StretchShrink::Fil(i),
25            MuStretchShrink::Fill(i) => StretchShrink::Fill(i),
26            MuStretchShrink::Filll(i) => StretchShrink::Filll(i),
27        });
28        let shrink = muskip.shrink.map(|s| match s {
29            MuStretchShrink::Mu(i) => StretchShrink::Dim(Self::mudim_to_dim(i, em)),
30            MuStretchShrink::Fil(i) => StretchShrink::Fil(i),
31            MuStretchShrink::Fill(i) => StretchShrink::Fill(i),
32            MuStretchShrink::Filll(i) => StretchShrink::Filll(i),
33        });
34        Skip::new(base, stretch, shrink)
35    }
36    /// Converts a [`Self::MuDim`] to a [`Self::Dim`], using the current font's `em`.
37    fn mudim_to_dim(mudim: Self::MuDim, em: Self::Dim) -> Self::Dim;
38    /// Converts a [`Self::Dim`] to a [`Self::Int`].
39    fn dim_to_int(dim: Self::Dim) -> Self::Int;
40}
41
42/// Generalization of integers, dimensions, skips, etc. In particular, provides a [`scale`](Self::scale) method to scale all of them by their integer type.
43pub trait Numeric<I: TeXInt>:
44    Eq
45    + Ord
46    + Neg<Output = Self>
47    + Add<Self, Output = Self>
48    + Mul<I, Output = Self>
49    + Div<I, Output = Self>
50    + Copy
51    + Default
52    + Debug
53    + Display
54{
55    #[must_use]
56    fn scale(&self, times: I, div: I) -> Self;
57}
58
59/// A TeX integer. By default `i32`.
60pub trait TeXInt:
61    Numeric<Self>
62    + From<i32>
63    + TryFrom<i64>
64    + Into<i64>
65    + TryInto<i32>
66    + Debug
67    + Display
68    + std::str::FromStr
69{
70    /// The minimum value of this integer type.
71    const MIN: Self;
72    /// The maximum value of this integer type.
73    const MAX: Self;
74}
75/// A TeX dimension. By default [`Dim32`].
76pub trait TeXDimen:
77    Copy
78    + Eq
79    + Ord
80    + Default
81    + Debug
82    + Display
83    + Add<Self, Output = Self>
84    + Sub<Self, Output = Self>
85    + Neg<Output = Self>
86    + Into<i64>
87    + std::iter::Sum
88{
89    /// The units used in this dimension. By default [`DEFAULT_UNITS`].
90    const UNITS: &'static [&'static [u8]] = DEFAULT_UNITS;
91    /// Scales this dimension by a floating-point number.
92    fn scale_float(&self, times: f64) -> Self;
93    /// Make a new dimension from a value in "scaled points" (`sp` = `1/65536 pt`).
94    fn from_sp(sp: i32) -> Self;
95    /// Make a new dimension from a floating-point number and a unit. The unit is assumed to be in [`Self::UNITS`].
96    fn from_float<ET: EngineTypes<Dim = Self>>(
97        engine: &EngineReferences<ET>,
98        f: f64,
99        dim: &[u8],
100    ) -> Self;
101}
102
103/// The default units for dimension values used in plain TeX:
104/// - sp (scaled points)
105/// - pt (points) = 65536sp
106/// - pc (picas) = 12pt
107/// - in (inches) = 72.27pt
108/// - bp (big points) = 1/72 in
109/// - cm (centimeters) = 2.54in
110/// - mm (millimeters) = 0.1mm
111/// - dd (didot points) = 1238/1157 pt
112/// - cc (cicero) = 12 dd
113/// - em (width of a capital M) = (`\fontdimen6` of the current font)
114/// - ex (height of an x) = (`\fontdimen5` of the current font)
115///
116/// - px: technically only available id pdftex; here: =1bp
117pub const DEFAULT_UNITS: &[&[u8]] = &[
118    b"pt", b"pc", b"in", b"bp", b"cm", b"mm", b"dd", b"cc", b"sp", b"em", b"ex", b"px",
119];
120
121/// The default [`NumSet`] used in plain TeX, using `i32`, [`Dim32`] and [`Mu`] for integers, dimensions and mu
122/// dimensions, respectively.
123#[derive(Clone, Copy, Eq, PartialEq, Debug)]
124pub struct DefaultNumSet;
125impl NumSet for DefaultNumSet {
126    type Int = i32;
127    type Dim = Dim32;
128    type MuDim = Mu;
129    fn mudim_to_dim(mudim: Mu, em: Dim32) -> Dim32 {
130        Dim32(((mudim.0 as f32 / 65536.0) * (em.0 as f32) / 18.0).round() as i32)
131    }
132    fn dim_to_int(dim: Dim32) -> i32 {
133        dim.0
134    }
135}
136
137impl Numeric<i32> for i32 {
138    fn scale(&self, times: i32, div: i32) -> Self {
139        ((*self as f64 * times as f64) / (div as f64)).round() as i32
140    }
141}
142impl TeXInt for i32 {
143    const MIN: Self = i32::MIN;
144    const MAX: Self = i32::MAX;
145}
146
147/// A plain TeX dimension, represented as a 32-bit integer in *scaled points (sp)*, where 65536sp = 1pt.
148#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Default)]
149pub struct Dim32(pub i32);
150impl Numeric<i32> for Dim32 {
151    fn scale(&self, times: i32, div: i32) -> Self {
152        Self(self.0.scale(times, div))
153    }
154}
155impl Add for Dim32 {
156    type Output = Self;
157    fn add(self, rhs: Self) -> Self::Output {
158        Dim32(self.0 + rhs.0)
159    }
160}
161impl Sub for Dim32 {
162    type Output = Self;
163    fn sub(self, rhs: Self) -> Self::Output {
164        Dim32(self.0 - rhs.0)
165    }
166}
167impl Div<i32> for Dim32 {
168    type Output = Self;
169    fn div(self, rhs: i32) -> Self::Output {
170        Self(self.0 / rhs)
171    }
172}
173impl Mul<i32> for Dim32 {
174    type Output = Self;
175    fn mul(self, rhs: i32) -> Self::Output {
176        Self(self.0 * rhs)
177    }
178}
179impl Dim32 {
180    /// Display a number representing 65536 * `unit` (e.g. `pt` in scaled points).
181    pub fn display_num(num: i32, unit: &str, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        let mut val = num;
183        if val < 0 {
184            write!(f, "-")?;
185            val = -val;
186        }
187        write!(f, "{}.", val / 65536)?;
188        val = 10 * (val % 65536) + 5;
189        let mut delta = 10;
190        if val < delta {
191            return write!(f, "0{}", unit);
192        }
193        while val > delta {
194            if delta > 65536 {
195                val = val + 32768 - 50000;
196            }
197            write!(f, "{}", val / 65536)?;
198            val = 10 * (val % 65536);
199            delta *= 10;
200        }
201        write!(f, "{}", unit)
202    }
203}
204impl Neg for Dim32 {
205    type Output = Self;
206    fn neg(self) -> Self::Output {
207        Dim32(-self.0)
208    }
209}
210impl Display for Dim32 {
211    // B-Book ยง103
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        Self::display_num(self.0, "pt", f)
214    }
215}
216impl From<Dim32> for i64 {
217    fn from(d: Dim32) -> i64 {
218        d.0 as i64
219    }
220}
221
222impl TeXDimen for Dim32 {
223    #[inline]
224    #[allow(clippy::cast_possible_truncation)]
225    fn scale_float(&self, times: f64) -> Self {
226        let times = (times * 65536.0).round() as i64;
227        Self((i64::from(self.0) * times / 65536) as i32)
228    }
229    fn from_sp(sp: i32) -> Self {
230        Self(sp)
231    }
232    fn from_float<ET: EngineTypes<Dim = Self>>(
233        engine: &EngineReferences<ET>,
234        float: f64,
235        dim: &[u8],
236    ) -> Self {
237        match dim {
238            b"sp" => Self(1).scale_float(float),
239            b"pt" => Self(65536).scale_float(float),
240            b"pc" => Self(786_432).scale_float(float),
241            b"in" => Self(4_736_286).scale_float(float),
242            b"bp" => Self(65_781).scale_float(float),
243            b"px" => Self(65_781).scale_float(float),
244            b"cm" => Self(1_864_679).scale_float(float),
245            b"mm" => Self(186_467).scale_float(float),
246            b"dd" => Self(70_124).scale_float(float),
247            b"cc" => Self(841_489).scale_float(float),
248            b"em" => {
249                let f = engine.state.get_current_font();
250                let em = f.get_dim(5);
251                em.scale_float(float)
252            }
253            b"ex" => {
254                let f = engine.state.get_current_font();
255                let ex = f.get_dim(4);
256                ex.scale_float(float)
257            }
258            _ => unreachable!(),
259        }
260    }
261}
262impl std::iter::Sum for Dim32 {
263    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
264        iter.fold(Self::default(), |a, b| a + b)
265    }
266}
267
268/// The units for strech/shrink values: `fil`, `fill` and `filll`.
269pub const STRETCH_SHRINK_UNITS: &[&[u8]] = &[b"fil", b"fill", b"filll"];
270
271/// A skip a.k.a. glue value, i.e. a dimension with optional stretch and shrink components.
272#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
273pub struct Skip<D: TeXDimen> {
274    pub base: D,
275    pub stretch: Option<StretchShrink<D>>,
276    pub shrink: Option<StretchShrink<D>>,
277}
278
279/// A stretch/shrink component of a [`Skip`].
280#[derive(Clone, Copy, Eq, PartialEq, Debug)]
281pub enum StretchShrink<D: TeXDimen> {
282    Dim(D),
283    Fil(i32),
284    Fill(i32),
285    Filll(i32),
286}
287impl<D: TeXDimen> StretchShrink<D> {
288    /// Returns a new [`StretchShrink`] from a floating-point number and a unit. The unit is assumed to be
289    /// `fil`, `fill`, `filll` or in [`D::UNITS`](TeXDimen::UNITS).
290    pub fn from_float<ET: EngineTypes<Dim = D>>(
291        engine: &EngineReferences<ET>,
292        float: f64,
293        dim: &[u8],
294    ) -> Self {
295        match dim {
296            b"fil" => Self::Fil((float * 65536.0).round() as i32),
297            b"fill" => Self::Fill((float * 65536.0).round() as i32),
298            b"filll" => Self::Filll((float * 65536.0).round() as i32),
299            _ => Self::Dim(D::from_float(engine, float, dim)),
300        }
301    }
302}
303impl<D: TeXDimen> Add<StretchShrink<D>> for StretchShrink<D> {
304    type Output = Self;
305    fn add(self, rhs: Self) -> Self::Output {
306        match (self, rhs) {
307            (Self::Dim(d1), Self::Dim(d2)) => Self::Dim(d1 + d2),
308            (Self::Fil(i1), Self::Fil(i2)) => Self::Fil(i1 + i2),
309            (Self::Fill(i1), Self::Fill(i2)) => Self::Fill(i1 + i2),
310            (Self::Filll(i1), Self::Filll(i2)) => Self::Filll(i1 + i2),
311            _ => self.max(rhs),
312        }
313    }
314}
315impl<D: TeXDimen> PartialOrd for StretchShrink<D> {
316    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
317        Some(self.cmp(other))
318    }
319}
320impl<D: TeXDimen> Ord for StretchShrink<D> {
321    fn cmp(&self, other: &Self) -> Ordering {
322        match (self, other) {
323            (Self::Dim(d1), Self::Dim(d2)) => d1.cmp(d2),
324            (Self::Fil(i1), Self::Fil(i2)) => i1.cmp(i2),
325            (Self::Fill(i1), Self::Fill(i2)) => i1.cmp(i2),
326            (Self::Filll(i1), Self::Filll(i2)) => i1.cmp(i2),
327            (Self::Filll(_), _) => Ordering::Greater,
328            (_, Self::Filll(_)) => Ordering::Less,
329            (Self::Fill(_), _) => Ordering::Greater,
330            (_, Self::Fill(_)) => Ordering::Less,
331            (Self::Fil(_), _) => Ordering::Greater,
332            (_, Self::Fil(_)) => Ordering::Less,
333        }
334    }
335}
336
337impl<D: TeXDimen> Add<D> for Skip<D> {
338    type Output = Self;
339    fn add(self, rhs: D) -> Self::Output {
340        Self {
341            base: self.base + rhs,
342            stretch: self.stretch,
343            shrink: self.shrink,
344        }
345    }
346}
347impl<D: TeXDimen> PartialOrd for Skip<D> {
348    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
349        Some(self.cmp(other))
350    }
351}
352impl<D: TeXDimen> Ord for Skip<D> {
353    fn cmp(&self, other: &Self) -> Ordering {
354        self.base.cmp(&other.base)
355    }
356}
357impl<D: TeXDimen> Add<Self> for Skip<D> {
358    type Output = Self;
359    fn add(self, rhs: Self) -> Self::Output {
360        Self {
361            base: self.base + rhs.base,
362            stretch: match (self.stretch, rhs.stretch) {
363                (Some(a), Some(b)) => Some(a + b),
364                (Some(a), None) => Some(a),
365                (None, Some(b)) => Some(b),
366                _ => None,
367            },
368            shrink: match (self.shrink, rhs.shrink) {
369                (Some(a), Some(b)) => Some(a + b),
370                (Some(a), None) => Some(a),
371                (None, Some(b)) => Some(b),
372                _ => None,
373            },
374        }
375    }
376}
377impl<I: TeXInt, D: TeXDimen + Numeric<I>> Div<I> for Skip<D> {
378    type Output = Self;
379    fn div(self, rhs: I) -> Self::Output {
380        self.scale(1.into(), rhs)
381    }
382}
383impl<I: TeXInt, D: TeXDimen + Numeric<I>> Mul<I> for Skip<D> {
384    type Output = Self;
385    fn mul(self, rhs: I) -> Self::Output {
386        self.scale(rhs, 1.into())
387    }
388}
389
390impl<D: TeXDimen> Display for Skip<D> {
391    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
392        write!(f, "{}", self.base)?;
393        if let Some(stretch) = &self.stretch {
394            write!(f, " plus ")?;
395            match stretch {
396                StretchShrink::Dim(d) => write!(f, "{}", d)?,
397                StretchShrink::Fil(i) => Dim32::display_num(*i, "fil", f)?,
398                StretchShrink::Fill(i) => Dim32::display_num(*i, "fill", f)?,
399                StretchShrink::Filll(i) => Dim32::display_num(*i, "filll", f)?,
400            }
401        }
402        if let Some(shrink) = &self.shrink {
403            write!(f, " minus ")?;
404            match shrink {
405                StretchShrink::Dim(d) => write!(f, "{}", d)?,
406                StretchShrink::Fil(i) => Dim32::display_num(*i, "fil", f)?,
407                StretchShrink::Fill(i) => Dim32::display_num(*i, "fill", f)?,
408                StretchShrink::Filll(i) => Dim32::display_num(*i, "filll", f)?,
409            }
410        }
411        Ok(())
412    }
413}
414impl<D: TeXDimen> Neg for Skip<D> {
415    type Output = Self;
416    fn neg(self) -> Self::Output {
417        Self {
418            base: -self.base,
419            stretch: self.stretch.map(|s| match s {
420                StretchShrink::Dim(d) => StretchShrink::Dim(-d),
421                StretchShrink::Fil(i) => StretchShrink::Fil(-i),
422                StretchShrink::Fill(i) => StretchShrink::Fill(-i),
423                StretchShrink::Filll(i) => StretchShrink::Filll(-i),
424            }),
425            shrink: self.shrink.map(|s| match s {
426                StretchShrink::Dim(d) => StretchShrink::Dim(-d),
427                StretchShrink::Fil(i) => StretchShrink::Fil(-i),
428                StretchShrink::Fill(i) => StretchShrink::Fill(-i),
429                StretchShrink::Filll(i) => StretchShrink::Filll(-i),
430            }),
431        }
432    }
433}
434impl<D: TeXDimen> Skip<D> {
435    pub fn new(
436        base: D,
437        stretch: Option<StretchShrink<D>>,
438        shrink: Option<StretchShrink<D>>,
439    ) -> Self {
440        Self {
441            base,
442            stretch: match stretch {
443                Some(StretchShrink::Dim(d)) if d == D::default() => None,
444                Some(StretchShrink::Fil(0) | StretchShrink::Fill(0) | StretchShrink::Filll(0)) => {
445                    None
446                }
447                _ => stretch,
448            },
449            shrink: match shrink {
450                Some(StretchShrink::Dim(d)) if d == D::default() => None,
451                Some(StretchShrink::Fil(0) | StretchShrink::Fill(0) | StretchShrink::Filll(0)) => {
452                    None
453                }
454                _ => shrink,
455            },
456        }
457    }
458}
459
460impl<I: TeXInt, D: TeXDimen + Numeric<I>> Numeric<I> for Skip<D> {
461    fn scale(&self, times: I, div: I) -> Self {
462        Self {
463            base: self.base.scale(times, div),
464            stretch: self.stretch,
465            shrink: self.shrink,
466        }
467    }
468}
469
470/// A math dimension; i.e. the base component of an `\mskip`
471pub trait MuDim:
472    Display + Debug + Clone + Copy + Neg<Output = Self> + Add<Output = Self> + PartialEq + Ord + Default
473{
474    /// The set of math units; by default, only `mu`
475    const UNITS: &'static [&'static [u8]] = &[b"mu"];
476    /// Converts a floating-point number and a unit to a [`Self`]. The unit is assumed to be in [`Self::UNITS`].
477    fn from_float<ET: EngineTypes>(engine: &EngineReferences<ET>, float: f64, dim: &[u8]) -> Self;
478}
479
480/// A math skip/glue consisting of a [`MuDim`] and optional stretch and shrink components.
481#[derive(Clone, Copy, Eq, PartialEq, Debug)]
482pub struct MuSkip<M: MuDim> {
483    pub base: M,
484    pub stretch: Option<MuStretchShrink<M>>,
485    pub shrink: Option<MuStretchShrink<M>>,
486}
487impl<M: MuDim> MuSkip<M> {
488    /// Returns a new [`MuSkip`] from a [`MuDim`] and optional stretch and shrink components.
489    pub fn new(
490        base: M,
491        stretch: Option<MuStretchShrink<M>>,
492        shrink: Option<MuStretchShrink<M>>,
493    ) -> Self {
494        Self {
495            base,
496            stretch: match stretch {
497                Some(MuStretchShrink::Mu(d)) if d == M::default() => None,
498                Some(
499                    MuStretchShrink::Fil(0) | MuStretchShrink::Fill(0) | MuStretchShrink::Filll(0),
500                ) => None,
501                _ => stretch,
502            },
503            shrink: match shrink {
504                Some(MuStretchShrink::Mu(d)) if d == M::default() => None,
505                Some(
506                    MuStretchShrink::Fil(0) | MuStretchShrink::Fill(0) | MuStretchShrink::Filll(0),
507                ) => None,
508                _ => shrink,
509            },
510        }
511    }
512}
513
514/// A stretch/shrink component of a [`MuSkip`].
515#[derive(Clone, Copy, Eq, PartialEq, Debug)]
516pub enum MuStretchShrink<M: MuDim> {
517    Mu(M),
518    Fil(i32),
519    Fill(i32),
520    Filll(i32),
521}
522impl<M: MuDim> MuStretchShrink<M> {
523    /// Returns a new [`MuStretchShrink`] from a floating-point number and a unit. The unit is assumed to be
524    /// `fil`, `fill`, `filll` or in [`M::UNITS`](MuDim::UNITS).
525    pub fn from_float<ET: EngineTypes>(
526        engine: &EngineReferences<ET>,
527        float: f64,
528        dim: &[u8],
529    ) -> Self {
530        match dim {
531            b"fil" => Self::Fil((float * 65536.0).round() as i32),
532            b"fill" => Self::Fill((float * 65536.0).round() as i32),
533            b"filll" => Self::Filll((float * 65536.0).round() as i32),
534            _ => Self::Mu(M::from_float(engine, float, dim)),
535        }
536    }
537}
538impl<M: MuDim> Add<MuStretchShrink<M>> for MuStretchShrink<M> {
539    type Output = Self;
540    fn add(self, rhs: Self) -> Self::Output {
541        match (self, rhs) {
542            (Self::Mu(d1), Self::Mu(d2)) => Self::Mu(d1 + d2),
543            (Self::Fil(i1), Self::Fil(i2)) => Self::Fil(i1 + i2),
544            (Self::Fill(i1), Self::Fill(i2)) => Self::Fill(i1 + i2),
545            (Self::Filll(i1), Self::Filll(i2)) => Self::Filll(i1 + i2),
546            _ => self.max(rhs),
547        }
548    }
549}
550impl<M: MuDim> PartialOrd for MuStretchShrink<M> {
551    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
552        Some(self.cmp(other))
553    }
554}
555impl<M: MuDim> Ord for MuStretchShrink<M> {
556    fn cmp(&self, other: &Self) -> Ordering {
557        match (self, other) {
558            (Self::Mu(d1), Self::Mu(d2)) => d1.cmp(d2),
559            (Self::Fil(i1), Self::Fil(i2)) => i1.cmp(i2),
560            (Self::Fill(i1), Self::Fill(i2)) => i1.cmp(i2),
561            (Self::Filll(i1), Self::Filll(i2)) => i1.cmp(i2),
562            (Self::Filll(_), _) => Ordering::Greater,
563            (_, Self::Filll(_)) => Ordering::Less,
564            (Self::Fill(_), _) => Ordering::Greater,
565            (_, Self::Fill(_)) => Ordering::Less,
566            (Self::Fil(_), _) => Ordering::Greater,
567            (_, Self::Fil(_)) => Ordering::Less,
568        }
569    }
570}
571
572/// A plain TeX mu dimension, represented as a 32-bit integer analogously to *scaled points*, i.e. [`Mu`]`(65536) = 1mu`.
573/// (where `18mu = 1em`).
574#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, PartialOrd, Ord)]
575pub struct Mu(pub i32);
576impl Numeric<i32> for Mu {
577    fn scale(&self, times: i32, div: i32) -> Self {
578        Self((self.0 as i64 * (times as i64) / (div as i64)) as i32)
579    }
580}
581impl Div<i32> for Mu {
582    type Output = Self;
583    fn div(self, rhs: i32) -> Self::Output {
584        Self(self.0 / rhs)
585    }
586}
587impl Mul<i32> for Mu {
588    type Output = Self;
589    fn mul(self, rhs: i32) -> Self::Output {
590        Self(self.0 * rhs)
591    }
592}
593impl MuDim for Mu {
594    fn from_float<ET: EngineTypes>(_engine: &EngineReferences<ET>, float: f64, dim: &[u8]) -> Self {
595        match dim {
596            b"mu" => Mu((float * 65536.0).round() as i32),
597            _ => unreachable!(),
598        }
599    }
600}
601impl Neg for Mu {
602    type Output = Self;
603    fn neg(self) -> Self::Output {
604        Mu(-self.0)
605    }
606}
607impl Add<Mu> for Mu {
608    type Output = Self;
609    fn add(self, rhs: Self) -> Self::Output {
610        Mu(self.0 + rhs.0)
611    }
612}
613impl Display for Mu {
614    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
615        Dim32::display_num(self.0, "mu", f)
616    }
617}
618
619impl<M: MuDim> PartialOrd for MuSkip<M> {
620    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
621        Some(self.cmp(other))
622    }
623}
624impl<M: MuDim> Ord for MuSkip<M> {
625    fn cmp(&self, other: &Self) -> Ordering {
626        self.base.cmp(&other.base)
627    }
628}
629impl<M: MuDim> Display for MuSkip<M> {
630    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
631        write!(f, "{}", self.base)?;
632        if let Some(stretch) = &self.stretch {
633            write!(f, " plus ")?;
634            match stretch {
635                MuStretchShrink::Mu(d) => write!(f, "{}", d)?,
636                MuStretchShrink::Fil(i) => Dim32::display_num(*i, "fil", f)?,
637                MuStretchShrink::Fill(i) => Dim32::display_num(*i, "fill", f)?,
638                MuStretchShrink::Filll(i) => Dim32::display_num(*i, "filll", f)?,
639            }
640        }
641        if let Some(shrink) = &self.shrink {
642            write!(f, " minus ")?;
643            match shrink {
644                MuStretchShrink::Mu(d) => write!(f, "{}", d)?,
645                MuStretchShrink::Fil(i) => Dim32::display_num(*i, "fil", f)?,
646                MuStretchShrink::Fill(i) => Dim32::display_num(*i, "fill", f)?,
647                MuStretchShrink::Filll(i) => Dim32::display_num(*i, "filll", f)?,
648            }
649        }
650        Ok(())
651    }
652}
653impl<M: MuDim> Neg for MuSkip<M> {
654    type Output = Self;
655    fn neg(self) -> Self::Output {
656        Self {
657            base: -self.base,
658            stretch: self.stretch.map(|s| match s {
659                MuStretchShrink::Mu(d) => MuStretchShrink::Mu(-d),
660                MuStretchShrink::Fil(i) => MuStretchShrink::Fil(-i),
661                MuStretchShrink::Fill(i) => MuStretchShrink::Fill(-i),
662                MuStretchShrink::Filll(i) => MuStretchShrink::Filll(-i),
663            }),
664            shrink: self.shrink.map(|s| match s {
665                MuStretchShrink::Mu(d) => MuStretchShrink::Mu(-d),
666                MuStretchShrink::Fil(i) => MuStretchShrink::Fil(-i),
667                MuStretchShrink::Fill(i) => MuStretchShrink::Fill(-i),
668                MuStretchShrink::Filll(i) => MuStretchShrink::Filll(-i),
669            }),
670        }
671    }
672}
673impl<M: MuDim> Default for MuSkip<M> {
674    fn default() -> Self {
675        Self {
676            base: M::default(),
677            stretch: None,
678            shrink: None,
679        }
680    }
681}
682impl<I: TeXInt, M: MuDim + Numeric<I>> Numeric<I> for MuSkip<M> {
683    fn scale(&self, times: I, div: I) -> Self {
684        Self {
685            base: self.base.scale(times, div),
686            stretch: self.stretch,
687            shrink: self.shrink,
688        }
689    }
690}
691
692impl<M: MuDim> Add<Self> for MuSkip<M> {
693    type Output = Self;
694    fn add(self, rhs: Self) -> Self::Output {
695        Self {
696            base: self.base + rhs.base,
697            stretch: match (self.stretch, rhs.stretch) {
698                (Some(a), Some(b)) => Some(a + b),
699                (Some(a), None) => Some(a),
700                (None, Some(b)) => Some(b),
701                _ => None,
702            },
703            shrink: match (self.shrink, rhs.shrink) {
704                (Some(a), Some(b)) => Some(a + b),
705                (Some(a), None) => Some(a),
706                (None, Some(b)) => Some(b),
707                _ => None,
708            },
709        }
710    }
711}
712impl<I: TeXInt, M: MuDim + Numeric<I>> Div<I> for MuSkip<M> {
713    type Output = Self;
714    fn div(self, rhs: I) -> Self::Output {
715        self.scale(1.into(), rhs)
716    }
717}
718impl<I: TeXInt, M: MuDim + Numeric<I>> Mul<I> for MuSkip<M> {
719    type Output = Self;
720    fn mul(self, rhs: I) -> Self::Output {
721        self.scale(rhs, 1.into())
722    }
723}