dodecet_encoder/
dodecet.rs1use crate::{DodecetError, Result, MAX_DODECET, NIBBLES};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[derive(Default)]
23pub struct Dodecet {
24 value: u16,
25}
26
27impl Dodecet {
28 pub fn new(value: u16) -> Result<Self> {
42 if value > MAX_DODECET {
43 Err(DodecetError::Overflow)
44 } else {
45 Ok(Dodecet { value })
46 }
47 }
48
49 #[inline]
63 pub unsafe fn from_hex_unchecked(value: u16) -> Self {
64 Dodecet { value }
65 }
66
67 #[inline]
78 pub const fn from_hex(value: u16) -> Self {
79 Dodecet {
80 value: value & MAX_DODECET,
81 }
82 }
83
84 #[inline]
95 pub fn from_signed(value: i16) -> Self {
96 let unsigned = if value < 0 {
97 (value + 4096) as u16
98 } else {
99 value as u16
100 };
101 Dodecet {
102 value: unsigned & MAX_DODECET,
103 }
104 }
105
106 #[inline]
117 pub fn value(self) -> u16 {
118 self.value
119 }
120
121 #[inline]
137 pub fn nibble(self, index: u8) -> Result<u8> {
138 if index >= NIBBLES {
139 return Err(DodecetError::InvalidNibble);
140 }
141 Ok(((self.value >> (index * 4)) & 0xF) as u8)
142 }
143
144 #[inline]
160 pub fn set_nibble(&mut self, index: u8, nibble: u8) -> Result<()> {
161 if index >= NIBBLES {
162 return Err(DodecetError::InvalidNibble);
163 }
164 if nibble > 0xF {
165 return Err(DodecetError::Overflow);
166 }
167
168 let mask = !(0xFu16 << (index * 4));
169 self.value = (self.value & mask) | ((nibble as u16) << (index * 4));
170 Ok(())
171 }
172
173 #[inline]
184 pub fn is_zero(self) -> bool {
185 self.value == 0
186 }
187
188 #[inline]
199 pub fn is_max(self) -> bool {
200 self.value == MAX_DODECET
201 }
202
203 #[inline]
214 pub fn count_ones(self) -> u32 {
215 self.value.count_ones()
216 }
217
218 #[inline]
230 pub fn count_zeros(self) -> u32 {
231 self.value.count_zeros()
232 }
233
234 #[inline]
236 pub fn and(self, other: Dodecet) -> Dodecet {
237 Dodecet {
238 value: self.value & other.value,
239 }
240 }
241
242 #[inline]
244 pub fn or(self, other: Dodecet) -> Dodecet {
245 Dodecet {
246 value: self.value | other.value,
247 }
248 }
249
250 #[inline]
252 pub fn xor(self, other: Dodecet) -> Dodecet {
253 Dodecet {
254 value: self.value ^ other.value,
255 }
256 }
257
258 #[inline]
260 pub fn not(self) -> Dodecet {
261 Dodecet {
262 value: (!self.value) & MAX_DODECET,
263 }
264 }
265
266 #[inline]
268 pub fn wrapping_add(self, other: Dodecet) -> Dodecet {
269 Dodecet {
270 value: self.value.wrapping_add(other.value) & MAX_DODECET,
271 }
272 }
273
274 #[inline]
276 pub fn wrapping_sub(self, other: Dodecet) -> Dodecet {
277 Dodecet {
278 value: self.value.wrapping_sub(other.value) & MAX_DODECET,
279 }
280 }
281
282 #[inline]
284 pub fn wrapping_mul(self, other: Dodecet) -> Dodecet {
285 Dodecet {
286 value: self.value.wrapping_mul(other.value) & MAX_DODECET,
287 }
288 }
289
290 pub fn to_hex_string(self) -> String {
301 format!("{:03X}", self.value)
302 }
303
304 pub fn from_hex_str(s: &str) -> Result<Self> {
315 let value = u16::from_str_radix(s.trim(), 16)
316 .map_err(|_| DodecetError::InvalidHex)?;
317
318 if value > MAX_DODECET {
319 return Err(DodecetError::Overflow);
320 }
321
322 Ok(Dodecet { value })
323 }
324
325 pub fn to_binary_string(self) -> String {
336 format!("{:012b}", self.value)
337 }
338
339 #[inline]
350 pub fn as_signed(self) -> i16 {
351 if self.value & 0x800 != 0 {
352 (self.value as i16) - 4096
353 } else {
354 self.value as i16
355 }
356 }
357
358 #[inline]
369 pub fn normalize(self) -> f64 {
370 self.value as f64 / MAX_DODECET as f64
371 }
372}
373
374
375impl From<u8> for Dodecet {
376 fn from(value: u8) -> Self {
377 Dodecet { value: value as u16 }
378 }
379}
380
381impl TryFrom<u16> for Dodecet {
382 type Error = DodecetError;
383
384 fn try_from(value: u16) -> std::result::Result<Self, Self::Error> {
385 Dodecet::new(value)
386 }
387}
388
389impl From<Dodecet> for u16 {
390 fn from(d: Dodecet) -> Self {
391 d.value
392 }
393}
394
395impl std::fmt::Display for Dodecet {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 write!(f, "0x{:03X}", self.value)
398 }
399}
400
401impl std::fmt::Binary for Dodecet {
402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403 write!(f, "{:012b}", self.value)
404 }
405}
406
407impl std::fmt::Octal for Dodecet {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 write!(f, "{:04o}", self.value)
410 }
411}
412
413impl std::ops::Add for Dodecet {
414 type Output = Self;
415
416 fn add(self, other: Self) -> Self::Output {
417 self.wrapping_add(other)
418 }
419}
420
421impl std::ops::Sub for Dodecet {
422 type Output = Self;
423
424 fn sub(self, other: Self) -> Self::Output {
425 self.wrapping_sub(other)
426 }
427}
428
429impl std::ops::Mul for Dodecet {
430 type Output = Self;
431
432 fn mul(self, other: Self) -> Self::Output {
433 self.wrapping_mul(other)
434 }
435}
436
437impl std::ops::BitAnd for Dodecet {
438 type Output = Self;
439
440 fn bitand(self, other: Self) -> Self::Output {
441 self.and(other)
442 }
443}
444
445impl std::ops::BitOr for Dodecet {
446 type Output = Self;
447
448 fn bitor(self, other: Self) -> Self::Output {
449 self.or(other)
450 }
451}
452
453impl std::ops::BitXor for Dodecet {
454 type Output = Self;
455
456 fn bitxor(self, other: Self) -> Self::Output {
457 self.xor(other)
458 }
459}
460
461impl std::ops::Not for Dodecet {
462 type Output = Self;
463
464 fn not(self) -> Self::Output {
465 self.not()
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472
473 #[test]
474 fn test_creation() {
475 let d = Dodecet::new(0xABC).unwrap();
476 assert_eq!(d.value(), 0xABC);
477
478 let d2 = Dodecet::from_hex(0xDEF);
479 assert_eq!(d2.value(), 0xDEF);
480 }
481
482 #[test]
483 fn test_nibbles() {
484 let d = Dodecet::from_hex(0xABC);
485 assert_eq!(d.nibble(0).unwrap(), 0xC);
486 assert_eq!(d.nibble(1).unwrap(), 0xB);
487 assert_eq!(d.nibble(2).unwrap(), 0xA);
488 }
489
490 #[test]
491 fn test_set_nibble() {
492 let mut d = Dodecet::from_hex(0xABC);
493 d.set_nibble(0, 0xD).unwrap();
494 assert_eq!(d.value(), 0xABD);
495
496 d.set_nibble(1, 0xE).unwrap();
497 assert_eq!(d.value(), 0xAED);
498
499 d.set_nibble(2, 0x1).unwrap();
500 assert_eq!(d.value(), 0x1ED);
501 }
502
503 #[test]
504 fn test_overflow() {
505 assert!(Dodecet::new(0x1000).is_err());
506 assert!(Dodecet::new(0xFFF).is_ok());
507 }
508
509 #[test]
510 fn test_bitwise_ops() {
511 let a = Dodecet::from_hex(0xF0F);
512 let b = Dodecet::from_hex(0x0F0);
513
514 assert_eq!((a & b).value(), 0x000);
515 assert_eq!((a | b).value(), 0xFFF);
516 assert_eq!((a ^ b).value(), 0xFFF);
517 assert_eq!((!a).value(), 0x0F0);
518 }
519
520 #[test]
521 fn test_arithmetic() {
522 let a = Dodecet::from_hex(0x800);
523 let b = Dodecet::from_hex(0x800);
524
525 let c = a + b;
526 assert_eq!(c.value(), 0x000); let d = Dodecet::from_hex(0x100) - Dodecet::from_hex(0x001);
529 assert_eq!(d.value(), 0x0FF);
530 }
531
532 #[test]
533 fn test_conversions() {
534 let d = Dodecet::from_hex(0xABC);
535
536 assert_eq!(d.to_hex_string(), "ABC");
537 assert_eq!(d.to_binary_string(), "101010111100");
538
539 let d2 = Dodecet::from_hex_str("ABC").unwrap();
540 assert_eq!(d2.value(), 0xABC);
541 }
542
543 #[test]
544 fn test_signed() {
545 let d = Dodecet::from_hex(0x800);
546 assert_eq!(d.as_signed(), -2048);
547
548 let d = Dodecet::from_hex(0x7FF);
549 assert_eq!(d.as_signed(), 2047);
550
551 let d = Dodecet::from_hex(0x000);
552 assert_eq!(d.as_signed(), 0);
553 }
554
555 #[test]
556 fn test_normalize() {
557 let d = Dodecet::from_hex(0x000);
558 assert_eq!(d.normalize(), 0.0);
559
560 let d = Dodecet::from_hex(0xFFF);
561 assert!((d.normalize() - 1.0).abs() < f64::EPSILON);
562
563 let d = Dodecet::from_hex(0x800);
564 assert!((d.normalize() - 0.5).abs() < 0.001);
565 }
566
567 #[test]
568 fn test_count_bits() {
569 let d = Dodecet::from_hex(0xFFF);
570 assert_eq!(d.count_ones(), 12);
571 assert_eq!(d.count_zeros(), 4);
573
574 let d = Dodecet::from_hex(0x000);
575 assert_eq!(d.count_ones(), 0);
576 assert_eq!(d.count_zeros(), 16);
577 }
578
579 #[test]
580 fn test_display() {
581 let d = Dodecet::from_hex(0xABC);
582 assert_eq!(format!("{}", d), "0xABC");
583 assert_eq!(format!("{:b}", d), "101010111100");
584 assert_eq!(format!("{:o}", d), "5274");
585 }
586}