///|
/// The time portion of a duration, held as a single count of nanoseconds.
///
/// Time components can be normalized against one another because their
/// lengths are fixed, so the whole time part collapses to one integer. The
/// magnitude never exceeds [`max_time_duration`].
///
/// Spec: 
struct TimeDuration(@int128.Int128) derive(Eq, Compare, Debug)

///|
/// A zero time duration.
let time_duration_zero : TimeDuration = TimeDuration(@int128.zero)

///|
/// Returns the total nanoseconds.
fn TimeDuration::nanoseconds(self : TimeDuration) -> @int128.Int128 {
  self.0
}

///|
/// `TimeDurationFromComponents`: builds a time duration from signed
/// components, preserving each component's own sign.
fn TimeDuration::from_components(
  hours : Int64,
  minutes : Int64,
  seconds : Int64,
  milliseconds : Int64,
  microseconds : @int128.Int128,
  nanoseconds : @int128.Int128,
) -> TimeDuration {
  let total = @int128.of_int64(hours)
    .mul(@int128.of_int64(NS_PER_HOUR))
    .add(@int128.of_int64(minutes).mul(@int128.of_int64(NS_PER_MINUTE)))
    .add(@int128.of_int64(seconds).mul(i128_billion))
    .add(@int128.of_int64(milliseconds).mul(i128_million))
    .add(microseconds.mul(i128_thousand))
    .add(nanoseconds)
  TimeDuration(total)
}

///|
/// `NormalizeTimeDuration`: collapses a `Duration`'s time components.
fn TimeDuration::from_duration(duration : Duration) -> TimeDuration {
  TimeDuration::from_components(
    duration.hours,
    duration.minutes,
    duration.seconds,
    duration.milliseconds,
    duration.microseconds,
    duration.nanoseconds,
  )
}

///|
/// `TimeDurationFromEpochNanosecondsDifference`.
fn TimeDuration::from_nanosecond_difference(
  one : @int128.Int128,
  two : @int128.Int128,
) -> TimeDuration raise TemporalError {
  TimeDuration::checked(one.sub(two))
}

///|
/// Wraps a nanosecond count, rejecting values beyond [`max_time_duration`].
fn TimeDuration::checked(
  nanoseconds : @int128.Int128,
) -> TimeDuration raise TemporalError {
  if nanoseconds.abs().compare(max_time_duration) > 0 {
    raise RangeError("TimeDuration exceeds maxTimeDuration")
  }
  TimeDuration(nanoseconds)
}

///|
/// `Add24HourDaysToTimeDuration`.
fn TimeDuration::add_days(
  self : TimeDuration,
  days : Int64,
) -> TimeDuration raise TemporalError {
  TimeDuration::checked(self.0.add(@int128.of_int64(days).mul(i128_ns_per_day)))
}

///|
/// `AddTimeDuration`.
fn TimeDuration::add(
  self : TimeDuration,
  other : TimeDuration,
) -> TimeDuration raise TemporalError {
  TimeDuration::checked(self.0.add(other.0))
}

///|
/// Subtracts another time duration.
fn TimeDuration::sub(
  self : TimeDuration,
  other : TimeDuration,
) -> TimeDuration raise TemporalError {
  TimeDuration::checked(self.0.sub(other.0))
}

///|
/// Returns the negated time duration.
fn TimeDuration::negated(self : TimeDuration) -> TimeDuration {
  TimeDuration(self.0.neg())
}

///|
/// `TimeDurationSign`.
fn TimeDuration::sign(self : TimeDuration) -> Sign {
  Sign::of_int(self.0.signum())
}

///|
/// Returns the whole seconds, truncated toward zero.
fn TimeDuration::seconds(self : TimeDuration) -> Int64 {
  self.0.div(i128_billion).to_int64_saturating()
}

///|
/// Returns the sub-second nanoseconds, carrying the duration's sign.
fn TimeDuration::subseconds(self : TimeDuration) -> Int {
  self.0.rem(i128_billion).to_int64_saturating().to_int()
}

///|
/// `RoundTimeDuration`: rounds to a multiple of the resolved smallest unit.
fn TimeDuration::round(
  self : TimeDuration,
  options : ResolvedRoundingOptions,
) -> TimeDuration raise TemporalError {
  let divisor = temporal_unwrap(
    options.smallest_unit.as_nanoseconds(),
    "smallest unit must be a time unit",
  )
  let increment = @int128.of_int(options.increment.get()).mul(
    @int128.of_int64(divisor),
  )
  self.round_to_increment(increment, options.rounding_mode)
}

///|
/// Rounds to a multiple of `increment` nanoseconds.
fn TimeDuration::round_to_increment(
  self : TimeDuration,
  increment : @int128.Int128,
  mode : RoundingMode,
) -> TimeDuration raise TemporalError {
  let rounded = IncrementRounder::from_signed_num(self.0, increment).round(mode)
  TimeDuration::checked(rounded)
}

