Skip to main content

ff_format/time/
timestamp.rs

1//! [`Timestamp`] type for representing media timestamps.
2
3// These casts are intentional for media timestamp arithmetic.
4// The values involved (PTS, time bases, frame rates) are well within
5// the safe ranges for these conversions in practical video/audio scenarios.
6#![allow(
7    clippy::cast_possible_truncation,
8    clippy::cast_possible_wrap,
9    clippy::cast_precision_loss,
10    clippy::cast_sign_loss
11)]
12
13use std::cmp::Ordering;
14use std::fmt;
15use std::ops::{Add, Sub};
16use std::time::Duration;
17
18use super::Rational;
19
20/// A timestamp representing a point in time within a media stream.
21///
22/// Timestamps are represented as a presentation timestamp (PTS) value
23/// combined with a time base that defines the unit of measurement.
24///
25/// # Time Base
26///
27/// The time base is a rational number that represents the duration of
28/// one timestamp unit. For example:
29/// - `1/90000`: Each PTS unit is 1/90000 of a second (MPEG-TS)
30/// - `1/1000`: Each PTS unit is 1 millisecond
31/// - `1/48000`: Each PTS unit is one audio sample at 48kHz
32///
33/// # Examples
34///
35/// ```
36/// use ff_format::{Rational, Timestamp};
37/// use std::time::Duration;
38///
39/// // Create a timestamp at 1 second using 90kHz time base
40/// let time_base = Rational::new(1, 90000);
41/// let ts = Timestamp::new(90000, time_base);
42///
43/// assert!((ts.as_secs_f64() - 1.0).abs() < 0.0001);
44/// assert_eq!(ts.as_millis(), 1000);
45///
46/// // Convert from Duration
47/// let ts2 = Timestamp::from_duration(Duration::from_secs(1), time_base);
48/// assert_eq!(ts2.pts(), 90000);
49/// ```
50#[derive(Debug, Clone, Copy)]
51pub struct Timestamp {
52    pts: i64,
53    time_base: Rational,
54}
55
56impl Timestamp {
57    /// Creates a new timestamp with the given PTS value and time base.
58    ///
59    /// # Arguments
60    ///
61    /// * `pts` - The presentation timestamp value
62    /// * `time_base` - The time base (duration of one PTS unit)
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use ff_format::{Rational, Timestamp};
68    ///
69    /// let time_base = Rational::new(1, 1000);  // milliseconds
70    /// let ts = Timestamp::new(500, time_base);  // 500ms
71    /// assert_eq!(ts.as_millis(), 500);
72    /// ```
73    #[must_use]
74    pub const fn new(pts: i64, time_base: Rational) -> Self {
75        Self { pts, time_base }
76    }
77
78    /// Creates a timestamp representing zero (0 PTS).
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use ff_format::{Rational, Timestamp};
84    ///
85    /// let time_base = Rational::new(1, 90000);
86    /// let zero = Timestamp::zero(time_base);
87    /// assert_eq!(zero.pts(), 0);
88    /// assert_eq!(zero.as_secs_f64(), 0.0);
89    /// ```
90    #[must_use]
91    pub const fn zero(time_base: Rational) -> Self {
92        Self { pts: 0, time_base }
93    }
94
95    /// Creates a timestamp from a Duration value.
96    ///
97    /// # Arguments
98    ///
99    /// * `duration` - The duration to convert
100    /// * `time_base` - The target time base for the resulting timestamp
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use ff_format::{Rational, Timestamp};
106    /// use std::time::Duration;
107    ///
108    /// let time_base = Rational::new(1, 90000);
109    /// let ts = Timestamp::from_duration(Duration::from_millis(1000), time_base);
110    /// assert_eq!(ts.pts(), 90000);
111    /// ```
112    #[must_use]
113    pub fn from_duration(duration: Duration, time_base: Rational) -> Self {
114        let secs = duration.as_secs_f64();
115        let pts = (secs / time_base.as_f64()).round() as i64;
116        Self { pts, time_base }
117    }
118
119    /// Creates a timestamp from a seconds value.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use ff_format::{Rational, Timestamp};
125    ///
126    /// let time_base = Rational::new(1, 1000);
127    /// let ts = Timestamp::from_secs_f64(1.5, time_base);
128    /// assert_eq!(ts.pts(), 1500);
129    /// ```
130    #[must_use]
131    pub fn from_secs_f64(secs: f64, time_base: Rational) -> Self {
132        let pts = (secs / time_base.as_f64()).round() as i64;
133        Self { pts, time_base }
134    }
135
136    /// Creates a timestamp from milliseconds.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use ff_format::{Rational, Timestamp};
142    ///
143    /// let time_base = Rational::new(1, 90000);
144    /// let ts = Timestamp::from_millis(1000, time_base);
145    /// assert_eq!(ts.pts(), 90000);
146    /// ```
147    #[must_use]
148    pub fn from_millis(millis: i64, time_base: Rational) -> Self {
149        let secs = millis as f64 / 1000.0;
150        Self::from_secs_f64(secs, time_base)
151    }
152
153    /// Returns the presentation timestamp value.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use ff_format::{Rational, Timestamp};
159    ///
160    /// let ts = Timestamp::new(12345, Rational::new(1, 90000));
161    /// assert_eq!(ts.pts(), 12345);
162    /// ```
163    #[must_use]
164    #[inline]
165    pub const fn pts(&self) -> i64 {
166        self.pts
167    }
168
169    /// Returns the time base.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use ff_format::{Rational, Timestamp};
175    ///
176    /// let time_base = Rational::new(1, 90000);
177    /// let ts = Timestamp::new(100, time_base);
178    /// assert_eq!(ts.time_base(), time_base);
179    /// ```
180    #[must_use]
181    #[inline]
182    pub const fn time_base(&self) -> Rational {
183        self.time_base
184    }
185
186    /// Converts the timestamp to a Duration.
187    ///
188    /// Note: Negative timestamps will be clamped to zero Duration.
189    ///
190    /// # Examples
191    ///
192    /// ```
193    /// use ff_format::{Rational, Timestamp};
194    /// use std::time::Duration;
195    ///
196    /// let ts = Timestamp::new(90000, Rational::new(1, 90000));
197    /// let duration = ts.as_duration();
198    /// assert_eq!(duration, Duration::from_secs(1));
199    /// ```
200    #[must_use]
201    pub fn as_duration(&self) -> Duration {
202        let secs = self.as_secs_f64();
203        if secs < 0.0 {
204            log::warn!(
205                "timestamp is negative, clamping to zero \
206                 secs={secs} fallback=Duration::ZERO"
207            );
208            Duration::ZERO
209        } else {
210            Duration::from_secs_f64(secs)
211        }
212    }
213
214    /// Converts the timestamp to seconds as a floating-point value.
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// use ff_format::{Rational, Timestamp};
220    ///
221    /// let ts = Timestamp::new(45000, Rational::new(1, 90000));
222    /// assert!((ts.as_secs_f64() - 0.5).abs() < 0.0001);
223    /// ```
224    #[must_use]
225    #[inline]
226    pub fn as_secs_f64(&self) -> f64 {
227        self.pts as f64 * self.time_base.as_f64()
228    }
229
230    /// Converts the timestamp to milliseconds.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use ff_format::{Rational, Timestamp};
236    ///
237    /// let ts = Timestamp::new(90000, Rational::new(1, 90000));
238    /// assert_eq!(ts.as_millis(), 1000);
239    /// ```
240    #[must_use]
241    #[inline]
242    pub fn as_millis(&self) -> i64 {
243        (self.as_secs_f64() * 1000.0).round() as i64
244    }
245
246    /// Converts the timestamp to microseconds.
247    ///
248    /// # Examples
249    ///
250    /// ```
251    /// use ff_format::{Rational, Timestamp};
252    ///
253    /// let ts = Timestamp::new(90, Rational::new(1, 90000));
254    /// assert_eq!(ts.as_micros(), 1000);  // 90/90000 = 0.001 sec = 1000 microseconds
255    /// ```
256    #[must_use]
257    #[inline]
258    pub fn as_micros(&self) -> i64 {
259        (self.as_secs_f64() * 1_000_000.0).round() as i64
260    }
261
262    /// Converts the timestamp to a frame number at the given frame rate.
263    ///
264    /// # Arguments
265    ///
266    /// * `fps` - The frame rate (frames per second)
267    ///
268    /// # Examples
269    ///
270    /// ```
271    /// use ff_format::{Rational, Timestamp};
272    ///
273    /// let ts = Timestamp::new(90000, Rational::new(1, 90000));  // 1 second
274    /// assert_eq!(ts.as_frame_number(30.0), 30);  // 30 fps
275    /// assert_eq!(ts.as_frame_number(60.0), 60);  // 60 fps
276    /// ```
277    #[must_use]
278    #[inline]
279    pub fn as_frame_number(&self, fps: f64) -> u64 {
280        let secs = self.as_secs_f64();
281        if secs < 0.0 {
282            log::warn!(
283                "timestamp is negative, returning frame 0 \
284                 secs={secs} fps={fps} fallback=0"
285            );
286            0
287        } else {
288            (secs * fps).round() as u64
289        }
290    }
291
292    /// Converts the timestamp to a frame number using a rational frame rate.
293    ///
294    /// # Arguments
295    ///
296    /// * `fps` - The frame rate as a rational number
297    ///
298    /// # Examples
299    ///
300    /// ```
301    /// use ff_format::{Rational, Timestamp};
302    ///
303    /// let ts = Timestamp::new(90000, Rational::new(1, 90000));  // 1 second
304    /// let fps = Rational::new(30000, 1001);  // 29.97 fps
305    /// let frame = ts.as_frame_number_rational(fps);
306    /// assert!(frame == 29 || frame == 30);  // Should be approximately 30
307    /// ```
308    #[must_use]
309    pub fn as_frame_number_rational(&self, fps: Rational) -> u64 {
310        self.as_frame_number(fps.as_f64())
311    }
312
313    /// Rescales this timestamp to a different time base.
314    ///
315    /// # Arguments
316    ///
317    /// * `new_time_base` - The target time base
318    ///
319    /// # Examples
320    ///
321    /// ```
322    /// use ff_format::{Rational, Timestamp};
323    ///
324    /// let ts = Timestamp::new(1000, Rational::new(1, 1000));  // 1 second
325    /// let rescaled = ts.rescale(Rational::new(1, 90000));
326    /// assert_eq!(rescaled.pts(), 90000);
327    /// ```
328    #[must_use]
329    pub fn rescale(&self, new_time_base: Rational) -> Self {
330        let secs = self.as_secs_f64();
331        Self::from_secs_f64(secs, new_time_base)
332    }
333
334    /// Returns true if this timestamp is zero.
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// use ff_format::{Rational, Timestamp};
340    ///
341    /// let zero = Timestamp::zero(Rational::new(1, 90000));
342    /// assert!(zero.is_zero());
343    ///
344    /// let non_zero = Timestamp::new(100, Rational::new(1, 90000));
345    /// assert!(!non_zero.is_zero());
346    /// ```
347    #[must_use]
348    #[inline]
349    pub const fn is_zero(&self) -> bool {
350        self.pts == 0
351    }
352
353    /// Returns true if this timestamp is negative.
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// use ff_format::{Rational, Timestamp};
359    ///
360    /// let negative = Timestamp::new(-100, Rational::new(1, 90000));
361    /// assert!(negative.is_negative());
362    /// ```
363    #[must_use]
364    #[inline]
365    pub const fn is_negative(&self) -> bool {
366        self.pts < 0
367    }
368
369    /// Returns a sentinel `Timestamp` representing "no PTS available".
370    ///
371    /// This mirrors `FFmpeg`'s `AV_NOPTS_VALUE` (`INT64_MIN`). Use [`is_valid`](Self::is_valid)
372    /// to check before calling any conversion method.
373    ///
374    /// # Examples
375    ///
376    /// ```
377    /// use ff_format::Timestamp;
378    ///
379    /// let ts = Timestamp::invalid();
380    /// assert!(!ts.is_valid());
381    /// ```
382    #[must_use]
383    pub const fn invalid() -> Self {
384        Self {
385            pts: i64::MIN,
386            time_base: Rational::new(1, 1),
387        }
388    }
389
390    /// Returns `true` if this timestamp represents a real PTS value.
391    ///
392    /// Returns `false` when the timestamp was constructed via [`invalid`](Self::invalid),
393    /// which corresponds to `FFmpeg`'s `AV_NOPTS_VALUE`.
394    ///
395    /// # Examples
396    ///
397    /// ```
398    /// use ff_format::{Timestamp, Rational};
399    ///
400    /// let valid = Timestamp::new(1000, Rational::new(1, 48000));
401    /// assert!(valid.is_valid());
402    ///
403    /// let invalid = Timestamp::invalid();
404    /// assert!(!invalid.is_valid());
405    /// ```
406    #[must_use]
407    pub const fn is_valid(&self) -> bool {
408        self.pts != i64::MIN
409    }
410}
411
412impl Default for Timestamp {
413    /// Returns a default timestamp (0 with 1/90000 time base).
414    fn default() -> Self {
415        Self::new(0, Rational::new(1, 90000))
416    }
417}
418
419impl fmt::Display for Timestamp {
420    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421        let secs = self.as_secs_f64();
422        let hours = (secs / 3600.0).floor() as u32;
423        let mins = ((secs % 3600.0) / 60.0).floor() as u32;
424        let secs_remainder = secs % 60.0;
425        write!(f, "{hours:02}:{mins:02}:{secs_remainder:06.3}")
426    }
427}
428
429impl PartialEq for Timestamp {
430    fn eq(&self, other: &Self) -> bool {
431        // Compare by converting to common representation (seconds)
432        (self.as_secs_f64() - other.as_secs_f64()).abs() < 1e-9
433    }
434}
435
436impl Eq for Timestamp {}
437
438impl PartialOrd for Timestamp {
439    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
440        Some(self.cmp(other))
441    }
442}
443
444impl Ord for Timestamp {
445    fn cmp(&self, other: &Self) -> Ordering {
446        self.as_secs_f64()
447            .partial_cmp(&other.as_secs_f64())
448            .unwrap_or_else(|| {
449                log::warn!(
450                    "NaN timestamp comparison, treating as equal \
451                     self_pts={} other_pts={} fallback=Ordering::Equal",
452                    self.pts,
453                    other.pts
454                );
455                Ordering::Equal
456            })
457    }
458}
459
460impl Add for Timestamp {
461    type Output = Self;
462
463    fn add(self, rhs: Self) -> Self::Output {
464        let secs = self.as_secs_f64() + rhs.as_secs_f64();
465        Self::from_secs_f64(secs, self.time_base)
466    }
467}
468
469impl Sub for Timestamp {
470    type Output = Self;
471
472    fn sub(self, rhs: Self) -> Self::Output {
473        let secs = self.as_secs_f64() - rhs.as_secs_f64();
474        Self::from_secs_f64(secs, self.time_base)
475    }
476}
477
478impl Add<Duration> for Timestamp {
479    type Output = Self;
480
481    fn add(self, rhs: Duration) -> Self::Output {
482        let secs = self.as_secs_f64() + rhs.as_secs_f64();
483        Self::from_secs_f64(secs, self.time_base)
484    }
485}
486
487impl Sub<Duration> for Timestamp {
488    type Output = Self;
489
490    fn sub(self, rhs: Duration) -> Self::Output {
491        let secs = self.as_secs_f64() - rhs.as_secs_f64();
492        Self::from_secs_f64(secs, self.time_base)
493    }
494}
495
496#[cfg(test)]
497#[allow(
498    clippy::unwrap_used,
499    clippy::float_cmp,
500    clippy::similar_names,
501    clippy::redundant_closure_for_method_calls
502)]
503mod tests {
504    use super::*;
505
506    /// Helper for approximate float comparison in tests
507    fn approx_eq(a: f64, b: f64) -> bool {
508        (a - b).abs() < 1e-9
509    }
510
511    mod timestamp_tests {
512        use super::*;
513
514        fn time_base_90k() -> Rational {
515            Rational::new(1, 90000)
516        }
517
518        fn time_base_1k() -> Rational {
519            Rational::new(1, 1000)
520        }
521
522        #[test]
523        fn test_new() {
524            let ts = Timestamp::new(90000, time_base_90k());
525            assert_eq!(ts.pts(), 90000);
526            assert_eq!(ts.time_base(), time_base_90k());
527        }
528
529        #[test]
530        fn test_zero() {
531            let ts = Timestamp::zero(time_base_90k());
532            assert_eq!(ts.pts(), 0);
533            assert!(ts.is_zero());
534            assert!(approx_eq(ts.as_secs_f64(), 0.0));
535        }
536
537        #[test]
538        fn test_from_duration() {
539            let ts = Timestamp::from_duration(Duration::from_secs(1), time_base_90k());
540            assert_eq!(ts.pts(), 90000);
541
542            let ts = Timestamp::from_duration(Duration::from_millis(500), time_base_90k());
543            assert_eq!(ts.pts(), 45000);
544        }
545
546        #[test]
547        fn test_from_secs_f64() {
548            let ts = Timestamp::from_secs_f64(1.5, time_base_1k());
549            assert_eq!(ts.pts(), 1500);
550        }
551
552        #[test]
553        fn test_from_millis() {
554            let ts = Timestamp::from_millis(1000, time_base_90k());
555            assert_eq!(ts.pts(), 90000);
556
557            let ts = Timestamp::from_millis(500, time_base_1k());
558            assert_eq!(ts.pts(), 500);
559        }
560
561        #[test]
562        fn test_as_duration() {
563            let ts = Timestamp::new(90000, time_base_90k());
564            let duration = ts.as_duration();
565            assert_eq!(duration, Duration::from_secs(1));
566
567            // Negative timestamp clamps to zero
568            let ts = Timestamp::new(-100, time_base_90k());
569            assert_eq!(ts.as_duration(), Duration::ZERO);
570        }
571
572        #[test]
573        fn test_as_secs_f64() {
574            let ts = Timestamp::new(45000, time_base_90k());
575            assert!((ts.as_secs_f64() - 0.5).abs() < 0.0001);
576        }
577
578        #[test]
579        fn test_as_millis() {
580            let ts = Timestamp::new(90000, time_base_90k());
581            assert_eq!(ts.as_millis(), 1000);
582
583            let ts = Timestamp::new(45000, time_base_90k());
584            assert_eq!(ts.as_millis(), 500);
585        }
586
587        #[test]
588        fn test_as_micros() {
589            let ts = Timestamp::new(90, time_base_90k());
590            assert_eq!(ts.as_micros(), 1000); // 90/90000 = 0.001 sec = 1000 us
591        }
592
593        #[test]
594        fn test_as_frame_number() {
595            let ts = Timestamp::new(90000, time_base_90k()); // 1 second
596            assert_eq!(ts.as_frame_number(30.0), 30);
597            assert_eq!(ts.as_frame_number(60.0), 60);
598            assert_eq!(ts.as_frame_number(24.0), 24);
599
600            // Negative timestamp
601            let ts = Timestamp::new(-90000, time_base_90k());
602            assert_eq!(ts.as_frame_number(30.0), 0);
603        }
604
605        #[test]
606        fn test_as_frame_number_rational() {
607            let ts = Timestamp::new(90000, time_base_90k()); // 1 second
608            let fps = Rational::new(30, 1);
609            assert_eq!(ts.as_frame_number_rational(fps), 30);
610        }
611
612        #[test]
613        fn test_rescale() {
614            let ts = Timestamp::new(1000, time_base_1k()); // 1 second
615            let rescaled = ts.rescale(time_base_90k());
616            assert_eq!(rescaled.pts(), 90000);
617        }
618
619        #[test]
620        fn test_is_zero() {
621            assert!(Timestamp::zero(time_base_90k()).is_zero());
622            assert!(!Timestamp::new(1, time_base_90k()).is_zero());
623        }
624
625        #[test]
626        fn test_is_negative() {
627            assert!(Timestamp::new(-100, time_base_90k()).is_negative());
628            assert!(!Timestamp::new(100, time_base_90k()).is_negative());
629            assert!(!Timestamp::new(0, time_base_90k()).is_negative());
630        }
631
632        #[test]
633        fn test_display() {
634            // 1 hour, 2 minutes, 3.456 seconds
635            let secs = 3600.0 + 120.0 + 3.456;
636            let ts = Timestamp::from_secs_f64(secs, time_base_90k());
637            let display = format!("{ts}");
638            assert!(display.starts_with("01:02:03"));
639        }
640
641        #[test]
642        fn test_eq() {
643            let ts1 = Timestamp::new(90000, time_base_90k());
644            let ts2 = Timestamp::new(1000, time_base_1k());
645            assert_eq!(ts1, ts2); // Both are 1 second
646        }
647
648        #[test]
649        fn test_ord() {
650            let ts1 = Timestamp::new(45000, time_base_90k()); // 0.5 sec
651            let ts2 = Timestamp::new(90000, time_base_90k()); // 1.0 sec
652            assert!(ts1 < ts2);
653            assert!(ts2 > ts1);
654        }
655
656        #[test]
657        fn test_add() {
658            let ts1 = Timestamp::new(45000, time_base_90k());
659            let ts2 = Timestamp::new(45000, time_base_90k());
660            let sum = ts1 + ts2;
661            assert_eq!(sum.pts(), 90000);
662        }
663
664        #[test]
665        fn test_sub() {
666            let ts1 = Timestamp::new(90000, time_base_90k());
667            let ts2 = Timestamp::new(45000, time_base_90k());
668            let diff = ts1 - ts2;
669            assert_eq!(diff.pts(), 45000);
670        }
671
672        #[test]
673        fn test_add_duration() {
674            let ts = Timestamp::new(45000, time_base_90k());
675            let result = ts + Duration::from_millis(500);
676            assert_eq!(result.pts(), 90000);
677        }
678
679        #[test]
680        fn test_sub_duration() {
681            let ts = Timestamp::new(90000, time_base_90k());
682            let result = ts - Duration::from_millis(500);
683            assert_eq!(result.pts(), 45000);
684        }
685
686        #[test]
687        fn test_default() {
688            let ts = Timestamp::default();
689            assert_eq!(ts.pts(), 0);
690            assert_eq!(ts.time_base(), Rational::new(1, 90000));
691        }
692
693        #[test]
694        fn test_video_timestamps() {
695            // Common video time base: 1/90000 (MPEG-TS)
696            let time_base = Rational::new(1, 90000);
697
698            // At 30 fps, each frame is 3000 PTS units
699            let frame_duration_pts = 90000 / 30;
700            assert_eq!(frame_duration_pts, 3000);
701
702            // Frame 0
703            let frame0 = Timestamp::new(0, time_base);
704            assert_eq!(frame0.as_frame_number(30.0), 0);
705
706            // Frame 30 (1 second)
707            let frame30 = Timestamp::new(90000, time_base);
708            assert_eq!(frame30.as_frame_number(30.0), 30);
709        }
710
711        #[test]
712        fn test_audio_timestamps() {
713            // Audio at 48kHz - each sample is 1/48000 seconds
714            let time_base = Rational::new(1, 48000);
715
716            // 1024 samples (common audio frame size)
717            let ts = Timestamp::new(1024, time_base);
718            let ms = ts.as_secs_f64() * 1000.0;
719            assert!((ms - 21.333).abs() < 0.01); // ~21.33 ms
720        }
721
722        #[test]
723        fn invalid_timestamp_is_not_valid() {
724            let ts = Timestamp::invalid();
725            assert!(!ts.is_valid());
726        }
727
728        #[test]
729        fn zero_timestamp_is_valid() {
730            let ts = Timestamp::zero(Rational::new(1, 48000));
731            assert!(ts.is_valid());
732        }
733
734        #[test]
735        fn real_timestamp_is_valid() {
736            let ts = Timestamp::new(1000, Rational::new(1, 48000));
737            assert!(ts.is_valid());
738        }
739
740        #[test]
741        fn default_timestamp_is_valid() {
742            // Timestamp::default() has pts=0 (not the sentinel)
743            let ts = Timestamp::default();
744            assert!(ts.is_valid());
745        }
746    }
747}