module PeriodScan (periodScan, periodNucleus) where import Control.Parallel.Strategies (parMap, rseq) import Data.List (genericIndex, nubBy) import Data.Maybe (listToMaybe) import Data.Function (on) import Data.Vec (NearZero, nearZero) import Text.FShow.Raw (DecimalFormat(nanTest, infTest)) import Fractal.RUFF.Types.Complex (Complex((:+))) import Utils (safeLast) periodScan :: (Floating r, Ord r) => r -> Complex r -> Maybe Integer periodScan r c = let cs = [ c + (r:+r), c + (r:+(-r)), c + ((-r):+(-r)), c + ((-r):+r) ] zs = iterate (zipWith (\cc z -> z * z + cc) cs) [0,0,0,0] in fmap fst . listToMaybe . dropWhile (not . straddlesOrigin . snd) . zip [0 ..] $ zs periodNucleus1 :: (Floating r, Ord r) => Integer -> r -> Complex r -> [(r, Complex r)] periodNucleus1 p r0 c0 = map fst . filter (straddlesOrigin . snd) . parMap rseq (fmap (parMap rseq (\c -> iterate (\z->z * z + c) 0 `genericIndex` p))) . fmap (\(r, c) -> ((r, c), [c + (r:+r), c + ((-r):+r), c + ((-r):+(-r)), c + (r:+(-r))])) $( [ (r', c0 + d) | i <- [-1,1], j <- [-1,1 ], let d = ((r'*i) :+ (r'*j)) ] ++ [ (r', c0 + d) | i <- [ 0 ], j <- [ 0 ], let d = ((r'*i) :+ (r'*j)) ]) {- of [] -> [] xs -> fmap fst . minimumBy (comparing $ offsetFromOrigin . snd) $ xs -} where r' = r0 / 2 periodNucleus :: (NearZero r, DecimalFormat r, Floating r, Ord r) => Integer -> r -> Complex r -> Maybe (Complex r) periodNucleus p r0 c0 = let ok (r, (x:+y)) = all ok' [r, x, y] ok' x = not (nanTest x || infTest x) in fmap snd . safeLast . takeWhile ok . concat . takeWhile (not . null) . iterate (nubBy (approxEq `on` snd) . (uncurry (periodNucleus1 p) =<<)) $ [(r0, c0)] straddlesOrigin :: (Ord r, Num r) => [Complex r] -> Bool straddlesOrigin ps = odd . length . filter id . zipWith positiveReal ps $ (drop 1 ps ++ take 1 ps) positiveReal :: (Ord r, Num r) => Complex r -> Complex r -> Bool positiveReal (u:+v) (x:+y) | v < 0 && y < 0 = False | v > 0 && y > 0 = False | (u * (y - v) - v * (x - u)) * (y - v) > 0 = True | otherwise = False {- offsetFromOrigin :: (Floating r) => [Complex r] -> r offsetFromOrigin = magnitude . sum -} approxEq :: (Num c, NearZero c) => c -> c -> Bool approxEq w z = nearZero (w - z)