import Options.Applicative import Control.Monad import Control.Monad.State import Foreign.Ptr import Foreign.ForeignPtr import Foreign.C.Types import Foreign.C.String import Foreign.Marshal.Array import Data.Number.Flint main = run =<< customExecParser (prefs showHelpOnEmpty) opts where desc = "Compute nth iterate of the logistic map x_{n+1} = r x_n (1-x_n)." opts = info (parameters <**> helper) ( fullDesc <> progDesc desc <> header desc) run params@(Parameters n xs rs digits) = do print params let goal = round (fromIntegral digits / logBase 10 2 + 3) :: CLong withNewArb $ \x -> do withNewArb $ \r -> do withNewArb $ \s -> do withNewArb $ \t -> do _ <- runStateT (next n goal) (0, xs, rs, x, r, s, t, 64) putStr $ "x_" ++ show n ++ " = " arb_printn x digits arb_str_none putStr "\n" next :: CLong -> CLong -> StateT (CLong, String, String, Ptr CArb, Ptr CArb, Ptr CArb, Ptr CArb, CLong) IO () next n goal = do (i, xs, rs, x, r, s, t, prec) <- get when (i == 0) $ liftIO $ do putStr $ "Trying precision " ++ show prec ++ " bits ... " getArb x xs prec getArb r rs prec if i < n then do success <- liftIO $ do arb_sub_ui t x 1 prec arb_neg t t arb_mul x x t prec arb_mul x x r prec p <- arb_rel_accuracy_bits x return $ p >= goal if success then do put (i + 1, xs, rs, x, r, s, t, prec) else do liftIO $ putStrLn $ "ran out of precision at step " ++ show i put (0, xs, rs, x, r, s, t, 2 * prec) next n goal else do liftIO $ putStrLn "success!" getArb x s prec = do withCString s $ \cs -> do flag <- arb_set_str x cs prec is_finite <- arb_is_finite x when (flag /= 0 || is_finite /= 1) $ do error $ "could no parse " ++ s data Parameters = Parameters { n :: CLong , x0 :: String , r :: String , digits :: CLong } deriving Show parameters :: Parser Parameters parameters = Parameters <$> argument pos ( help "nth iterate of the logistic map." <> metavar "n") <*> strOption ( help "starting point." <> long "x0" <> value "0.5" <> metavar "x0") <*> strOption ( help "r parameter of logistic map." <> long "r" <> value "3.75" <> metavar "r") <*> option auto ( help "number of digits." <> long "digits" <> short 'd' <> value 10 <> metavar "digits") pos :: (Read a, Integral a) => ReadM a pos = eitherReader $ \s -> do let result = read s if result >= 0 then Right result else Left "expected positive number"