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]
63 pub const fn adjusted_advance_using_drift(&mut self, elapsed: &Dt, drift: &Drift) {
64 let dtau = elapsed.add(drift.time_diff_after(elapsed));
65 *self = self.add(dtau);
66 }
67
68 #[inline]
70 pub const fn to_diff_raw(&self, other: Self) -> Dt {
71 Self::diff_raw_internal(self.sec, self.attos, other.sec, other.attos)
72 }
73
74 #[inline]
76 pub const fn to_diff_raw_f(&self, other: Self) -> Real {
77 self.to_sec_f() - other.to_sec_f()
78 }
79
80 #[inline]
82 pub const fn add_1sec(&mut self) {
83 self.sec = self.sec.saturating_add(1);
84 }
85
86 #[inline]
88 pub const fn add_1min(&mut self) {
89 self.sec = self.sec.saturating_add(60);
90 }
91
92 #[inline]
94 pub const fn add_1hr(&mut self) {
95 self.sec = self.sec.saturating_add(3600);
96 }
97
98 #[inline]
102 pub const fn add_1ms(&mut self) {
103 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_MS);
104 }
105
106 #[inline]
110 pub const fn add_1us(&mut self) {
111 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_US);
112 }
113
114 #[inline]
118 pub const fn add_1ns(&mut self) {
119 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_NS);
120 }
121
122 #[inline]
124 pub const fn add_sec(&mut self, n: i64) {
125 self.sec = self.sec.saturating_add(n);
126 }
127
128 #[inline]
130 pub const fn add_min(&mut self, n: i64) {
131 self.sec = self.sec.saturating_add(n.saturating_mul(60));
132 }
133
134 #[inline]
136 pub const fn add_hr(&mut self, n: i64) {
137 self.sec = self.sec.saturating_add(n.saturating_mul(3600));
138 }
139
140 #[inline]
144 pub const fn add_ms(&mut self, n: i64) {
145 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_MS);
146 }
147
148 #[inline]
152 pub const fn add_us(&mut self, n: i64) {
153 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_US);
154 }
155
156 #[inline]
160 pub const fn add_ns(&mut self, n: i64) {
161 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_NS);
162 }
163
164 #[inline]
168 pub const fn add_ps(&mut self, n: i64) {
169 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_PS);
170 }
171
172 #[inline]
176 pub const fn add_fs(&mut self, n: i64) {
177 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_FS);
178 }
179
180 #[inline]
184 pub const fn add_attos(&mut self, n: i64) {
185 Self::add_attos_span(&mut self.sec, &mut self.attos, n, 1);
186 }
187
188 #[inline]
190 pub const fn sub_1hr(&mut self) {
191 self.sec = self.sec.saturating_sub(3600);
192 }
193
194 #[inline]
196 pub const fn sub_1min(&mut self) {
197 self.sec = self.sec.saturating_sub(60);
198 }
199
200 #[inline]
202 pub const fn sub_1sec(&mut self) {
203 self.sec = self.sec.saturating_sub(1);
204 }
205
206 #[inline]
210 pub const fn sub_1ms(&mut self) {
211 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_MS);
212 }
213
214 #[inline]
218 pub const fn sub_1us(&mut self) {
219 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_US);
220 }
221
222 #[inline]
226 pub const fn sub_1ns(&mut self) {
227 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_NS);
228 }
229
230 #[inline]
232 pub const fn sub_sec(&mut self, n: i64) {
233 self.sec = self.sec.saturating_sub(n);
234 }
235
236 #[inline]
238 pub const fn sub_min(&mut self, n: i64) {
239 self.sec = self.sec.saturating_sub(n.saturating_mul(60));
240 }
241
242 #[inline]
244 pub const fn sub_hr(&mut self, n: i64) {
245 self.sec = self.sec.saturating_sub(n.saturating_mul(3600));
246 }
247
248 #[inline]
252 pub const fn sub_ms(&mut self, n: i64) {
253 Self::add_attos_span(
254 &mut self.sec,
255 &mut self.attos,
256 n.saturating_neg(),
257 ATTOS_PER_MS,
258 );
259 }
260
261 #[inline]
265 pub const fn sub_us(&mut self, n: i64) {
266 Self::add_attos_span(
267 &mut self.sec,
268 &mut self.attos,
269 n.saturating_neg(),
270 ATTOS_PER_US,
271 );
272 }
273
274 #[inline]
278 pub const fn sub_ns(&mut self, n: i64) {
279 Self::add_attos_span(
280 &mut self.sec,
281 &mut self.attos,
282 n.saturating_neg(),
283 ATTOS_PER_NS,
284 );
285 }
286
287 #[inline]
291 pub const fn sub_ps(&mut self, n: i64) {
292 Self::add_attos_span(
293 &mut self.sec,
294 &mut self.attos,
295 n.saturating_neg(),
296 ATTOS_PER_PS,
297 );
298 }
299
300 #[inline]
304 pub const fn sub_fs(&mut self, n: i64) {
305 Self::add_attos_span(
306 &mut self.sec,
307 &mut self.attos,
308 n.saturating_neg(),
309 ATTOS_PER_FS,
310 );
311 }
312
313 #[inline]
317 pub const fn sub_attos(&mut self, n: i64) {
318 Self::add_attos_span(&mut self.sec, &mut self.attos, n.saturating_neg(), 1);
319 }
320
321 #[inline]
323 pub const fn to_attos(&self) -> i128 {
324 (self.sec as i128) * ATTOS_PER_SEC_I128 + (self.attos as i128)
325 }
326
327 #[inline]
329 pub const fn to_ms(&self) -> i128 {
330 self.to_attos() / (ATTOS_PER_MS as i128)
331 }
332
333 #[inline]
335 pub const fn to_us(&self) -> i128 {
336 self.to_attos() / (ATTOS_PER_US as i128)
337 }
338
339 #[inline]
341 pub const fn to_ns(&self) -> i128 {
342 self.to_attos() / (ATTOS_PER_NS as i128)
343 }
344
345 #[inline]
347 pub const fn to_ps(&self) -> i128 {
348 self.to_attos() / (ATTOS_PER_PS as i128)
349 }
350
351 #[inline]
353 pub const fn to_fs(&self) -> i128 {
354 self.to_attos() / (ATTOS_PER_FS as i128)
355 }
356
357 pub(crate) const fn add_time(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> (i64, u64) {
359 let mut sec = sec_a.saturating_add(sec_b);
360 let mut attos = sub_a as i64 + sub_b as i64;
361
362 if attos >= ATTOS_PER_SEC as i64 {
363 if sec < i64::MAX {
364 sec = sec.saturating_add(1);
365 }
366 attos -= ATTOS_PER_SEC as i64;
367 } else if attos < 0 {
368 if sec > i64::MIN {
369 sec = sec.saturating_sub(1);
370 }
371 attos += ATTOS_PER_SEC as i64;
372 }
373
374 let attos = if sec == i64::MAX {
375 ATTOS_PER_SEC - 1
376 } else if sec == i64::MIN {
377 0
378 } else {
379 attos as u64
380 };
381
382 (sec, attos)
383 }
384
385 pub(crate) const fn sub_time(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> (i64, u64) {
387 let mut sec = sec_a.saturating_sub(sec_b);
388 let mut attos = sub_a as i64 - sub_b as i64;
389
390 if attos < 0 {
391 if sec > i64::MIN {
392 sec = sec.saturating_sub(1);
393 }
394 attos += ATTOS_PER_SEC as i64;
395 } else if attos >= ATTOS_PER_SEC as i64 {
396 if sec < i64::MAX {
397 sec = sec.saturating_add(1);
398 }
399 attos -= ATTOS_PER_SEC as i64;
400 }
401
402 let attos = if sec == i64::MAX {
403 ATTOS_PER_SEC - 1
404 } else if sec == i64::MIN {
405 0
406 } else {
407 attos as u64
408 };
409
410 (sec, attos)
411 }
412
413 #[inline]
415 pub const fn is_zero(&self) -> bool {
416 self.sec == 0 && self.attos == 0
417 }
418
419 #[inline(always)]
421 pub const fn is_positive(&self) -> bool {
422 self.to_attos() > 0
423 }
424
425 pub const fn mul(self, rhs: i64) -> Self {
429 if rhs == 0 || self.is_zero() {
430 return Self::ZERO;
431 }
432 let total: i128 = self.to_attos().saturating_mul(rhs as i128);
433 Self::from_attos(total, Scale::TAI)
434 }
435
436 pub const fn div(self, rhs: i64) -> Self {
441 if rhs == 0 || self.is_zero() {
442 return Self::ZERO;
443 }
444 let total = self.to_attos();
445 let result = total / (rhs as i128);
446 Self::from_attos(result, Scale::TAI)
447 }
448
449 pub const fn floor(&self, unit: Self) -> Self {
452 if unit.is_zero() {
453 return *self;
454 }
455 let a = self.to_attos();
456 let b = unit.to_attos();
457 let q = safe_div_euc!(a, b, 0i128);
458 let result = q.wrapping_mul(b);
459 Self::from_attos(result, Scale::TAI)
460 }
461
462 pub const fn ceil(&self, unit: Self) -> Self {
465 if unit.is_zero() {
466 return *self;
467 }
468 let a = self.to_attos();
469 let b = unit.to_attos();
470 let neg_a = a.wrapping_neg();
472 let q = safe_div_euc!(neg_a, b, 0i128);
473 let q_ceil = q.wrapping_neg();
474 let result = q_ceil.wrapping_mul(b);
475 Self::from_attos(result, Scale::TAI)
476 }
477
478 pub const fn round(&self, unit: Self) -> Self {
487 if unit.is_zero() {
488 return *self;
489 }
490
491 let a = self.to_attos();
492 let b = unit.to_attos();
493
494 let abs_a = a.wrapping_abs();
495 let abs_b = b.wrapping_abs();
496
497 let q = safe_div_euc!(abs_a, abs_b, 0i128);
498 let r = safe_rem_euc!(abs_a, abs_b, 0i128);
499
500 let half = (abs_b + 1) / 2;
501
502 let q_rounded = if r >= half { q + 1 } else { q };
503
504 let rounded_abs = q_rounded.wrapping_mul(abs_b);
505
506 let result = if a < 0 { -rounded_abs } else { rounded_abs };
507
508 Self::from_attos(result, Scale::TAI)
509 }
510
511 pub const fn abs_div_floor(&self, unit: Self) -> usize {
515 if unit.is_zero() {
516 return 0;
517 }
518 let a = self.to_attos().wrapping_abs();
519 let b = unit.to_attos().wrapping_abs();
520 let q = safe_div_euc!(a, b, 0i128);
521
522 if q > (usize::MAX as i128) {
523 usize::MAX
524 } else {
525 q as usize
526 }
527 }
528
529 pub const fn mul_by_f(&self, rhs: Real) -> Self {
532 if rhs.is_nan() {
533 return Self::ZERO;
534 }
535 if rhs.is_infinite() {
536 if self.is_zero() {
537 return Self::ZERO;
538 }
539 let self_pos = self.sec > 0 || (self.sec == 0 && self.attos != 0);
540 return if (rhs > 0.0) == self_pos {
541 Self::MAX
542 } else {
543 Self::MIN
544 };
545 }
546 if self.is_zero() || rhs == 0.0 {
547 return Self::ZERO;
548 }
549
550 let self_attos = self.to_attos();
551 let max_attos = Self::MAX.to_attos();
552 let min_attos = Self::MIN.to_attos();
553
554 let int_part = if rhs >= (i128::MAX as Real) {
556 i128::MAX
557 } else if rhs <= (i128::MIN as Real) {
558 i128::MIN
559 } else {
560 floor_f(rhs) as i128
561 };
562
563 if int_part == i128::MAX || int_part == i128::MIN {
565 let self_pos = self.sec > 0 || (self.sec == 0 && self.attos != 0);
566 return if (rhs > 0.0) == self_pos {
567 Self::MAX
568 } else {
569 Self::MIN
570 };
571 }
572
573 let frac_part = rhs - f!(int_part); let int_attos = if int_part == 0 {
577 0
578 } else if int_part > 0 {
579 if self_attos > 0 {
580 if int_part > max_attos / self_attos {
581 max_attos
582 } else {
583 self_attos * int_part
584 }
585 } else {
586 let abs_self = self_attos.wrapping_neg();
587 let abs_min = min_attos.wrapping_neg();
588 if int_part > abs_min / abs_self {
589 min_attos
590 } else {
591 self_attos * int_part
592 }
593 }
594 } else {
595 if self_attos > 0 {
597 let abs_int = int_part.wrapping_neg();
598 let abs_min = min_attos.wrapping_neg();
599 if abs_int > abs_min / self_attos {
600 min_attos
601 } else {
602 self_attos * int_part
603 }
604 } else {
605 let abs_self = self_attos.wrapping_neg();
606 let abs_int = int_part.wrapping_neg();
607 if abs_int > max_attos / abs_self {
608 max_attos
609 } else {
610 self_attos * int_part
611 }
612 }
613 };
614
615 const SCALE: i128 = 1_000_000_000_000_000; let frac_scaled = (frac_part * (SCALE as Real)) as i128;
618
619 let frac_attos = if self_attos >= 0 {
620 let high = self_attos / SCALE;
621 let low = self_attos % SCALE;
622 let high_part = high * frac_scaled;
623 let low_part = (low * frac_scaled) / SCALE;
624 high_part + low_part
625 } else {
626 let abs_self = self_attos.wrapping_neg();
627 let high = abs_self / SCALE;
628 let low = abs_self % SCALE;
629 let high_part = high * frac_scaled;
630 let low_part = (low * frac_scaled) / SCALE;
631 let pos = high_part + low_part;
632 pos.wrapping_neg()
633 };
634
635 let total_attos = int_attos.saturating_add(frac_attos);
637 let clamped = if total_attos > max_attos {
638 max_attos
639 } else if total_attos < min_attos {
640 min_attos
641 } else {
642 total_attos
643 };
644
645 Self::from_attos(clamped, Scale::TAI)
646 }
647
648 #[inline]
650 pub const fn div_by_f(&self, rhs: Real) -> Self {
651 if rhs == 0.0 || rhs.is_nan() {
652 return if self.sec >= 0 { Self::MAX } else { Self::MIN };
653 }
654 self.mul_by_f(1.0 / rhs)
655 }
656
657 #[inline]
659 pub const fn div_by_2(&self) -> Self {
660 self.div_by_f(2.0)
661 }
662
663 #[doc(hidden)]
665 pub(crate) const fn add_attos_to(sec: &mut i64, attos: &mut u64, amount: u64) {
666 let total = *attos + amount;
667 let carry_sec = total / ATTOS_PER_SEC;
668 *attos = total % ATTOS_PER_SEC;
669 *sec = sec.saturating_add(carry_sec as i64);
670 }
671
672 #[doc(hidden)]
679 pub(crate) const fn add_attos_span(sec: &mut i64, attos: &mut u64, n: i64, unit: u64) {
680 if n == 0 {
681 return;
682 }
683
684 let mps = ATTOS_PER_SEC;
685
686 if n >= 0 {
687 let amount = (n as u64).saturating_mul(unit);
688 let total = attos.saturating_add(amount);
689
690 let carry = total / mps;
691 let new_frac = total % mps;
692
693 *sec = sec.saturating_add(carry as i64);
694 *attos = new_frac;
695 } else {
696 let amount = n.unsigned_abs().saturating_mul(unit);
697 let borrow_sec = amount / mps;
698 let borrow_frac = amount % mps;
699
700 *sec = sec.saturating_sub(borrow_sec as i64);
701
702 if *attos >= borrow_frac {
703 *attos -= borrow_frac;
704 } else {
705 *attos += mps - borrow_frac;
706 *sec = sec.saturating_sub(1);
707 }
708 }
709
710 if *sec == i64::MAX {
712 *attos = mps - 1;
713 } else if *sec == i64::MIN {
714 *attos = 0;
715 }
716 }
717
718 #[inline]
720 pub const fn to_sec(&mut self) -> i64 {
721 let Dt { sec, .. } = self.carry_attos();
722 sec
723 }
724
725 pub(crate) const fn diff_raw_internal(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> Self {
726 if sub_a >= sub_b {
727 Self {
728 sec: sec_a.saturating_sub(sec_b),
729 attos: sub_a - sub_b,
730 }
731 } else {
732 Self {
733 sec: sec_a.saturating_sub(sec_b).saturating_sub(1),
734 attos: sub_a.saturating_add(ATTOS_PER_SEC.saturating_sub(sub_b)),
735 }
736 }
737 }
738
739 #[inline(always)]
741 pub(crate) const fn clamp_i128_to_i64(x: i128) -> i64 {
742 let y = x as i64;
743 if x == y as i128 {
744 y
745 } else if x > 0 {
746 i64::MAX
747 } else {
748 i64::MIN
749 }
750 }
751
752 pub(crate) const fn clamp_u8(value: u8, min: u8, max: u8) -> u8 {
759 if value < min {
760 min
761 } else if value > max {
762 max
763 } else {
764 value
765 }
766 }
767}