///|
/// Rounds to a whole number of days, returning the day count.
fn TimeDuration::round_to_fractional_days(
  self : TimeDuration,
  increment : RoundingIncrement,
  mode : RoundingMode,
) -> Int64 raise TemporalError {
  let adjusted = @int128.of_int(increment.get()).mul(i128_ns_per_day)
  let rounded = IncrementRounder::from_signed_num(self.0, adjusted).round(mode)
  rounded.div(i128_ns_per_day).to_int64_saturating()
}

///|
/// Divides by a nanosecond count, truncating toward zero.
fn TimeDuration::truncated_divide(
  self : TimeDuration,
  divisor : Int64,
) -> Int64 {
  self.0.div(@int128.of_int64(divisor)).to_int64_saturating()
}

///|
/// `TotalTimeDuration`: the duration expressed as a fractional count of
/// `unit`.
fn TimeDuration::total(
  self : TimeDuration,
  unit : DateTimeUnit,
) -> Double raise TemporalError {
  let divisor = temporal_unwrap(
    unit.as_nanoseconds(),
    "unit must have a fixed length",
  )
  exact_ratio_to_double(self.0, @int128.of_int64(divisor))
}

///|
/// Converts the exact rational `numerator / denominator` to the nearest
/// `Double`, with ties resolved to even.
///
/// Temporal totals routinely exceed `2^53`, so dividing after converting each
/// side to a `Double` would round twice and drift. Instead the quotient is
/// computed in 128-bit integer arithmetic with enough spare bits that a single
/// final rounding decides the result, with the discarded remainder folded in
/// as a sticky bit.
fn exact_ratio_to_double(
  numerator : @int128.Int128,
  denominator : @int128.Int128,
) -> Double {
  if numerator.is_zero() {
    return 0.0
  }
  let negative = numerator.is_negative() != denominator.is_negative()
  let n = numerator.abs()
  let d = denominator.abs()
  // Scale so the quotient carries at least 55 significant bits: 53 for the
  // mantissa, one to decide the rounding, and one for the sticky bit.
  let shift = @cmp.maximum(0, 55 - (n.bit_length() - d.bit_length()))
  let (quotient, remainder) = n.shl(shift).div_rem(d)
  // Fold "there was a remainder" into the lowest bit. The quotient has far
  // more than 53 bits here, so bit 0 is well below the mantissa and cannot
  // disturb anything but the tie decision.
  let sticky = if remainder.is_zero() { @int128.zero } else { @int128.one }
  let adjusted = quotient.land(@int128.one.neg()).lor(sticky)
  let magnitude = adjusted.to_double() / two_pow_double(shift)
  if negative {
    -magnitude
  } else {
    magnitude
  }
}

///|
/// Returns `2^exp` as a `Double` for non-negative `exp`.
fn two_pow_double(exp : Int) -> Double {
  let mut result = 1.0
  for _ in 0..
struct InternalDurationRecord {
  date : DateDuration
  time : TimeDuration
} derive(Debug)

///|
/// `CombineDateAndTimeDuration`, without the sign agreement check.
fn InternalDurationRecord::combine(
  date : DateDuration,
  time : TimeDuration,
) -> InternalDurationRecord {
  { date, time }
}

///|
/// `CreateNormalizedDurationRecord`: combines the two parts, requiring that
/// they agree in sign when both are non-zero.
fn InternalDurationRecord::new(
  date : DateDuration,
  time : TimeDuration,
) -> InternalDurationRecord raise TemporalError {
  if date.sign() != Zero && time.sign() != Zero && date.sign() != time.sign() {
    raise RangeError(
      "DateDuration and TimeDuration must agree in sign when both are non-zero",
    )
  }
  { date, time }
}

///|
/// Builds a record with an empty time part.
fn InternalDurationRecord::from_date_duration(
  date : DateDuration,
) -> InternalDurationRecord raise TemporalError {
  InternalDurationRecord::new(date, time_duration_zero)
}

///|
/// A record with both parts empty.
let internal_duration_zero : InternalDurationRecord = {
  date: DateDuration::default(),
  time: time_duration_zero,
}

///|
/// `ToInternalDurationRecordWith24HourDays`: folds the duration's days into
/// the time part, treating each as exactly 24 hours.
fn InternalDurationRecord::from_duration_with_24_hour_days(
  duration : Duration,
) -> InternalDurationRecord raise TemporalError {
  let time = TimeDuration::from_duration(duration).add_days(duration.days)
  let date = DateDuration::new_unchecked(
    duration.years,
    duration.months,
    duration.weeks,
    0L,
  )
  InternalDurationRecord::new(date, time)
}

///|
/// `ToDateDurationRecordWithoutTime`: truncates the time part to whole days.
fn InternalDurationRecord::to_date_duration_record_without_time(
  self : InternalDurationRecord,
) -> DateDuration raise TemporalError {
  let days = self.time.nanoseconds().div(i128_ns_per_day).to_int64_saturating()
  DateDuration::new(self.date.years, self.date.months, self.date.weeks, days)
}

///|
/// The sign of the record: the date part's sign if non-zero, else the time
/// part's.
fn InternalDurationRecord::sign(self : InternalDurationRecord) -> Sign {
  let date_sign = self.date.sign()
  if date_sign == Zero {
    self.time.sign()
  } else {
    date_sign
  }
}