///|
pub fn Ipv4Address::zero() -> Ipv4Address {
  { value: 0U }
}

///|
pub fn Ipv4Address::max() -> Ipv4Address {
  { value: 4294967295U }
}

///|
pub fn Ipv4Address::from_uint(value : UInt) -> Ipv4Address {
  { value, }
}

///|
pub fn Ipv4Address::next(self : Ipv4Address) -> Ipv4Address? {
  if self.value == 4294967295U {
    None
  } else {
    Some({ value: self.value + 1U })
  }
}

///|
pub fn Ipv4Address::previous(self : Ipv4Address) -> Ipv4Address? {
  if self.value == 0U {
    None
  } else {
    Some({ value: self.value - 1U })
  }
}

///|
pub fn Ipv4Address::checked_add(
  self : Ipv4Address,
  step : UInt,
) -> Ipv4Address? {
  if step > 4294967295U - self.value {
    None
  } else {
    Some({ value: self.value + step })
  }
}

///|
pub fn Ipv4Address::checked_sub(
  self : Ipv4Address,
  step : UInt,
) -> Ipv4Address? {
  if step > self.value {
    None
  } else {
    Some({ value: self.value - step })
  }
}

///|
pub fn Ipv4Address::distance_to(
  self : Ipv4Address,
  other : Ipv4Address,
) -> UInt {
  if self.value >= other.value {
    self.value - other.value
  } else {
    other.value - self.value
  }
}

///|
pub fn Ipv4Network::netmask(self : Ipv4Network) -> Ipv4Address {
  { value: ipv4_mask(self.prefix()) }
}

///|
pub fn Ipv4Network::wildcard_mask(self : Ipv4Network) -> Ipv4Address {
  { value: 4294967295U ^ ipv4_mask(self.prefix()) }
}

///|
pub fn Ipv4Network::host_count(self : Ipv4Network) -> UInt64 {
  if self.prefix() == 0 {
    4294967296UL
  } else {
    1UL << (32 - self.prefix())
  }
}

///|
pub fn Ipv4Network::usable_host_count(self : Ipv4Network) -> UInt64 {
  if self.prefix() >= 31 {
    self.host_count()
  } else {
    self.host_count() - 2UL
  }
}

///|
pub fn Ipv4Network::overlaps(self : Ipv4Network, other : Ipv4Network) -> Bool {
  self.contains(other.first()) ||
  self.contains(other.last()) ||
  other.contains(self.first()) ||
  other.contains(self.last())
}

///|
pub fn Ipv4Network::is_adjacent_to(
  self : Ipv4Network,
  other : Ipv4Network,
) -> Bool {
  let left = Ipv4Range::from_network(self)
  let right = Ipv4Range::from_network(other)
  left.is_adjacent_to(right)
}

///|
pub fn Ipv4Network::supernet(
  self : Ipv4Network,
  new_prefix : Int,
) -> Result[Ipv4Network, CidrError] {
  if new_prefix < 0 || new_prefix > self.prefix() {
    Err(PrefixOutOfRange(new_prefix))
  } else {
    Ipv4Network::new(self.address(), new_prefix)
  }
}

///|
pub fn Ipv4Network::parent(self : Ipv4Network) -> Ipv4Network? {
  if self.prefix() == 0 {
    None
  } else {
    Some(self.supernet(self.prefix() - 1).unwrap())
  }
}

///|
pub fn Ipv4Network::sibling(self : Ipv4Network) -> Ipv4Network? {
  if self.prefix() == 0 {
    None
  } else {
    let block = self.host_count().to_uint()
    let sibling_start = self.address().to_uint() ^ block
    Some(
      Ipv4Network::new(Ipv4Address::from_uint(sibling_start), self.prefix()).unwrap(),
    )
  }
}

///|
pub fn Ipv4Network::subnets(
  self : Ipv4Network,
  new_prefix : Int,
) -> Result[Array[Ipv4Network], CidrError] {
  if new_prefix < self.prefix() || new_prefix > 32 {
    return Err(PrefixOutOfRange(new_prefix))
  }
  let delta = new_prefix - self.prefix()
  if delta > 12 {
    return Err(TooManySubnets(4097))
  }
  let count = 1 << delta
  let step = if new_prefix == 0 {
    4294967296UL
  } else {
    1UL << (32 - new_prefix)
  }
  let result = Array::new()
  let mut index = 0
  while index < count {
    let offset = (step * index.to_uint64()).to_uint()
    let start = self.address().to_uint() + offset
    result.push(
      Ipv4Network::new(Ipv4Address::from_uint(start), new_prefix).unwrap(),
    )
    index = index + 1
  }
  Ok(result)
}

///|
pub fn Ipv4Network::split(
  self : Ipv4Network,
) -> Result[(Ipv4Network, Ipv4Network), CidrError] {
  if self.prefix() == 32 {
    Err(PrefixOutOfRange(33))
  } else {
    let children = self.subnets(self.prefix() + 1).unwrap()
    Ok((children[0], children[1]))
  }
}

///|
pub fn Ipv4Network::nth_subnet(
  self : Ipv4Network,
  new_prefix : Int,
  index : Int,
) -> Result[Ipv4Network, CidrError] {
  if new_prefix < self.prefix() || new_prefix > 32 {
    return Err(PrefixOutOfRange(new_prefix))
  }
  let count = 1UL << (new_prefix - self.prefix())
  if index < 0 || index.to_uint64() >= count {
    return Err(InvalidRange("subnet index out of range"))
  }
  let step = if new_prefix == 0 {
    4294967296UL
  } else {
    1UL << (32 - new_prefix)
  }
  let start = self.address().to_uint() + (step * index.to_uint64()).to_uint()
  Ipv4Network::new(Ipv4Address::from_uint(start), new_prefix)
}

///|
pub fn Ipv4Network::contains_range(
  self : Ipv4Network,
  range : Ipv4Range,
) -> Bool {
  self.contains(range.start()) && self.contains(range.end())
}

///|
pub fn Ipv4Network::to_cisco_wildcard(self : Ipv4Network) -> String {
  "\{self.address().to_string()} \{self.wildcard_mask().to_string()}"
}

///|
pub fn Ipv4Network::from_address_and_netmask(
  address : Ipv4Address,
  mask : Ipv4Address,
) -> Result[Ipv4Network, CidrError] {
  match prefix_from_mask(mask) {
    Err(error) => Err(error)
    Ok(prefix) => Ipv4Network::new(address, prefix)
  }
}

///|
pub fn Ipv4Network::from_address_and_wildcard(
  address : Ipv4Address,
  wildcard : Ipv4Address,
) -> Result[Ipv4Network, CidrError] {
  let mask = Ipv4Address::from_uint(4294967295U ^ wildcard.to_uint())
  Ipv4Network::from_address_and_netmask(address, mask)
}

///|
fn prefix_from_mask(mask : Ipv4Address) -> Result[Int, CidrError] {
  let value = mask.to_uint()
  let mut prefix = 0
  let mut seen_zero = false
  let mut bit = 31
  while bit >= 0 {
    let one = ((value >> bit) & 1U) == 1U
    if one {
      if seen_zero {
        return Err(InvalidPrefix(mask.to_string()))
      }
      prefix = prefix + 1
    } else {
      seen_zero = true
    }
    bit = bit - 1
  }
  Ok(prefix)
}