1use crate::{
2 ATTOS_PER_FS, ATTOS_PER_MS, ATTOS_PER_NS, ATTOS_PER_PS, ATTOS_PER_SEC, ATTOS_PER_SEC_I128,
3 ATTOS_PER_SECF, ATTOS_PER_US, Drift, Dt, Real, Scale, Spacetime, floor_f,
4};
5
6impl Dt {
7 #[inline]
8 pub const fn add(self, span: Dt) -> Self {
9 if !span.is_zero() {
10 let (sec, attos) = Dt::add_time(self.sec, self.attos, span.sec, span.attos);
11 Self { sec, attos }
12 } else {
13 self
14 }
15 }
16
17 #[inline]
18 pub const fn sub(self, span: Dt) -> Self {
19 if !span.is_zero() {
20 let (sec, attos) = Dt::sub_time(self.sec, self.attos, span.sec, span.attos);
21 Self { sec, attos }
22 } else {
23 self
24 }
25 }
26
27 pub const fn to_sec_f(&self) -> Real {
30 let Dt { sec, attos: rem } = self.carry_attos();
31
32 if sec < 0 && rem > ATTOS_PER_SEC / 2 {
33 let small = ATTOS_PER_SEC - rem; let small_f = f!(small) / ATTOS_PER_SECF;
38 (f!(sec) + 1.0) - small_f
39 } else {
40 f!(sec) + f!(rem) / ATTOS_PER_SECF
42 }
43 }
44
45 #[inline]
51 pub const fn adjusted_advance(&mut self, elapsed: &Dt, spacetime: &Spacetime) {
52 let dtau = elapsed.add(Drift::from_spacetime(spacetime).time_diff_after(elapsed));
53 *self = self.add(dtau);
54 }
55
56 #[inline]
64 pub const fn adjusted_advance_using_drift(&mut self, elapsed: &Dt, drift: &Drift) {
65 let dtau = elapsed.add(drift.time_diff_after(elapsed));
66 *self = self.add(dtau);
67 }
68
69 #[inline]
71 pub const fn to_diff_raw(&self, other: Self) -> Dt {
72 Self::diff_raw_internal(self.sec, self.attos, other.sec, other.attos)
73 }
74
75 #[inline]
77 pub const fn to_diff_raw_f(&self, other: Self) -> Real {
78 self.to_sec_f() - other.to_sec_f()
79 }
80
81 #[inline]
83 pub const fn add_1sec(&mut self) {
84 self.sec = self.sec.saturating_add(1);
85 }
86
87 #[inline]
89 pub const fn add_1min(&mut self) {
90 self.sec = self.sec.saturating_add(60);
91 }
92
93 #[inline]
95 pub const fn add_1hr(&mut self) {
96 self.sec = self.sec.saturating_add(3600);
97 }
98
99 #[inline]
103 pub const fn add_1ms(&mut self) {
104 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_MS);
105 }
106
107 #[inline]
111 pub const fn add_1us(&mut self) {
112 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_US);
113 }
114
115 #[inline]
119 pub const fn add_1ns(&mut self) {
120 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_NS);
121 }
122
123 #[inline]
125 pub const fn add_sec(&mut self, n: i64) {
126 self.sec = self.sec.saturating_add(n);
127 }
128
129 #[inline]
131 pub const fn add_min(&mut self, n: i64) {
132 self.sec = self.sec.saturating_add(n.saturating_mul(60));
133 }
134
135 #[inline]
137 pub const fn add_hr(&mut self, n: i64) {
138 self.sec = self.sec.saturating_add(n.saturating_mul(3600));
139 }
140
141 #[inline]
145 pub const fn add_ms(&mut self, n: i64) {
146 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_MS);
147 }
148
149 #[inline]
153 pub const fn add_us(&mut self, n: i64) {
154 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_US);
155 }
156
157 #[inline]
161 pub const fn add_ns(&mut self, n: i64) {
162 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_NS);
163 }
164
165 #[inline]
169 pub const fn add_ps(&mut self, n: i64) {
170 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_PS);
171 }
172
173 #[inline]
177 pub const fn add_fs(&mut self, n: i64) {
178 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_FS);
179 }
180
181 #[inline]
185 pub const fn add_attos(&mut self, n: i64) {
186 Self::add_attos_span(&mut self.sec, &mut self.attos, n, 1);
187 }
188
189 #[inline]
191 pub const fn sub_1hr(&mut self) {
192 self.sec = self.sec.saturating_sub(3600);
193 }
194
195 #[inline]
197 pub const fn sub_1min(&mut self) {
198 self.sec = self.sec.saturating_sub(60);
199 }
200
201 #[inline]
203 pub const fn sub_1sec(&mut self) {
204 self.sec = self.sec.saturating_sub(1);
205 }
206
207 #[inline]
211 pub const fn sub_1ms(&mut self) {
212 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_MS);
213 }
214
215 #[inline]
219 pub const fn sub_1us(&mut self) {
220 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_US);
221 }
222
223 #[inline]
227 pub const fn sub_1ns(&mut self) {
228 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_NS);
229 }
230
231 #[inline]
233 pub const fn sub_sec(&mut self, n: i64) {
234 self.sec = self.sec.saturating_sub(n);
235 }
236
237 #[inline]
239 pub const fn sub_min(&mut self, n: i64) {
240 self.sec = self.sec.saturating_sub(n.saturating_mul(60));
241 }
242
243 #[inline]
245 pub const fn sub_hr(&mut self, n: i64) {
246 self.sec = self.sec.saturating_sub(n.saturating_mul(3600));
247 }
248
249 #[inline]
253 pub const fn sub_ms(&mut self, n: i64) {
254 Self::add_attos_span(
255 &mut self.sec,
256 &mut self.attos,
257 n.saturating_neg(),
258 ATTOS_PER_MS,
259 );
260 }
261
262 #[inline]
266 pub const fn sub_us(&mut self, n: i64) {
267 Self::add_attos_span(
268 &mut self.sec,
269 &mut self.attos,
270 n.saturating_neg(),
271 ATTOS_PER_US,
272 );
273 }
274
275 #[inline]
279 pub const fn sub_ns(&mut self, n: i64) {
280 Self::add_attos_span(
281 &mut self.sec,
282 &mut self.attos,
283 n.saturating_neg(),
284 ATTOS_PER_NS,
285 );
286 }
287
288 #[inline]
292 pub const fn sub_ps(&mut self, n: i64) {
293 Self::add_attos_span(
294 &mut self.sec,
295 &mut self.attos,
296 n.saturating_neg(),
297 ATTOS_PER_PS,
298 );
299 }
300
301 #[inline]
305 pub const fn sub_fs(&mut self, n: i64) {
306 Self::add_attos_span(
307 &mut self.sec,
308 &mut self.attos,
309 n.saturating_neg(),
310 ATTOS_PER_FS,
311 );
312 }
313
314 #[inline]
318 pub const fn sub_attos(&mut self, n: i64) {
319 Self::add_attos_span(&mut self.sec, &mut self.attos, n.saturating_neg(), 1);
320 }
321
322 #[inline]
324 pub const fn to_attos(&self) -> i128 {
325 (self.sec as i128) * ATTOS_PER_SEC_I128 + (self.attos as i128)
326 }
327
328 #[inline]
330 pub const fn to_ms(&self) -> i128 {
331 self.to_attos() / (ATTOS_PER_MS as i128)
332 }
333
334 #[inline]
336 pub const fn to_us(&self) -> i128 {
337 self.to_attos() / (ATTOS_PER_US as i128)
338 }
339
340 #[inline]
342 pub const fn to_ns(&self) -> i128 {
343 self.to_attos() / (ATTOS_PER_NS as i128)
344 }
345
346 #[inline]
348 pub const fn to_ps(&self) -> i128 {
349 self.to_attos() / (ATTOS_PER_PS as i128)
350 }
351
352 #[inline]
354 pub const fn to_fs(&self) -> i128 {
355 self.to_attos() / (ATTOS_PER_FS as i128)
356 }
357
358 pub(crate) const fn add_time(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> (i64, u64) {
360 let mut sec = sec_a.saturating_add(sec_b);
361 let mut attos = sub_a as i64 + sub_b as i64;
362
363 if attos >= ATTOS_PER_SEC as i64 {
364 if sec < i64::MAX {
365 sec = sec.saturating_add(1);
366 }
367 attos -= ATTOS_PER_SEC as i64;
368 } else if attos < 0 {
369 if sec > i64::MIN {
370 sec = sec.saturating_sub(1);
371 }
372 attos += ATTOS_PER_SEC as i64;
373 }
374
375 let attos = if sec == i64::MAX {
376 ATTOS_PER_SEC - 1
377 } else if sec == i64::MIN {
378 0
379 } else {
380 attos as u64
381 };
382
383 (sec, attos)
384 }
385
386 pub(crate) const fn sub_time(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> (i64, u64) {
388 let mut sec = sec_a.saturating_sub(sec_b);
389 let mut attos = sub_a as i64 - sub_b as i64;
390
391 if attos < 0 {
392 if sec > i64::MIN {
393 sec = sec.saturating_sub(1);
394 }
395 attos += ATTOS_PER_SEC as i64;
396 } else if attos >= ATTOS_PER_SEC as i64 {
397 if sec < i64::MAX {
398 sec = sec.saturating_add(1);
399 }
400 attos -= ATTOS_PER_SEC as i64;
401 }
402
403 let attos = if sec == i64::MAX {
404 ATTOS_PER_SEC - 1
405 } else if sec == i64::MIN {
406 0
407 } else {
408 attos as u64
409 };
410
411 (sec, attos)
412 }
413
414 #[inline(always)]
416 pub const fn is_zero(&self) -> bool {
417 self.sec == 0 && self.attos == 0
418 }
419
420 #[inline(always)]
422 pub const fn is_positive(&self) -> bool {
423 self.to_attos() > 0
424 }
425
426 pub const fn mul(self, rhs: i64) -> Self {
430 if rhs == 0 || self.is_zero() {
431 return Self::ZERO;
432 }
433 let total: i128 = self.to_attos().saturating_mul(rhs as i128);
434 Self::from_attos(total, Scale::TAI)
435 }
436
437 pub const fn div(self, rhs: i64) -> Self {
442 if rhs == 0 || self.is_zero() {
443 return Self::ZERO;
444 }
445 let total = self.to_attos();
446 let result = total / (rhs as i128);
447 Self::from_attos(result, Scale::TAI)
448 }
449
450 pub const fn floor(&self, unit: Self) -> Self {
453 if unit.is_zero() {
454 return *self;
455 }
456 let a = self.to_attos();
457 let b = unit.to_attos();
458 let q = safe_div_euc!(a, b, 0i128);
459 let result = q.wrapping_mul(b);
460 Self::from_attos(result, Scale::TAI)
461 }
462
463 pub const fn ceil(&self, unit: Self) -> Self {
466 if unit.is_zero() {
467 return *self;
468 }
469 let a = self.to_attos();
470 let b = unit.to_attos();
471 let neg_a = a.wrapping_neg();
473 let q = safe_div_euc!(neg_a, b, 0i128);
474 let q_ceil = q.wrapping_neg();
475 let result = q_ceil.wrapping_mul(b);
476 Self::from_attos(result, Scale::TAI)
477 }
478
479 pub const fn round(&self, unit: Self) -> Self {
488 if unit.is_zero() {
489 return *self;
490 }
491
492 let a = self.to_attos();
493 let b = unit.to_attos();
494
495 let abs_a = a.wrapping_abs();
496 let abs_b = b.wrapping_abs();
497
498 let q = safe_div_euc!(abs_a, abs_b, 0i128);
499 let r = safe_rem_euc!(abs_a, abs_b, 0i128);
500
501 let half = (abs_b + 1) / 2;
502
503 let q_rounded = if r >= half { q + 1 } else { q };
504
505 let rounded_abs = q_rounded.wrapping_mul(abs_b);
506
507 let result = if a < 0 { -rounded_abs } else { rounded_abs };
508
509 Self::from_attos(result, Scale::TAI)
510 }
511
512 pub const fn abs_div_floor(&self, unit: Self) -> usize {
516 if unit.is_zero() {
517 return 0;
518 }
519 let a = self.to_attos().wrapping_abs();
520 let b = unit.to_attos().wrapping_abs();
521 let q = safe_div_euc!(a, b, 0i128);
522
523 if q > (usize::MAX as i128) {
524 usize::MAX
525 } else {
526 q as usize
527 }
528 }
529
530 pub const fn mul_by_f(&self, rhs: Real) -> Self {
533 if rhs.is_nan() {
534 return Self::ZERO;
535 }
536 if rhs.is_infinite() {
537 if self.is_zero() {
538 return Self::ZERO;
539 }
540 let self_pos = self.sec > 0 || (self.sec == 0 && self.attos != 0);
541 return if (rhs > 0.0) == self_pos {
542 Self::MAX
543 } else {
544 Self::MIN
545 };
546 }
547 if self.is_zero() || rhs == 0.0 {
548 return Self::ZERO;
549 }
550
551 let self_attos = self.to_attos();
552 let max_attos = Self::MAX.to_attos();
553 let min_attos = Self::MIN.to_attos();
554
555 let int_part = if rhs >= (i128::MAX as Real) {
557 i128::MAX
558 } else if rhs <= (i128::MIN as Real) {
559 i128::MIN
560 } else {
561 floor_f(rhs) as i128
562 };
563
564 if int_part == i128::MAX || int_part == i128::MIN {
566 let self_pos = self.sec > 0 || (self.sec == 0 && self.attos != 0);
567 return if (rhs > 0.0) == self_pos {
568 Self::MAX
569 } else {
570 Self::MIN
571 };
572 }
573
574 let frac_part = rhs - f!(int_part); let int_attos = if int_part == 0 {
578 0
579 } else if int_part > 0 {
580 if self_attos > 0 {
581 if int_part > max_attos / self_attos {
582 max_attos
583 } else {
584 self_attos * int_part
585 }
586 } else {
587 let abs_self = self_attos.wrapping_neg();
588 let abs_min = min_attos.wrapping_neg();
589 if int_part > abs_min / abs_self {
590 min_attos
591 } else {
592 self_attos * int_part
593 }
594 }
595 } else {
596 if self_attos > 0 {
598 let abs_int = int_part.wrapping_neg();
599 let abs_min = min_attos.wrapping_neg();
600 if abs_int > abs_min / self_attos {
601 min_attos
602 } else {
603 self_attos * int_part
604 }
605 } else {
606 let abs_self = self_attos.wrapping_neg();
607 let abs_int = int_part.wrapping_neg();
608 if abs_int > max_attos / abs_self {
609 max_attos
610 } else {
611 self_attos * int_part
612 }
613 }
614 };
615
616 const SCALE: i128 = 1_000_000_000_000_000; let frac_scaled = (frac_part * (SCALE as Real)) as i128;
619
620 let frac_attos = if self_attos >= 0 {
621 let high = self_attos / SCALE;
622 let low = self_attos % SCALE;
623 let high_part = high * frac_scaled;
624 let low_part = (low * frac_scaled) / SCALE;
625 high_part + low_part
626 } else {
627 let abs_self = self_attos.wrapping_neg();
628 let high = abs_self / SCALE;
629 let low = abs_self % SCALE;
630 let high_part = high * frac_scaled;
631 let low_part = (low * frac_scaled) / SCALE;
632 let pos = high_part + low_part;
633 pos.wrapping_neg()
634 };
635
636 let total_attos = int_attos.saturating_add(frac_attos);
638 let clamped = if total_attos > max_attos {
639 max_attos
640 } else if total_attos < min_attos {
641 min_attos
642 } else {
643 total_attos
644 };
645
646 Self::from_attos(clamped, Scale::TAI)
647 }
648
649 #[inline]
651 pub const fn div_by_f(&self, rhs: Real) -> Self {
652 if rhs == 0.0 || rhs.is_nan() {
653 return if self.sec >= 0 { Self::MAX } else { Self::MIN };
654 }
655 self.mul_by_f(1.0 / rhs)
656 }
657
658 #[inline]
660 pub const fn div_by_2(&self) -> Self {
661 self.div_by_f(2.0)
662 }
663
664 #[doc(hidden)]
666 pub(crate) const fn add_attos_to(sec: &mut i64, attos: &mut u64, amount: u64) {
667 let total = *attos + amount;
668 let carry_sec = total / ATTOS_PER_SEC;
669 *attos = total % ATTOS_PER_SEC;
670 *sec = sec.saturating_add(carry_sec as i64);
671 }
672
673 #[doc(hidden)]
680 pub(crate) const fn add_attos_span(sec: &mut i64, attos: &mut u64, n: i64, unit: u64) {
681 if n == 0 {
682 return;
683 }
684
685 let mps = ATTOS_PER_SEC;
686
687 if n >= 0 {
688 let amount = (n as u64).saturating_mul(unit);
689 let total = attos.saturating_add(amount);
690
691 let carry = total / mps;
692 let new_frac = total % mps;
693
694 *sec = sec.saturating_add(carry as i64);
695 *attos = new_frac;
696 } else {
697 let amount = n.unsigned_abs().saturating_mul(unit);
698 let borrow_sec = amount / mps;
699 let borrow_frac = amount % mps;
700
701 *sec = sec.saturating_sub(borrow_sec as i64);
702
703 if *attos >= borrow_frac {
704 *attos -= borrow_frac;
705 } else {
706 *attos += mps - borrow_frac;
707 *sec = sec.saturating_sub(1);
708 }
709 }
710
711 if *sec == i64::MAX {
713 *attos = mps - 1;
714 } else if *sec == i64::MIN {
715 *attos = 0;
716 }
717 }
718
719 #[inline]
721 pub const fn to_sec(&mut self) -> i64 {
722 let Dt { sec, .. } = self.carry_attos();
723 sec
724 }
725
726 pub(crate) const fn diff_raw_internal(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> Self {
727 if sub_a >= sub_b {
728 Self {
729 sec: sec_a.saturating_sub(sec_b),
730 attos: sub_a - sub_b,
731 }
732 } else {
733 Self {
734 sec: sec_a.saturating_sub(sec_b).saturating_sub(1),
735 attos: sub_a.saturating_add(ATTOS_PER_SEC.saturating_sub(sub_b)),
736 }
737 }
738 }
739
740 #[inline(always)]
742 pub(crate) const fn clamp_i128_to_i64(x: i128) -> i64 {
743 let y = x as i64;
744 if x == y as i128 {
745 y
746 } else if x > 0 {
747 i64::MAX
748 } else {
749 i64::MIN
750 }
751 }
752
753 pub(crate) const fn clamp_u8(value: u8, min: u8, max: u8) -> u8 {
760 if value < min {
761 min
762 } else if value > max {
763 max
764 } else {
765 value
766 }
767 }
768}