1use std::fmt::Display;
2use itertools::Itertools;
3use num_traits::ToPrimitive;
4use crate::IntoDigits;
5
6pub fn paren_expr<S>(s: S) -> String
7where S: Display {
8 let s = s.to_string();
9 if s.contains(' ') {
10 format!("({s})")
11 } else {
12 s
13 }
14}
15
16pub fn lc<X, R, S>(mut terms: S) -> String
17where
18 X: Display,
19 R: Display,
20 S: Iterator<Item = (X, R)>
21{
22 let mut res: Vec<String> = vec![];
23
24 if let Some((x, r)) = terms.next() {
25 let r = paren_expr(r);
26 let x = x.to_string();
27
28 let term = if r == "1" {
29 x
30 } else if r == "-1" {
31 format!("-{x}")
32 } else if x == "1" {
33 format!("{r}")
34 } else {
35 format!("{r}{x}")
36 };
37
38 res.push(term)
39 };
40
41 for (x, r) in terms {
42 let r = paren_expr(r);
43 let x = x.to_string();
44
45 let (op, r) = if let Some(r) = r.strip_prefix('-') {
46 ("-", r.to_owned())
47 } else {
48 ("+", r.to_owned())
49 };
50
51 let term = if r == "1" {
52 x
53 } else if x == "1" {
54 r.to_string()
55 } else {
56 format!("{r}{x}")
57 };
58
59 res.push(op.to_string());
60 res.push(term);
61 }
62
63 if res.is_empty() {
64 "0".to_string()
65 } else {
66 res.join(" ")
67 }
68}
69
70pub fn subscript<I>(i: I) -> String
71where I: ToPrimitive {
72 let i = i.to_isize().unwrap();
73
74 if i == 0 {
75 return '\u{2080}'.into()
76 }
77
78 let (init, i) = if i > 0 {
79 (String::new(), i as usize)
80 } else {
81 ('\u{208B}'.into(), -i as usize)
82 };
83
84 i.into_digits().into_iter().fold(init, |mut res, d| {
85 let c = char::from_u32( ('\u{2080}' as u32) + (d as u32) ).unwrap();
86 res.push(c);
87 res
88 })
89}
90
91pub fn superscript<I>(i: I) -> String
92where I: ToPrimitive {
93 let i = i.to_isize().unwrap();
94
95 if i == 0 {
96 return '\u{2070}'.into()
97 }
98
99 let (init, i) = if i > 0 {
100 (String::new(), i as usize)
101 } else {
102 ('\u{207B}'.into(), -i as usize)
103 };
104
105 i.into_digits().into_iter().fold(init, |mut res, d| {
106 let c = match d {
107 1 => '\u{00B9}',
108 2 => '\u{00B2}',
109 3 => '\u{00B3}',
110 _ => char::from_u32(('\u{2070}' as u32) + (d as u32)).unwrap()
111 };
112 res.push(c);
113 res
114 })
115}
116
117pub fn table<S, I, J, I1, I2, D, F>(head: S, rows: I1, cols: I2, entry: F) -> String
118where
119 S: Display,
120 I: Display,
121 J: Display,
122 I1: Iterator<Item = I>,
123 I2: Iterator<Item = J>,
124 D: Display,
125 F: Fn(&I, &J) -> D
126{
127 use prettytable::*;
128
129 let rows = rows.collect_vec();
130 let cols = cols.collect_vec();
131
132 fn row<I>(head: String, cols: I) -> Row
133 where I: Iterator<Item = String> {
134 let mut cells = vec![Cell::new(head.as_str())];
135 cells.extend(cols.map(|str| Cell::new(str.as_str())));
136 Row::new(cells)
137 }
138
139 let mut table = Table::new();
140
141 table.set_format(*format::consts::FORMAT_CLEAN);
142 table.set_titles(row(
143 head.to_string(),
144 cols.iter().map(|j| j.to_string() )
145 ));
146
147 for i in rows.iter() {
148 table.add_row(row(
149 i.to_string(),
150 cols.iter().map(|j| format!("{}", entry(i, j)))
151 ));
152 }
153
154 table.to_string()
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_subscript() {
163 assert_eq!(subscript(0), "₀");
164 assert_eq!(subscript(1234567890), "₁₂₃₄₅₆₇₈₉₀");
165 assert_eq!(subscript(-1234567890), "₋₁₂₃₄₅₆₇₈₉₀");
166 }
167
168 #[test]
169 fn test_superscript() {
170 assert_eq!(superscript(0), "⁰");
171 assert_eq!(superscript(1234567890), "¹²³⁴⁵⁶⁷⁸⁹⁰");
172 assert_eq!(superscript(-1234567890), "⁻¹²³⁴⁵⁶⁷⁸⁹⁰");
173 }
174
175 #[test]
176 fn test_table() {
177 let table = table("", 1..=3, 4..=6, |i, j| i * 10 + j);
178 let a = " 4 5 6 \n 1 14 15 16 \n 2 24 25 26 \n 3 34 35 36 \n";
179 assert_eq!(table, a.to_string());
180 }
181}