{-# LANGUAGE FlexibleContexts, ParallelListComp #-}
{-# OPTIONS_GHC -fno-warn-unused-binds #-}
module Validate (Similar(..), validate, validate') where
import Data.Int
import Data.Word
import Data.Array.IArray
import Foreign.C.Types
import Foreign.Storable
import Control.Exception (assert)
import Unsafe.Coerce
class Similar a where
sim :: a -> a -> Bool
instance Similar Int where sim = (==)
instance Similar Int8 where sim = (==)
instance Similar Int16 where sim = (==)
instance Similar Int32 where sim = (==)
instance Similar Int64 where sim = (==)
instance Similar Word where sim = (==)
instance Similar Word8 where sim = (==)
instance Similar Word16 where sim = (==)
instance Similar Word32 where sim = (==)
instance Similar Word64 where sim = (==)
instance Similar CShort where sim = (==)
instance Similar CUShort where sim = (==)
instance Similar CInt where sim = (==)
instance Similar CUInt where sim = (==)
instance Similar CLong where sim = (==)
instance Similar CULong where sim = (==)
instance Similar CLLong where sim = (==)
instance Similar CULLong where sim = (==)
instance Similar Bool where sim = (==)
instance Similar Char where sim = (==)
instance Similar CChar where sim = (==)
instance Similar CSChar where sim = (==)
instance Similar CUChar where sim = (==)
instance Similar Float where sim = absoluteOrRelative
instance Similar CFloat where sim = absoluteOrRelative
instance Similar Double where sim = absoluteOrRelative
instance Similar CDouble where sim = absoluteOrRelative
instance (Similar a, Similar b) => Similar (a,b) where
(x,y) `sim` (u,v) = x `sim` u && y `sim` v
--
-- http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
--
absoluteOrRelative :: (Fractional a, Ord a) => a -> a -> Bool
absoluteOrRelative u v
| abs (u-v) < epsilonAbs = True
| abs u > abs v = abs ((u-v) / u) < epsilonRel
| otherwise = abs ((v-u) / v) < epsilonRel
where
epsilonRel = 0.001
epsilonAbs = 0.00001
-- Comparisons using lexicographically ordered floating-point numbers
-- reinterpreted as twos-complement integers.
--
lexicographic32 :: (Num a, Storable a) => Int -> a -> a -> Bool
lexicographic32 maxUlps a b
= assert (sizeOf a == 4 && maxUlps > 0 && maxUlps < 4 * 1024 * 1024)
$ intDiff < fromIntegral maxUlps
where
intDiff = abs (toInt a - toInt b)
toInt x | x' < 0 = 0x80000000 - x'
| otherwise = x'
where x' = unsafeCoerce x :: Int32
lexicographic64 :: (Num a, Storable a) => Int -> a -> a -> Bool
lexicographic64 maxUlps a b
= assert (sizeOf a == 8 && maxUlps > 0 && maxUlps < 8 * 1024 * 1024)
$ intDiff < fromIntegral maxUlps
where
intDiff = abs (toInt a - toInt b)
toInt x | x' < 0 = 0x8000000000000000 - x'
| otherwise = x'
where x' = unsafeCoerce x :: Int64
-- Compare two vectors element-wise for equality, for a given measure of
-- similarity. The index and values are returned for pairs that fail.
--
validate
:: (IArray array e, Ix ix, Similar e)
=> array ix e
-> array ix e
-> [(ix,(e,e))]
validate ref arr = validate' (assocs ref) (elems arr)
validate' :: (Ix ix, Similar e) => [(ix,e)] -> [e] -> [(ix,(e,e))]
validate' ref arr =
filter (not . uncurry sim . snd) [ (i,(x,y)) | (i,x) <- ref | y <- arr ]