diff --git a/numhask-range.cabal b/numhask-range.cabal
--- a/numhask-range.cabal
+++ b/numhask-range.cabal
@@ -3,60 +3,60 @@
 -- see: https://github.com/sol/hpack
 
 name:           numhask-range
-version:        0.1.2
+version:        0.1.3.0
 synopsis:       Numbers that are range representations
 description:    Numbers that represent ranges of all sorts.
 category:       project
 homepage:       https://github.com/tonyday567/numhask-range#readme
 bug-reports:    https://github.com/tonyday567/numhask-range/issues
-license:        BSD3
-license-file:   LICENSE
 author:         Tony Day
 maintainer:     tonyday567@gmail.com
 copyright:      Tony Day
+license:        BSD3
+license-file:   LICENSE
 build-type:     Simple
 cabal-version:  >= 1.10
 
 extra-source-files:
-  readme.md
-  stack.yaml
+    readme.md
+    stack.yaml
 
 source-repository head
   type: git
   location: https://github.com/tonyday567/numhask-range
 
 library
-  default-language: Haskell2010
   hs-source-dirs:
-    src
+      src
+  default-extensions: DeriveGeneric DeriveTraversable FlexibleContexts InstanceSigs MultiParamTypeClasses NegativeLiterals NoImplicitPrelude OverloadedStrings PatternSynonyms TypeFamilies UnicodeSyntax
+  build-depends:
+      numhask >=0.1.3
+    , base >=4.7 && <4.11
+    , protolude
+    , QuickCheck
+    , adjunctions
+    , distributive
+    , semigroupoids
   exposed-modules:
-    NumHask.Space
-    NumHask.Range
-    NumHask.Rect
-    NumHask.Pair
+      NumHask.Space
+      NumHask.Range
+      NumHask.RangeD
+      NumHask.Rect
+      NumHask.Pair
   other-modules:
-    Paths_numhask_range
-  build-depends:
-    numhask >=0.1.3,
-    base >=4.7 && <4.11,
-    protolude >=0.1.8 && <0.3,
-    QuickCheck >=2.8 && <3,
-    adjunctions >=4.3 && <5,
-    distributive >=0.5 && <0.6,
-    semigroupoids >=5.1 && <6
-  default-extensions: DeriveGeneric DeriveTraversable FlexibleContexts InstanceSigs MultiParamTypeClasses NegativeLiterals NoImplicitPrelude OverloadedStrings PatternSynonyms TypeFamilies UnicodeSyntax
+      Paths_numhask_range
+  default-language: Haskell2010
 
 test-suite test
-  default-language: Haskell2010
   type: exitcode-stdio-1.0
-  hs-source-dirs:
-    test
   main-is: test.hs
-  build-depends:
-    numhask >=0.1.3,
-    base >=4.7 && <5,
-    doctest,
-    numhask-range,
-    tasty,
-    tasty-quickcheck
+  hs-source-dirs:
+      test
   default-extensions: DeriveGeneric DeriveTraversable FlexibleContexts InstanceSigs MultiParamTypeClasses NegativeLiterals NoImplicitPrelude OverloadedStrings PatternSynonyms TypeFamilies UnicodeSyntax
+  build-depends:
+      numhask >=0.1.3
+    , base >=4.7 && <5
+    , doctest
+    , numhask-range
+    , tasty
+  default-language: Haskell2010
diff --git a/src/NumHask/Range.hs b/src/NumHask/Range.hs
--- a/src/NumHask/Range.hs
+++ b/src/NumHask/Range.hs
@@ -169,7 +169,7 @@
 
 -- | times may well be some sort of affine projection lurking under the hood
 instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeMagma (Range a) where
-    times a b = Range (m - r/two) (m + r/two)
+    times a b = bool (Range (m - r/two) (m + r/two)) zero (a == zero || b == zero)
         where
           m = mid a + mid b
           r = width a * width b
@@ -297,3 +297,6 @@
 
 instance Singleton Range where
     singleton a = Range a a
