diff --git a/Data/Trees/KdTree.hs b/Data/Trees/KdTree.hs
new file mode 100644
--- /dev/null
+++ b/Data/Trees/KdTree.hs
@@ -0,0 +1,125 @@
+module Data.Trees.KdTree where
+
+-- Haskell implementation of http://en.wikipedia.org/wiki/K-d_tree
+-- by Issac Trotts
+
+import Data.Maybe
+
+import qualified Data.Foldable as F
+import qualified Data.List as L
+import Test.QuickCheck
+
+class Point p where
+      -- |dimension returns the number of coordinates of a point.
+      dimension :: p -> Int
+
+      -- |coord gets the k'th coordinate, starting from 0.
+      coord :: Int -> p -> Double
+
+      -- |dist2 returns the squared distance between two points.
+      dist2 :: p -> p -> Double
+      dist2 a b = sum . map diff2 $ [0..dimension a - 1]
+	where diff2 i = (coord i a - coord i b)^2
+
+-- |compareDistance p a b  compares the distances of a and b to p.
+compareDistance :: (Point p) => p -> p -> p -> Ordering
+compareDistance p a b = dist2 p a `compare` dist2 p b
+
+data Point3d = Point3d { p3x :: Double, p3y :: Double, p3z :: Double }
+    deriving (Eq, Ord, Show)
+
+instance Point Point3d where
+    dimension _ = 3
+
+    coord 0 p = p3x p
+    coord 1 p = p3y p
+    coord 2 p = p3z p
+
+
+data KdTree point = KdNode { kdLeft :: KdTree point,
+			     kdPoint :: point,
+                             kdRight :: KdTree point,
+			     kdAxis :: Int }
+                  | KdEmpty
+     deriving (Eq, Ord, Show)
+
+instance Functor KdTree where
+    fmap _ KdEmpty = KdEmpty
+    fmap f (KdNode l x r axis) = KdNode (fmap f l) (f x) (fmap f r) axis
+
+instance F.Foldable KdTree where
+    foldr f init KdEmpty = init
+    foldr f init (KdNode l x r _) = F.foldr f init3 l
+	where 	init3 = f x init2
+		init2 = F.foldr f init r
+
+fromList :: Point p => [p] -> KdTree p
+fromList points = fromListWithDepth points 0
+
+-- Select axis based on depth so that axis cycles through all valid values
+fromListWithDepth :: Point p => [p] -> Int -> KdTree p
+fromListWithDepth [] _ = KdEmpty
+fromListWithDepth points depth = node
+    where   axis = axisFromDepth (head points) depth
+
+	    -- Sort point list and choose median as pivot element
+	    sortedPoints =
+		L.sortBy (\a b -> coord axis a `compare` coord axis b) points
+	    medianIndex = length sortedPoints `div` 2
+	
+	    -- Create node and construct subtrees
+	    node = KdNode { kdLeft = fromListWithDepth (take medianIndex sortedPoints) (depth+1),
+			    kdPoint = sortedPoints !! medianIndex,
+			    kdRight = fromListWithDepth (drop (medianIndex+1) sortedPoints) (depth+1),
+			    kdAxis = axis }
+
+axisFromDepth :: Point p => p -> Int -> Int
+axisFromDepth p depth = depth `mod` k
+    where k = dimension p
+
+toList :: KdTree p -> [p]
+toList t = F.foldr (:) [] t
+
+subtrees :: KdTree p -> [KdTree p]
+subtrees KdEmpty = [KdEmpty]
+subtrees t@(KdNode l x r axis) = subtrees l ++ [t] ++ subtrees r
+
+nearestNeighbor :: Point p => KdTree p -> p -> Maybe p
+nearestNeighbor KdEmpty probe = Nothing
+nearestNeighbor (KdNode KdEmpty p KdEmpty _) probe = Just p
+nearestNeighbor (KdNode l p r axis) probe =
+    if xProbe <= xp then doStuff l r else doStuff r l
+    where xProbe = coord axis probe
+	  xp = coord axis p
+          doStuff tree1 tree2 =
+		let candidates1 = case nearestNeighbor tree1 probe of
+				    Nothing -> [p]
+				    Just best1 -> [best1, p]
+		    sphereIntersectsPlane = (xProbe - xp)^2 <= dist2 probe p
+		    candidates2 = if sphereIntersectsPlane
+				    then candidates1 ++ maybeToList (nearestNeighbor tree2 probe)
+				    else candidates1 in
+		Just . L.minimumBy (compareDistance probe) $ candidates2
+
+-- |invariant tells whether the KD tree property holds for a given tree and
+-- all its subtrees.
+-- Specifically, it tests that all points in the left subtree lie to the left
+-- of the plane, p is on the plane, and all points in the right subtree lie to
+-- the right.
+invariant :: Point p => KdTree p -> Bool
+invariant KdEmpty = True
+invariant (KdNode l p r axis) = leftIsGood && rightIsGood
+    where x = coord axis p
+	  leftIsGood = all ((<= x) . coord axis) (toList l)
+	  rightIsGood = all ((>= x) . coord axis) (toList r)
+
+invariant' :: Point p => KdTree p -> Bool
+invariant' = all invariant . subtrees
+
+instance Arbitrary Point3d where
+    arbitrary = do
+	x <- arbitrary
+	y <- arbitrary
+	z <- arbitrary
+	return (Point3d x y z)
+
diff --git a/KdTree.cabal b/KdTree.cabal
new file mode 100644
--- /dev/null
+++ b/KdTree.cabal
@@ -0,0 +1,35 @@
+Name:                KdTree
+
+-- The package version. See the Haskell package versioning policy
+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
+-- standards guiding when and how versions should be incremented.
+Version:             0.1
+Synopsis:            KdTree, for efficient search in K-dimensional point clouds.
+Description:         
+    This is a simple library for k-d trees in Haskell. It enables efficient
+    searching through collections of points in O(log N) time on average,
+    using the nearestNeighbor function.
+
+Homepage:            https://github.com/ijt/kdtree
+License:             BSD3
+License-file:        LICENSE
+Author:              Issac Trotts
+Maintainer:          issac.trotts@gmail.com
+Copyright:           Copyright 2011, Issac Trotts
+Category:            Graphics
+Build-type:          Simple
+Cabal-version:       >=1.6
+Extra-source-files:  README
+
+source-repository head
+  type:     git
+  location: git@github.com:ijt/kdtree.git
+
+Library
+  Exposed-modules: Data.Trees.KdTree
+  Build-depends: base < 5
+  
+Executable KdTreeTest
+  Main-is: KdTreeTest.hs
+  Build-depends: QuickCheck
+
diff --git a/KdTreeTest.hs b/KdTreeTest.hs
new file mode 100644
--- /dev/null
+++ b/KdTreeTest.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Main where
+
+import Data.Maybe
+import qualified Data.List as L
+
+import Test.QuickCheck
+import Test.QuickCheck.All
+
+import qualified Data.Trees.KdTree as Kd
+
+prop_invariant :: [Kd.Point3d] -> Bool
+prop_invariant points = Kd.invariant' . Kd.fromList $ points
+
+prop_samePoints :: [Kd.Point3d] -> Bool
+prop_samePoints points = L.sort points == (L.sort . Kd.toList . Kd.fromList $ points)
+
+prop_nearestNeighbor :: [Kd.Point3d] -> Kd.Point3d -> Bool
+prop_nearestNeighbor points probe =
+    Kd.nearestNeighbor tree probe == bruteNearestNeighbor points probe
+    where tree = Kd.fromList points
+
+prop_pointsAreClosestToThemselves :: [Kd.Point3d] -> Bool
+prop_pointsAreClosestToThemselves points =
+    map Just points == map (Kd.nearestNeighbor tree) points
+    where tree = Kd.fromList points
+
+bruteNearestNeighbor :: [Kd.Point3d] -> Kd.Point3d -> Maybe Kd.Point3d
+bruteNearestNeighbor [] _ = Nothing
+bruteNearestNeighbor points probe =
+    Just . head . L.sortBy (Kd.compareDistance probe) $ points
+
+main = $quickCheckAll
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2011, Issac Trotts
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Issac Trotts nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+OWNER OR CONTRIBUTORS 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 b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,21 @@
+This is a simple library for k-d trees in Haskell, based on the algorithms
+at http://en.wikipedia.org/wiki/K-d_tree.
+
+It enables efficient searching through collections of points in O(log N) time
+for randomly distributed points, using the nearestNeighbor function.
+
+Here is an example of an interactive session using this module:
+
+[ ~/haskell/KdTree ] ghci
+GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
+...
+Prelude> :m Data.Trees.KdTree 
+Prelude Data.Trees.KdTree> import Test.QuickCheck
+Prelude Data.Trees.KdTree Test.QuickCheck> points <- sample' arbitrary :: IO [Point3d]
+...
+Prelude Data.Trees.KdTree Test.QuickCheck> let tree = fromList points
+Prelude Data.Trees.KdTree Test.QuickCheck> nearestNeighbor tree (head points)
+Just (Point3d {p3x = 0.0, p3y = 0.0, p3z = 0.0})
+Prelude Data.Trees.KdTree Test.QuickCheck> head points
+Point3d {p3x = 0.0, p3y = 0.0, p3z = 0.0}
+
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
