diff --git a/ip.cabal b/ip.cabal
--- a/ip.cabal
+++ b/ip.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.2
 name: ip
-version: 1.6.0
+version: 1.7.0
 synopsis: Library for IP and MAC addresses
 homepage: https://github.com/andrewthad/haskell-ip#readme
 license: BSD-3-Clause
@@ -48,7 +48,7 @@
   build-depends:
     , aeson >= 1.0 && < 1.5
     , attoparsec >= 0.13 && < 0.14
-    , base >= 4.9.1.0 && < 5
+    , base >= 4.9 && < 5
     , byteslice >= 0.1.2 && < 0.2
     , bytesmith >= 0.3 && < 0.4
     , bytestring >= 0.10.8 && < 0.11
@@ -56,7 +56,7 @@
     , hashable >= 1.2 && < 1.4
     , natural-arithmetic >= 0.1 && <0.2
     , primitive >= 0.6.4 && < 0.8
-    , small-bytearray-builder >= 0.2.1 && <0.3
+    , small-bytearray-builder >= 0.2.1 && <0.4
     , text >= 1.2 && < 1.3
     , text-short >= 0.1.3 && < 0.2
     , vector >= 0.11 && < 0.13
@@ -101,7 +101,6 @@
     , ip
     , wide-word
   other-modules:
-    Net.IPv4.RangeSpec
     Net.IPv4Spec
   ghc-options: -Wall -O2
   default-language: Haskell2010
@@ -117,6 +116,7 @@
     , doctest >= 0.10
     , ip
     , wide-word
+    , vector
   default-language:    Haskell2010
 
 benchmark criterion
diff --git a/src/Net/IP.hs b/src/Net/IP.hs
--- a/src/Net/IP.hs
+++ b/src/Net/IP.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveGeneric #-}
 
@@ -56,6 +57,8 @@
 import Data.Aeson (FromJSON(..),ToJSON(..))
 import Data.Bits
 import Data.Coerce (coerce)
+import Data.Data (Data)
+import Data.Ix (Ix)
 import Data.Text (Text)
 import Data.WideWord (Word128(..))
 import Data.Word (Word8,Word16)
@@ -222,7 +225,7 @@
 --   of this type. All functions and typeclass methods that convert
 --   'IP' values to text will display it as an 'IPv4' address if possible.
 newtype IP = IP { getIP :: IPv6 }
-  deriving (Eq,Ord,Generic)
+  deriving (Eq,Ord,Generic,Ix,Data)
 
 instance NFData IP
 
diff --git a/src/Net/IPv4.hs b/src/Net/IPv4.hs
--- a/src/Net/IPv4.hs
+++ b/src/Net/IPv4.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE MagicHash #-}
@@ -94,10 +95,12 @@
 import Control.Monad.ST (ST,runST)
 import Data.Aeson (FromJSON(..),ToJSON(..))
 import Data.Aeson (ToJSONKey(..),FromJSONKey(..),ToJSONKeyFunction(..),FromJSONKeyFunction(..))
-import Data.Bits ((.&.),(.|.),shiftR,shiftL,unsafeShiftR,complement,shift)
+import Data.Bits (Bits(..))
 import Data.ByteString (ByteString)
 import Data.Coerce (coerce)
+import Data.Data (Data)
 import Data.Hashable
