+-- The "tetrahedron" function pattern matches on the integers zero
+-- through twenty-three, but doesn't handle the "otherwise" case, for
+-- performance reasons.
+{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
+
module Cube (
Cube(..),
cube_properties,
find_containing_tetrahedron,
tetrahedra,
- tetrahedron
- )
+ tetrahedron )
where
-import Data.Maybe (fromJust)
+import Data.Maybe ( fromJust )
import qualified Data.Vector as V (
Vector,
findIndex,
minimum,
singleton,
snoc,
- unsafeIndex
- )
-import Prelude hiding (LT)
-import Test.Framework (Test, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.QuickCheck (Arbitrary(..), Gen, Positive(..), choose)
-
-import Cardinal
-import Comparisons ((~=), (~~=))
-import qualified Face (Face(..), center)
-import FunctionValues (FunctionValues, eval, rotate)
-import Misc (all_equal, disjoint)
-import Point (Point(..), dot)
-import Tetrahedron (Tetrahedron(..), barycenter, c, volume)
+ unsafeIndex)
+import Prelude(
+ Bool,
+ Double,
+ Int,
+ Eq( (==) ),
+ Fractional( (/) ),
+ Maybe,
+ Num( (+), (-), (*) ),
+ Ord( (>=), (<=) ),
+ Show( show ),
+ ($),
+ (.),
+ (&&),
+ (++),
+ abs,
+ all,
+ and,
+ fromIntegral,
+ head,
+ map,
+ otherwise,
+ return,
+ tail )
+import Test.Tasty ( TestTree, testGroup )
+import Test.Tasty.QuickCheck (
+ Arbitrary( arbitrary ),
+ Gen,
+ Positive( Positive ),
+ choose,
+ testProperty )
+import Cardinal (
+ Cardinal(F, B, L, R, D, T, FL, FR, FD, FT,
+ BL, BR, BD, BT, LD, LT, RD, RT, I),
+ ccwx,
+ ccwy,
+ ccwz,
+ cwx,
+ cwy,
+ cwz )
+import Comparisons ( (~=), (~~=) )
+import qualified Face ( Face(..), center )
+import FunctionValues ( FunctionValues, eval, rotate )
+import Misc ( all_equal, disjoint )
+import Point ( Point( Point ), dot )
+import Tetrahedron (
+ Tetrahedron(Tetrahedron, function_values, v0, v1, v2, v3),
+ barycenter,
+ c,
+ volume )
data Cube = Cube { i :: !Int,
j :: !Int,
-- these numbers don't overflow 64 bits. This number is not
-- magic in any other sense than that it does not cause test
-- failures, while 2^23 does.
- coordmax = 4194304 -- 2^22
+ coordmax = 4194304 :: Int -- 2^22
coordmin = -coordmax
-- | The left-side boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
xmin :: Cube -> Double
-xmin cube = (i' - 1/2)
+xmin cube = (i' - 1 / 2)
where
i' = fromIntegral (i cube) :: Double
-- | The right-side boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
xmax :: Cube -> Double
-xmax cube = (i' + 1/2)
+xmax cube = (i' + 1 / 2)
where
i' = fromIntegral (i cube) :: Double
-- | The front boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
ymin :: Cube -> Double
-ymin cube = (j' - 1/2)
+ymin cube = (j' - 1 / 2)
where
j' = fromIntegral (j cube) :: Double
-- | The back boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
ymax :: Cube -> Double
-ymax cube = (j' + 1/2)
+ymax cube = (j' + 1 / 2)
where
j' = fromIntegral (j cube) :: Double
-- | The bottom boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
zmin :: Cube -> Double
-zmin cube = (k' - 1/2)
+zmin cube = (k' - 1 / 2)
where
k' = fromIntegral (k cube) :: Double
-- | The top boundary of the cube. See Sorokina and Zeilfelder,
-- p. 76.
zmax :: Cube -> Double
-zmax cube = (k' + 1/2)
+zmax cube = (k' + 1 / 2)
where
k' = fromIntegral (k cube) :: Double
top_face :: Cube -> Face.Face
top_face cube = Face.Face v0' v1' v2' v3'
where
- delta = 1/2
+ delta = (1 / 2) :: Double
cc = center cube
v0' = cc + ( Point delta (-delta) delta )
v1' = cc + ( Point delta delta delta )
back_face :: Cube -> Face.Face
back_face cube = Face.Face v0' v1' v2' v3'
where
- delta = 1/2
+ delta = (1 / 2) :: Double
cc = center cube
v0' = cc + ( Point delta (-delta) (-delta) )
v1' = cc + ( Point delta delta (-delta) )
down_face :: Cube -> Face.Face
down_face cube = Face.Face v0' v1' v2' v3'
where
- delta = 1/2
+ delta = (1 / 2) :: Double
cc = center cube
v0' = cc + ( Point (-delta) (-delta) (-delta) )
v1' = cc + ( Point (-delta) delta (-delta) )
front_face :: Cube -> Face.Face
front_face cube = Face.Face v0' v1' v2' v3'
where
- delta = 1/2
+ delta = (1 / 2) :: Double
cc = center cube
v0' = cc + ( Point (-delta) (-delta) delta )
v1' = cc + ( Point (-delta) delta delta )
left_face :: Cube -> Face.Face
left_face cube = Face.Face v0' v1' v2' v3'
where
- delta = 1/2
+ delta = (1 / 2) :: Double
cc = center cube
v0' = cc + ( Point delta (-delta) delta )
v1' = cc + ( Point (-delta) (-delta) delta )
right_face :: Cube -> Face.Face
right_face cube = Face.Face v0' v1' v2' v3'
where
- delta = 1/2
+ delta = (1 / 2) :: Double
cc = center cube
v0' = cc + ( Point (-delta) delta delta)
v1' = cc + ( Point delta delta delta )
left_half = in_left_half cube p
candidates :: V.Vector Tetrahedron
- candidates =
- if front_half then
-
+ candidates
+ | front_half =
if left_half then
if top_half then
front_left_top_tetrahedra cube
else
front_right_down_tetrahedra cube
- else -- bottom half
-
+ | otherwise = -- back half
if left_half then
if top_half then
back_left_top_tetrahedra cube
--- Tests
-
--- Quickcheck tests.
+-- * Tests
prop_opposite_octant_tetrahedra_disjoint1 :: Cube -> Bool
prop_opposite_octant_tetrahedra_disjoint1 cube =
-- we'd expect the volume of each one to be 1/24.
prop_all_volumes_exact :: Cube -> Bool
prop_all_volumes_exact cube =
- and [volume t ~~= 1/24 | t <- tetrahedra cube]
+ and [volume t ~~= 1 / 24 | t <- tetrahedra cube]
-- | All tetrahedron should have their v0 located at the center of the
-- cube.
prop_c3000_identity :: Cube -> Bool
prop_c3000_identity cube =
c t0 3 0 0 0 ~= c t0 2 1 0 0 + c t6 2 1 0 0
- - ((c t0 2 0 1 0 + c t0 2 0 0 1)/ 2)
+ - ((c t0 2 0 1 0 + c t0 2 0 0 1) / 2)
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
prop_c2010_identity :: Cube -> Bool
prop_c2010_identity cube =
c t0 2 0 1 0 ~= c t0 1 1 1 0 + c t6 1 1 0 1
- - ((c t0 1 0 2 0 + c t0 1 0 1 1)/ 2)
+ - ((c t0 1 0 2 0 + c t0 1 0 1 1) / 2)
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
prop_c2001_identity :: Cube -> Bool
prop_c2001_identity cube =
c t0 2 0 0 1 ~= c t0 1 1 0 1 + c t6 1 1 1 0
- - ((c t0 1 0 0 2 + c t0 1 0 1 1)/ 2)
+ - ((c t0 1 0 0 2 + c t0 1 0 1 1) / 2)
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
prop_c1020_identity :: Cube -> Bool
prop_c1020_identity cube =
c t0 1 0 2 0 ~= c t0 0 1 2 0 + c t6 0 1 0 2
- - ((c t0 0 0 3 0 + c t0 0 0 2 1)/ 2)
+ - ((c t0 0 0 3 0 + c t0 0 0 2 1) / 2)
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
prop_c1002_identity :: Cube -> Bool
prop_c1002_identity cube =
c t0 1 0 0 2 ~= c t0 0 1 0 2 + c t6 0 1 2 0
- - ((c t0 0 0 0 3 + c t0 0 0 1 2)/ 2)
+ - ((c t0 0 0 0 3 + c t0 0 0 1 2) / 2)
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
prop_c1011_identity :: Cube -> Bool
prop_c1011_identity cube =
c t0 1 0 1 1 ~= c t0 0 1 1 1 + c t6 0 1 1 1 -
- ((c t0 0 0 1 2 + c t0 0 0 2 1)/ 2)
+ ((c t0 0 0 1 2 + c t0 0 0 2 1) / 2)
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
-- This test checks the rotation works as expected.
prop_c_tilde_2100_rotation_correct :: Cube -> Bool
prop_c_tilde_2100_rotation_correct cube =
- expr1 == expr2
+ expr1 ~= expr2
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
-- What gets computed for c2100 of t6.
expr1 = eval (function_values t6) $
- (3/8)*I +
- (1/12)*(T + R + L + D) +
- (1/64)*(FT + FR + FL + FD) +
- (7/48)*F +
- (1/48)*B +
- (1/96)*(RT + LD + LT + RD) +
- (1/192)*(BT + BR + BL + BD)
+ (3 / 8)*I +
+ (1 / 12)*(T + R + L + D) +
+ (1 / 64)*(FT + FR + FL + FD) +
+ (7 / 48)*F +
+ (1 / 48)*B +
+ (1 / 96)*(RT + LD + LT + RD) +
+ (1 / 192)*(BT + BR + BL + BD)
-- What should be computed for c2100 of t6.
expr2 = eval (function_values t0) $
- (3/8)*I +
- (1/12)*(F + R + L + B) +
- (1/64)*(FT + RT + LT + BT) +
- (7/48)*T +
- (1/48)*D +
- (1/96)*(FR + FL + BR + BL) +
- (1/192)*(FD + RD + LD + BD)
+ (3 / 8)*I +
+ (1 / 12)*(F + R + L + B) +
+ (1 / 64)*(FT + RT + LT + BT) +
+ (7 / 48)*T +
+ (1 / 48)*D +
+ (1 / 96)*(FR + FL + BR + BL) +
+ (1 / 192)*(FD + RD + LD + BD)
-- | We know what (c t6 2 1 0 0) should be from Sorokina and
-- even meaningful!
prop_c_tilde_2100_correct :: Cube -> Bool
prop_c_tilde_2100_correct cube =
- c t6 2 1 0 0 == expected
+ c t6 2 1 0 0 ~= expected
where
t0 = tetrahedron cube 0
t6 = tetrahedron cube 6
fvs = function_values t0
expected = eval fvs $
- (3/8)*I +
- (1/12)*(F + R + L + B) +
- (1/64)*(FT + RT + LT + BT) +
- (7/48)*T +
- (1/48)*D +
- (1/96)*(FR + FL + BR + BL) +
- (1/192)*(FD + RD + LD + BD)
+ (3 / 8)*I +
+ (1 / 12)*(F + R + L + B) +
+ (1 / 64)*(FT + RT + LT + BT) +
+ (7 / 48)*T +
+ (1 / 48)*D +
+ (1 / 96)*(FR + FL + BR + BL) +
+ (1 / 192)*(FD + RD + LD + BD)
-- Tests to check that the correct edges are incidental.
t20 = tetrahedron cube 20
-p79_26_properties :: Test.Framework.Test
+p79_26_properties :: TestTree
p79_26_properties =
- testGroup "p. 79, Section (2.6) Properties" [
+ testGroup "p. 79, Section (2.6) properties" [
testProperty "c0120 identity1" prop_c0120_identity1,
testProperty "c0120 identity2" prop_c0120_identity2,
testProperty "c0120 identity3" prop_c0120_identity3,
testProperty "c1200 identity1" prop_c1200_identity1,
testProperty "c2100 identity1" prop_c2100_identity1]
-p79_27_properties :: Test.Framework.Test
+p79_27_properties :: TestTree
p79_27_properties =
- testGroup "p. 79, Section (2.7) Properties" [
+ testGroup "p. 79, Section (2.7) properties" [
testProperty "c0102 identity1" prop_c0102_identity1,
testProperty "c0201 identity1" prop_c0201_identity1,
testProperty "c0300 identity2" prop_c0300_identity2,
testProperty "c2100 identity2" prop_c2100_identity2 ]
-p79_28_properties :: Test.Framework.Test
+p79_28_properties :: TestTree
p79_28_properties =
- testGroup "p. 79, Section (2.8) Properties" [
+ testGroup "p. 79, Section (2.8) properties" [
testProperty "c3000 identity" prop_c3000_identity,
testProperty "c2010 identity" prop_c2010_identity,
testProperty "c2001 identity" prop_c2001_identity,
testProperty "c1011 identity" prop_c1011_identity ]
-edge_incidence_tests :: Test.Framework.Test
+edge_incidence_tests :: TestTree
edge_incidence_tests =
- testGroup "Edge Incidence Tests" [
+ testGroup "Edge incidence tests" [
testProperty "t0 shares edge with t6" prop_t0_shares_edge_with_t6,
testProperty "t0 shares edge with t1" prop_t0_shares_edge_with_t1,
testProperty "t0 shares edge with t3" prop_t0_shares_edge_with_t3,
testProperty "t6 shares edge with t7" prop_t6_shares_edge_with_t7,
testProperty "t7 shares edge with t20" prop_t7_shares_edge_with_t20 ]
-cube_properties :: Test.Framework.Test
+cube_properties :: TestTree
cube_properties =
- testGroup "Cube Properties" [
+ testGroup "Cube properties" [
p79_26_properties,
p79_27_properties,
p79_28_properties,