packages feed

point-octree-0.5.5.3: tests/PropTests/OctreeTests/Internal.hs

{- |
   Module     : PropTests.OctreeTests.Internal
   Copyright  : Copyright (c) 2016 Michal J. Gajda
   License    : BSD3
 
   Maintainer : Michael Litchard
   Stability  : experimental
   Portability: not portable
                            
   This module provides tests for internal helper functions:
                                                 
-}

{-# LANGUAGE ScopedTypeVariables #-}
module PropTests.OctreeTests.Internal
  ( prop_depth
  , prop_cmp1
  , prop_cmp2
  , prop_stepDescription
  , prop_octantDistanceNoGreaterThanInterpointDistance0
  , prop_octantDistanceNoGreaterThanInterpointDistance
  , prop_octantDistanceNoGreaterThanInterpointDistanceZero
  , prop_octantDistanceNoGreaterThanCentroidDistance
  , prop_pickClosest
  ) where

import Prelude hiding(lookup)
import Data.List(sort, sortBy)

import Control.Arrow(second)

import Data.Octree.Internal
import Data.Octree() -- test that interface module is not broken

import PropTests.Common

-- for easier testing
origin :: Vector3
origin = 0

prop_depth [] = True -- fudging , what is true fix?
prop_depth a = 
  (depth oct <= ((+1)        . ceiling $ expectedDepth)) &&
  (depth oct >= ((\a -> a-1) . floor   $ expectedDepth))
  where
    expectedDepth =
      (logBase 8 :: Double -> Double) . fromIntegral . length $ a
    oct :: Octree Int = fromList a

prop_cmp1 a b = cmp a b == joinStep (dx >= 0, dy >= 0, dz >= 0)
  where Vector3 dx dy dz = a - b

prop_cmp2 a = cmp a origin == joinStep (dx >= 0, dy >= 0, dz >= 0)
  where Vector3 dx dy dz = a

prop_stepDescription a b = 
  splitStep (cmp a b) == (v3x a >= v3x b, v3y a >= v3y b, v3z a >= v3z b)

prop_octantDistanceNoGreaterThanInterpointDistance0 ptA ptB = triangleInequality
  where 
    triangleInequality = octantDistance' aptA (cmp ptB origin) <= dist aptA ptB
    aptA                   = abs ptA

prop_octantDistanceNoGreaterThanInterpointDistance ptA ptB vp = 
  triangleInequality
  where 
    triangleInequality = octantDistance (ptA - vp) (cmp ptB vp) <= dist ptA ptB
    sameOctant         = cmp ptA vp == cmp ptB vp

prop_octantDistanceNoGreaterThanInterpointDistanceZero ptA ptB = 
  triangleInequality
  where 
    triangleInequality = octantDistance ptA (cmp ptB origin) <= dist ptA ptB
    sameOctant         = cmp ptA origin == cmp ptB origin

prop_octantDistanceNoGreaterThanInterpointDistanceZero0 ptA ptB = 
  triangleInequality
  where 
    triangleInequality = octantDistance aptA (cmp ptB origin) <= dist aptA ptB
    sameOctant         = cmp aptA origin                      == cmp ptB origin
    aptA               = abs ptA

prop_octantDistanceNoGreaterThanCentroidDistance pt vp = all testFun allOctants
  where testFun odir = octantDistance (pt - vp) odir <= dist pt vp

prop_splitByPrime splitPt pt = 
  (unLeaf . octreeStep ot . cmp pt $ splitPt) == [arg]
  where 
    ot   = splitBy' Leaf splitPt [arg] 
    arg  = (pt, dist pt splitPt)


prop_pickClosest :: (Eq a) => [(Vector3, a)] -> Vector3 -> Bool
prop_pickClosest l pt = pickClosest pt l == naiveNearest pt l

-- | These are tests for exposed functions:

prop_lookup l = all isIn l
  where 
    ot = fromList l
    isIn x = lookup ot (fst x) == Just x

prop_fromToList         l = sort l == (sort . toList . fromList $ l)
prop_insertionPreserved l = 
  sort l == (sort . toList . foldr insert (Leaf []) $ l)
prop_nearest            l pt = nearest (fromList l) pt == naiveNearest pt l
prop_naiveWithinRange   r l pt = 
  naiveWithinRange r pt l == testPoints
  where 
    testPoints = 
      sort . map fst . (\o -> withinRange o r pt) . fromList . tuplify pt $ l

tuplify pt = map (\a -> (a, dist pt a))

compareDistance pt (a,_) (b,_) = compare (dist pt a) (dist pt b)

naiveNearest pt [] = Nothing
naiveNearest pt l  = Just $ head byDist
  where byDist = sortBy (compareDistance pt) l

naiveWithinRange r pt = sort . filter withinRange
  where withinRange p = dist pt p <= r

-- unfortunately there is no Arbitrary for (a -> b)
-- since generic properties are quite common, I wonder how to force Quickcheck to default something reasonable?
prop_fmap1,prop_fmap2 :: [(Vector3, Int)] -> Bool
prop_fmap1 = genericPropertyFmap (+1)
prop_fmap2 = genericPropertyFmap (*2)
prop_fmap3 = genericPropertyFmap (show :: Int -> String)

genericPropertyFmap f l = 
  (sort . map (Control.Arrow.second f) $ l) == testFmap 
  where testFmap = sort . toList . fmap f . fromList $ l

prop_depth_empty = depth (Leaf []) == 0

prop_depth_upper_bound l = 
  depth ot <= max 0 (ceiling . logBase 2 . realToFrac $ size) -- worst splitting ratio possible when we take midpoint (and inputs are colinear)
  where 
    ot   = fromList l
    size = length l

prop_size l = size (fromList l) == length l