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, Spacetime, Real, Scale, 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 #[inline]
32 pub const fn to_sec_f(&self) -> Real {
33 f!(self.sec) + f!(self.attos) / ATTOS_PER_SECF
34 }
35
36 #[inline]
42 pub const fn adjusted_advance(&mut self, elapsed: &Dt, spacetime: &Spacetime) {
43 let dtau =
44 elapsed.add(Drift::from_spacetime(spacetime).time_diff_after(elapsed));
45 *self = self.add(dtau);
46 }
47
48 #[inline]
54 pub const fn adjusted_advance_using_drift(&mut self, elapsed: &Dt, drift: &Drift) {
55 let dtau = elapsed.add(drift.time_diff_after(elapsed));
56 *self = self.add(dtau);
57 }
58
59 #[inline]
61 pub const fn to_diff_raw(&self, other: Self) -> Dt {
62 Self::diff_raw_internal(self.sec, self.attos, other.sec, other.attos)
63 }
64
65 #[inline]
67 pub const fn to_diff_raw_f(&self, other: Self) -> Real {
68 self.to_sec_f() - other.to_sec_f()
69 }
70
71 #[inline]
73 pub const fn add_1sec(&mut self) {
74 self.sec = self.sec.saturating_add(1);
75 }
76
77 #[inline]
79 pub const fn add_1min(&mut self) {
80 self.sec = self.sec.saturating_add(60);
81 }
82
83 #[inline]
85 pub const fn add_1hr(&mut self) {
86 self.sec = self.sec.saturating_add(3600);
87 }
88
89 #[inline]
93 pub const fn add_1ms(&mut self) {
94 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_MS);
95 }
96
97 #[inline]
101 pub const fn add_1us(&mut self) {
102 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_US);
103 }
104
105 #[inline]
109 pub const fn add_1ns(&mut self) {
110 Self::add_attos_to(&mut self.sec, &mut self.attos, ATTOS_PER_NS);
111 }
112
113 #[inline]
115 pub const fn add_sec(&mut self, n: i64) {
116 self.sec = self.sec.saturating_add(n);
117 }
118
119 #[inline]
121 pub const fn add_min(&mut self, n: i64) {
122 self.sec = self.sec.saturating_add(n.saturating_mul(60));
123 }
124
125 #[inline]
127 pub const fn add_hr(&mut self, n: i64) {
128 self.sec = self.sec.saturating_add(n.saturating_mul(3600));
129 }
130
131 #[inline]
135 pub const fn add_ms(&mut self, n: i64) {
136 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_MS);
137 }
138
139 #[inline]
143 pub const fn add_us(&mut self, n: i64) {
144 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_US);
145 }
146
147 #[inline]
151 pub const fn add_ns(&mut self, n: i64) {
152 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_NS);
153 }
154
155 #[inline]
159 pub const fn add_ps(&mut self, n: i64) {
160 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_PS);
161 }
162
163 #[inline]
167 pub const fn add_fs(&mut self, n: i64) {
168 Self::add_attos_span(&mut self.sec, &mut self.attos, n, ATTOS_PER_FS);
169 }
170
171 #[inline]
175 pub const fn add_attos(&mut self, n: i64) {
176 Self::add_attos_span(&mut self.sec, &mut self.attos, n, 1);
177 }
178
179 #[inline]
181 pub const fn sub_1hr(&mut self) {
182 self.sec = self.sec.saturating_sub(3600);
183 }
184
185 #[inline]
187 pub const fn sub_1min(&mut self) {
188 self.sec = self.sec.saturating_sub(60);
189 }
190
191 #[inline]
193 pub const fn sub_1sec(&mut self) {
194 self.sec = self.sec.saturating_sub(1);
195 }
196
197 #[inline]
201 pub const fn sub_1ms(&mut self) {
202 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_MS);
203 }
204
205 #[inline]
209 pub const fn sub_1us(&mut self) {
210 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_US);
211 }
212
213 #[inline]
217 pub const fn sub_1ns(&mut self) {
218 Self::add_attos_span(&mut self.sec, &mut self.attos, -1, ATTOS_PER_NS);
219 }
220
221 #[inline]
223 pub const fn sub_sec(&mut self, n: i64) {
224 self.sec = self.sec.saturating_sub(n);
225 }
226
227 #[inline]
229 pub const fn sub_min(&mut self, n: i64) {
230 self.sec = self.sec.saturating_sub(n.saturating_mul(60));
231 }
232
233 #[inline]
235 pub const fn sub_hr(&mut self, n: i64) {
236 self.sec = self.sec.saturating_sub(n.saturating_mul(3600));
237 }
238
239 #[inline]
243 pub const fn sub_ms(&mut self, n: i64) {
244 Self::add_attos_span(
245 &mut self.sec,
246 &mut self.attos,
247 n.saturating_neg(),
248 ATTOS_PER_MS,
249 );
250 }
251
252 #[inline]
256 pub const fn sub_us(&mut self, n: i64) {
257 Self::add_attos_span(
258 &mut self.sec,
259 &mut self.attos,
260 n.saturating_neg(),
261 ATTOS_PER_US,
262 );
263 }
264
265 #[inline]
269 pub const fn sub_ns(&mut self, n: i64) {
270 Self::add_attos_span(
271 &mut self.sec,
272 &mut self.attos,
273 n.saturating_neg(),
274 ATTOS_PER_NS,
275 );
276 }
277
278 #[inline]
282 pub const fn sub_ps(&mut self, n: i64) {
283 Self::add_attos_span(
284 &mut self.sec,
285 &mut self.attos,
286 n.saturating_neg(),
287 ATTOS_PER_PS,
288 );
289 }
290
291 #[inline]
295 pub const fn sub_fs(&mut self, n: i64) {
296 Self::add_attos_span(
297 &mut self.sec,
298 &mut self.attos,
299 n.saturating_neg(),
300 ATTOS_PER_FS,
301 );
302 }
303
304 #[inline]
308 pub const fn sub_attos(&mut self, n: i64) {
309 Self::add_attos_span(&mut self.sec, &mut self.attos, n.saturating_neg(), 1);
310 }
311
312 #[inline]
314 pub const fn to_attos(&self) -> i128 {
315 (self.sec as i128) * ATTOS_PER_SEC_I128 + (self.attos as i128)
316 }
317
318 #[inline]
320 pub const fn to_ms(&self) -> i128 {
321 self.to_attos() / (ATTOS_PER_MS as i128)
322 }
323
324 #[inline]
326 pub const fn to_us(&self) -> i128 {
327 self.to_attos() / (ATTOS_PER_US as i128)
328 }
329
330 #[inline]
332 pub const fn to_ns(&self) -> i128 {
333 self.to_attos() / (ATTOS_PER_NS as i128)
334 }
335
336 #[inline]
338 pub const fn to_ps(&self) -> i128 {
339 self.to_attos() / (ATTOS_PER_PS as i128)
340 }
341
342 #[inline]
344 pub const fn to_fs(&self) -> i128 {
345 self.to_attos() / (ATTOS_PER_FS as i128)
346 }
347
348 pub(crate) const fn add_time(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> (i64, u64) {
350 let mut sec = sec_a.saturating_add(sec_b);
351 let mut attos = sub_a as i64 + sub_b as i64;
352
353 if attos >= ATTOS_PER_SEC as i64 {
354 if sec < i64::MAX {
355 sec = sec.saturating_add(1);
356 }
357 attos -= ATTOS_PER_SEC as i64;
358 } else if attos < 0 {
359 if sec > i64::MIN {
360 sec = sec.saturating_sub(1);
361 }
362 attos += ATTOS_PER_SEC as i64;
363 }
364
365 let attos = if sec == i64::MAX {
366 ATTOS_PER_SEC - 1
367 } else if sec == i64::MIN {
368 0
369 } else {
370 attos as u64
371 };
372
373 (sec, attos)
374 }
375
376 pub(crate) const fn sub_time(sec_a: i64, sub_a: u64, sec_b: i64, sub_b: u64) -> (i64, u64) {
378 let mut sec = sec_a.saturating_sub(sec_b);
379 let mut attos = sub_a as i64 - sub_b as i64;
380
381 if attos < 0 {
382 if sec > i64::MIN {
383 sec = sec.saturating_sub(1);
384 }
385 attos += ATTOS_PER_SEC as i64;
386 } else if attos >= ATTOS_PER_SEC as i64 {
387 if sec < i64::MAX {
388 sec = sec.saturating_add(1);
389 }
390 attos -= ATTOS_PER_SEC as i64;
391 }
392
393 let attos = if sec == i64::MAX {
394 ATTOS_PER_SEC - 1
395 } else if sec == i64::MIN {
396 0
397 } else {
398 attos as u64
399 };
400
401 (sec, attos)
402 }
403
404 #[inline]
406 pub const fn is_zero(&self) -> bool {
407 self.sec == 0 && self.attos == 0
408 }
409
410 #[inline]
412 pub const fn is_positive(&self) -> bool {
413 if self.sec > 0 {
414 true
415 } else if self.sec == 0 {
416 self.attos != 0
417 } else {
418 let k = (-self.sec) as u64;
419 let quot = self.attos / ATTOS_PER_SEC;
420 let rem = self.attos % ATTOS_PER_SEC;
421
422 quot > k || (quot == k && rem > 0)
423 }
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 {
443 if rhs == 0 || self.is_zero() {
444 return Self::ZERO;
445 }
446 let total = self.to_attos();
447 let result = total.div_euclid(rhs as i128);
448 Self::from_attos(result, Scale::TAI)
449 }
450
451 pub const fn floor(&self, unit: Self) -> Self {
454 if unit.is_zero() {
455 return *self;
456 }
457 let a = self.to_attos();
458 let b = unit.to_attos();
459 let q = a.div_euclid(b);
460 let result = q.wrapping_mul(b);
461 Self::from_attos(result, Scale::TAI)
462 }
463
464 pub const fn ceil(&self, unit: Self) -> Self {
467 if unit.is_zero() {
468 return *self;
469 }
470 let a = self.to_attos();
471 let b = unit.to_attos();
472 let neg_a = a.wrapping_neg();
474 let q = neg_a.div_euclid(b);
475 let q_ceil = q.wrapping_neg();
476 let result = q_ceil.wrapping_mul(b);
477 Self::from_attos(result, Scale::TAI)
478 }
479
480 pub const fn round(&self, unit: Self) -> Self {
484 if unit.is_zero() {
485 return *self;
486 }
487 let a = self.to_attos();
488 let b = unit.to_attos();
489
490 let q = a.div_euclid(b);
491 let r = a.rem_euclid(b);
492
493 let abs_b = b.wrapping_abs();
495 let two = 2i128;
496 let half = (abs_b + 1) / two;
497
498 if r >= half {
499 let one = 1i128;
501 let q_rounded = if a < 0 { q - one } else { q + one };
502 let result = q_rounded.wrapping_mul(b);
503 Self::from_attos(result, Scale::TAI)
504 } else {
505 let result = q.wrapping_mul(b);
506 Self::from_attos(result, Scale::TAI)
507 }
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 = a.div_euclid(b);
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 self.carry_over();
721 self.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}