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