+
+instance (Ord a, BoundedField a, FromInteger a) => Ord (Range a) where
+    (<=) a b = lower (abs a) <= lower (abs b)
diff --git a/src/NumHask/RangeD.hs b/src/NumHask/RangeD.hs
new file mode 100644
--- /dev/null
+++ b/src/NumHask/RangeD.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE ExtendedDefaultRules #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE InstanceSigs #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+#if ( __GLASGOW_HASKELL__ < 820 )
+{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
+{-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-}
+#endif
+
+-- | representation of a possibly discontinuous range
+module NumHask.RangeD
+  ( RangeD(..)
+  , normalise
+  ) where
+
+import NumHask.Prelude as P
+import NumHask.Space
+import NumHask.Range
+
+import Test.QuickCheck.Arbitrary (Arbitrary(..))
+
+newtype RangeD a = RangeD [Range a]
+  deriving (Eq, Generic, Show, Functor, Foldable, Traversable, Arbitrary)
+
+instance NFData a => NFData (RangeD a)
+
+normalise :: (Ord (Range a), Ord a, BoundedField a, FromInteger a) =>
+    RangeD a -> RangeD a
+normalise (RangeD rs) = RangeD $ reverse $ foldl' step [] (sort rs)
+  where
+    step [] a = [a]
+    step (x:xs) a = (a `unify` x) <> xs
+
+    unify a b = bool (bool [a,b] [b,a] (a<b)) [a + b] (a `intersects` b)
+
+instance (FromInteger a, Ord a, BoundedField a) => AdditiveMagma (RangeD a) where
+    plus (RangeD l0) (RangeD l1) = normalise $ RangeD $ l0 <> l1
+
+instance (FromInteger a, Ord a, BoundedField a) => AdditiveUnital (RangeD a) where
+    zero = RangeD []
+
+instance (FromInteger a, Ord a, BoundedField a) => AdditiveAssociative (RangeD a)
+
+instance (FromInteger a, Ord a, BoundedField a) => AdditiveInvertible (RangeD a) where
+    negate (RangeD rs) = normalise $ RangeD $ negate <$> rs
+
+instance (FromInteger a, Ord a, BoundedField a) => AdditiveCommutative (RangeD a)
+instance (FromInteger a, Ord a, BoundedField a) => Additive (RangeD a)
+instance (FromInteger a, Ord a, BoundedField a) => AdditiveGroup (RangeD a)
+
+-- | times may well be some sort of affine projection lurking under the hood
+instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeMagma (RangeD a) where
+    times (RangeD a) (RangeD b) = normalise $ RangeD $ times <$> a <*> b
+
+instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeUnital (RangeD a) where
+    one = RangeD [one]
+
+instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeAssociative (RangeD a)
+
+instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeInvertible (RangeD a) where
+    recip (RangeD rs) = normalise $ RangeD $ recip <$> rs
+
+instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeCommutative (RangeD a)
+instance (FromInteger a, Ord a, BoundedField a) => Multiplicative (RangeD a)
+instance (FromInteger a, Ord a, BoundedField a) => MultiplicativeGroup (RangeD a)
+
+
diff --git a/src/NumHask/Space.hs b/src/NumHask/Space.hs
--- a/src/NumHask/Space.hs
+++ b/src/NumHask/Space.hs
@@ -44,7 +44,25 @@
     element a s = a >= lower s && a <= upper s
     -- | is a space contained within another?
     contains :: s -> s -> Bool
-    contains s0 s1 = lower s0 <= lower s1 && upper s0 >= upper s1
+    contains s0 s1 =
+        ( lower s0 <= lower s1 &&
+          upper s0 >= upper s1) ||
+        ( lower s1 <= lower s0 &&
+          upper s1 >= upper s0)
+    -- | do two spaces intersect?
+    disjoint :: s -> s -> Bool
+    disjoint s0 s1 =
+        ( lower s0 < lower s1 &&
+          lower s0 < upper s1 &&
+          upper s0 < lower s1 &&
+          upper s0 < upper s1) ||
+        ( lower s0 > lower s1 &&
+          lower s0 > upper s1 &&
+          upper s0 > lower s1 &&
+          upper s0 > upper s1)
+    -- | do two spaces intersect?
+    intersects :: s -> s -> Bool
+    intersects s0 s1 = not $ disjoint s0 s1
     -- | convex hull
     union :: s -> s -> s
     -- | null space, which can be interpreted as mempty
@@ -63,4 +81,4 @@
     gridSpace :: s -> Grid s -> [s]
 
 -- | Pos suggests where data points are placed on a grid across a range. Pos can also be thought about as whether the lower and upper points on the range are open or closed (plus the mid-point as an extra option).
-data Pos = OuterPos | InnerPos | LowerPos | UpperPos | MidPos deriving (Eq)
+data Pos = OuterPos | InnerPos | LowerPos | UpperPos | MidPos deriving (Show, Generic, Eq)
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,9 +1,6 @@
-resolver: nightly-2017-11-07
+resolver: nightly-2017-12-27
 
 packages:
-  - '.'
+  - .
 
-extra-deps:
-  #- git: https://github.com/tonyday567/numhask.git
-  #  commit: c7300b5f2a56554f3948d7c5aacb7ac8ed924e31
-  - numhask-0.1.3
+extra-deps: []
diff --git a/test/test.hs b/test/test.hs
--- a/test/test.hs
+++ b/test/test.hs
@@ -14,8 +14,7 @@
 import NumHask.Laws
 
 import Test.DocTest
-import Test.Tasty (TestName, TestTree, defaultMain, testGroup)
-import Test.Tasty.QuickCheck
+import Test.Tasty (defaultMain, testGroup)
 
 main :: IO ()
 main = do
@@ -220,3 +219,25 @@
            n < abs (size a) ||
            n < abs (size b) || n < abs (size c) || (a + b) * c ≈ a * c + b * c))
   ]
+
+semiringFuzzyLaws ::
+     ( Ord a
+     , Semiring a
+     , Epsilon a
+     , Eq a)
+  => [Law a]
+semiringFuzzyLaws = additiveLaws <> distributionFuzzyLaws
+
+distributionFuzzyLaws :: (Epsilon a, Eq a, Distribution a) => [Law a]
+distributionFuzzyLaws =
+  [ ( "left annihilation: a * zero == zero"
+    , Unary (\a -> a `times` zero == zero))
+  , ( "right annihilation: zero * a == zero"
+    , Unary (\a -> zero `times` a == zero))
+  , ( "left distributivity: a * (b + c) ≈ a * b + a * c"
+    , Ternary (\a b c -> a `times` (b + c) ≈ a `times` b + a `times` c))
+  , ( "right distributivity: (a + b) * c ≈ a * c + b * c"
+    , Ternary (\a b c -> (a + b) `times` c ≈ a `times` c + b `times` c))
+  ]
+
+
