use std::cmp::Eq;
use std::cmp::Ord;
use std::cmp::Ordering;
use std::cmp::PartialEq;
use std::cmp::PartialOrd;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::ops::BitAnd;
use std::ops::BitOr;
use serde::Deserialize;
use serde::Serialize;
#[derive(Clone, Copy, Deserialize, Serialize)]
pub struct GuestAddress(pub u64);
impl Debug for GuestAddress {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "GuestAddress({:#018x})", self.0)
    }
}
impl GuestAddress {
    pub fn offset_from(self, base: GuestAddress) -> u64 {
        self.0 - base.0
    }
    pub fn offset(self) -> u64 {
        self.0
    }
    pub fn checked_add(self, other: u64) -> Option<GuestAddress> {
        self.0.checked_add(other).map(GuestAddress)
    }
    #[inline]
    pub fn unchecked_add(self, offset: u64) -> GuestAddress {
        GuestAddress(self.0.wrapping_add(offset))
    }
    pub fn checked_sub(self, other: u64) -> Option<GuestAddress> {
        self.0.checked_sub(other).map(GuestAddress)
    }
    pub fn mask(self, mask: u64) -> GuestAddress {
        GuestAddress(self.0 & mask)
    }
    pub fn align(self, align: u64) -> Option<GuestAddress> {
        if align <= 1 {
            return Some(self);
        }
        self.checked_add(align - 1).map(|a| a & !(align - 1))
    }
    pub fn align_down(self, align: u64) -> GuestAddress {
        if align <= 1 {
            return self;
        }
        self & !(align - 1)
    }
}
impl BitAnd<u64> for GuestAddress {
    type Output = GuestAddress;
    fn bitand(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 & other)
    }
}
impl BitOr<u64> for GuestAddress {
    type Output = GuestAddress;
    fn bitor(self, other: u64) -> GuestAddress {
        GuestAddress(self.0 | other)
    }
}
impl PartialEq for GuestAddress {
    fn eq(&self, other: &GuestAddress) -> bool {
        self.0 == other.0
    }
}
impl Eq for GuestAddress {}
impl Ord for GuestAddress {
    fn cmp(&self, other: &GuestAddress) -> Ordering {
        self.0.cmp(&other.0)
    }
}
impl PartialOrd for GuestAddress {
    fn partial_cmp(&self, other: &GuestAddress) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl Display for GuestAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn equals() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x300);
        let c = GuestAddress(0x301);
        assert_eq!(a, b);
        assert_eq!(b, a);
        assert_ne!(a, c);
        assert_ne!(c, a);
    }
    #[test]
    #[allow(clippy::eq_op)]
    #[allow(clippy::nonminimal_bool)]
    fn cmp() {
        let a = GuestAddress(0x300);
        let b = GuestAddress(0x301);
        assert!(a < b);
        assert!(b > a);
        assert!(!(a < a));
        assert!(a >= a);
    }
    #[test]
    fn mask() {
        let a = GuestAddress(0x5050);
        assert_eq!(GuestAddress(0x5000), a & 0xff00u64);
        assert_eq!(GuestAddress(0x5055), a | 0x0005u64);
    }
    #[test]
    fn add_sub() {
        let a = GuestAddress(0x50);
        let b = GuestAddress(0x60);
        assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60));
        assert_eq!(0x10, b.offset_from(a));
    }
    #[test]
    fn checked_add_overflow() {
        let a = GuestAddress(0xffffffffffffff55);
        assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2));
        assert!(a.checked_add(0xf0).is_none());
    }
    #[test]
    fn align() {
        assert_eq!(GuestAddress(12345).align(0), Some(GuestAddress(12345)));
        assert_eq!(GuestAddress(12345).align(1), Some(GuestAddress(12345)));
        assert_eq!(GuestAddress(12345).align(2), Some(GuestAddress(12346)));
        assert_eq!(GuestAddress(0).align(4096), Some(GuestAddress(0)));
        assert_eq!(GuestAddress(1).align(4096), Some(GuestAddress(4096)));
        assert_eq!(GuestAddress(4095).align(4096), Some(GuestAddress(4096)));
        assert_eq!(GuestAddress(4096).align(4096), Some(GuestAddress(4096)));
        assert_eq!(GuestAddress(4097).align(4096), Some(GuestAddress(8192)));
        assert_eq!(
            GuestAddress(u64::MAX & !4095).align(4096),
            Some(GuestAddress(u64::MAX & !4095)),
        );
        assert_eq!(GuestAddress(u64::MAX).align(2), None);
    }
    #[test]
    fn align_down() {
        assert_eq!(GuestAddress(12345).align_down(0), GuestAddress(12345));
        assert_eq!(GuestAddress(12345).align_down(1), GuestAddress(12345));
        assert_eq!(GuestAddress(12345).align_down(2), GuestAddress(12344));
        assert_eq!(GuestAddress(0).align_down(4096), GuestAddress(0));
        assert_eq!(GuestAddress(1).align_down(4096), GuestAddress(0));
        assert_eq!(GuestAddress(4095).align_down(4096), GuestAddress(0));
        assert_eq!(GuestAddress(4096).align_down(4096), GuestAddress(4096));
        assert_eq!(GuestAddress(4097).align_down(4096), GuestAddress(4096));
        assert_eq!(
            GuestAddress(u64::MAX & !4095).align_down(4096),
            GuestAddress(u64::MAX & !4095),
        );
        assert_eq!(
            GuestAddress(u64::MAX).align_down(2),
            GuestAddress(u64::MAX - 1)
        );
    }
}