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