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]
420 pub const fn is_positive(&self) -> bool {
421 if self.sec > 0 {
422 true
423 } else if self.sec == 0 {
424 self.attos != 0
425 } else {
426 let k = (-self.sec) as u64;
427 let quot = self.attos / ATTOS_PER_SEC;
428 let rem = self.attos % ATTOS_PER_SEC;
429
430 quot > k || (quot == k && rem > 0)
431 }
432 }
433
434 pub const fn mul(self, rhs: i64) -> Self {
438 if rhs == 0 || self.is_zero() {
439 return Self::ZERO;
440 }
441 let total: i128 = self.to_attos().saturating_mul(rhs as i128);
442 Self::from_attos(total, Scale::TAI)
443 }
444
445 pub const fn div(self, rhs: i64) -> Self {
451 if rhs == 0 || self.is_zero() {
452 return Self::ZERO;
453 }
454 let total = self.to_attos();
455 let result = safe_div_euc!(total, rhs as i128, 0i128);
456 Self::from_attos(result, Scale::TAI)
457 }
458
459 pub const fn floor(&self, unit: Self) -> Self {
462 if unit.is_zero() {
463 return *self;
464 }
465 let a = self.to_attos();
466 let b = unit.to_attos();
467 let q = safe_div_euc!(a, b, 0i128);
468 let result = q.wrapping_mul(b);
469 Self::from_attos(result, Scale::TAI)
470 }
471
472 pub const fn ceil(&self, unit: Self) -> Self {
475 if unit.is_zero() {
476 return *self;
477 }
478 let a = self.to_attos();
479 let b = unit.to_attos();
480 let neg_a = a.wrapping_neg();
482 let q = safe_div_euc!(neg_a, b, 0i128);
483 let q_ceil = q.wrapping_neg();
484 let result = q_ceil.wrapping_mul(b);
485 Self::from_attos(result, Scale::TAI)
486 }
487
488 pub const fn round(&self, unit: Self) -> Self {
497 if unit.is_zero() {
498 return *self;
499 }
500
501 let a = self.to_attos();
502 let b = unit.to_attos();
503
504 let abs_a = a.wrapping_abs();
505 let abs_b = b.wrapping_abs();
506
507 let q = safe_div_euc!(abs_a, abs_b, 0i128);
508 let r = safe_rem_euc!(abs_a, abs_b, 0i128);
509
510 let half = (abs_b + 1) / 2;
511
512 let q_rounded = if r >= half { q + 1 } else { q };
513
514 let rounded_abs = q_rounded.wrapping_mul(abs_b);
515
516 let result = if a < 0 { -rounded_abs } else { rounded_abs };
517
518 Self::from_attos(result, Scale::TAI)
519 }
520
521 pub const fn abs_div_floor(&self, unit: Self) -> usize {
525 if unit.is_zero() {
526 return 0;
527 }
528 let a = self.to_attos().wrapping_abs();
529 let b = unit.to_attos().wrapping_abs();
530 let q = safe_div_euc!(a, b, 0i128);
531
532 if q > (usize::MAX as i128) {
533 usize::MAX
534 } else {
535 q as usize
536 }
537 }
538
539 pub const fn mul_by_f(&self, rhs: Real) -> Self {
542 if rhs.is_nan() {
543 return Self::ZERO;
544 }
545 if rhs.is_infinite() {
546 if self.is_zero() {
547 return Self::ZERO;
548 }
549 let self_pos = self.sec > 0 || (self.sec == 0 && self.attos != 0);
550 return if (rhs > 0.0) == self_pos {
551 Self::MAX
552 } else {
553 Self::MIN
554 };
555 }
556 if self.is_zero() || rhs == 0.0 {
557 return Self::ZERO;
558 }
559
560 let self_attos = self.to_attos();
561 let max_attos = Self::MAX.to_attos();
562 let min_attos = Self::MIN.to_attos();
563
564 let int_part = if rhs >= (i128::MAX as Real) {
566 i128::MAX
567 } else if rhs <= (i128::MIN as Real) {
568 i128::MIN
569 } else {
570 floor_f(rhs) as i128
571 };
572
573 if int_part == i128::MAX || int_part == i128::MIN {
575 let self_pos = self.sec > 0 || (self.sec == 0 && self.attos != 0);
576 return if (rhs > 0.0) == self_pos {
577 Self::MAX
578 } else {
579 Self::MIN
580 };
581 }
582
583 let frac_part = rhs - f!(int_part); let int_attos = if int_part == 0 {
587 0
588 } else if int_part > 0 {
589 if self_attos > 0 {
590 if int_part > max_attos / self_attos {
591 max_attos
592 } else {
593 self_attos * int_part
594 }
595 } else {
596 let abs_self = self_attos.wrapping_neg();
597 let abs_min = min_attos.wrapping_neg();
598 if int_part > abs_min / abs_self {
599 min_attos
600 } else {
601 self_attos * int_part
602 }
603 }
604 } else {
605 if self_attos > 0 {
607 let abs_int = int_part.wrapping_neg();
608 let abs_min = min_attos.wrapping_neg();
609 if abs_int > abs_min / self_attos {
610 min_attos
611 } else {
612 self_attos * int_part
613 }
614 } else {
615 let abs_self = self_attos.wrapping_neg();
616 let abs_int = int_part.wrapping_neg();
617 if abs_int > max_attos / abs_self {
618 max_attos
619 } else {
620 self_attos * int_part
621 }
622 }
623 };
624
625 const SCALE: i128 = 1_000_000_000_000_000; let frac_scaled = (frac_part * (SCALE as Real)) as i128;
628
629 let frac_attos = if self_attos >= 0 {
630 let high = self_attos / SCALE;
631 let low = self_attos % SCALE;
632 let high_part = high * frac_scaled;
633 let low_part = (low * frac_scaled) / SCALE;
634 high_part + low_part
635 } else {
636 let abs_self = self_attos.wrapping_neg();
637 let high = abs_self / SCALE;
638 let low = abs_self % SCALE;
639 let high_part = high * frac_scaled;
640 let low_part = (low * frac_scaled) / SCALE;
641 let pos = high_part + low_part;
642 pos.wrapping_neg()
643 };
644
645 let total_attos = int_attos.saturating_add(frac_attos);
647 let clamped = if total_attos > max_attos {
648 max_attos
649 } else if total_attos < min_attos {
650 min_attos
651 } else {
652 total_attos
653 };
654
655 Self::from_attos(clamped, Scale::TAI)
656 }
657
658 #[inline]
660 pub const fn div_by_f(&self, rhs: Real) -> Self {
661 if rhs == 0.0 || rhs.is_nan() {
662 return if self.sec >= 0 { Self::MAX } else { Self::MIN };
663 }
664 self.mul_by_f(1.0 / rhs)
665 }
666
667 #[inline]
669 pub const fn div_by_2(&self) -> Self {
670 self.div_by_f(2.0)
671 }
672
673 #[doc(hidden)]
675 pub(crate) const fn add_attos_to(sec: &mut i64, attos: &mut u64, amount: u64) {
676 let total = *attos + amount;
677 let carry_sec = total / ATTOS_PER_SEC;
678 *attos = total % ATTOS_PER_SEC;
679 *sec = sec.saturating_add(carry_sec as i64);
680 }
681
682 #[doc(hidden)]
689 pub(crate) const fn add_attos_span(sec: &mut i64, attos: &mut u64, n: i64, unit: u64) {
690 if n == 0 {
691 return;
692 }
693
694 let mps = ATTOS_PER_SEC;
695
696 if n >= 0 {
697 let amount = (n as u64).saturating_mul(unit);
698 let total = attos.saturating_add(amount);
699
700 let carry = total / mps;
701 let new_frac = total % mps;
702
703 *sec = sec.saturating_add(carry as i64);
704 *attos = new_frac;
705 } else {
706 let amount = n.unsigned_abs().saturating_mul(unit);
707 let borrow_sec = amount / mps;
708 let borrow_frac = amount % mps;
709
710 *sec = sec.saturating_sub(borrow_sec as i64);
711
712 if *attos >= borrow_frac {
713 *attos -= borrow_frac;
714 } else {
715 *attos += mps - borrow_frac;
716 *sec = sec.saturating_sub(1);
717 }
718 }
719
720 if *sec == i64::MAX {
722 *attos = mps - 1;
723 } else if *sec == i64::MIN {
724 *attos = 0;
725 }
726 }
727
728 #[inline]
730 pub const fn to_sec(&mut self) -> i64 {
731 let Dt { sec, .. } = self.carry_over();
732 sec
733 }
734
735 pub(crate) const fn diff_raw_internal(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> Self {
736 if sub_a >= sub_b {
737 Self {
738 sec: sec_a.saturating_sub(sec_b),
739 attos: sub_a - sub_b,
740 }
741 } else {
742 Self {
743 sec: sec_a.saturating_sub(sec_b).saturating_sub(1),
744 attos: sub_a.saturating_add(ATTOS_PER_SEC.saturating_sub(sub_b)),
745 }
746 }
747 }
748
749 pub(crate) const fn clamp_i128_to_i64(x: i128) -> i64 {
751 if x > i64::MAX as i128 {
752 i64::MAX
753 } else if x < i64::MIN as i128 {
754 i64::MIN
755 } else {
756 x as i64
757 }
758 }
759}