pub const F64_MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;

#[must_use]
pub fn u128_to_u64_saturating(value: u128) -> u64 {
    if value <= u128::from(u64::MAX) {
        #[allow(clippy::cast_possible_truncation)]
        let result = value as u64;
        result
    } else {
        u64::MAX
    }
}

#[must_use]
pub fn u128_to_f64_saturating(value: u128) -> f64 {
    if value <= u128::from(F64_MAX_SAFE_INTEGER) {
        #[allow(clippy::cast_precision_loss)]
        let result = value as f64;
        result
    } else {
        f64::MAX
    }
}

#[must_use]
pub fn usize_to_f64_saturating(value: usize) -> f64 {
    u64_to_f64_saturating(value as u64)
}

#[must_use]
pub fn u64_to_f64_saturating(value: u64) -> f64 {
    if value <= F64_MAX_SAFE_INTEGER {
        #[allow(clippy::cast_precision_loss)]
        let result = value as f64;
        result
    } else {
        f64::MAX
    }
}

#[must_use]
pub fn u128_to_u32_saturating(value: u128) -> u32 {
    if value <= u128::from(u32::MAX) {
        #[allow(clippy::cast_possible_truncation)]
        let result = value as u32;
        result
    } else {
        u32::MAX
    }
}

#[must_use]
pub fn usize_to_u32_saturating(value: usize) -> u32 {
    u32::try_from(value).unwrap_or(u32::MAX)
}

#[must_use]
pub fn u64_to_u32_saturating(value: u64) -> u32 {
    u32::try_from(value).unwrap_or(u32::MAX)
}

#[must_use]
pub fn u64_to_u16_saturating(value: u64) -> u16 {
    u16::try_from(value).unwrap_or(u16::MAX)
}

#[must_use]
pub fn usize_to_u16_saturating(value: usize) -> u16 {
    u16::try_from(value).unwrap_or(u16::MAX)
}

#[must_use]
pub fn u64_to_usize_saturating(value: u64) -> usize {
    usize::try_from(value).unwrap_or(usize::MAX)
}

#[must_use]
pub fn i32_to_u32_saturating(value: i32) -> u32 {
    if value < 0 {
        0
    } else {
        #[allow(clippy::cast_sign_loss)]
        let result = value as u32;
        result
    }
}

#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn f64_to_u64_saturating(value: f64) -> u64 {
    const U64_MAX_AS_F64: f64 = u64::MAX as f64;
    if value < 0.0 {
        0
    } else if value >= U64_MAX_AS_F64 {
        u64::MAX
    } else {
        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
        let result = value as u64;
        result
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_u128_to_u64_saturating() {
        assert_eq!(u128_to_u64_saturating(0), 0);
        assert_eq!(u128_to_u64_saturating(1000), 1000);
        assert_eq!(u128_to_u64_saturating(u128::from(u64::MAX)), u64::MAX);
        assert_eq!(u128_to_u64_saturating(u128::MAX), u64::MAX);
    }

    #[test]
    fn test_u128_to_f64_saturating() {
        assert!((u128_to_f64_saturating(0) - 0.0).abs() < f64::EPSILON);
        assert!((u128_to_f64_saturating(1000) - 1000.0).abs() < f64::EPSILON);
        assert!((u128_to_f64_saturating(u128::MAX) - f64::MAX).abs() < f64::EPSILON);
    }

    #[test]
    fn test_usize_to_f64_saturating() {
        assert!((usize_to_f64_saturating(0) - 0.0).abs() < f64::EPSILON);
        assert!((usize_to_f64_saturating(1000) - 1000.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_usize_to_u32_saturating() {
        assert_eq!(usize_to_u32_saturating(0), 0);
        assert_eq!(usize_to_u32_saturating(1000), 1000);
        assert_eq!(usize_to_u32_saturating(u32::MAX as usize), u32::MAX);
    }

    #[test]
    fn test_u64_to_u16_saturating() {
        assert_eq!(u64_to_u16_saturating(0), 0);
        assert_eq!(u64_to_u16_saturating(1000), 1000);
        assert_eq!(u64_to_u16_saturating(u64::from(u16::MAX)), u16::MAX);
        assert_eq!(u64_to_u16_saturating(u64::MAX), u16::MAX);
    }

    #[test]
    fn test_u64_to_usize_saturating() {
        assert_eq!(u64_to_usize_saturating(0), 0);
        assert_eq!(u64_to_usize_saturating(1000), 1000);
        assert_eq!(
            u64_to_usize_saturating(u64::from(u32::MAX)),
            u32::MAX as usize
        );
    }

    #[test]
    fn test_i32_to_u32_saturating() {
        assert_eq!(i32_to_u32_saturating(-100), 0);
        assert_eq!(i32_to_u32_saturating(0), 0);
        assert_eq!(i32_to_u32_saturating(1000), 1000);
        #[allow(clippy::cast_sign_loss)]
        let expected = i32::MAX as u32;
        assert_eq!(i32_to_u32_saturating(i32::MAX), expected);
    }
}
