    module Data.IP.Op where

    import Data.Bits
    import Data.IP.Addr
    import Data.IP.Mask
    import Data.IP.Range

    ----------------------------------------------------------------

    {-|

    >>> toIPv4 [127,0,2,1] `masked` intToMask 7
    126.0.0.0
    -}
    class Eq a => Addr a where
        {-|
          The 'masked' function takes an 'Addr' and a contiguous
          mask and returned a masked 'Addr'.
        -}
        masked :: a -> a -> a
        {-|

          The 'intToMask' function takes an 'Int' representing the number of bits to
          be set in the returned contiguous mask. When this integer is positive the
          bits will be starting from the MSB and from the LSB otherwise.

          >>> intToMask 16 :: IPv4
          255.255.0.0

          >>> intToMask (-16) :: IPv4
          0.0.255.255

          >>> intToMask 16 :: IPv6
          ffff::

          >>> intToMask (-16) :: IPv6
          ::ffff

        -}
        intToMask :: Int -> a

    instance Addr IPv4 where
        masked :: IPv4 -> IPv4 -> IPv4
    masked    = IPv4 -> IPv4 -> IPv4
    maskedIPv4
        intToMask :: Int -> IPv4
    intToMask = Int -> IPv4
    maskIPv4

    instance Addr IPv6 where
        masked :: IPv6 -> IPv6 -> IPv6
    masked    = IPv6 -> IPv6 -> IPv6
    maskedIPv6
        intToMask :: Int -> IPv6
    intToMask = Int -> IPv6
    maskIPv6

    ----------------------------------------------------------------

    {-|
      The >:> operator takes two 'AddrRange'. It returns 'True' if
      the first 'AddrRange' contains the second 'AddrRange'. Otherwise,
      it returns 'False'.

    >>> makeAddrRange ("127.0.2.1" :: IPv4) 8 >:> makeAddrRange "127.0.2.1" 24
    True
    >>> makeAddrRange ("127.0.2.1" :: IPv4) 24 >:> makeAddrRange "127.0.2.1" 8
    False
    >>> makeAddrRange ("2001:DB8::1" :: IPv6) 16 >:> makeAddrRange "2001:DB8::1" 32
    True
    >>> makeAddrRange ("2001:DB8::1" :: IPv6) 32 >:> makeAddrRange "2001:DB8::1" 16
    False
    -}
    (>:>) :: Addr a => AddrRange a -> AddrRange a -> Bool
    a :: AddrRange a
    a >:> :: AddrRange a -> AddrRange a -> Bool
    >:> b :: AddrRange a
    b = AddrRange a -> Int
    forall a. AddrRange a -> Int
    mlen AddrRange a
    a Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= AddrRange a -> Int
    forall a. AddrRange a -> Int
    mlen AddrRange a
    b Bool -> Bool -> Bool
    && (AddrRange a -> a
    forall a. AddrRange a -> a
    addr AddrRange a
    b a -> a -> a
    forall a. Addr a => a -> a -> a
    `masked` AddrRange a -> a
    forall a. AddrRange a -> a
    mask AddrRange a
    a) a -> a -> Bool
    forall a. Eq a => a -> a -> Bool
    == AddrRange a -> a
    forall a. AddrRange a -> a
    addr AddrRange a
    a

    {-|
      The 'isMatchedTo' function take an 'Addr' address and an 'AddrRange',
      and returns 'True' if the range contains the address.

    >>> ("127.0.2.0" :: IPv4) `isMatchedTo` makeAddrRange "127.0.2.1" 24
    True
    >>> ("127.0.2.0" :: IPv4) `isMatchedTo` makeAddrRange "127.0.2.1" 32
    False
    >>> ("2001:DB8::1" :: IPv6) `isMatchedTo` makeAddrRange "2001:DB8::1" 32
    True
    >>> ("2001:DB8::" :: IPv6) `isMatchedTo` makeAddrRange "2001:DB8::1" 128
    False
    -}

    isMatchedTo :: Addr a => a -> AddrRange a -> Bool
    isMatchedTo :: a -> AddrRange a -> Bool
    isMatchedTo a :: a
    a r :: AddrRange a
    r = a
    a a -> a -> a
    forall a. Addr a => a -> a -> a
    `masked` AddrRange a -> a
    forall a. AddrRange a -> a
    mask AddrRange a
    r a -> a -> Bool
    forall a. Eq a => a -> a -> Bool
    == AddrRange a -> a
    forall a. AddrRange a -> a
    addr AddrRange a
    r

    {-|
      The 'makeAddrRange' functions takes an 'Addr' address and a mask
      length. It creates a bit mask from the mask length and masks
      the 'Addr' address, then returns 'AddrRange' made of them.

    >>> makeAddrRange (toIPv4 [127,0,2,1]) 8
    127.0.0.0/8
    >>> makeAddrRange (toIPv6 [0x2001,0xDB8,0,0,0,0,0,1]) 8
    2000::/8
    -}
    makeAddrRange :: Addr a => a -> Int -> AddrRange a
    makeAddrRange :: a -> Int -> AddrRange a
    makeAddrRange ad :: a
    ad len :: Int
    len = a -> a -> Int -> AddrRange a
    forall a. a -> a -> Int -> AddrRange a
    AddrRange a
    adr a
    msk Int
    len
      where
        msk :: a
    msk = Int -> a
    forall a. Addr a => Int -> a
    intToMask Int
    len
        adr :: a
    adr = a
    ad a -> a -> a
    forall a. Addr a => a -> a -> a
    `masked` a
    msk

    -- | Convert IPv4 range to IPV4-embedded-in-IPV6 range
    ipv4RangeToIPv6 :: AddrRange IPv4 -> AddrRange IPv6
    ipv4RangeToIPv6 :: AddrRange IPv4 -> AddrRange IPv6
    ipv4RangeToIPv6 range :: AddrRange IPv4
    range =
      IPv6 -> Int -> AddrRange IPv6
    forall a. Addr a => a -> Int -> AddrRange a
    makeAddrRange ([Int] -> IPv6
    toIPv6 [0,0,0,0,0,0xffff, (Int
    i1 Int -> Int -> Int
    forall a. Bits a => a -> Int -> a
    `shift` 8) Int -> Int -> Int
    forall a. Bits a => a -> a -> a
    .|. Int
    i2, (Int
    i3 Int -> Int -> Int
    forall a. Bits a => a -> Int -> a
    `shift` 8) Int -> Int -> Int
    forall a. Bits a => a -> a -> a
    .|. Int
    i4]) (Int
    masklen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + 96)
      where
        (ip :: IPv4
    ip, masklen :: Int
    masklen) = AddrRange IPv4 -> (IPv4, Int)
    forall a. Addr a => AddrRange a -> (a, Int)
    addrRangePair AddrRange IPv4
    range
        [i1 :: Int
    i1,i2 :: Int
    i2,i3 :: Int
    i3,i4 :: Int
    i4] = IPv4 -> [Int]
    fromIPv4 IPv4
    ip

    {-|
      The 'unmakeAddrRange' functions take a 'AddrRange' and
      returns the network address and a mask length.

    >>> addrRangePair ("127.0.0.0/8" :: AddrRange IPv4)
    (127.0.0.0,8)
    >>> addrRangePair ("2000::/8" :: AddrRange IPv6)
    (2000::,8)
    -}
    addrRangePair :: Addr a => AddrRange a -> (a, Int)
    addrRangePair :: AddrRange a -> (a, Int)
    addrRangePair (AddrRange adr :: a
    adr _ len :: Int
    len) = (a
    adr, Int
    len)