1use std::error::Error;
99use std::fmt::{Display, Formatter};
100use std::ops::Div;
101
102pub trait TryFromSnafu: Sized {
104 fn try_from_snafu(value: &str) -> Result<Self, ConversionError>;
116}
117
118pub trait FromSnafu {
120 fn from_snafu(value: &str) -> Self;
135}
136
137pub trait IntoSnafu {
139 fn into_snafu(self) -> String;
153}
154
155#[derive(Debug, Copy, Clone, Eq, PartialEq)]
156pub enum ConversionError {
157 InvalidDigit,
159 Overflow,
161 OutOfBounds,
164}
165
166impl Display for ConversionError {
167 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168 match self {
169 ConversionError::InvalidDigit => write!(f, "An invalid digit was specified"),
170 ConversionError::Overflow => write!(f, "The calculation overflowed"),
171 ConversionError::OutOfBounds => {
172 write!(f, "The input was out of bounds for the target type")
173 }
174 }
175 }
176}
177
178impl Error for ConversionError {}
179
180macro_rules! impl_try_from {
188 ($max_len: literal, $target:ty, $lift:ty) => {
189 impl TryFromSnafu for $target {
190 fn try_from_snafu(value: &str) -> Result<Self, ConversionError> {
191 if value.len() > $max_len {
192 return Err(ConversionError::OutOfBounds);
193 }
194
195 let (sum, _) =
196 value
197 .chars()
198 .rev()
199 .try_fold((0 as $lift, 1 as $lift), |(sum, pow), c| {
200 let digit = map_digit(c)?;
201 let value = digit as $lift * pow;
202 Ok((sum + value, pow * 5))
203 })?;
204
205 if sum > (<$target>::MAX as $lift) {
206 return Err(ConversionError::OutOfBounds);
207 }
208
209 Ok(sum as $target)
210 }
211 }
212 };
213}
214
215impl_try_from!(55, i128, i128);
216impl_try_from!(28, i64, i128);
217impl_try_from!(28, u64, i128);
218impl_try_from!(14, i32, i64);
219impl_try_from!(14, u32, i64);
220impl_try_from!(7, i16, i32);
221impl_try_from!(7, u16, i32);
222impl_try_from!(4, i8, i16);
223impl_try_from!(4, u8, i16);
224
225impl<T> FromSnafu for T
226where
227 T: TryFromSnafu,
228{
229 fn from_snafu(value: &str) -> Self {
230 match T::try_from_snafu(value) {
231 Ok(value) => value,
232 Err(e) => panic!("Unable to convert to SNAFU: {e}"),
233 }
234 }
235}
236
237static SYMBOLS: &[char] = &['=', '-', '0', '1', '2'];
239
240macro_rules! impl_into {
245 ($target:ty) => {
246 impl IntoSnafu for $target {
247 fn into_snafu(mut self) -> String {
248 let mut digits = Vec::default();
250
251 loop {
252 self += 2;
253 let selector = self % 5;
254 let digit = SYMBOLS[selector as usize];
255 digits.push(digit);
256
257 if self < 5 {
258 break;
259 }
260 self = self.div(5);
261 }
262
263 String::from_iter(digits.into_iter().rev())
264 }
265 }
266 };
267}
268
269impl_into!(usize);
270impl_into!(isize);
271impl_into!(u128);
272impl_into!(i128);
273impl_into!(u64);
274impl_into!(i64);
275impl_into!(u32);
276impl_into!(i32);
277impl_into!(u16);
278impl_into!(i16);
279impl_into!(u8);
280impl_into!(i8);
281
282#[inline(always)]
284const fn map_digit(digit: char) -> Result<i8, ConversionError> {
285 match digit {
286 '2' => Ok(2),
287 '1' => Ok(1),
288 '0' => Ok(0),
289 '-' => Ok(-1),
290 '=' => Ok(-2),
291 _ => Err(ConversionError::InvalidDigit),
292 }
293}
294
295#[cfg(test)]
298fn num_bits_for_len(len: usize) -> u32 {
299 debug_assert_ne!(len, 0, "value must be positive");
300 num_bits_for_pos(len - 1)
301}
302
303#[cfg(test)]
324fn num_bits_for_pos(pos: usize) -> u32 {
325 ((pos + 1) as f32 * 5.0f32.log2()).floor() as u32
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn u8_from_snafu_works() {
336 assert_eq!(u8::from_snafu("2=0="), 198);
337 assert_eq!(u8::from_snafu("21"), 11);
338 assert_eq!(u8::from_snafu("2=01"), 201);
339 assert_eq!(u8::from_snafu("111"), 31);
340 assert_eq!(u8::from_snafu("112"), 32);
341 assert_eq!(u8::from_snafu("1-12"), 107);
342 assert_eq!(u8::from_snafu("12"), 7);
343 assert_eq!(u8::from_snafu("1="), 3);
344 assert_eq!(u8::from_snafu("122"), 37);
345 }
346
347 #[test]
348 fn u16_from_snafu_works() {
349 assert_eq!(u16::from_snafu("1=-0-2"), 1747);
350 assert_eq!(u16::from_snafu("12111"), 906);
351 assert_eq!(u16::from_snafu("2=0="), 198);
352 assert_eq!(u16::from_snafu("21"), 11);
353 assert_eq!(u16::from_snafu("2=01"), 201);
354 assert_eq!(u16::from_snafu("111"), 31);
355 assert_eq!(u16::from_snafu("20012"), 1257);
356 assert_eq!(u16::from_snafu("112"), 32);
357 assert_eq!(u16::from_snafu("1=-1="), 353);
358 assert_eq!(u16::from_snafu("1-12"), 107);
359 assert_eq!(u16::from_snafu("12"), 7);
360 assert_eq!(u16::from_snafu("1="), 3);
361 assert_eq!(u16::from_snafu("122"), 37);
362 assert_eq!(u16::from_snafu("2=-01"), 976);
363 }
364
365 #[test]
366 fn u32_from_snafu_works() {
367 assert_eq!(u32::from_snafu("1=-0-2"), 1747);
368 assert_eq!(u32::from_snafu("12111"), 906);
369 assert_eq!(u32::from_snafu("2=0="), 198);
370 assert_eq!(u32::from_snafu("21"), 11);
371 assert_eq!(u32::from_snafu("2=01"), 201);
372 assert_eq!(u32::from_snafu("111"), 31);
373 assert_eq!(u32::from_snafu("20012"), 1257);
374 assert_eq!(u32::from_snafu("112"), 32);
375 assert_eq!(u32::from_snafu("1=-1="), 353);
376 assert_eq!(u32::from_snafu("1-12"), 107);
377 assert_eq!(u32::from_snafu("12"), 7);
378 assert_eq!(u32::from_snafu("1="), 3);
379 assert_eq!(u32::from_snafu("122"), 37);
380 assert_eq!(u32::from_snafu("2=-01"), 976);
381 }
382
383 #[test]
384 fn u64_from_snafu_works() {
385 assert_eq!(u64::from_snafu("1=-0-2"), 1747);
386 assert_eq!(u64::from_snafu("12111"), 906);
387 assert_eq!(u64::from_snafu("2=0="), 198);
388 assert_eq!(u64::from_snafu("21"), 11);
389 assert_eq!(u64::from_snafu("2=01"), 201);
390 assert_eq!(u64::from_snafu("111"), 31);
391 assert_eq!(u64::from_snafu("20012"), 1257);
392 assert_eq!(u64::from_snafu("112"), 32);
393 assert_eq!(u64::from_snafu("1=-1="), 353);
394 assert_eq!(u64::from_snafu("1-12"), 107);
395 assert_eq!(u64::from_snafu("12"), 7);
396 assert_eq!(u64::from_snafu("1="), 3);
397 assert_eq!(u64::from_snafu("122"), 37);
398 assert_eq!(u64::from_snafu("2=-01"), 976);
399 }
400
401 #[test]
402 fn i128_from_snafu_works() {
403 assert_eq!(i128::from_snafu("1=-0-2"), 1747);
404 assert_eq!(i128::from_snafu("12111"), 906);
405 assert_eq!(i128::from_snafu("2=0="), 198);
406 assert_eq!(i128::from_snafu("21"), 11);
407 assert_eq!(i128::from_snafu("2=01"), 201);
408 assert_eq!(i128::from_snafu("111"), 31);
409 assert_eq!(i128::from_snafu("20012"), 1257);
410 assert_eq!(i128::from_snafu("112"), 32);
411 assert_eq!(i128::from_snafu("1=-1="), 353);
412 assert_eq!(i128::from_snafu("1-12"), 107);
413 assert_eq!(i128::from_snafu("12"), 7);
414 assert_eq!(i128::from_snafu("1="), 3);
415 assert_eq!(i128::from_snafu("122"), 37);
416 assert_eq!(i128::from_snafu("2=-01"), 976);
417 }
418
419 #[test]
420 fn highest_number() {
421 assert_eq!(naive_num_bits(0), 1); assert_eq!(max_for_length(1), 2); assert_eq!(naive_num_bits(2), 2);
425 assert_eq!(num_bits_for_pos(0), 2);
426 assert_eq!(num_bits_for_len(1), 2);
427
428 assert_eq!(max_for_length(2), 12); assert_eq!(naive_num_bits(12), 4); assert_eq!(num_bits_for_pos(1), 4);
431 assert_eq!(num_bits_for_len(2), 4);
432
433 assert_eq!(max_for_length(3), 62); assert_eq!(naive_num_bits(62), 6); assert_eq!(num_bits_for_pos(2), 6);
436
437 assert_eq!(num_bits_for_len(3), 6);
440 assert_eq!(naive_num_bits(i8::MAX as u128), 7);
441 assert_eq!(naive_num_bits(u8::MAX as u128), 8);
442
443 assert_eq!(max_for_length(4), 312); assert_eq!(naive_num_bits(312), 9); assert_eq!(num_bits_for_pos(3), 9);
448
449 assert_eq!(max_for_length(5), 1562);
450 assert_eq!(naive_num_bits(1562), 11); assert_eq!(num_bits_for_pos(4), 11);
452
453 assert_eq!(max_for_length(6), 7812);
454 assert_eq!(naive_num_bits(7812), 13); assert_eq!(num_bits_for_pos(5), 13);
456
457 assert_eq!(max_for_length(7), 39062);
458 assert_eq!(naive_num_bits(39062), 16); assert_eq!(num_bits_for_pos(6), 16);
460
461 assert_eq!(num_bits_for_len(7), 16);
464 assert_eq!(naive_num_bits(i16::MAX as u128), 15);
465 assert_eq!(naive_num_bits(u16::MAX as u128), 16);
466
467 assert_eq!(max_for_length(8), 195312);
470 assert_eq!(naive_num_bits(195312), 18); assert_eq!(num_bits_for_pos(7), 18);
472
473 assert_eq!(max_for_length(9), 976562);
474 assert_eq!(naive_num_bits(976562), 20); assert_eq!(num_bits_for_pos(8), 20);
476
477 assert_eq!(max_for_length(10), 4882812);
478 assert_eq!(naive_num_bits(4882812), 23); assert_eq!(num_bits_for_pos(9), 23);
480
481 assert_eq!(max_for_length(11), 24414062);
482 assert_eq!(naive_num_bits(24414062), 25); assert_eq!(num_bits_for_pos(10), 25);
484
485 assert_eq!(max_for_length(12), 122070312);
486 assert_eq!(naive_num_bits(122070312), 27); assert_eq!(num_bits_for_pos(11), 27);
488
489 assert_eq!(max_for_length(13), 610351562);
490 assert_eq!(naive_num_bits(610351562), 30); assert_eq!(num_bits_for_pos(12), 30);
492
493 assert_eq!(max_for_length(14), 3051757812);
494 assert_eq!(naive_num_bits(3051757812), 32); assert_eq!(num_bits_for_pos(13), 32);
496
497 assert_eq!(num_bits_for_len(14), 32);
500 assert_eq!(naive_num_bits(i32::MAX as u128), 31);
501 assert_eq!(naive_num_bits(u32::MAX as u128), 32);
502
503 assert_eq!(max_for_length(15), 15258789062);
506 assert_eq!(naive_num_bits(15258789062), 34); assert_eq!(num_bits_for_pos(14), 34);
508
509 assert_eq!(max_for_length(16), 76293945312);
510 assert_eq!(naive_num_bits(76293945312), 37); assert_eq!(num_bits_for_pos(15), 37);
512
513 assert_eq!(max_for_length(17), 381469726562);
514 assert_eq!(naive_num_bits(381469726562), 39); assert_eq!(num_bits_for_pos(16), 39);
516
517 assert_eq!(max_for_length(18), 1907348632812);
518 assert_eq!(naive_num_bits(1907348632812), 41); assert_eq!(num_bits_for_pos(17), 41);
520
521 assert_eq!(max_for_length(19), 9536743164062);
522 assert_eq!(naive_num_bits(9536743164062), 44); assert_eq!(num_bits_for_pos(18), 44);
524
525 assert_eq!(max_for_length(20), 47683715820312);
526 assert_eq!(naive_num_bits(47683715820312), 46); assert_eq!(num_bits_for_pos(19), 46);
528
529 assert_eq!(max_for_length(21), 238418579101562);
530 assert_eq!(naive_num_bits(238418579101562), 48); assert_eq!(num_bits_for_pos(20), 48);
532
533 assert_eq!(max_for_length(22), 1192092895507812);
534 assert_eq!(naive_num_bits(1192092895507812), 51); assert_eq!(num_bits_for_pos(21), 51);
536
537 assert_eq!(max_for_length(23), 5960464477539062);
538 assert_eq!(naive_num_bits(5960464477539062), 53); assert_eq!(num_bits_for_pos(22), 53);
540
541 assert_eq!(max_for_length(24), 29802322387695312);
542 assert_eq!(naive_num_bits(29802322387695312), 55); assert_eq!(num_bits_for_pos(23), 55);
544
545 assert_eq!(max_for_length(25), 149011611938476562);
546 assert_eq!(naive_num_bits(149011611938476562), 58); assert_eq!(num_bits_for_pos(24), 58);
548
549 assert_eq!(max_for_length(26), 745058059692382812);
550 assert_eq!(naive_num_bits(745058059692382812), 60); assert_eq!(num_bits_for_pos(25), 60);
552
553 assert_eq!(max_for_length(27), 3725290298461914062);
554 assert_eq!(naive_num_bits(3725290298461914062), 62); assert_eq!(num_bits_for_pos(26), 62);
556
557 assert_eq!(num_bits_for_len(27), 62);
560 assert_eq!(naive_num_bits(i64::MAX as u128), 63);
561 assert_eq!(naive_num_bits(u64::MAX as u128), 64);
562
563 assert_eq!(max_for_length(28), 18626451492309570312);
566 assert_eq!(naive_num_bits(18626451492309570312), 65); assert_eq!(num_bits_for_pos(27), 65);
568
569 assert_eq!(max_for_length(29), 93132257461547851562);
570 assert_eq!(naive_num_bits(93132257461547851562), 67); assert_eq!(num_bits_for_pos(28), 67);
572
573 assert_eq!(max_for_length(30), 465661287307739257812);
574 assert_eq!(naive_num_bits(465661287307739257812), 69); assert_eq!(num_bits_for_pos(29), 69);
576
577 assert_eq!(max_for_length(31), 2328306436538696289062);
578 assert_eq!(naive_num_bits(2328306436538696289062), 71); assert_eq!(num_bits_for_pos(30), 71);
580
581 assert_eq!(max_for_length(32), 11641532182693481445312);
582 assert_eq!(naive_num_bits(11641532182693481445312), 74); assert_eq!(num_bits_for_pos(31), 74);
584
585 assert_eq!(max_for_length(33), 58207660913467407226562);
586 assert_eq!(naive_num_bits(58207660913467407226562), 76); assert_eq!(num_bits_for_pos(32), 76);
588
589 assert_eq!(max_for_length(34), 291038304567337036132812);
590 assert_eq!(naive_num_bits(291038304567337036132812), 78); assert_eq!(num_bits_for_pos(33), 78);
592
593 assert_eq!(max_for_length(35), 1455191522836685180664062);
594 assert_eq!(naive_num_bits(1455191522836685180664062), 81); assert_eq!(num_bits_for_pos(34), 81);
596
597 assert_eq!(max_for_length(36), 7275957614183425903320312);
598 assert_eq!(naive_num_bits(7275957614183425903320312), 83); assert_eq!(num_bits_for_pos(35), 83);
600
601 assert_eq!(max_for_length(55), 138777878078144567552953958511352539062);
604 assert_eq!(naive_num_bits(138777878078144567552953958511352539062), 127);
605 assert_eq!(num_bits_for_pos(54), 127);
606
607 assert_eq!(num_bits_for_len(55), 127);
608 assert_eq!(naive_num_bits(i128::MAX as u128), 127);
609 assert_eq!(naive_num_bits(u128::MAX), 128);
610
611 assert_eq!(num_bits_for_pos(55), 130);
614 }
615
616 fn max_for_length(len: u32) -> u128 {
619 let mut sum: u128 = 0;
620 for n in 0..len {
621 sum += 2 * 5_u128.pow(n);
622 }
623 sum
624 }
625
626 fn naive_num_bits(value: u128) -> u32 {
628 1 + if value == 0 { 0 } else { value.ilog2() }
629 }
630
631 #[test]
632 fn u128_into_snafu() {
633 assert_eq!(1_u128.into_snafu(), "1");
634 assert_eq!(2_u128.into_snafu(), "2");
635 assert_eq!(3_u128.into_snafu(), "1=");
636 assert_eq!(4_u128.into_snafu(), "1-");
637 assert_eq!(5_u128.into_snafu(), "10");
638 assert_eq!(6_u128.into_snafu(), "11");
639 assert_eq!(7_u128.into_snafu(), "12");
640 assert_eq!(8_u128.into_snafu(), "2=");
641 assert_eq!(9_u128.into_snafu(), "2-");
642 assert_eq!(10_u128.into_snafu(), "20");
643 assert_eq!(11_u128.into_snafu(), "21");
644 assert_eq!(12_u128.into_snafu(), "22");
645 assert_eq!(15_u128.into_snafu(), "1=0");
646 assert_eq!(20_u128.into_snafu(), "1-0");
647 assert_eq!(976_u128.into_snafu(), "2=-01");
648 assert_eq!(2022_u128.into_snafu(), "1=11-2");
649 assert_eq!(12345_u128.into_snafu(), "1-0---0");
650 assert_eq!(314159265_u128.into_snafu(), "1121-1110-1=0");
651 }
652
653 #[test]
654 fn i32_into_snafu() {
655 assert_eq!(1_i32.into_snafu(), "1");
656 assert_eq!(2_i32.into_snafu(), "2");
657 assert_eq!(3_i32.into_snafu(), "1=");
658 assert_eq!(4_i32.into_snafu(), "1-");
659 assert_eq!(5_i32.into_snafu(), "10");
660 assert_eq!(6_i32.into_snafu(), "11");
661 assert_eq!(7_i32.into_snafu(), "12");
662 assert_eq!(8_i32.into_snafu(), "2=");
663 assert_eq!(9_i32.into_snafu(), "2-");
664 assert_eq!(10_i32.into_snafu(), "20");
665 assert_eq!(11_i32.into_snafu(), "21");
666 assert_eq!(12_i32.into_snafu(), "22");
667 assert_eq!(15_i32.into_snafu(), "1=0");
668 assert_eq!(20_i32.into_snafu(), "1-0");
669 assert_eq!(976_i32.into_snafu(), "2=-01");
670 assert_eq!(2022_i32.into_snafu(), "1=11-2");
671 assert_eq!(12345_i32.into_snafu(), "1-0---0");
672 assert_eq!(314159265_i32.into_snafu(), "1121-1110-1=0");
673 }
674
675 #[test]
676 fn i8_into_snafu() {
677 assert_eq!(1_i8.into_snafu(), "1");
678 assert_eq!(2_i8.into_snafu(), "2");
679 assert_eq!(3_i8.into_snafu(), "1=");
680 assert_eq!(4_i8.into_snafu(), "1-");
681 assert_eq!(5_i8.into_snafu(), "10");
682 assert_eq!(6_i8.into_snafu(), "11");
683 assert_eq!(7_i8.into_snafu(), "12");
684 assert_eq!(8_i8.into_snafu(), "2=");
685 assert_eq!(9_i8.into_snafu(), "2-");
686 assert_eq!(10_i8.into_snafu(), "20");
687 assert_eq!(11_i8.into_snafu(), "21");
688 assert_eq!(12_i8.into_snafu(), "22");
689 assert_eq!(15_i8.into_snafu(), "1=0");
690 assert_eq!(20_i8.into_snafu(), "1-0");
691 }
692
693 #[test]
694 fn into_snafu_reference_works() {
695 assert_eq!(into_snafu_reference(1), "1");
696 assert_eq!(into_snafu_reference(2), "2");
697 assert_eq!(into_snafu_reference(3), "1=");
698 assert_eq!(into_snafu_reference(4), "1-");
699 assert_eq!(into_snafu_reference(5), "10");
700 assert_eq!(into_snafu_reference(6), "11");
701 assert_eq!(into_snafu_reference(7), "12");
702 assert_eq!(into_snafu_reference(8), "2=");
703 assert_eq!(into_snafu_reference(9), "2-");
704 assert_eq!(into_snafu_reference(10), "20");
705 assert_eq!(into_snafu_reference(11), "21");
706 assert_eq!(into_snafu_reference(12), "22");
707 assert_eq!(into_snafu_reference(15), "1=0");
708 assert_eq!(into_snafu_reference(20), "1-0");
709 assert_eq!(into_snafu_reference(976), "2=-01");
710 assert_eq!(into_snafu_reference(2022), "1=11-2");
711 assert_eq!(into_snafu_reference(2023), "1=110=");
712 assert_eq!(into_snafu_reference(12345), "1-0---0");
713 assert_eq!(into_snafu_reference(314159265), "1121-1110-1=0");
714 }
715
716 fn into_snafu_reference(mut value: u128) -> String {
717 let mut digits = Vec::default();
718
719 loop {
720 value += 2;
721 let selector = value % 5;
722 let digit = SYMBOLS[selector as usize];
723 digits.push(digit);
724
725 if value < 5 {
726 break;
727 }
728 value = value.div(5);
729 }
730
731 String::from_iter(digits.into_iter().rev())
732 }
733}