+import Data.Ix (Ix)
 import Data.Primitive.Types (Prim)
 import Data.Text (Text)
 import Data.Text.Encoding (decodeUtf8')
@@ -156,7 +159,9 @@
 -- >>> import qualified Data.Text.IO as T
 -- >>> instance Arbitrary IPv4 where { arbitrary = fmap IPv4 arbitrary }
 -- >>> instance Arbitrary IPv4Range where { arbitrary = IPv4Range <$> arbitrary <*> arbitrary }
+-- >>> import qualified Data.Bytes.Chunks as Chunks
 
+
 -- | Create an 'IPv4' address from four octets. The first argument
 --   is the most significant octet. The last argument is the least
 --   significant. Since IP addresses are commonly written using dot-decimal
@@ -452,7 +457,7 @@
 
 -- | Encode an 'IPv4' address as a unbounded byte array builder.
 --
--- >>> UB.run 1 (byteArrayBuilderUtf8 (fromOctets 192 168 2 13))
+-- >>> Chunks.concat (UB.run 1 (byteArrayBuilderUtf8 (fromOctets 192 168 2 13)))
 -- [0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x32, 0x2e, 0x31, 0x33]
 --
 -- Note that period is encoded by UTF-8 as @0x2e@.
@@ -533,11 +538,11 @@
 -- convert between this and the lifted 'IPv4'.
 type IPv4# = Word#
 
--- | Convert an unboxed IPv4 address to a boxed one. 
+-- | Convert an unboxed IPv4 address to a boxed one.
 box :: IPv4# -> IPv4
 box w = IPv4 (W32# w)
 
--- | Convert a boxed IPv4 address to an unboxed one. 
+-- | Convert a boxed IPv4 address to an unboxed one.
 unbox :: IPv4 -> IPv4#
 unbox (IPv4 (W32# w)) = w
 
@@ -546,7 +551,7 @@
 --   convert the underlying 'Word32' from host byte order to network byte
 --   order.
 newtype IPv4 = IPv4 { getIPv4 :: Word32 }
-  deriving (Eq,Ord,Enum,Bounded,Hashable,Generic,Prim,Storable)
+  deriving (Bits.Bits,Bounded,Data,Enum,Eq,Bits.FiniteBits,Generic,Hashable,Ix,Ord,Prim,Storable)
 
 instance NFData IPv4
 
@@ -642,28 +647,6 @@
   Nothing -> fail "Could not parse IPv4 address"
   Just addr -> return addr
 
-ipv4Bitwise :: (Word32 -> Word32 -> Word32) -> IPv4 -> IPv4 -> IPv4
-ipv4Bitwise fun l r = IPv4 $ (getIPv4 l) `fun` (getIPv4 r)
-
--- | Note: we use network order (big endian) as opposed to host order (little
---   endian) which differs from the underlying IPv4 type representation.
-instance Bits.Bits IPv4 where
-    (.&.) = ipv4Bitwise (.&.)
-    (.|.) = ipv4Bitwise (.|.)
-    xor = ipv4Bitwise Bits.xor
-    complement = IPv4 . Bits.complement . getIPv4
-    shift ip i = IPv4 $ Bits.shift (getIPv4 ip) i
-    rotate ip i = IPv4 $ Bits.rotate (getIPv4 ip) i
-    bitSize = Bits.finiteBitSize
-    bitSizeMaybe = Bits.bitSizeMaybe . getIPv4
-    isSigned = Bits.isSigned . getIPv4
-    testBit ip i = Bits.testBit (getIPv4 ip) $ Bits.finiteBitSize ip - 1 - i
-    bit i = IPv4 $ Bits.bit $ Bits.finiteBitSize any - 1 - i
-    popCount = Bits.popCount . getIPv4
-
-instance Bits.FiniteBits IPv4 where
-    finiteBitSize = Bits.finiteBitSize . getIPv4
-
 ------------------------------------
 -- Internal functions, not exported
 ------------------------------------
@@ -1074,7 +1057,7 @@
 data IPv4Range = IPv4Range
   { ipv4RangeBase   :: {-# UNPACK #-} !IPv4
   , ipv4RangeLength :: {-# UNPACK #-} !Word8
-  } deriving (Eq,Ord,Show,Read,Generic)
+  } deriving (Eq,Ord,Show,Read,Generic,Data)
 
 instance NFData IPv4Range
 instance Hashable IPv4Range
@@ -1196,47 +1179,6 @@
   elemseq _ (IPv4Range a b)
       = GVector.elemseq (undefined :: UVector.Vector a) a
         . GVector.elemseq (undefined :: UVector.Vector b) b
-
-rangeBitwise :: (IPv4 -> IPv4 -> IPv4) -> IPv4Range -> IPv4Range -> IPv4Range
-rangeBitwise fun l r = range ip len
-  where
-    -- Normalise first
-    l' = normalize l
-    r' = normalize r
-    ip = (ipv4RangeBase l') `fun` (ipv4RangeBase r')
-    len = maximum [ipv4RangeLength l, ipv4RangeLength r]
-
-rangeRebase :: (IPv4 -> IPv4) -> IPv4Range -> IPv4Range
-rangeRebase fun r = range (fun $ ipv4RangeBase r) (ipv4RangeLength r)
-
--- | Notes:
---
---     * bit operations use network order (big endian),
---
---     * do not operate on host bits,
---
---     * return a normalized range dropping host bits,
---
---     * and "promote operands" by extending the length to the larger of two
---       ranges.
---
-instance Bits.Bits IPv4Range where
-  (.&.) = rangeBitwise (.&.)
-  (.|.) = rangeBitwise (.|.)
-  xor = rangeBitwise Bits.xor
-  complement = rangeRebase Bits.complement
-  shift r i = rangeRebase (flip Bits.shift i) r
-  rotate r i = rangeRebase (flip Bits.rotate i) r
-  bitSize = Bits.finiteBitSize
-  bitSizeMaybe = Just . Bits.finiteBitSize
-  isSigned = Bits.isSigned . ipv4RangeBase
-  testBit ip i = Bits.testBit (ipv4RangeBase ip) i
-  bit i = IPv4Range (Bits.bit i) $ fromIntegral $ i + 1
-  popCount = Bits.popCount . ipv4RangeBase . normalize
-
--- | Note: the size is determined by the range length
-instance Bits.FiniteBits IPv4Range where
-  finiteBitSize = fromIntegral . ipv4RangeLength
 
 -----------------
 -- Internal Stuff
diff --git a/src/Net/IPv6.hs b/src/Net/IPv6.hs
--- a/src/Net/IPv6.hs
+++ b/src/Net/IPv6.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE InstanceSigs #-}
@@ -66,9 +67,12 @@
 
 import Control.Applicative
 import Control.DeepSeq (NFData)
+import Control.Monad (mzero)
 import Control.Monad.ST (ST)
 import Data.Bits
 import Data.Char (chr)
+import Data.Data (Data)
+import Data.Ix (Ix)
 import Data.List (intercalate, group)
 import Data.Primitive (MutablePrimArray)
 import Data.Primitive.Types (Prim)
@@ -115,7 +119,7 @@
 
 -- | A 128-bit Internet Protocol version 6 address.
 newtype IPv6 = IPv6 { getIPv6 :: Word128 }
-  deriving (Bounded,Enum,Eq,Integral,Num,Ord,Real,Storable,Bits,FiniteBits,NFData,Prim)
+  deriving (Bounded,Enum,Eq,Ord,Storable,Bits,FiniteBits,NFData,Prim,Ix,Data)
 
 instance Show IPv6 where
   showsPrec p addr = showParen (p > 10)
@@ -749,9 +753,18 @@
 data IPv6Range = IPv6Range
   { ipv6RangeBase   :: {-# UNPACK #-} !IPv6
   , ipv6RangeLength :: {-# UNPACK #-} !Word8
-  } deriving (Eq,Ord,Show,Read,Generic)
+  } deriving (Eq,Ord,Show,Read,Generic,Data)
 
 instance NFData IPv6Range
+
+instance Aeson.ToJSON IPv6Range where
+  toJSON = Aeson.String . encodeRange
+
+instance Aeson.FromJSON IPv6Range where
+  parseJSON (Aeson.String t) = case decodeRange t of
+    Nothing -> fail "Could not decodeRange IPv6 range"
+    Just res -> return res
+  parseJSON _ = mzero
 
 mask128 :: IPv6
 mask128 = maxBound
diff --git a/src/Net/Mac.hs b/src/Net/Mac.hs
--- a/src/Net/Mac.hs
+++ b/src/Net/Mac.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE InstanceSigs #-}
@@ -53,7 +54,9 @@
 import Data.Bits ((.|.),unsafeShiftL,unsafeShiftR,(.&.))
 import Data.ByteString (ByteString)
 import Data.Char (ord,chr)
+import Data.Data (Data)
 import Data.Hashable (Hashable)
+import Data.Ix (Ix)
 import Data.Primitive.Types (Prim(..))
 #if !MIN_VERSION_base(4,11,0)
 import Data.Semigroup ((<>))
@@ -607,7 +610,7 @@
 --   type. It is not considered part of the stable API, and it
 --   allows you to construct invalid MAC addresses.
 newtype Mac = Mac Word64
-  deriving (Eq,Ord,Generic)
+  deriving (Eq,Ord,Generic,Ix,Data)
 
 instance NFData Mac
 
@@ -737,7 +740,7 @@
 data MacCodec = MacCodec
   { macCodecGrouping :: !MacGrouping
   , macCodecUpperCase :: !Bool
-  } deriving (Eq,Ord,Show,Read,Generic)
+  } deriving (Eq,Ord,Show,Read,Generic,Data)
 
 -- | The format expected by the mac address parser. The 'Word8' taken
 --   by some of these constructors is the ascii value of the character
@@ -748,7 +751,7 @@
   | MacGroupingTriples !Char -- ^ Three-character groups, @24B-F0A-025-829@
   | MacGroupingQuadruples !Char -- ^ Four-character groups, @A220.0745.CAC7@
   | MacGroupingNoSeparator -- ^ No separator, @24AF4B5B0780@
-  deriving (Eq,Ord,Show,Read,Generic)
+  deriving (Eq,Ord,Show,Read,Generic,Data)
 
 instance Hashable Mac
 
diff --git a/test/Net/IPv4/RangeSpec.hs b/test/Net/IPv4/RangeSpec.hs
deleted file mode 100644
--- a/test/Net/IPv4/RangeSpec.hs
+++ /dev/null
@@ -1,67 +0,0 @@
-{-# OPTIONS_GHC -Wno-deprecations #-}
-module Net.IPv4.RangeSpec (spec) where
-import Prelude hiding (any)
-import Data.Bits
-import Net.IPv4
-import Test.Hspec
-
-spec :: Spec
-spec = parallel $ do
-  describe "Bits" $ do
-    context "underlying IPv4 imlementation used correctly" $ do
-      let host = range (ipv4 255 255 0 0) 32
-          broadH = range broadcast 32
-          negBroadH = range (ipv4 0 0 255 255) 32
-      it ".&." $ do
-        host .&. broadH `shouldBe` host
-      it ".|." $ do
-        host .|. broadH `shouldBe` broadH
-      it "xor" $ do
-        host `xor` broadH `shouldBe` negBroadH
-      it "complement" $ do
-        complement host `shouldBe` negBroadH
-      it "shift" $ do
-        shift host 8 `shouldBe` range (ipv4 255 0 0 0) 32
-      it "rotate" $ do
-        rotate host 8 `shouldBe` range (ipv4 255 0 0 255) 32
-      it "isSigned" $ do
-        isSigned host `shouldBe` False
-    context "size operations use length correctly" $ do
-      it "bitSize" $ do
-          bitSize (range any 8) `shouldBe` 8
-          bitSize (range any 15) `shouldBe` 15
-          bitSize (range any 32) `shouldBe` 32
-      it "bitSizeMaybe" $ do
-          bitSizeMaybe (range broadcast 0) `shouldBe` Just 0
-          bitSizeMaybe (range broadcast 24) `shouldBe` Just 24
-          bitSizeMaybe (range broadcast 31) `shouldBe` Just 31
-      it "testBit" $ do
-        let prefix = range loopback 8
-        testBit prefix <$> [0..31] `shouldBe`
-          -- Note: final bit is False not True
-          [ False, True,  True,  True,  True,  True,  True,  True
-          , False, False, False, False, False, False, False, False
-          , False, False, False, False, False, False, False, False
-          , False, False, False, False, False, False, False, False]
-      it "bit" $ do
-        bit 0 `shouldBe` range (ipv4 128 0 0 0) 1
-        bit 1 `shouldBe` range (ipv4 64 0 0 0) 2
-        bit 31 `shouldBe` range (ipv4 0 0 0 1) 32
-      it "popCount" $ do
-        popCount (range any 0) `shouldBe` 0
-        popCount (range broadcast 0) `shouldBe` 0
-        popCount (range loopback 8) `shouldBe` 7
-        popCount (range loopback 32) `shouldBe` 8
-    context "operates on network bits only" $ do
-      it "bitwise: same length" $ do
-        (IPv4Range broadcast 16) .&. (IPv4Range broadcast 16)
-          `shouldBe` (IPv4Range (ipv4 255 255 0 0) 16)
-      it "bitwise: differing lengths ignoring host bits" $ do
-        (IPv4Range broadcast 8) .&. (IPv4Range broadcast 16)
-          `shouldBe` (IPv4Range (ipv4 255 0 0 0) 16)
-      it "rebase: ignores host bits" $ do
-        complement (IPv4Range loopback 16)
-          `shouldBe` (IPv4Range (ipv4 128 255 0 0) 16)
-  describe "FiniteBits" $ do
-    it "finiteBitSize" $ do
-      finiteBitSize . (range loopback) <$> [0..31] `shouldBe` [0..31]
diff --git a/test/Net/IPv4Spec.hs b/test/Net/IPv4Spec.hs
--- a/test/Net/IPv4Spec.hs
+++ b/test/Net/IPv4Spec.hs
@@ -49,14 +49,14 @@
             isSigned broadcast `shouldBe` False
         it "testBit" $ do
             testBit loopback <$> [0..31] `shouldBe`
-                [ False, True,  True,  True,  True,  True,  True,  True
-                , False, False, False, False, False, False, False, False
-                , False, False, False, False, False, False, False, False
-                , False, False, False, False, False, False, False, True]
+              [ True, False, False, False, False, False, False, False
+              , False, False, False, False, False, False, False, False
+              , False, False, False, False, False, False, False, False
+              , True, True, True, True, True, True, True, False ]
         it "bit" $ do
-            bit 0 `shouldBe` ipv4 128 0 0 0
-            bit 1 `shouldBe` ipv4 64 0 0 0
-            bit 31 `shouldBe` ipv4 0 0 0 1
+            bit 0 `shouldBe` ipv4 0 0 0 1
+            bit 1 `shouldBe` ipv4 0 0 0 2
+            bit 31 `shouldBe` ipv4 128 0 0 0
         it "popCount" $ do
             popCount any `shouldBe` 0
             popCount loopback `shouldBe` 8
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -16,7 +16,7 @@
 import Numeric (showHex)
 import Test.QuickCheck.Property (failed,succeeded,Result(..))
 import Data.Bifunctor
-import Test.QuickCheck.Classes (Laws(..),jsonLaws,showReadLaws,primLaws,boundedEnumLaws)
+import Test.QuickCheck.Classes (Laws(..),jsonLaws,showReadLaws,primLaws,boundedEnumLaws,bitsLaws)
 import qualified Test.Tasty.HUnit as PH
 
 import Net.Types (IP,IPv4(..),IPv4Range(..),Mac(..),IPv6(..),MacGrouping(..),MacCodec(..),IPv6Range(..))
@@ -96,7 +96,7 @@
       [ PH.testCase "Parser Test Cases" testIPv4Parser
       ]
     , testGroup "IPv6 encode/decode"
-      [ PH.testCase "Parser Test Cases" $ testIPv6Parser $ \str -> 
+      [ PH.testCase "Parser Test Cases" $ testIPv6Parser $ \str ->
           either (\_ -> Nothing) (Just . HexIPv6)
             (AT.parseOnly
               (IPv6.parser <* AT.endOfInput)
@@ -146,19 +146,23 @@
     [ testGroup "IPv4"
       [ lawsToTest (jsonLaws (Proxy :: Proxy IPv4))
       , lawsToTest (showReadLaws (Proxy :: Proxy IPv4))
-      -- , lawsToTest (bitsLaws (Proxy :: Proxy IPv4))
+       , lawsToTest (bitsLaws (Proxy :: Proxy IPv4))
       ]
     , testGroup "IPv4Range"
       [ lawsToTest (jsonLaws (Proxy :: Proxy IPv4Range))
       , lawsToTest (showReadLaws (Proxy :: Proxy IPv4Range))
-      -- , lawsToTest (bitsLaws (Proxy :: Proxy IPv4Range))
       ]
     , testGroup "IPv6"
       [ lawsToTest (jsonLaws (Proxy :: Proxy IPv6))
       , lawsToTest (showReadLaws (Proxy :: Proxy IPv6))
       , lawsToTest (primLaws (Proxy :: Proxy IPv6))
       , lawsToTest (boundedEnumLaws (Proxy :: Proxy IPv6))
+      , lawsToTest (bitsLaws (Proxy :: Proxy IPv6))
       ]
+    , testGroup "IPv6Range"
+      [ lawsToTest (jsonLaws (Proxy :: Proxy IPv6Range))
+      , lawsToTest (showReadLaws (Proxy :: Proxy IPv6Range))
+      ]
     , testGroup "IP"
       [ lawsToTest (jsonLaws (Proxy :: Proxy IP))
       , lawsToTest (showReadLaws (Proxy :: Proxy IP))
@@ -472,7 +476,6 @@
   shrink (IPv6Range addr mask) = liftA2 IPv6.range
     (shrink addr)
     (filter (/= mask) [0,div mask 2,if mask > 0 then mask - 1 else 0])
-    
 
 instance Arbitrary MacCodec where
   arbitrary = MacCodec <$> arbitrary <*> arbitrary
