Skip to main content

malachite_base/maps/
typst.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::strings::typst::ToTypst;
10#[cfg(not(feature = "std"))]
11use alloc::collections::BTreeMap;
12use alloc::vec::Vec;
13use core::fmt::{Formatter, Result};
14use core::hash::Hash;
15#[cfg(not(feature = "std"))]
16use hashbrown::HashMap;
17#[cfg(feature = "std")]
18use std::collections::{BTreeMap, HashMap};
19
20// Writes a sequence of entries as one braced, comma-separated Typst math-mode fragment, each
21// entry's key and value joined by a "maps to" arrow.
22fn fmt_typst_entries<'a, K: ToTypst + 'a, V: ToTypst + 'a>(
23    entries: impl Iterator<Item = (&'a K, &'a V)>,
24    f: &mut Formatter,
25) -> Result {
26    f.write_str("{")?;
27    for (i, (k, v)) in entries.enumerate() {
28        if i != 0 {
29            f.write_str(", ")?;
30        }
31        k.fmt_typst(f)?;
32        f.write_str(" |-> ")?;
33        v.fmt_typst(f)?;
34    }
35    f.write_str("}")
36}
37
38impl<K: ToTypst, V: ToTypst> ToTypst for BTreeMap<K, V> {
39    /// Writes a [`BTreeMap`] as a LaTeX math-mode fragment.
40    ///
41    /// Each entry is written as its key, a "maps to" arrow, and its value; the entries are
42    /// separated by commas and wrapped in braces, as a map is a set of associations. The entries
43    /// come in the map's own order, which is ascending by key.
44    ///
45    /// Typst grows a matched pair of delimiters to fit what is between them, so the braces fit an
46    /// entry that is taller than one line without being asked to. An empty map becomes `{}` rather
47    /// than nothing at all.
48    ///
49    /// # Worst-case complexity
50    /// $T(n) = O(n + \sum_{i=0}^{n-1}(T^\prime(i) + T^{\prime\prime}(i)))$
51    ///
52    /// $M(n) = O(\max_{i=0}^{n-1}(M^\prime(i) + M^{\prime\prime}(i)))$
53    ///
54    /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, $i$ is an entry's index,
55    /// $T^\prime$ and $M^\prime$ are the time and memory functions of `fmt_typst` for `K`, and
56    /// $T^{\prime\prime}$ and $M^{\prime\prime}$ are those for `V`.
57    ///
58    /// # Examples
59    /// ```
60    /// use malachite_base::strings::typst::ToTypst;
61    /// use std::collections::BTreeMap;
62    ///
63    /// let empty = BTreeMap::<u8, u8>::new();
64    /// assert_eq!(empty.to_typst_string(), "{}");
65    ///
66    /// let m = BTreeMap::from([(2u8, 20u8), (1, 10)]);
67    /// assert_eq!(m.to_typst_string(), "{1 |-> 10, 2 |-> 20}");
68    /// ```
69    ///
70    /// | value                                    | fragment                 |
71    /// |------------------------------------------|--------------------------|
72    /// | `BTreeMap::<u8, u8>::new()`              | `{}`                     |
73    /// | `BTreeMap::from([(2u8, 20u8), (1, 10)])` | `{1 \|-> 10, 2 \|-> 20}` |
74    #[inline]
75    fn fmt_typst(&self, f: &mut Formatter) -> Result {
76        fmt_typst_entries(self.iter(), f)
77    }
78}
79
80impl<K: Eq + Hash + Ord + ToTypst, V: ToTypst> ToTypst for HashMap<K, V> {
81    /// Writes a [`HashMap`] as a LaTeX math-mode fragment.
82    ///
83    /// Each entry is written as its key, a "maps to" arrow, and its value; the entries are
84    /// separated by commas and wrapped in braces, as a map is a set of associations.
85    ///
86    /// The entries are sorted by key first, which is why this asks for [`Ord`] where a [`HashMap`]
87    /// does not. A [`HashMap`] iterates in an order that depends on its hasher, so without sorting
88    /// two equal maps could have different fragments, and the same map could have a different
89    /// fragment in the next run. Sorting also makes a [`HashMap`]'s fragment agree with the
90    /// [`BTreeMap`] of the same entries.
91    ///
92    /// Typst grows a matched pair of delimiters to fit what is between them, so the braces fit an
93    /// entry that is taller than one line without being asked to. An empty map becomes `{}` rather
94    /// than nothing at all.
95    ///
96    /// # Worst-case complexity
97    /// $T(n) = O(n \log n + \sum_{i=0}^{n-1}(T^\prime(i) + T^{\prime\prime}(i)))$
98    ///
99    /// $M(n) = O(n + \max_{i=0}^{n-1}(M^\prime(i) + M^{\prime\prime}(i)))$
100    ///
101    /// where $T$ is time, $M$ is additional memory, $n$ is `self.len()`, $i$ is an entry's index,
102    /// $T^\prime$ and $M^\prime$ are the time and memory functions of `fmt_typst` for `K`, and
103    /// $T^{\prime\prime}$ and $M^{\prime\prime}$ are those for `V`.
104    ///
105    /// # Examples
106    /// ```
107    /// use malachite_base::strings::typst::ToTypst;
108    /// use std::collections::HashMap;
109    ///
110    /// let empty = HashMap::<u8, u8>::new();
111    /// assert_eq!(empty.to_typst_string(), "{}");
112    ///
113    /// // The entries are sorted by key, so the fragment does not depend on the hasher.
114    /// let m = HashMap::from([(2u8, 20u8), (1, 10)]);
115    /// assert_eq!(m.to_typst_string(), "{1 |-> 10, 2 |-> 20}");
116    /// ```
117    ///
118    /// | value                                   | fragment                 |
119    /// |-----------------------------------------|--------------------------|
120    /// | `HashMap::<u8, u8>::new()`              | `{}`                     |
121    /// | `HashMap::from([(2u8, 20u8), (1, 10)])` | `{1 \|-> 10, 2 \|-> 20}` |
122    fn fmt_typst(&self, f: &mut Formatter) -> Result {
123        let mut entries = self.iter().collect::<Vec<_>>();
124        entries.sort_unstable_by_key(|&(k, _)| k);
125        fmt_typst_entries(entries.into_iter(), f)
126    }
127}