]> gitweb.michael.orlitzky.com - spline3.git/blob - src/Cube.hs
5a70afc70b5b4c02a0906e4c42620c7051cfb28e
[spline3.git] / src / Cube.hs
1 module Cube (
2 Cube(..),
3 cube_properties,
4 find_containing_tetrahedron,
5 tetrahedra,
6 tetrahedron )
7 where
8
9 import Data.Maybe ( fromJust )
10 import qualified Data.Vector as V (
11 Vector,
12 findIndex,
13 map,
14 minimum,
15 singleton,
16 snoc,
17 unsafeIndex)
18 import Prelude hiding ( LT )
19 import Test.Tasty ( TestTree, testGroup )
20 import Test.Tasty.QuickCheck (
21 Arbitrary( arbitrary ),
22 Gen,
23 Positive( Positive ),
24 choose,
25 testProperty )
26 import Cardinal (
27 Cardinal(..),
28 ccwx,
29 ccwy,
30 ccwz,
31 cwx,
32 cwy,
33 cwz )
34 import Comparisons ( (~=), (~~=) )
35 import qualified Face ( Face(..), center )
36 import FunctionValues ( FunctionValues, eval, rotate )
37 import Misc ( all_equal, disjoint )
38 import Point ( Point( Point ), dot )
39 import Tetrahedron (
40 Tetrahedron(Tetrahedron, function_values, v0, v1, v2, v3),
41 barycenter,
42 c,
43 volume )
44
45 data Cube = Cube { i :: !Int,
46 j :: !Int,
47 k :: !Int,
48 fv :: !FunctionValues,
49 tetrahedra_volume :: !Double }
50 deriving (Eq)
51
52
53 instance Arbitrary Cube where
54 arbitrary = do
55 i' <- choose (coordmin, coordmax)
56 j' <- choose (coordmin, coordmax)
57 k' <- choose (coordmin, coordmax)
58 fv' <- arbitrary :: Gen FunctionValues
59 (Positive tet_vol) <- arbitrary :: Gen (Positive Double)
60 return (Cube i' j' k' fv' tet_vol)
61 where
62 -- The idea here is that, when cubed in the volume formula,
63 -- these numbers don't overflow 64 bits. This number is not
64 -- magic in any other sense than that it does not cause test
65 -- failures, while 2^23 does.
66 coordmax = 4194304 :: Int -- 2^22
67 coordmin = -coordmax
68
69
70 instance Show Cube where
71 show cube =
72 "Cube_" ++ subscript ++ "\n" ++
73 " Center: " ++ (show (center cube)) ++ "\n" ++
74 " xmin: " ++ (show (xmin cube)) ++ "\n" ++
75 " xmax: " ++ (show (xmax cube)) ++ "\n" ++
76 " ymin: " ++ (show (ymin cube)) ++ "\n" ++
77 " ymax: " ++ (show (ymax cube)) ++ "\n" ++
78 " zmin: " ++ (show (zmin cube)) ++ "\n" ++
79 " zmax: " ++ (show (zmax cube)) ++ "\n"
80 where
81 subscript =
82 (show (i cube)) ++ "," ++ (show (j cube)) ++ "," ++ (show (k cube))
83
84
85 -- | The left-side boundary of the cube. See Sorokina and Zeilfelder,
86 -- p. 76.
87 xmin :: Cube -> Double
88 xmin cube = (i' - 1/2)
89 where
90 i' = fromIntegral (i cube) :: Double
91
92 -- | The right-side boundary of the cube. See Sorokina and Zeilfelder,
93 -- p. 76.
94 xmax :: Cube -> Double
95 xmax cube = (i' + 1/2)
96 where
97 i' = fromIntegral (i cube) :: Double
98
99 -- | The front boundary of the cube. See Sorokina and Zeilfelder,
100 -- p. 76.
101 ymin :: Cube -> Double
102 ymin cube = (j' - 1/2)
103 where
104 j' = fromIntegral (j cube) :: Double
105
106 -- | The back boundary of the cube. See Sorokina and Zeilfelder,
107 -- p. 76.
108 ymax :: Cube -> Double
109 ymax cube = (j' + 1/2)
110 where
111 j' = fromIntegral (j cube) :: Double
112
113 -- | The bottom boundary of the cube. See Sorokina and Zeilfelder,
114 -- p. 76.
115 zmin :: Cube -> Double
116 zmin cube = (k' - 1/2)
117 where
118 k' = fromIntegral (k cube) :: Double
119
120 -- | The top boundary of the cube. See Sorokina and Zeilfelder,
121 -- p. 76.
122 zmax :: Cube -> Double
123 zmax cube = (k' + 1/2)
124 where
125 k' = fromIntegral (k cube) :: Double
126
127
128 -- | The center of Cube_ijk coincides with v_ijk at
129 -- (i, j, k). See Sorokina and Zeilfelder, p. 76.
130 center :: Cube -> Point
131 center cube =
132 Point x y z
133 where
134 x = fromIntegral (i cube) :: Double
135 y = fromIntegral (j cube) :: Double
136 z = fromIntegral (k cube) :: Double
137
138
139 -- Face stuff.
140
141 -- | The top (in the direction of z) face of the cube.
142 top_face :: Cube -> Face.Face
143 top_face cube = Face.Face v0' v1' v2' v3'
144 where
145 delta = (1/2) :: Double
146 cc = center cube
147 v0' = cc + ( Point delta (-delta) delta )
148 v1' = cc + ( Point delta delta delta )
149 v2' = cc + ( Point (-delta) delta delta )
150 v3' = cc + ( Point (-delta) (-delta) delta )
151
152
153
154 -- | The back (in the direction of x) face of the cube.
155 back_face :: Cube -> Face.Face
156 back_face cube = Face.Face v0' v1' v2' v3'
157 where
158 delta = (1/2) :: Double
159 cc = center cube
160 v0' = cc + ( Point delta (-delta) (-delta) )
161 v1' = cc + ( Point delta delta (-delta) )
162 v2' = cc + ( Point delta delta delta )
163 v3' = cc + ( Point delta (-delta) delta )
164
165
166 -- The bottom face (in the direction of -z) of the cube.
167 down_face :: Cube -> Face.Face
168 down_face cube = Face.Face v0' v1' v2' v3'
169 where
170 delta = (1/2) :: Double
171 cc = center cube
172 v0' = cc + ( Point (-delta) (-delta) (-delta) )
173 v1' = cc + ( Point (-delta) delta (-delta) )
174 v2' = cc + ( Point delta delta (-delta) )
175 v3' = cc + ( Point delta (-delta) (-delta) )
176
177
178
179 -- | The front (in the direction of -x) face of the cube.
180 front_face :: Cube -> Face.Face
181 front_face cube = Face.Face v0' v1' v2' v3'
182 where
183 delta = (1/2) :: Double
184 cc = center cube
185 v0' = cc + ( Point (-delta) (-delta) delta )
186 v1' = cc + ( Point (-delta) delta delta )
187 v2' = cc + ( Point (-delta) delta (-delta) )
188 v3' = cc + ( Point (-delta) (-delta) (-delta) )
189
190 -- | The left (in the direction of -y) face of the cube.
191 left_face :: Cube -> Face.Face
192 left_face cube = Face.Face v0' v1' v2' v3'
193 where
194 delta = (1/2) :: Double
195 cc = center cube
196 v0' = cc + ( Point delta (-delta) delta )
197 v1' = cc + ( Point (-delta) (-delta) delta )
198 v2' = cc + ( Point (-delta) (-delta) (-delta) )
199 v3' = cc + ( Point delta (-delta) (-delta) )
200
201
202 -- | The right (in the direction of y) face of the cube.
203 right_face :: Cube -> Face.Face
204 right_face cube = Face.Face v0' v1' v2' v3'
205 where
206 delta = (1/2) :: Double
207 cc = center cube
208 v0' = cc + ( Point (-delta) delta delta)
209 v1' = cc + ( Point delta delta delta )
210 v2' = cc + ( Point delta delta (-delta) )
211 v3' = cc + ( Point (-delta) delta (-delta) )
212
213
214 tetrahedron :: Cube -> Int -> Tetrahedron
215
216 tetrahedron cube 0 =
217 Tetrahedron (fv cube) v0' v1' v2' v3' vol
218 where
219 v0' = center cube
220 ff = front_face cube
221 v1' = Face.center ff
222 v2' = Face.v0 ff
223 v3' = Face.v1 ff
224 vol = tetrahedra_volume cube
225
226 tetrahedron cube 1 =
227 Tetrahedron fv' v0' v1' v2' v3' vol
228 where
229 v0' = center cube
230 ff = front_face cube
231 v1' = Face.center ff
232 v2' = Face.v1 ff
233 v3' = Face.v2 ff
234 fv' = rotate ccwx (fv cube)
235 vol = tetrahedra_volume cube
236
237 tetrahedron cube 2 =
238 Tetrahedron fv' v0' v1' v2' v3' vol
239 where
240 v0' = center cube
241 ff = front_face cube
242 v1' = Face.center ff
243 v2' = Face.v2 ff
244 v3' = Face.v3 ff
245 fv' = rotate ccwx $ rotate ccwx $ fv cube
246 vol = tetrahedra_volume cube
247
248 tetrahedron cube 3 =
249 Tetrahedron fv' v0' v1' v2' v3' vol
250 where
251 v0' = center cube
252 ff = front_face cube
253 v1' = Face.center ff
254 v2' = Face.v3 ff
255 v3' = Face.v0 ff
256 fv' = rotate cwx (fv cube)
257 vol = tetrahedra_volume cube
258
259 tetrahedron cube 4 =
260 Tetrahedron fv' v0' v1' v2' v3' vol
261 where
262 v0' = center cube
263 tf = top_face cube
264 v1' = Face.center tf
265 v2' = Face.v0 tf
266 v3' = Face.v1 tf
267 fv' = rotate cwy (fv cube)
268 vol = tetrahedra_volume cube
269
270 tetrahedron cube 5 =
271 Tetrahedron fv' v0' v1' v2' v3' vol
272 where
273 v0' = center cube
274 tf = top_face cube
275 v1' = Face.center tf
276 v2' = Face.v1 tf
277 v3' = Face.v2 tf
278 fv' = rotate cwy $ rotate cwz $ fv cube
279 vol = tetrahedra_volume cube
280
281 tetrahedron cube 6 =
282 Tetrahedron fv' v0' v1' v2' v3' vol
283 where
284 v0' = center cube
285 tf = top_face cube
286 v1' = Face.center tf
287 v2' = Face.v2 tf
288 v3' = Face.v3 tf
289 fv' = rotate cwy $ rotate cwz
290 $ rotate cwz
291 $ fv cube
292 vol = tetrahedra_volume cube
293
294 tetrahedron cube 7 =
295 Tetrahedron fv' v0' v1' v2' v3' vol
296 where
297 v0' = center cube
298 tf = top_face cube
299 v1' = Face.center tf
300 v2' = Face.v3 tf
301 v3' = Face.v0 tf
302 fv' = rotate cwy $ rotate ccwz $ fv cube
303 vol = tetrahedra_volume cube
304
305 tetrahedron cube 8 =
306 Tetrahedron fv' v0' v1' v2' v3' vol
307 where
308 v0' = center cube
309 bf = back_face cube
310 v1' = Face.center bf
311 v2' = Face.v0 bf
312 v3' = Face.v1 bf
313 fv' = rotate cwy $ rotate cwy $ fv cube
314 vol = tetrahedra_volume cube
315
316 tetrahedron cube 9 =
317 Tetrahedron fv' v0' v1' v2' v3' vol
318 where
319 v0' = center cube
320 bf = back_face cube
321 v1' = Face.center bf
322 v2' = Face.v1 bf
323 v3' = Face.v2 bf
324 fv' = rotate cwy $ rotate cwy
325 $ rotate cwx
326 $ fv cube
327 vol = tetrahedra_volume cube
328
329 tetrahedron cube 10 =
330 Tetrahedron fv' v0' v1' v2' v3' vol
331 where
332 v0' = center cube
333 bf = back_face cube
334 v1' = Face.center bf
335 v2' = Face.v2 bf
336 v3' = Face.v3 bf
337 fv' = rotate cwy $ rotate cwy
338 $ rotate cwx
339 $ rotate cwx
340 $ fv cube
341
342 vol = tetrahedra_volume cube
343
344 tetrahedron cube 11 =
345 Tetrahedron fv' v0' v1' v2' v3' vol
346 where
347 v0' = center cube
348 bf = back_face cube
349 v1' = Face.center bf
350 v2' = Face.v3 bf
351 v3' = Face.v0 bf
352 fv' = rotate cwy $ rotate cwy
353 $ rotate ccwx
354 $ fv cube
355 vol = tetrahedra_volume cube
356
357 tetrahedron cube 12 =
358 Tetrahedron fv' v0' v1' v2' v3' vol
359 where
360 v0' = center cube
361 df = down_face cube
362 v1' = Face.center df
363 v2' = Face.v0 df
364 v3' = Face.v1 df
365 fv' = rotate ccwy $ fv cube
366 vol = tetrahedra_volume cube
367
368 tetrahedron cube 13 =
369 Tetrahedron fv' v0' v1' v2' v3' vol
370 where
371 v0' = center cube
372 df = down_face cube
373 v1' = Face.center df
374 v2' = Face.v1 df
375 v3' = Face.v2 df
376 fv' = rotate ccwy $ rotate ccwz $ fv cube
377 vol = tetrahedra_volume cube
378
379 tetrahedron cube 14 =
380 Tetrahedron fv' v0' v1' v2' v3' vol
381 where
382 v0' = center cube
383 df = down_face cube
384 v1' = Face.center df
385 v2' = Face.v2 df
386 v3' = Face.v3 df
387 fv' = rotate ccwy $ rotate ccwz
388 $ rotate ccwz
389 $ fv cube
390 vol = tetrahedra_volume cube
391
392 tetrahedron cube 15 =
393 Tetrahedron fv' v0' v1' v2' v3' vol
394 where
395 v0' = center cube
396 df = down_face cube
397 v1' = Face.center df
398 v2' = Face.v3 df
399 v3' = Face.v0 df
400 fv' = rotate ccwy $ rotate cwz $ fv cube
401 vol = tetrahedra_volume cube
402
403 tetrahedron cube 16 =
404 Tetrahedron fv' v0' v1' v2' v3' vol
405 where
406 v0' = center cube
407 rf = right_face cube
408 v1' = Face.center rf
409 v2' = Face.v0 rf
410 v3' = Face.v1 rf
411 fv' = rotate ccwz $ fv cube
412 vol = tetrahedra_volume cube
413
414 tetrahedron cube 17 =
415 Tetrahedron fv' v0' v1' v2' v3' vol
416 where
417 v0' = center cube
418 rf = right_face cube
419 v1' = Face.center rf
420 v2' = Face.v1 rf
421 v3' = Face.v2 rf
422 fv' = rotate ccwz $ rotate cwy $ fv cube
423 vol = tetrahedra_volume cube
424
425 tetrahedron cube 18 =
426 Tetrahedron fv' v0' v1' v2' v3' vol
427 where
428 v0' = center cube
429 rf = right_face cube
430 v1' = Face.center rf
431 v2' = Face.v2 rf
432 v3' = Face.v3 rf
433 fv' = rotate ccwz $ rotate cwy
434 $ rotate cwy
435 $ fv cube
436 vol = tetrahedra_volume cube
437
438 tetrahedron cube 19 =
439 Tetrahedron fv' v0' v1' v2' v3' vol
440 where
441 v0' = center cube
442 rf = right_face cube
443 v1' = Face.center rf
444 v2' = Face.v3 rf
445 v3' = Face.v0 rf
446 fv' = rotate ccwz $ rotate ccwy
447 $ fv cube
448 vol = tetrahedra_volume cube
449
450 tetrahedron cube 20 =
451 Tetrahedron fv' v0' v1' v2' v3' vol
452 where
453 v0' = center cube
454 lf = left_face cube
455 v1' = Face.center lf
456 v2' = Face.v0 lf
457 v3' = Face.v1 lf
458 fv' = rotate cwz $ fv cube
459 vol = tetrahedra_volume cube
460
461 tetrahedron cube 21 =
462 Tetrahedron fv' v0' v1' v2' v3' vol
463 where
464 v0' = center cube
465 lf = left_face cube
466 v1' = Face.center lf
467 v2' = Face.v1 lf
468 v3' = Face.v2 lf
469 fv' = rotate cwz $ rotate ccwy $ fv cube
470 vol = tetrahedra_volume cube
471
472 tetrahedron cube 22 =
473 Tetrahedron fv' v0' v1' v2' v3' vol
474 where
475 v0' = center cube
476 lf = left_face cube
477 v1' = Face.center lf
478 v2' = Face.v2 lf
479 v3' = Face.v3 lf
480 fv' = rotate cwz $ rotate ccwy
481 $ rotate ccwy
482 $ fv cube
483 vol = tetrahedra_volume cube
484
485 tetrahedron cube 23 =
486 Tetrahedron fv' v0' v1' v2' v3' vol
487 where
488 v0' = center cube
489 lf = left_face cube
490 v1' = Face.center lf
491 v2' = Face.v3 lf
492 v3' = Face.v0 lf
493 fv' = rotate cwz $ rotate cwy
494 $ fv cube
495 vol = tetrahedra_volume cube
496
497
498 -- Only used in tests, so we don't need the added speed
499 -- of Data.Vector.
500 tetrahedra :: Cube -> [Tetrahedron]
501 tetrahedra cube = [ tetrahedron cube n | n <- [0..23] ]
502
503 front_left_top_tetrahedra :: Cube -> V.Vector Tetrahedron
504 front_left_top_tetrahedra cube =
505 V.singleton (tetrahedron cube 0) `V.snoc`
506 (tetrahedron cube 3) `V.snoc`
507 (tetrahedron cube 6) `V.snoc`
508 (tetrahedron cube 7) `V.snoc`
509 (tetrahedron cube 20) `V.snoc`
510 (tetrahedron cube 21)
511
512 front_left_down_tetrahedra :: Cube -> V.Vector Tetrahedron
513 front_left_down_tetrahedra cube =
514 V.singleton (tetrahedron cube 0) `V.snoc`
515 (tetrahedron cube 2) `V.snoc`
516 (tetrahedron cube 3) `V.snoc`
517 (tetrahedron cube 12) `V.snoc`
518 (tetrahedron cube 15) `V.snoc`
519 (tetrahedron cube 21)
520
521 front_right_top_tetrahedra :: Cube -> V.Vector Tetrahedron
522 front_right_top_tetrahedra cube =
523 V.singleton (tetrahedron cube 0) `V.snoc`
524 (tetrahedron cube 1) `V.snoc`
525 (tetrahedron cube 5) `V.snoc`
526 (tetrahedron cube 6) `V.snoc`
527 (tetrahedron cube 16) `V.snoc`
528 (tetrahedron cube 19)
529
530 front_right_down_tetrahedra :: Cube -> V.Vector Tetrahedron
531 front_right_down_tetrahedra cube =
532 V.singleton (tetrahedron cube 1) `V.snoc`
533 (tetrahedron cube 2) `V.snoc`
534 (tetrahedron cube 12) `V.snoc`
535 (tetrahedron cube 13) `V.snoc`
536 (tetrahedron cube 18) `V.snoc`
537 (tetrahedron cube 19)
538
539 back_left_top_tetrahedra :: Cube -> V.Vector Tetrahedron
540 back_left_top_tetrahedra cube =
541 V.singleton (tetrahedron cube 0) `V.snoc`
542 (tetrahedron cube 3) `V.snoc`
543 (tetrahedron cube 6) `V.snoc`
544 (tetrahedron cube 7) `V.snoc`
545 (tetrahedron cube 20) `V.snoc`
546 (tetrahedron cube 21)
547
548 back_left_down_tetrahedra :: Cube -> V.Vector Tetrahedron
549 back_left_down_tetrahedra cube =
550 V.singleton (tetrahedron cube 8) `V.snoc`
551 (tetrahedron cube 11) `V.snoc`
552 (tetrahedron cube 14) `V.snoc`
553 (tetrahedron cube 15) `V.snoc`
554 (tetrahedron cube 22) `V.snoc`
555 (tetrahedron cube 23)
556
557 back_right_top_tetrahedra :: Cube -> V.Vector Tetrahedron
558 back_right_top_tetrahedra cube =
559 V.singleton (tetrahedron cube 4) `V.snoc`
560 (tetrahedron cube 5) `V.snoc`
561 (tetrahedron cube 9) `V.snoc`
562 (tetrahedron cube 10) `V.snoc`
563 (tetrahedron cube 16) `V.snoc`
564 (tetrahedron cube 17)
565
566 back_right_down_tetrahedra :: Cube -> V.Vector Tetrahedron
567 back_right_down_tetrahedra cube =
568 V.singleton (tetrahedron cube 8) `V.snoc`
569 (tetrahedron cube 9) `V.snoc`
570 (tetrahedron cube 13) `V.snoc`
571 (tetrahedron cube 14) `V.snoc`
572 (tetrahedron cube 17) `V.snoc`
573 (tetrahedron cube 18)
574
575 in_top_half :: Cube -> Point -> Bool
576 in_top_half cube (Point _ _ z) =
577 distance_from_top <= distance_from_bottom
578 where
579 distance_from_top = abs $ (zmax cube) - z
580 distance_from_bottom = abs $ (zmin cube) - z
581
582 in_front_half :: Cube -> Point -> Bool
583 in_front_half cube (Point x _ _) =
584 distance_from_front <= distance_from_back
585 where
586 distance_from_front = abs $ (xmin cube) - x
587 distance_from_back = abs $ (xmax cube) - x
588
589
590 in_left_half :: Cube -> Point -> Bool
591 in_left_half cube (Point _ y _) =
592 distance_from_left <= distance_from_right
593 where
594 distance_from_left = abs $ (ymin cube) - y
595 distance_from_right = abs $ (ymax cube) - y
596
597
598 -- | Takes a 'Cube', and returns the Tetrahedra belonging to it that
599 -- contain the given 'Point'. This should be faster than checking
600 -- every tetrahedron individually, since we determine which half
601 -- (hemisphere?) of the cube the point lies in three times: once in
602 -- each dimension. This allows us to eliminate non-candidates
603 -- quickly.
604 --
605 -- This can throw an exception, but the use of 'head' might
606 -- save us some unnecessary computations.
607 --
608 {-# INLINE find_containing_tetrahedron #-}
609 find_containing_tetrahedron :: Cube -> Point -> Tetrahedron
610 find_containing_tetrahedron cube p =
611 candidates `V.unsafeIndex` (fromJust lucky_idx)
612 where
613 front_half = in_front_half cube p
614 top_half = in_top_half cube p
615 left_half = in_left_half cube p
616
617 candidates :: V.Vector Tetrahedron
618 candidates
619 | front_half =
620 if left_half then
621 if top_half then
622 front_left_top_tetrahedra cube
623 else
624 front_left_down_tetrahedra cube
625 else
626 if top_half then
627 front_right_top_tetrahedra cube
628 else
629 front_right_down_tetrahedra cube
630
631 | otherwise = -- back half
632 if left_half then
633 if top_half then
634 back_left_top_tetrahedra cube
635 else
636 back_left_down_tetrahedra cube
637 else
638 if top_half then
639 back_right_top_tetrahedra cube
640 else
641 back_right_down_tetrahedra cube
642
643 -- Use the dot product instead of Euclidean distance here to save
644 -- a sqrt(). So, "distances" below really means "distances
645 -- squared."
646 distances :: V.Vector Double
647 distances = V.map ((dot p) . barycenter) candidates
648
649 shortest_distance :: Double
650 shortest_distance = V.minimum distances
651
652 -- Compute the index of the tetrahedron with the center closest to
653 -- p. This is a bad algorithm, but don't change it! If you make it
654 -- smarter by finding the index of shortest_distance in distances
655 -- (this should give the same answer and avoids recomputing the
656 -- dot product), the program gets slower. Seriously!
657 lucky_idx :: Maybe Int
658 lucky_idx = V.findIndex
659 (\t -> (barycenter t) `dot` p == shortest_distance)
660 candidates
661
662
663
664
665
666
667 -- * Tests
668
669 prop_opposite_octant_tetrahedra_disjoint1 :: Cube -> Bool
670 prop_opposite_octant_tetrahedra_disjoint1 cube =
671 disjoint (front_left_top_tetrahedra cube) (front_right_down_tetrahedra cube)
672
673 prop_opposite_octant_tetrahedra_disjoint2 :: Cube -> Bool
674 prop_opposite_octant_tetrahedra_disjoint2 cube =
675 disjoint (back_left_top_tetrahedra cube) (back_right_down_tetrahedra cube)
676
677 prop_opposite_octant_tetrahedra_disjoint3 :: Cube -> Bool
678 prop_opposite_octant_tetrahedra_disjoint3 cube =
679 disjoint (front_left_top_tetrahedra cube) (back_right_top_tetrahedra cube)
680
681 prop_opposite_octant_tetrahedra_disjoint4 :: Cube -> Bool
682 prop_opposite_octant_tetrahedra_disjoint4 cube =
683 disjoint (front_left_down_tetrahedra cube) (back_right_down_tetrahedra cube)
684
685 prop_opposite_octant_tetrahedra_disjoint5 :: Cube -> Bool
686 prop_opposite_octant_tetrahedra_disjoint5 cube =
687 disjoint (front_left_top_tetrahedra cube) (back_left_down_tetrahedra cube)
688
689 prop_opposite_octant_tetrahedra_disjoint6 :: Cube -> Bool
690 prop_opposite_octant_tetrahedra_disjoint6 cube =
691 disjoint (front_right_top_tetrahedra cube) (back_right_down_tetrahedra cube)
692
693
694 -- | Since the grid size is necessarily positive, all tetrahedra
695 -- (which comprise cubes of positive volume) must have positive
696 -- volume as well.
697 prop_all_volumes_positive :: Cube -> Bool
698 prop_all_volumes_positive cube =
699 all (>= 0) volumes
700 where
701 ts = tetrahedra cube
702 volumes = map volume ts
703
704
705 -- | In fact, since all of the tetrahedra are identical, we should
706 -- already know their volumes. There's 24 tetrahedra to a cube, so
707 -- we'd expect the volume of each one to be 1/24.
708 prop_all_volumes_exact :: Cube -> Bool
709 prop_all_volumes_exact cube =
710 and [volume t ~~= 1/24 | t <- tetrahedra cube]
711
712 -- | All tetrahedron should have their v0 located at the center of the
713 -- cube.
714 prop_v0_all_equal :: Cube -> Bool
715 prop_v0_all_equal cube = (v0 t0) == (v0 t1)
716 where
717 t0 = head (tetrahedra cube) -- Doesn't matter which two we choose.
718 t1 = head $ tail (tetrahedra cube)
719
720
721 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Note that the
722 -- third and fourth indices of c-t3 have been switched. This is
723 -- because we store the triangles oriented such that their volume is
724 -- positive. If T and T-tilde share \<v0,v1,v2\> and v3,v3-tilde point
725 -- in opposite directions, one of them has to have negative volume!
726 prop_c0120_identity1 :: Cube -> Bool
727 prop_c0120_identity1 cube =
728 c t0 0 1 2 0 ~= (c t0 0 0 2 1 + c t3 0 0 1 2) / 2
729 where
730 t0 = tetrahedron cube 0
731 t3 = tetrahedron cube 3
732
733
734 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Repeats
735 -- 'prop_c0120_identity1' with tetrahedrons 1 and 2.
736 prop_c0120_identity2 :: Cube -> Bool
737 prop_c0120_identity2 cube =
738 c t1 0 1 2 0 ~= (c t1 0 0 2 1 + c t0 0 0 1 2) / 2
739 where
740 t0 = tetrahedron cube 0
741 t1 = tetrahedron cube 1
742
743 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Repeats
744 -- 'prop_c0120_identity1' with tetrahedrons 1 and 2.
745 prop_c0120_identity3 :: Cube -> Bool
746 prop_c0120_identity3 cube =
747 c t2 0 1 2 0 ~= (c t2 0 0 2 1 + c t1 0 0 1 2) / 2
748 where
749 t1 = tetrahedron cube 1
750 t2 = tetrahedron cube 2
751
752 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Repeats
753 -- 'prop_c0120_identity1' with tetrahedrons 2 and 3.
754 prop_c0120_identity4 :: Cube -> Bool
755 prop_c0120_identity4 cube =
756 c t3 0 1 2 0 ~= (c t3 0 0 2 1 + c t2 0 0 1 2) / 2
757 where
758 t2 = tetrahedron cube 2
759 t3 = tetrahedron cube 3
760
761
762 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Repeats
763 -- 'prop_c0120_identity1' with tetrahedrons 4 and 5.
764 prop_c0120_identity5 :: Cube -> Bool
765 prop_c0120_identity5 cube =
766 c t5 0 1 2 0 ~= (c t5 0 0 2 1 + c t4 0 0 1 2) / 2
767 where
768 t4 = tetrahedron cube 4
769 t5 = tetrahedron cube 5
770
771 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Repeats
772 -- 'prop_c0120_identity1' with tetrahedrons 5 and 6.
773 prop_c0120_identity6 :: Cube -> Bool
774 prop_c0120_identity6 cube =
775 c t6 0 1 2 0 ~= (c t6 0 0 2 1 + c t5 0 0 1 2) / 2
776 where
777 t5 = tetrahedron cube 5
778 t6 = tetrahedron cube 6
779
780
781 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). Repeats
782 -- 'prop_c0120_identity1' with tetrahedrons 6 and 7.
783 prop_c0120_identity7 :: Cube -> Bool
784 prop_c0120_identity7 cube =
785 c t7 0 1 2 0 ~= (c t7 0 0 2 1 + c t6 0 0 1 2) / 2
786 where
787 t6 = tetrahedron cube 6
788 t7 = tetrahedron cube 7
789
790
791 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). See
792 -- 'prop_c0120_identity1'.
793 prop_c0210_identity1 :: Cube -> Bool
794 prop_c0210_identity1 cube =
795 c t0 0 2 1 0 ~= (c t0 0 1 1 1 + c t3 0 1 1 1) / 2
796 where
797 t0 = tetrahedron cube 0
798 t3 = tetrahedron cube 3
799
800
801 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). See
802 -- 'prop_c0120_identity1'.
803 prop_c0300_identity1 :: Cube -> Bool
804 prop_c0300_identity1 cube =
805 c t0 0 3 0 0 ~= (c t0 0 2 0 1 + c t3 0 2 1 0) / 2
806 where
807 t0 = tetrahedron cube 0
808 t3 = tetrahedron cube 3
809
810
811 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). See
812 -- 'prop_c0120_identity1'.
813 prop_c1110_identity :: Cube -> Bool
814 prop_c1110_identity cube =
815 c t0 1 1 1 0 ~= (c t0 1 0 1 1 + c t3 1 0 1 1) / 2
816 where
817 t0 = tetrahedron cube 0
818 t3 = tetrahedron cube 3
819
820
821 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). See
822 -- 'prop_c0120_identity1'.
823 prop_c1200_identity1 :: Cube -> Bool
824 prop_c1200_identity1 cube =
825 c t0 1 2 0 0 ~= (c t0 1 1 0 1 + c t3 1 1 1 0) / 2
826 where
827 t0 = tetrahedron cube 0
828 t3 = tetrahedron cube 3
829
830
831 -- | Given in Sorokina and Zeilfelder, p. 79, (2.6). See
832 -- 'prop_c0120_identity1'.
833 prop_c2100_identity1 :: Cube -> Bool
834 prop_c2100_identity1 cube =
835 c t0 2 1 0 0 ~= (c t0 2 0 0 1 + c t3 2 0 1 0) / 2
836 where
837 t0 = tetrahedron cube 0
838 t3 = tetrahedron cube 3
839
840
841
842 -- | Given in Sorokina and Zeilfelder, p. 79, (2.7). Note that the
843 -- third and fourth indices of c-t3 have been switched. This is
844 -- because we store the triangles oriented such that their volume is
845 -- positive. If T and T-tilde share \<v0,v1,v2\> and v3,v3-tilde
846 -- point in opposite directions, one of them has to have negative
847 -- volume!
848 prop_c0102_identity1 :: Cube -> Bool
849 prop_c0102_identity1 cube =
850 c t0 0 1 0 2 ~= (c t0 0 0 1 2 + c t1 0 0 2 1) / 2
851 where
852 t0 = tetrahedron cube 0
853 t1 = tetrahedron cube 1
854
855
856 -- | Given in Sorokina and Zeilfelder, p. 79, (2.7). See
857 -- 'prop_c0102_identity1'.
858 prop_c0201_identity1 :: Cube -> Bool
859 prop_c0201_identity1 cube =
860 c t0 0 2 0 1 ~= (c t0 0 1 1 1 + c t1 0 1 1 1) / 2
861 where
862 t0 = tetrahedron cube 0
863 t1 = tetrahedron cube 1
864
865
866 -- | Given in Sorokina and Zeilfelder, p. 79, (2.7). See
867 -- 'prop_c0102_identity1'.
868 prop_c0300_identity2 :: Cube -> Bool
869 prop_c0300_identity2 cube =
870 c t0 0 3 0 0 ~= (c t0 0 2 1 0 + c t1 0 2 0 1) / 2
871 where
872 t0 = tetrahedron cube 0
873 t1 = tetrahedron cube 1
874
875
876 -- | Given in Sorokina and Zeilfelder, p. 79, (2.7). See
877 -- 'prop_c0102_identity1'.
878 prop_c1101_identity :: Cube -> Bool
879 prop_c1101_identity cube =
880 c t0 1 1 0 1 ~= (c t0 1 0 1 1 + c t1 1 0 1 1) / 2
881 where
882 t0 = tetrahedron cube 0
883 t1 = tetrahedron cube 1
884
885
886 -- | Given in Sorokina and Zeilfelder, p. 79, (2.7). See
887 -- 'prop_c0102_identity1'.
888 prop_c1200_identity2 :: Cube -> Bool
889 prop_c1200_identity2 cube =
890 c t0 1 2 0 0 ~= (c t0 1 1 1 0 + c t1 1 1 0 1) / 2
891 where
892 t0 = tetrahedron cube 0
893 t1 = tetrahedron cube 1
894
895
896 -- | Given in Sorokina and Zeilfelder, p. 79, (2.7). See
897 -- 'prop_c0102_identity1'.
898 prop_c2100_identity2 :: Cube -> Bool
899 prop_c2100_identity2 cube =
900 c t0 2 1 0 0 ~= (c t0 2 0 1 0 + c t1 2 0 0 1) / 2
901 where
902 t0 = tetrahedron cube 0
903 t1 = tetrahedron cube 1
904
905
906 -- | Given in Sorokina and Zeilfelder, p. 79, (2.8). The third and
907 -- fourth indices of c-t6 have been switched. This is because we
908 -- store the triangles oriented such that their volume is
909 -- positive. If T and T-tilde share \<v0,v1,v2\> and v3,v3-tilde
910 -- point in opposite directions, one of them has to have negative
911 -- volume!
912 prop_c3000_identity :: Cube -> Bool
913 prop_c3000_identity cube =
914 c t0 3 0 0 0 ~= c t0 2 1 0 0 + c t6 2 1 0 0
915 - ((c t0 2 0 1 0 + c t0 2 0 0 1)/ 2)
916 where
917 t0 = tetrahedron cube 0
918 t6 = tetrahedron cube 6
919
920
921 -- | Given in Sorokina and Zeilfelder, p. 79, (2.8). See
922 -- 'prop_c3000_identity'.
923 prop_c2010_identity :: Cube -> Bool
924 prop_c2010_identity cube =
925 c t0 2 0 1 0 ~= c t0 1 1 1 0 + c t6 1 1 0 1
926 - ((c t0 1 0 2 0 + c t0 1 0 1 1)/ 2)
927 where
928 t0 = tetrahedron cube 0
929 t6 = tetrahedron cube 6
930
931
932 -- | Given in Sorokina and Zeilfelder, p. 79, (2.8). See
933 -- 'prop_c3000_identity'.
934 prop_c2001_identity :: Cube -> Bool
935 prop_c2001_identity cube =
936 c t0 2 0 0 1 ~= c t0 1 1 0 1 + c t6 1 1 1 0
937 - ((c t0 1 0 0 2 + c t0 1 0 1 1)/ 2)
938 where
939 t0 = tetrahedron cube 0
940 t6 = tetrahedron cube 6
941
942
943 -- | Given in Sorokina and Zeilfelder, p. 79, (2.8). See
944 -- 'prop_c3000_identity'.
945 prop_c1020_identity :: Cube -> Bool
946 prop_c1020_identity cube =
947 c t0 1 0 2 0 ~= c t0 0 1 2 0 + c t6 0 1 0 2
948 - ((c t0 0 0 3 0 + c t0 0 0 2 1)/ 2)
949 where
950 t0 = tetrahedron cube 0
951 t6 = tetrahedron cube 6
952
953
954 -- | Given in Sorokina and Zeilfelder, p. 79, (2.8). See
955 -- 'prop_c3000_identity'.
956 prop_c1002_identity :: Cube -> Bool
957 prop_c1002_identity cube =
958 c t0 1 0 0 2 ~= c t0 0 1 0 2 + c t6 0 1 2 0
959 - ((c t0 0 0 0 3 + c t0 0 0 1 2)/ 2)
960 where
961 t0 = tetrahedron cube 0
962 t6 = tetrahedron cube 6
963
964
965 -- | Given in Sorokina and Zeilfelder, p. 79, (2.8). See
966 -- 'prop_c3000_identity'.
967 prop_c1011_identity :: Cube -> Bool
968 prop_c1011_identity cube =
969 c t0 1 0 1 1 ~= c t0 0 1 1 1 + c t6 0 1 1 1 -
970 ((c t0 0 0 1 2 + c t0 0 0 2 1)/ 2)
971 where
972 t0 = tetrahedron cube 0
973 t6 = tetrahedron cube 6
974
975
976 -- | The function values at the interior should be the same for all
977 -- tetrahedra.
978 prop_interior_values_all_identical :: Cube -> Bool
979 prop_interior_values_all_identical cube =
980 all_equal [ eval (function_values tet) I | tet <- tetrahedra cube ]
981
982
983 -- | We know what (c t6 2 1 0 0) should be from Sorokina and Zeilfelder, p. 87.
984 -- This test checks the rotation works as expected.
985 prop_c_tilde_2100_rotation_correct :: Cube -> Bool
986 prop_c_tilde_2100_rotation_correct cube =
987 expr1 ~= expr2
988 where
989 t0 = tetrahedron cube 0
990 t6 = tetrahedron cube 6
991
992 -- What gets computed for c2100 of t6.
993 expr1 = eval (function_values t6) $
994 (3/8)*I +
995 (1/12)*(T + R + L + D) +
996 (1/64)*(FT + FR + FL + FD) +
997 (7/48)*F +
998 (1/48)*B +
999 (1/96)*(RT + LD + LT + RD) +
1000 (1/192)*(BT + BR + BL + BD)
1001
1002 -- What should be computed for c2100 of t6.
1003 expr2 = eval (function_values t0) $
1004 (3/8)*I +
1005 (1/12)*(F + R + L + B) +
1006 (1/64)*(FT + RT + LT + BT) +
1007 (7/48)*T +
1008 (1/48)*D +
1009 (1/96)*(FR + FL + BR + BL) +
1010 (1/192)*(FD + RD + LD + BD)
1011
1012
1013 -- | We know what (c t6 2 1 0 0) should be from Sorokina and
1014 -- Zeilfelder, p. 87. This test checks the actual value based on
1015 -- the FunctionValues of the cube.
1016 --
1017 -- If 'prop_c_tilde_2100_rotation_correct' passes, then this test is
1018 -- even meaningful!
1019 prop_c_tilde_2100_correct :: Cube -> Bool
1020 prop_c_tilde_2100_correct cube =
1021 c t6 2 1 0 0 ~= expected
1022 where
1023 t0 = tetrahedron cube 0
1024 t6 = tetrahedron cube 6
1025 fvs = function_values t0
1026 expected = eval fvs $
1027 (3/8)*I +
1028 (1/12)*(F + R + L + B) +
1029 (1/64)*(FT + RT + LT + BT) +
1030 (7/48)*T +
1031 (1/48)*D +
1032 (1/96)*(FR + FL + BR + BL) +
1033 (1/192)*(FD + RD + LD + BD)
1034
1035
1036 -- Tests to check that the correct edges are incidental.
1037 prop_t0_shares_edge_with_t1 :: Cube -> Bool
1038 prop_t0_shares_edge_with_t1 cube =
1039 (v1 t0) == (v1 t1) && (v3 t0) == (v2 t1)
1040 where
1041 t0 = tetrahedron cube 0
1042 t1 = tetrahedron cube 1
1043
1044 prop_t0_shares_edge_with_t3 :: Cube -> Bool
1045 prop_t0_shares_edge_with_t3 cube =
1046 (v1 t0) == (v1 t3) && (v2 t0) == (v3 t3)
1047 where
1048 t0 = tetrahedron cube 0
1049 t3 = tetrahedron cube 3
1050
1051 prop_t0_shares_edge_with_t6 :: Cube -> Bool
1052 prop_t0_shares_edge_with_t6 cube =
1053 (v2 t0) == (v3 t6) && (v3 t0) == (v2 t6)
1054 where
1055 t0 = tetrahedron cube 0
1056 t6 = tetrahedron cube 6
1057
1058 prop_t1_shares_edge_with_t2 :: Cube -> Bool
1059 prop_t1_shares_edge_with_t2 cube =
1060 (v1 t1) == (v1 t2) && (v3 t1) == (v2 t2)
1061 where
1062 t1 = tetrahedron cube 1
1063 t2 = tetrahedron cube 2
1064
1065 prop_t1_shares_edge_with_t19 :: Cube -> Bool
1066 prop_t1_shares_edge_with_t19 cube =
1067 (v2 t1) == (v3 t19) && (v3 t1) == (v2 t19)
1068 where
1069 t1 = tetrahedron cube 1
1070 t19 = tetrahedron cube 19
1071
1072 prop_t2_shares_edge_with_t3 :: Cube -> Bool
1073 prop_t2_shares_edge_with_t3 cube =
1074 (v1 t1) == (v1 t2) && (v3 t1) == (v2 t2)
1075 where
1076 t1 = tetrahedron cube 1
1077 t2 = tetrahedron cube 2
1078
1079 prop_t2_shares_edge_with_t12 :: Cube -> Bool
1080 prop_t2_shares_edge_with_t12 cube =
1081 (v2 t2) == (v3 t12) && (v3 t2) == (v2 t12)
1082 where
1083 t2 = tetrahedron cube 2
1084 t12 = tetrahedron cube 12
1085
1086 prop_t3_shares_edge_with_t21 :: Cube -> Bool
1087 prop_t3_shares_edge_with_t21 cube =
1088 (v2 t3) == (v3 t21) && (v3 t3) == (v2 t21)
1089 where
1090 t3 = tetrahedron cube 3
1091 t21 = tetrahedron cube 21
1092
1093 prop_t4_shares_edge_with_t5 :: Cube -> Bool
1094 prop_t4_shares_edge_with_t5 cube =
1095 (v1 t4) == (v1 t5) && (v3 t4) == (v2 t5)
1096 where
1097 t4 = tetrahedron cube 4
1098 t5 = tetrahedron cube 5
1099
1100 prop_t4_shares_edge_with_t7 :: Cube -> Bool
1101 prop_t4_shares_edge_with_t7 cube =
1102 (v1 t4) == (v1 t7) && (v2 t4) == (v3 t7)
1103 where
1104 t4 = tetrahedron cube 4
1105 t7 = tetrahedron cube 7
1106
1107 prop_t4_shares_edge_with_t10 :: Cube -> Bool
1108 prop_t4_shares_edge_with_t10 cube =
1109 (v2 t4) == (v3 t10) && (v3 t4) == (v2 t10)
1110 where
1111 t4 = tetrahedron cube 4
1112 t10 = tetrahedron cube 10
1113
1114 prop_t5_shares_edge_with_t6 :: Cube -> Bool
1115 prop_t5_shares_edge_with_t6 cube =
1116 (v1 t5) == (v1 t6) && (v3 t5) == (v2 t6)
1117 where
1118 t5 = tetrahedron cube 5
1119 t6 = tetrahedron cube 6
1120
1121 prop_t5_shares_edge_with_t16 :: Cube -> Bool
1122 prop_t5_shares_edge_with_t16 cube =
1123 (v2 t5) == (v3 t16) && (v3 t5) == (v2 t16)
1124 where
1125 t5 = tetrahedron cube 5
1126 t16 = tetrahedron cube 16
1127
1128 prop_t6_shares_edge_with_t7 :: Cube -> Bool
1129 prop_t6_shares_edge_with_t7 cube =
1130 (v1 t6) == (v1 t7) && (v3 t6) == (v2 t7)
1131 where
1132 t6 = tetrahedron cube 6
1133 t7 = tetrahedron cube 7
1134
1135 prop_t7_shares_edge_with_t20 :: Cube -> Bool
1136 prop_t7_shares_edge_with_t20 cube =
1137 (v2 t7) == (v3 t20) && (v2 t7) == (v3 t20)
1138 where
1139 t7 = tetrahedron cube 7
1140 t20 = tetrahedron cube 20
1141
1142
1143 p79_26_properties :: TestTree
1144 p79_26_properties =
1145 testGroup "p. 79, Section (2.6) properties" [
1146 testProperty "c0120 identity1" prop_c0120_identity1,
1147 testProperty "c0120 identity2" prop_c0120_identity2,
1148 testProperty "c0120 identity3" prop_c0120_identity3,
1149 testProperty "c0120 identity4" prop_c0120_identity4,
1150 testProperty "c0120 identity5" prop_c0120_identity5,
1151 testProperty "c0120 identity6" prop_c0120_identity6,
1152 testProperty "c0120 identity7" prop_c0120_identity7,
1153 testProperty "c0210 identity1" prop_c0210_identity1,
1154 testProperty "c0300 identity1" prop_c0300_identity1,
1155 testProperty "c1110 identity" prop_c1110_identity,
1156 testProperty "c1200 identity1" prop_c1200_identity1,
1157 testProperty "c2100 identity1" prop_c2100_identity1]
1158
1159 p79_27_properties :: TestTree
1160 p79_27_properties =
1161 testGroup "p. 79, Section (2.7) properties" [
1162 testProperty "c0102 identity1" prop_c0102_identity1,
1163 testProperty "c0201 identity1" prop_c0201_identity1,
1164 testProperty "c0300 identity2" prop_c0300_identity2,
1165 testProperty "c1101 identity" prop_c1101_identity,
1166 testProperty "c1200 identity2" prop_c1200_identity2,
1167 testProperty "c2100 identity2" prop_c2100_identity2 ]
1168
1169
1170 p79_28_properties :: TestTree
1171 p79_28_properties =
1172 testGroup "p. 79, Section (2.8) properties" [
1173 testProperty "c3000 identity" prop_c3000_identity,
1174 testProperty "c2010 identity" prop_c2010_identity,
1175 testProperty "c2001 identity" prop_c2001_identity,
1176 testProperty "c1020 identity" prop_c1020_identity,
1177 testProperty "c1002 identity" prop_c1002_identity,
1178 testProperty "c1011 identity" prop_c1011_identity ]
1179
1180
1181 edge_incidence_tests :: TestTree
1182 edge_incidence_tests =
1183 testGroup "Edge incidence tests" [
1184 testProperty "t0 shares edge with t6" prop_t0_shares_edge_with_t6,
1185 testProperty "t0 shares edge with t1" prop_t0_shares_edge_with_t1,
1186 testProperty "t0 shares edge with t3" prop_t0_shares_edge_with_t3,
1187 testProperty "t1 shares edge with t2" prop_t1_shares_edge_with_t2,
1188 testProperty "t1 shares edge with t19" prop_t1_shares_edge_with_t19,
1189 testProperty "t2 shares edge with t3" prop_t2_shares_edge_with_t3,
1190 testProperty "t2 shares edge with t12" prop_t2_shares_edge_with_t12,
1191 testProperty "t3 shares edge with t21" prop_t3_shares_edge_with_t21,
1192 testProperty "t4 shares edge with t5" prop_t4_shares_edge_with_t5,
1193 testProperty "t4 shares edge with t7" prop_t4_shares_edge_with_t7,
1194 testProperty "t4 shares edge with t10" prop_t4_shares_edge_with_t10,
1195 testProperty "t5 shares edge with t6" prop_t5_shares_edge_with_t6,
1196 testProperty "t5 shares edge with t16" prop_t5_shares_edge_with_t16,
1197 testProperty "t6 shares edge with t7" prop_t6_shares_edge_with_t7,
1198 testProperty "t7 shares edge with t20" prop_t7_shares_edge_with_t20 ]
1199
1200 cube_properties :: TestTree
1201 cube_properties =
1202 testGroup "Cube properties" [
1203 p79_26_properties,
1204 p79_27_properties,
1205 p79_28_properties,
1206 edge_incidence_tests,
1207 testProperty "opposite octant tetrahedra are disjoint (1)"
1208 prop_opposite_octant_tetrahedra_disjoint1,
1209 testProperty "opposite octant tetrahedra are disjoint (2)"
1210 prop_opposite_octant_tetrahedra_disjoint2,
1211 testProperty "opposite octant tetrahedra are disjoint (3)"
1212 prop_opposite_octant_tetrahedra_disjoint3,
1213 testProperty "opposite octant tetrahedra are disjoint (4)"
1214 prop_opposite_octant_tetrahedra_disjoint4,
1215 testProperty "opposite octant tetrahedra are disjoint (5)"
1216 prop_opposite_octant_tetrahedra_disjoint5,
1217 testProperty "opposite octant tetrahedra are disjoint (6)"
1218 prop_opposite_octant_tetrahedra_disjoint6,
1219 testProperty "all volumes positive" prop_all_volumes_positive,
1220 testProperty "all volumes exact" prop_all_volumes_exact,
1221 testProperty "v0 all equal" prop_v0_all_equal,
1222 testProperty "interior values all identical"
1223 prop_interior_values_all_identical,
1224 testProperty "c-tilde_2100 rotation correct"
1225 prop_c_tilde_2100_rotation_correct,
1226 testProperty "c-tilde_2100 correct"
1227 prop_c_tilde_2100_correct ]