diff --git a/.travis.yml b/.travis.yml
new file mode 100644
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,1 @@
+language: haskell
diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,27 @@
+Copyright 2010-2013 Masahiro Sakai. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+   3. The name of the author may not be used to endorse or promote
+      products derived from this software without specific prior
+      written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+data-interval
+=============
+
+Interval datatype and interval arithmetic for Haskell
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+#! /usr/bin/env runhaskell
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/data-interval.cabal b/data-interval.cabal
new file mode 100644
--- /dev/null
+++ b/data-interval.cabal
@@ -0,0 +1,43 @@
+Name:		data-interval
+Version:	0.1.0
+License:	BSD3
+License-File:	COPYING
+Author:		Masahiro Sakai (masahiro.sakai@gmail.com)
+Maintainer:	masahiro.sakai@gmail.com
+Category:	Data
+Cabal-Version:	>= 1.8
+Synopsis:	Interval Arithmetic
+Description:
+   Unlike the intervals package (<http://hackage.haskell.org/package/intervals>),
+   this module provides both open and closed intervals and is intended to be used
+   with @Rational@.
+Bug-Reports:	https://github.com/msakai/data-interval/issues
+Extra-Source-Files:
+   README.md
+   COPYING
+   .travis.yml
+Build-Type: Simple
+
+source-repository head
+  type:     git
+  location: git://github.com/msakai/data-interval.git
+
+Library
+  Exposed: False
+  Hs-source-dirs: src
+  Build-Depends:
+     base >=4 && <5, lattices >=1.2.1.1
+  Extensions:
+     ScopedTypeVariables
+     FlexibleInstances
+     MultiParamTypeClasses
+     DeriveDataTypeable
+  Exposed-Modules:
+     Data.Interval
+
+Test-suite TestInterval
+  Type:              exitcode-stdio-1.0
+  HS-Source-Dirs:    test
+  Main-is:           TestInterval.hs
+  Build-depends:     base >=4 && <5, containers, data-interval, test-framework, test-framework-th, test-framework-hunit, test-framework-quickcheck2, HUnit, QuickCheck >=2 && <3
+  Extensions: TemplateHaskell, DoAndIfThenElse
diff --git a/src/Data/Interval.hs b/src/Data/Interval.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Interval.hs
@@ -0,0 +1,504 @@
+{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Interval
+-- Copyright   :  (c) Masahiro Sakai 2011-2013
+-- License     :  BSD-style
+-- 
+-- Maintainer  :  masahiro.sakai@gmail.com
+-- Stability   :  provisional
+-- Portability :  non-portable (ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable)
+--
+-- Interval datatype and interval arithmetic.
+--
+-- Unlike the intervals package (<http://hackage.haskell.org/package/intervals>),
+-- this module provides both open and closed intervals and is intended to be used
+-- with @Rational@.
+-- 
+-----------------------------------------------------------------------------
+module Data.Interval
+  (
+  -- * Interval type
+    Interval
+  , EndPoint (..)
+
+  -- * Construction
+  , interval
+  , (<=..<=)
+  , (<..<=)
+  , (<=..<)
+  , (<..<)
+  , whole
+  , empty
+  , singleton
+
+  -- * Query
+  , null
+  , member
+  , notMember
+  , isSubsetOf
+  , isProperSubsetOf
+  , lowerBound
+  , upperBound
+  , lowerBound'
+  , upperBound'
+  , width
+
+  -- * Comparison
+  , (<!), (<=!), (==!), (>=!), (>!)
+  , (<?), (<=?), (==?), (>=?), (>?)
+
+  -- * Combine
+  , intersection
+  , hull
+
+  -- * Operations
+  , pickup
+  ) where
+
+import Algebra.Lattice
+import Control.Exception (assert)
+import Control.Monad hiding (join)
+import Data.List hiding (null)
+import Data.Maybe
+import Data.Monoid
+import Data.Typeable
+import Prelude hiding (null)
+
+-- | Interval
+data Interval r = Interval !(EndPoint r, Bool) !(EndPoint r, Bool)
+  deriving (Eq, Typeable)  
+
+-- | Lower bound of the interval
+lowerBound :: Num r => Interval r -> EndPoint r
+lowerBound (Interval (lb,_) _) = lb
+
+-- | Upper bound of the interval
+upperBound :: Num r => Interval r -> EndPoint r
+upperBound (Interval _ (ub,_)) = ub
+
+-- | Lower bound of the interval and whether it is included in the interval
+lowerBound' :: Num r => Interval r -> (EndPoint r, Bool)
+lowerBound' (Interval lb _) = lb
+
+-- | Upper bound of the interval and whether it is included in the interval
+upperBound' :: Num r => Interval r -> (EndPoint r, Bool)
+upperBound' (Interval _ ub) = ub
+
+instance (Num r, Ord r) => JoinSemiLattice (Interval r) where
+  join = hull
+
+instance (Num r, Ord r) => MeetSemiLattice (Interval r) where
+  meet = hull
+
+instance (Num r, Ord r) => Lattice (Interval r)
+
+instance (Num r, Ord r) => BoundedJoinSemiLattice (Interval r) where
+  bottom = empty
+
+instance (Num r, Ord r) => BoundedMeetSemiLattice (Interval r) where
+  top = whole
+
+instance (Num r, Ord r) => BoundedLattice (Interval r)
+
+instance (Num r, Ord r, Show r) => Show (Interval r) where
+  showsPrec p x | null x = showString "empty"
+  showsPrec p x = showParen (p > appPrec) $
+    showString "interval " .
+    showsPrec appPrec1 (lowerBound' x) .
+    showChar ' ' . 
+    showsPrec appPrec1 (upperBound' x)
+
+instance (Num r, Ord r, Read r) => Read (Interval r) where
+  readsPrec p r =
+    (readParen (p > appPrec) $ \s0 -> do
+      ("interval",s1) <- lex s0
+      (lb,s2) <- readsPrec (appPrec+1) s1
+      (ub,s3) <- readsPrec (appPrec+1) s2
+      return (interval lb ub, s3)) r
+    ++
+    (do ("empty", s) <- lex r
+        return (empty, s))
+
+-- | smart constructor for 'Interval'
+interval
+  :: (Ord r, Num r)
+  => (EndPoint r, Bool) -- ^ lower bound and whether it is included 
+  -> (EndPoint r, Bool) -- ^ upper bound and whether it is included
+  -> Interval r
+interval lb@(x1,in1) ub@(x2,in2) =
+  case x1 `compare` x2 of
+    GT -> empty --  empty interval
+    LT -> Interval (normalize lb) (normalize ub)
+    EQ -> if in1 && in2 && isFinite x1 then Interval lb ub else empty
+  where
+    normalize x@(Finite _, _) = x
+    normalize (x, _) = (x, False)
+
+-- | closed interval [@l@,@u@]
+(<=..<=)
+  :: (Ord r, Num r)
+  => EndPoint r -- ^ lower bound @l@
+  -> EndPoint r -- ^ upper bound @u@
+  -> Interval r
+(<=..<=) lb ub = interval (lb, True) (ub, True)
+
+-- | left-open right-closed interval (@l@,@u@]
+(<..<=)
+  :: (Ord r, Num r)
+  => EndPoint r -- ^ lower bound @l@
+  -> EndPoint r -- ^ upper bound @u@
+  -> Interval r
+(<..<=) lb ub = interval (lb, False) (ub, True)
+
+-- | left-closed right-open interval [@l@, @u@)
+(<=..<)
+  :: (Ord r, Num r)
+  => EndPoint r -- ^ lower bound @l@
+  -> EndPoint r -- ^ upper bound @u@
+  -> Interval r
+(<=..<) lb ub = interval (lb, True) (ub, False)
+
+-- | open interval (@l@, @u@)
+(<..<)
+  :: (Ord r, Num r)
+  => EndPoint r -- ^ lower bound @l@
+  -> EndPoint r -- ^ upper bound @u@
+  -> Interval r
+(<..<) lb ub = interval (lb, False) (ub, False)
+
+-- | whole real number line (-∞, ∞)
+whole :: (Num r, Ord r) => Interval r
+whole = Interval (NegInf, False) (PosInf, False)
+
+-- | empty (contradicting) interval
+empty :: Num r => Interval r
+empty = Interval (PosInf, False) (NegInf, False)
+
+-- | singleton set \[x,x\]
+singleton :: (Num r, Ord r) => r -> Interval r
+singleton x = interval (Finite x, True) (Finite x, True)
+
+-- | intersection (greatest lower bounds) of two intervals
+intersection :: forall r. (Ord r, Num r) => Interval r -> Interval r -> Interval r
+intersection (Interval l1 u1) (Interval l2 u2) = interval (maxLB l1 l2) (minUB u1 u2)
+  where
+    maxLB :: (EndPoint r, Bool) -> (EndPoint r, Bool) -> (EndPoint r, Bool)
+    maxLB (x1,in1) (x2,in2) =
+      ( max x1 x2
+      , case x1 `compare` x2 of
+          EQ -> in1 && in2
+          LT -> in2
+          GT -> in1
+      )
+    minUB :: (EndPoint r, Bool) -> (EndPoint r, Bool) -> (EndPoint r, Bool)
+    minUB (x1,in1) (x2,in2) =
+      ( min x1 x2
+      , case x1 `compare` x2 of
+          EQ -> in1 && in2
+          LT -> in1
+          GT -> in2
+      )
+
+-- | convex hull of two intervals
+hull :: forall r. (Ord r, Num r) => Interval r -> Interval r -> Interval r
+hull x1 x2
+  | null x1 = x2
+  | null x2 = x1
+hull (Interval l1 u1) (Interval l2 u2) = interval (minLB l1 l2) (maxUB u1 u2)
+  where
+    maxUB :: (EndPoint r, Bool) -> (EndPoint r, Bool) -> (EndPoint r, Bool)
+    maxUB (x1,in1) (x2,in2) =
+      ( max x1 x2
+      , case x1 `compare` x2 of
+          EQ -> in1 || in2
+          LT -> in2
+          GT -> in1
+      )
+    minLB :: (EndPoint r, Bool) -> (EndPoint r, Bool) -> (EndPoint r, Bool)
+    minLB (x1,in1) (x2,in2) =
+      ( min x1 x2
+      , case x1 `compare` x2 of
+          EQ -> in1 || in2
+          LT -> in1
+          GT -> in2
+      )
+
+-- | Is the interval empty?
+null :: Ord r => Interval r -> Bool
+null (Interval (x1,in1) (x2,in2)) = 
+  case x1 `compare` x2 of
+    EQ -> assert (in1 && in2) False
+    LT -> False
+    GT -> True
+
+-- | Is the element in the interval?
+member :: Ord r => r -> Interval r -> Bool
+member x (Interval (x1,in1) (x2,in2)) = condLB && condUB
+  where
+    condLB = if in1 then x1 <= Finite x else x1 < Finite x
+    condUB = if in2 then Finite x <= x2 else Finite x < x2
+
+-- | Is the element not in the interval?
+notMember :: Ord r => r -> Interval r -> Bool
+notMember a i = not $ member a i
+
+-- | Is this a subset?
+-- @(i1 `isSubsetOf` i2)@ tells whether @i1@ is a subset of @i2@.
+isSubsetOf :: Ord r => Interval r -> Interval r -> Bool
+isSubsetOf (Interval lb1 ub1) (Interval lb2 ub2) = testLB lb1 lb2 && testUB ub1 ub2
+  where
+    testLB (x1,in1) (x2,in2) =
+      case x1 `compare` x2 of
+        GT -> True
+        LT -> False
+        EQ -> not in1 || in2 -- in1 => in2
+    testUB (x1,in1) (x2,in2) =
+      case x1 `compare` x2 of
+        LT -> True
+        GT -> False
+        EQ -> not in1 || in2 -- in1 => in2
+
+-- | Is this a proper subset? (ie. a subset but not equal).
+isProperSubsetOf :: Ord r => Interval r -> Interval r -> Bool
+isProperSubsetOf i1 i2 = i1 /= i2 && i1 `isSubsetOf` i2
+
+-- | Width of a interval. Width of an unbounded interval is @undefined@.
+width :: (Num r, Ord r) => Interval r -> r
+width x | null x = 0
+width (Interval (Finite l, _) (Finite u, _)) = u - l
+width _ = error "Data.Interval.width: unbounded interval"
+
+-- | pick up an element from the interval if the interval is not empty.
+pickup :: (Real r, Fractional r) => Interval r -> Maybe r
+pickup (Interval (NegInf,in1) (PosInf,in2))   = Just 0
+pickup (Interval (Finite x1, in1) (PosInf,_)) = Just $ if in1 then x1 else x1+1
+pickup (Interval (NegInf,_) (Finite x2, in2)) = Just $ if in2 then x2 else x2-1
+pickup (Interval (Finite x1, in1) (Finite x2, in2)) =
+  case x1 `compare` x2 of
+    GT -> Nothing
+    LT -> Just $ (x1+x2) / 2
+    EQ -> if in1 && in2 then Just x1 else Nothing
+pickup x = Nothing
+
+-- | For all @x@ in @X@, @y@ in @Y@. @x '<' y@
+(<!) :: Real r => Interval r -> Interval r -> Bool
+a <! b =
+  case ub_a `compare` lb_b of
+    LT -> True
+    GT -> False
+    EQ ->
+      case ub_a of
+        NegInf   -> True -- a is empty, so it holds vacuously
+        PosInf   -> True -- b is empty, so it holds vacuously
+        Finite x -> not (in1 && in2)
+  where
+    (ub_a, in1) = upperBound' a
+    (lb_b, in2) = lowerBound' b
+
+-- | For all @x@ in @X@, @y@ in @Y@. @x '<=' y@
+(<=!) :: Real r => Interval r -> Interval r -> Bool
+a <=! b = upperBound a <= lowerBound b
+
+-- | For all @x@ in @X@, @y@ in @Y@. @x '==' y@
+(==!) :: Real r => Interval r -> Interval r -> Bool
+a ==! b = a <=! b && a >=! b
+
+-- | For all @x@ in @X@, @y@ in @Y@. @x '>=' y@
+(>=!) :: Real r => Interval r -> Interval r -> Bool
+(>=!) = flip (<=!)
+
+-- | For all @x@ in @X@, @y@ in @Y@. @x '>' y@
+(>!) :: Real r => Interval r -> Interval r -> Bool
+(>!) = flip (<!)
+
+-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<' y@?
+(<?) :: Real r => Interval r -> Interval r -> Bool
+a <? b = lb_a < ub_b
+  where
+    lb_a = lowerBound a
+    ub_b = upperBound b
+
+-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '<=' y@?
+(<=?) :: Real r => Interval r -> Interval r -> Bool
+a <=? b　=
+  case lb_a `compare` ub_b of
+    LT -> True
+    GT -> False
+    EQ -> 
+      case lb_a of
+        NegInf -> False -- b is empty
+        PosInf -> True  -- a is empty
+        Finite x -> in1 && in2
+  where
+    (lb_a, in1) = lowerBound' a
+    (ub_b, in2) = upperBound' b
+
+-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '==' y@?
+(==?) :: Real r => Interval r -> Interval r -> Bool
+a ==? b = not $ null $ intersection a b
+
+-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>=' y@?
+(>=?) :: Real r => Interval r -> Interval r -> Bool
+(>=?) = flip (<=?)
+
+-- | Does there exist an @x@ in @X@, @y@ in @Y@ such that @x '>' y@?
+(>?) :: Real r => Interval r -> Interval r -> Bool
+(>?) = flip (<?)
+
+appPrec, appPrec1 :: Int
+appPrec = 10
+appPrec1 = appPrec + 1
+
+scaleInterval :: (Num r, Ord r) => r -> Interval r -> Interval r
+scaleInterval _ x | null x = empty
+scaleInterval c (Interval lb ub) =
+  case compare c 0 of
+    EQ -> singleton 0
+    LT -> interval (scaleInf' c ub) (scaleInf' c lb)
+    GT -> interval (scaleInf' c lb) (scaleInf' c ub)
+
+instance (Num r, Ord r) => Num (Interval r) where
+  a + b | null a || null b = empty
+  Interval lb1 ub1 + Interval lb2 ub2 = interval (f lb1 lb2) (g ub1 ub2)
+    where
+      f (Finite x1, in1) (Finite x2, in2) = (Finite (x1+x2), in1 && in2)
+      f (NegInf,_) _ = (NegInf, False)
+      f _ (NegInf,_) = (NegInf, False)
+      f _ _ = error "Interval.(+) should not happen"
+
+      g (Finite x1, in1) (Finite x2, in2) = (Finite (x1+x2), in1 && in2)
+      g (PosInf,_) _ = (PosInf, False)
+      g _ (PosInf,_) = (PosInf, False)
+      g _ _ = error "Interval.(+) should not happen"
+
+  negate a = scaleInterval (-1) a
+
+  fromInteger i = singleton (fromInteger i)
+
+  abs x = ((x `intersection` nonneg) `hull` (negate x `intersection` nonneg))
+    where
+      nonneg = Finite 0 <=..< PosInf
+
+  signum x = zero `hull` pos `hull` neg
+    where
+      zero = if member 0 x then singleton 0 else empty
+      pos = if null $ (Finite 0 <..< PosInf) `intersection` x
+            then empty
+            else singleton 1
+      neg = if null $ (NegInf <..< Finite 0) `intersection` x
+            then empty
+            else singleton (-1)
+
+  a * b | null a || null b = empty
+  Interval lb1 ub1 * Interval lb2 ub2 = interval lb3 ub3
+    where
+      xs = [ mulInf' x1 x2 | x1 <- [lb1, ub1], x2 <- [lb2, ub2] ]
+      ub3 = maximumBy cmpUB xs
+      lb3 = minimumBy cmpLB xs
+
+instance forall r. (Real r, Fractional r) => Fractional (Interval r) where
+  fromRational r = singleton (fromRational r)
+  recip a | null a = empty
+  recip i | 0 `member` i = whole -- should be error?
+  recip (Interval lb ub) = interval lb3 ub3
+    where
+      ub3 = maximumBy cmpUB xs
+      lb3 = minimumBy cmpLB xs
+      xs = [recipLB lb, recipUB ub]
+
+cmpUB, cmpLB :: Ord r => (EndPoint r, Bool) -> (EndPoint r, Bool) -> Ordering
+cmpUB (x1,in1) (x2,in2) = compare x1 x2 `mappend` compare in1 in2
+cmpLB (x1,in1) (x2,in2) = compare x1 x2 `mappend` flip compare in1 in2
+
+-- | Endpoints of intervals
+data EndPoint r
+  = NegInf    -- ^ negative infinity (-∞)
+  | Finite !r -- ^ finite value
+  | PosInf    -- ^ positive infinity (+∞)
+  deriving (Ord, Eq, Show, Read, Typeable)
+
+instance Bounded (EndPoint r) where
+  minBound = NegInf
+  maxBound = PosInf
+
+instance Functor EndPoint where
+  fmap f NegInf = NegInf
+  fmap f (Finite x) = Finite (f x)
+  fmap f PosInf = PosInf
+
+isFinite :: EndPoint r -> Bool
+isFinite (Finite _) = True
+isFinite _ = False
+
+negateEndPoint :: Num r => EndPoint r -> EndPoint r
+negateEndPoint NegInf = PosInf
+negateEndPoint PosInf = NegInf
+negateEndPoint (Finite x) = Finite (negate x)
+
+scaleInf' :: (Num r, Ord r) => r -> (EndPoint r, Bool) -> (EndPoint r, Bool)
+scaleInf' a (x1, in1) = (scaleEndPoint a x1, in1)
+
+scaleEndPoint :: (Num r, Ord r) => r -> EndPoint r -> EndPoint r
+scaleEndPoint a inf =
+  case a `compare` 0 of
+    EQ -> Finite 0
+    GT ->
+      case inf of
+        NegInf   -> NegInf
+        Finite b -> Finite (a*b)
+        PosInf   -> PosInf
+    LT ->
+      case inf of
+        NegInf   -> PosInf
+        Finite b -> Finite (a*b)
+        PosInf   -> NegInf
+
+mulInf' :: (Num r, Ord r) => (EndPoint r, Bool) -> (EndPoint r, Bool) -> (EndPoint r, Bool)
+mulInf' (Finite 0, True) _ = (Finite 0, True)
+mulInf' _ (Finite 0, True) = (Finite 0, True)
+mulInf' (x1,in1) (x2,in2) = (mulEndPoint x1 x2, in1 && in2)
+
+mulEndPoint :: (Num r, Ord r) => EndPoint r -> EndPoint r -> EndPoint r
+mulEndPoint (Finite x1) (Finite x2) = Finite (x1 * x2)
+mulEndPoint inf (Finite x2) =
+  case compare x2 0 of
+    EQ -> Finite 0
+    GT -> inf
+    LT -> negateEndPoint inf
+mulEndPoint (Finite x1) inf =
+  case compare x1 0 of
+    EQ -> Finite 0
+    GT -> inf
+    LT -> negateEndPoint inf
+mulEndPoint PosInf PosInf = PosInf
+mulEndPoint PosInf NegInf = NegInf
+mulEndPoint NegInf PosInf = NegInf
+mulEndPoint NegInf NegInf = PosInf
+
+recipLB :: (Fractional r, Ord r) => (EndPoint r, Bool) -> (EndPoint r, Bool)
+recipLB (Finite 0, _) = (PosInf, False)
+recipLB (x1, in1) = (recipEndPoint x1, in1)
+
+recipUB :: (Fractional r, Ord r) => (EndPoint r, Bool) -> (EndPoint r, Bool)
+recipUB (Finite 0, _) = (NegInf, False)
+recipUB (x1, in1) = (recipEndPoint x1, in1)
+
+recipEndPoint :: (Fractional r, Ord r) => EndPoint r -> EndPoint r
+recipEndPoint PosInf = Finite 0
+recipEndPoint NegInf = Finite 0
+recipEndPoint (Finite x) = Finite (1/x)
+
+-- | Combining two @Maybe@ values using given function.
+combineMaybe :: (a -> a -> a) -> Maybe a -> Maybe a -> Maybe a
+combineMaybe _ Nothing y = y
+combineMaybe _ x Nothing = x
+combineMaybe f (Just x) (Just y) = Just (f x y)
+
+-- | is the number integral?
+--
+-- @
+--    isInteger x = fromInteger (round x) == x
+-- @
+isInteger :: RealFrac a => a -> Bool
+isInteger x = fromInteger (round x) == x
diff --git a/test/TestInterval.hs b/test/TestInterval.hs
new file mode 100644
--- /dev/null
+++ b/test/TestInterval.hs
@@ -0,0 +1,480 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+import Control.Monad
+import Data.Maybe
+import Data.Ratio
+import Test.HUnit hiding (Test)
+import Test.QuickCheck
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.TH
+import Test.Framework.Providers.HUnit
+import Test.Framework.Providers.QuickCheck2
+
+import Data.Interval (Interval, EndPoint (..), (<=..<=), (<=..<), (<..<=), (<..<), (<!), (<=!), (==!), (>=!), (>!), (<?), (<=?), (==?), (>=?), (>?))
+import qualified Data.Interval as Interval
+
+{--------------------------------------------------------------------
+  empty
+--------------------------------------------------------------------}
+
+prop_empty_is_bottom =
+  forAll intervals $ \a ->
+    Interval.isSubsetOf Interval.empty a
+
+prop_null_empty =
+  forAll intervals $ \a ->
+    Interval.null a == (a == Interval.empty)
+
+case_null_empty =
+  Interval.null (Interval.empty :: Interval Rational) @?= True
+
+{--------------------------------------------------------------------
+  whole
+--------------------------------------------------------------------}
+
+prop_whole_is_top =
+  forAll intervals $ \a ->
+    Interval.isSubsetOf a Interval.whole
+
+case_nonnull_top =
+  Interval.null (Interval.whole :: Interval Rational) @?= False
+
+{--------------------------------------------------------------------
+  singleton
+--------------------------------------------------------------------}
+
+prop_singleton_member =
+  forAll arbitrary $ \r ->
+    Interval.member (r::Rational) (Interval.singleton r)
+
+prop_singleton_member_intersection =
+  forAll intervals $ \a ->
+  forAll arbitrary $ \r ->
+    let b = Interval.singleton r
+    in Interval.member (r::Rational) a
+       ==> Interval.intersection a b == b
+
+prop_singleton_nonnull =
+  forAll arbitrary $ \r1 ->
+    not $ Interval.null $ Interval.singleton (r1::Rational)
+
+prop_distinct_singleton_intersection =
+  forAll arbitrary $ \r1 ->
+  forAll arbitrary $ \r2 ->
+    (r1::Rational) /= r2 ==>
+      Interval.intersection (Interval.singleton r1) (Interval.singleton r2)
+      == Interval.empty
+
+{--------------------------------------------------------------------
+  Intersection
+--------------------------------------------------------------------}
+
+prop_intersection_comm =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    Interval.intersection a b == Interval.intersection b a
+
+prop_intersection_assoc =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+  forAll intervals $ \c ->
+    Interval.intersection a (Interval.intersection b c) ==
+    Interval.intersection (Interval.intersection a b) c
+
+prop_intersection_unitL =
+  forAll intervals $ \a ->
+    Interval.intersection Interval.whole a == a
+
+prop_intersection_unitR =
+  forAll intervals $ \a ->
+    Interval.intersection a Interval.whole == a
+
+prop_intersection_empty =
+  forAll intervals $ \a ->
+    Interval.intersection a Interval.empty == Interval.empty
+
+prop_intersection_isSubsetOf =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    Interval.isSubsetOf (Interval.intersection a b) a
+
+prop_intersection_isSubsetOf_equiv =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    (Interval.intersection a b == a)
+    == Interval.isSubsetOf a b
+
+{--------------------------------------------------------------------
+  Hull
+--------------------------------------------------------------------}
+
+prop_hull_comm =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    Interval.hull a b == Interval.hull b a
+
+prop_hull_assoc =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+  forAll intervals $ \c ->
+    Interval.hull a (Interval.hull b c) ==
+    Interval.hull (Interval.hull a b) c
+
+prop_hull_unitL =
+  forAll intervals $ \a ->
+    Interval.hull Interval.empty a == a
+
+prop_hull_unitR =
+  forAll intervals $ \a ->
+    Interval.hull a Interval.empty == a
+
+prop_hull_whole =
+  forAll intervals $ \a ->
+    Interval.hull a Interval.whole == Interval.whole
+
+prop_hull_isSubsetOf =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    Interval.isSubsetOf a (Interval.hull a b)
+
+prop_hull_isSubsetOf_equiv =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    (Interval.hull a b == b)
+    == Interval.isSubsetOf a b
+
+{--------------------------------------------------------------------
+  member
+--------------------------------------------------------------------}
+
+prop_member_isSubsetOf =
+  forAll arbitrary $ \r ->
+  forAll intervals $ \a ->
+    Interval.member r a == Interval.isSubsetOf (Interval.singleton r) a
+
+{--------------------------------------------------------------------
+  isSubsetOf
+--------------------------------------------------------------------}
+
+prop_isSubsetOf_refl =
+  forAll intervals $ \a ->
+    Interval.isSubsetOf a a
+
+prop_isSubsetOf_trans =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+  forAll intervals $ \c ->
+    Interval.isSubsetOf a b && Interval.isSubsetOf b c
+    ==> Interval.isSubsetOf a c
+
+-- prop_isSubsetOf_antisym =
+--   forAll intervals $ \a ->
+--   forAll intervals $ \b ->
+--     Interval.isSubsetOf a b && Interval.isSubsetOf b a
+--     ==> a == b
+
+{--------------------------------------------------------------------
+  pickup
+--------------------------------------------------------------------}
+
+prop_pickup_member_null =
+  forAll intervals $ \a ->
+    case Interval.pickup a of
+      Nothing -> Interval.null a
+      Just x -> Interval.member x a
+
+case_pickup_empty =
+  Interval.pickup (Interval.empty :: Interval Rational) @?= Nothing
+
+case_pickup_whole =
+  isJust (Interval.pickup (Interval.whole :: Interval Rational)) @?= True
+
+{--------------------------------------------------------------------
+  Comparison
+--------------------------------------------------------------------}
+
+case_lt_all_1 = (a <! b) @?= False
+  where
+    a, b :: Interval Rational
+    a = NegInf <..<= Finite 0
+    b = Finite 0 <=..< PosInf
+
+case_lt_all_2 = (a <! b) @?= True
+  where
+    a, b :: Interval Rational
+    a = NegInf <..< Finite 0
+    b = Finite 0 <=..< PosInf
+
+case_lt_all_3 = (a <! b) @?= True
+  where
+    a, b :: Interval Rational
+    a = NegInf <..<= Finite 0
+    b = Finite 0 <..< PosInf
+
+case_lt_all_4 = (a <! b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <=..< PosInf
+    b = Finite 1 <=..< PosInf
+
+case_lt_some_1 = (a <? b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <=..< PosInf
+    b = NegInf <..<= Finite 0
+
+case_lt_some_2 = (a <? b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <..< PosInf
+    b = NegInf <..<= Finite 0
+
+case_lt_some_3 = (a <? b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <=..< PosInf
+    b = NegInf <..< Finite 0
+
+case_lt_some_4 = (a <! b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <=..< PosInf
+    b = Finite 1 <=..< PosInf
+
+case_le_some_1 = (a <=? b) @?= True
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <=..< PosInf
+    b = NegInf <..<= Finite 0
+
+case_le_some_2 = (a <=? b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <..< PosInf
+    b = NegInf <..<= Finite 0
+
+case_le_some_3 = (a <=? b) @?= False
+  where
+    a, b :: Interval Rational
+    a = Finite 0 <=..< PosInf
+    b = NegInf <..< Finite 0
+
+prop_lt_all_not_refl =
+  forAll intervals $ \a -> not (Interval.null a) ==> not (a <! a)
+
+prop_le_some_refl =
+  forAll intervals $ \a -> not (Interval.null a) ==> a <=? a
+
+prop_lt_all_singleton =
+  forAll arbitrary $ \a ->
+  forAll arbitrary $ \b ->
+    (a::Rational) < b ==> Interval.singleton a <! Interval.singleton b
+
+prop_lt_all_singleton_2 =
+  forAll arbitrary $ \a ->
+    not $ Interval.singleton (a::Rational) <! Interval.singleton a
+
+prop_le_all_singleton =
+  forAll arbitrary $ \a ->
+  forAll arbitrary $ \b ->
+    (a::Rational) <= b ==> Interval.singleton a <=! Interval.singleton b
+
+prop_le_all_singleton_2 =
+  forAll arbitrary $ \a ->
+    Interval.singleton (a::Rational) <=! Interval.singleton a
+
+prop_lt_some_singleton =
+  forAll arbitrary $ \a ->
+  forAll arbitrary $ \b ->
+    (a::Rational) < b ==> Interval.singleton a <? Interval.singleton b
+
+prop_lt_some_singleton_2 =
+  forAll arbitrary $ \a ->
+    not $ Interval.singleton (a::Rational) <? Interval.singleton a
+
+prop_le_some_singleton =
+  forAll arbitrary $ \a ->
+  forAll arbitrary $ \b ->
+    (a::Rational) <= b ==> Interval.singleton a <=? Interval.singleton b
+
+prop_le_some_singleton_2 =
+  forAll arbitrary $ \a ->
+    Interval.singleton (a::Rational) <=? Interval.singleton a
+
+{--------------------------------------------------------------------
+  Num
+--------------------------------------------------------------------}
+
+prop_scale_empty =
+  forAll arbitrary $ \r ->
+    Interval.singleton (r::Rational) * Interval.empty == Interval.empty
+
+prop_add_comm =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    a + b == b + a
+
+prop_add_assoc =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+  forAll intervals $ \c ->
+    a + (b + c) == (a + b) + c
+
+prop_add_unitL =
+  forAll intervals $ \a ->
+    Interval.singleton 0 + a == a
+
+prop_add_unitR =
+  forAll intervals $ \a ->
+    a + Interval.singleton 0 == a
+
+prop_add_member =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    and [ (x+y) `Interval.member` (a+b)
+        | x <- maybeToList $ Interval.pickup a
+        , y <- maybeToList $ Interval.pickup b
+        ]
+
+prop_mult_comm =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    a * b == b * a
+
+prop_mult_assoc =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+  forAll intervals $ \c ->
+    a * (b * c) == (a * b) * c
+
+prop_mult_unitL =
+  forAll intervals $ \a ->
+    Interval.singleton 1 * a == a
+
+prop_mult_unitR =
+  forAll intervals $ \a ->
+    a * Interval.singleton 1 == a
+
+prop_mult_dist =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+  forAll intervals $ \c ->
+    (a * (b + c)) `Interval.isSubsetOf` (a * b + a * c)
+
+prop_mult_empty =
+  forAll intervals $ \a ->
+    Interval.empty * a == Interval.empty
+
+prop_mult_zero = 
+  forAll intervals $ \a ->
+    not (Interval.null a) ==> Interval.singleton 0 * a ==  Interval.singleton 0
+
+prop_mult_member =
+  forAll intervals $ \a ->
+  forAll intervals $ \b ->
+    and [ (x*y) `Interval.member` (a*b)
+        | x <- maybeToList $ Interval.pickup a
+        , y <- maybeToList $ Interval.pickup b
+        ]
+
+case_mult_test1 = ival1 * ival2 @?= ival3
+  where
+    ival1 = Finite 1 <=..<= Finite 2
+    ival2 = Finite 1 <=..<= Finite 2
+    ival3 = Finite 1 <=..<= Finite 4
+
+case_mult_test2 = ival1 * ival2 @?= ival3
+  where
+    ival1 = Finite 1 <=..<= Finite 2
+    ival2 = Finite 1 <..< Finite 2
+    ival3 = Finite 1 <..< Finite 4
+
+case_mult_test3 = ival1 * ival2 @?= ival3
+  where
+    ival1 = Finite 1 <..< Finite 2
+    ival2 = Finite 1 <..< Finite 2
+    ival3 = Finite 1 <..< Finite 4
+
+case_mult_test4 = ival1 * ival2 @?= ival3
+  where
+    ival1 = Finite 2 <..< PosInf
+    ival2 = Finite 3 <..< PosInf
+    ival3 = Finite 6 <..< PosInf
+
+case_mult_test5 = ival1 * ival2 @?= ival3
+  where
+    ival1 = NegInf <..< Finite (-3)
+    ival2 = NegInf <..< Finite (-2)
+    ival3 = Finite 6 <..< PosInf
+
+case_mult_test6 = ival1 * ival2 @?= ival3
+  where
+    ival1 = Finite 2 <..< PosInf
+    ival2 = NegInf <..< Finite (-2)
+    ival3 = NegInf <..< Finite (-4)
+
+{--------------------------------------------------------------------
+  Fractional
+--------------------------------------------------------------------}
+
+prop_recip_singleton =
+  forAll arbitrary $ \r ->
+    let n = fromIntegral (numerator r)
+        d = fromIntegral (denominator r)
+    in Interval.singleton n / Interval.singleton d == Interval.singleton (r::Rational)
+
+case_recip_pos =
+  recip pos @?= pos
+
+case_recip_neg =
+  recip neg @?= neg
+
+case_recip_test1 = recip i1 @?= i2
+  where
+    i1, i2 :: Interval Rational
+    i1 = Finite 2 <=..< PosInf
+    i2 = Finite 0 <..<= Finite (1/2)
+
+{--------------------------------------------------------------------
+  Read
+--------------------------------------------------------------------}
+
+prop_show_read_invariance =
+  forAll intervals $ \i -> do
+    i == read (show i)
+
+{--------------------------------------------------------------------
+  Generators
+--------------------------------------------------------------------}
+
+instance Arbitrary r => Arbitrary (EndPoint r) where
+  arbitrary = 
+    oneof
+    [ return NegInf
+    , return PosInf
+    , liftM Finite arbitrary
+    ]
+
+intervals :: Gen (Interval Rational)
+intervals = do
+  lb <- arbitrary
+  ub <- arbitrary
+  return $ Interval.interval lb ub
+
+pos :: Interval Rational
+pos = Finite 0 <..< PosInf
+
+neg :: Interval Rational
+neg = NegInf <..< Finite 0
+
+nonpos :: Interval Rational
+nonpos = NegInf <..<= Finite 0
+
+nonneg :: Interval Rational
+nonneg = Finite 0 <=..< PosInf
+
+------------------------------------------------------------------------
+-- Test harness
+
+main :: IO ()
+main = $(defaultMainGenerator)
