gruff 0.2 → 0.2.1
raw patch · 4 files changed
+223/−61 lines, 4 files
Files
- gruff.cabal +14/−4
- src/Browser.hs +90/−41
- src/View.hs +11/−9
- src/gruff.hs +108/−7
gruff.cabal view
@@ -1,5 +1,5 @@ Name: gruff-Version: 0.2+Version: 0.2.1 Synopsis: fractal explorer GUI using the ruff library Description: Mandelbrot Set fractal explorer using the ruff library.@@ -10,15 +10,25 @@ with integer-simple instead of the default integer-gmp. To install without MPFR support, use @cabal install gruff -f-mpfr@. .+ This version is a bugfix release. Changes since gruff-0.2:+ .+ * Supersampling bugs are fixed (no more undocumented quantization+ leading to inconsistent appearance at different zoom levels).+ .+ * Race condition bugs in cache management are fixed (no more+ incompletely rendered images).+ .+ * Internal changes/refactoring to make future additions easier.+ . Features in this version include: . * Interactive fractal browser display (left click to zoom in, right click to zoom out, middle-click to center). .- * Session persistance (stored in @~/.gruff/state.gruff@ - states can+ * Session persistance (stored in @~\/.gruff\/state.gruff@ - states can also be loaded from and saved to other files). .- * Tile cache (by default in @~/.gruff/cache@ - symlink it somewhere+ * Tile cache (by default in @~\/.gruff\/cache@ - symlink it somewhere with a few GB of space if you plan on exploring a lot). . * High-level feature finding using angled internal addresses@@ -37,7 +47,7 @@ * Limited amount of customizable colouring (colours for interior, border, and exterior points). .- * Supersampling for more detailed images (useful range is 1 to 4).+ * Supersampling for more detailed images (useful range is 1 to 16). . Future features might include image saving, external ray and feature information overlays, more feature finding and identification
src/Browser.hs view
@@ -2,7 +2,7 @@ import Prelude hiding (log) import Control.Concurrent- ( forkIO, MVar, newMVar, takeMVar, putMVar, tryTakeMVar, threadDelay )+ ( forkIO, MVar, newEmptyMVar, newMVar, takeMVar, putMVar, tryTakeMVar, threadDelay ) import Control.Monad (foldM, forever, forM_, replicateM, unless, when) import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef) import Data.List (sortBy)@@ -71,6 +71,9 @@ , combineProg :: Program , tsheet0 :: TextureObject , tsheet1 :: TextureObject+ , sheetCount0 :: Int+ , sheetCount1 :: Int+ , sheetCountTarget :: Int , fbo :: GLuint , cacheSizeMin :: Int , cacheSizeMax :: Int@@ -83,8 +86,8 @@ , doClear :: Bool } -sheetSize :: GruffImage -> Size-sheetSize g = let s = texels . bufferSize . window $ g in Size s s+sheetSize :: GruffImage -> BufferSize+sheetSize g = bufferSize (sheetOffset g) (window g) zoomPhase :: GruffImage -> Double zoomPhase = delta . location@@ -92,9 +95,23 @@ rotationA :: GruffImage -> Double rotationA = orient . viewport +sheetBlend :: GruffImage -> (Int, Double)+sheetBlend g =+ let z = zoomPhase g+ s = supersamples (window g)+ b = logBase 4 (4 ** z * s)+ h = floor b+ d = fromIntegral h - b+ in (h, 1 - (1 - 4**d) / (4**(d+1) - 4**d))++sheetOffset :: GruffImage -> Int+sheetOffset = fst . sheetBlend++blendFactor :: GruffImage -> Double+blendFactor = snd . sheetBlend+ iDisplay :: IORef GruffImage -> IO () iDisplay iR = do- prune iR s0 <- readIORef iR mtls <- tryTakeMVar (queue s0) case mtls of@@ -116,11 +133,11 @@ when c $ do clearSheet s False clearSheet s True- todo0 <- renderSheet s False- todo1 <- renderSheet s True- atomicModifyIORef iR $ \s' -> (s'{ viewQuads = (todo0, todo1) }, ())- let complete = null todo0 && null todo1- w = width (window s)+ atomicModifyIORef iR $ \s' -> (s'{ sheetCount0 = 0, sheetCount1 = 0 }, ())+ (todo0, upped0) <- renderSheet s False+ (todo1, upped1) <- renderSheet s True+ atomicModifyIORef iR $ \s' -> (s'{ viewQuads = (todo0, todo1), sheetCount0 = sheetCount0 s' + upped0 , sheetCount1 = sheetCount1 s' + upped1 }, ())+ let w = width (window s) h = height (window s) GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral w) (fromIntegral h))@@ -134,10 +151,7 @@ lblend <- get $ uniformLocation (combineProg s) "blend" uniform lsheet0 $= TexCoord1 (0 :: GLint) uniform lsheet1 $= TexCoord1 (1 :: GLint)- uniform lblend $= TexCoord1 (realToFrac (- let p = negate $ zoomPhase s- q = 4 -- realToFrac $ supersamples (window s) -- FIXME figure out correct blend- in 1 - (1 - q**p) / (q**(p + 1) - q**p) ) :: GLfloat)+ uniform lblend $= TexCoord1 (realToFrac (blendFactor s) :: GLfloat) activeTexture $= TextureUnit 0 textureBinding Texture2D $= Just (tsheet0 s) activeTexture $= TextureUnit 1@@ -155,7 +169,7 @@ v :: Int -> Int -> IO () v x y = vertex $ Vertex2 (fromIntegral x :: GLdouble) (fromIntegral y :: GLdouble)- k = 0.25 * realToFrac (0.5 ** zoomPhase s) :: GLdouble+ k = 0.125 * realToFrac (0.5 ** zoomPhase s) :: GLdouble renderPrimitive Quads $ do t (-1) 1 >> v 0 h t (-1) (-1) >> v 0 0@@ -165,17 +179,21 @@ activeTexture $= TextureUnit 0 textureBinding Texture2D $= Nothing currentProgram $= Nothing- when complete $ case doneCallback s of- Nothing -> return ()- Just act -> do- atomicModifyIORef iR $ \s' -> (s'{ doneCallback = Nothing, abortCallback = Nothing }, ())- act+ s'' <- readIORef iR+ when (sheetCount0 s'' + sheetCount1 s'' >= sheetCountTarget s'') $ do+ case doneCallback s of+ Nothing -> return ()+ Just act -> do+ atomicModifyIORef iR $ \s' -> (s'{ doneCallback = Nothing, abortCallback = Nothing }, ())+ act+ prune iR err <- glGetError when (err /= 0) $ print ("error: " ++ show err) clearSheet :: GruffImage -> Bool -> IO () clearSheet s b = do- let Size tw' th' = sheetSize s+ let tw' = texels $ sheetSize s+ th' = tw' tw = if b then tw' * 2 else tw' th = if b then th' * 2 else th' tsheet = (if b then tsheet1 else tsheet0) s@@ -190,14 +208,15 @@ glGenerateMipmap gl_TEXTURE_2D textureBinding Texture2D $= Nothing -renderSheet :: GruffImage -> Bool -> IO [(Complex Int, Quad)]+renderSheet :: GruffImage -> Bool -> IO ([(Complex Int, Quad)], Int) renderSheet s b = do- let Size tw' th' = sheetSize s+ let tw' = texels $ sheetSize s+ th' = tw' tw = if b then tw' * 2 else tw' th = if b then th' * 2 else th' tsheet = (if b then tsheet1 else tsheet0) s vquads = (if b then snd else fst) (viewQuads s)- if null vquads then return [] else do+ if null vquads then return ([], 0) else do bindFBO (fbo s) tsheet GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral tw) (fromIntegral th))@@ -220,11 +239,12 @@ todo <- foldM (\a t -> do a' <- drawQuad (tiles s) t ; return (a' ++ a)) [] vquads currentProgram $= Nothing unbindFBO- when (length todo < length vquads) $ do+ let upped = length vquads - length todo+ when (upped > 0) $ do textureBinding Texture2D $= Just tsheet glGenerateMipmap gl_TEXTURE_2D textureBinding Texture2D $= Nothing- return todo+ return (todo, upped) drawQuad :: Map Quad TextureObject3 -> (Complex Int, Quad) -> IO [(Complex Int, Quad)] drawQuad m ijq@(i :+ j, q) = case q `M.lookup` m of@@ -331,7 +351,7 @@ , " --> ", show (cacheSizeMin s0) ] bad <- atomicModifyIORef sR $ \s -> - let Just q0 = originQuad (location s) (bufferSize (window s))+ let Just q0 = originQuad (location s) (sheetSize s) score = quadDistance q0 o = comparing (score . fst) (good, bad)@@ -345,17 +365,17 @@ s' <- readIORef sR log s' Debug $ "updateCallback " todo' <- atomicModifyIORef sR $ \s ->- let vq@(qs0, qs1) = fromMaybe ([],[]) $ visibleQuads (window s) (viewport s) (location s)+ let vq@(qs0, qs1) = fromMaybe ([],[]) $ visibleQuads (window s) (viewport s) (location s) (sheetOffset s) qs = qs0 ++ qs1 todo = S.filter (`M.notMember` tiles s) (S.fromList (map snd qs))- in (s{ viewQuads = vq }, todo)+ in (s{ viewQuads = vq, sheetCountTarget = length qs }, todo) -- cancel in-progress jobs that aren't still needed _ <- takeMVar (jobs s') p <- atomicModifyIORef sR $ \s -> (s{ progress = M.filter (`S.member` todo') (progress s) }, progress s) mapM_ (`poke` 1) . filter (\w -> case M.lookup w p of Nothing -> False ; Just q -> q `S.notMember` todo') . workers $ s' -- set new jobs s <- readIORef sR- let Just q0 = originQuad (location s) (bufferSize (window s))+ let Just q0 = originQuad (location s) (sheetSize s) score = quadDistance q0 putMVar (jobs s') . sortBy (comparing score) . S.toList $ todo' \\ S.fromList (M.elems p) @@ -426,20 +446,27 @@ data Browser = Browser { browserWindow :: GTK.Window+ , browserGL :: GLUTGtk , browserRender :: Image -> IO () -> IO () -> IO () , browserAbort :: IO () , browserSaveImage :: FilePath -> IO () , browserSetExitCallback :: IO () -> IO () , browserSetReshapeCallback :: (Int -> Int -> IO ()) -> IO () , browserSetMouseCallback :: (Complex R -> Double -> IO ()) -> IO ()- , browserResize :: Int -> Int -> Double -> IO ()+-- , browserResize :: Int -> Int -> Double -> IO () } browserRenders :: Browser -> [(Image, FilePath)] -> IO () browserRenders _ [] = print "done"-browserRenders b ((i, f):ifs) = browserRender b i- (browserSaveImage b f >> browserRenders b ifs)- (print "aborted")+browserRenders b ((i, f):ifs) = do+ result <- newEmptyMVar+ postGUISync $ do+ browserRender b i+ (browserSaveImage b f >> putMVar result True)+ (putMVar result False)+ postRedisplay (browserGL b)+ r <- takeMVar result+ if r then browserRenders b ifs else print "aborted" browserNew :: GLUTGtk -> Pixbuf -> Logger -> FilePath -> IO Browser browserNew gl' icon lg cacheDir' = do@@ -484,6 +511,9 @@ , combineProg = error "combineProg" , tsheet0 = TextureObject 0 , tsheet1 = TextureObject 0+ , sheetCount0 = 0+ , sheetCount1 = 0+ , sheetCountTarget = maxBound , cacheSizeMin = 1000 , cacheSizeMax = 1500 , doClear = False@@ -495,7 +525,11 @@ i <- readIORef iR putJobs iR [] mapM_ (`poke` 1) . workers $ i- act <- atomicModifyIORef iR $ \s -> (s{ progress = M.empty, doneCallback = Nothing, abortCallback = Nothing }, abortCallback s)+ act <- atomicModifyIORef iR $ \s -> (s+ { progress = M.empty+ , doneCallback = Nothing+ , abortCallback = Nothing+ }, abortCallback s) fromMaybe (return ()) act atExit = do i <- readIORef iR@@ -503,13 +537,22 @@ Nothing -> return () Just act -> act browserRender' img done aborted = do+ s <- readIORef iR+ let wr = width (imageWindow img)+ hr = height (imageWindow img)+ unless (width (window s) == wr && height (window s) == hr) $ do+ windowResize iw wr hr+ atomicModifyIORef iR $ \s' -> (s'+ { window = imageWindow img+ , viewport = imageViewport img+ , location = imageLocation img+ , colours = imageColours img+ }, ())+ s' <- readIORef iR+ unless (sheetSize s == sheetSize s') (reallocateBuffers iR) atomicModifyIORef iR $ \i -> (i { doneCallback = Just done , abortCallback = Just aborted- , location = imageLocation img- , viewport = imageViewport img- , window = imageWindow img- , colours = imageColours img , doClear = True }, ()) update iR@@ -522,19 +565,20 @@ ( i{ Browser.reshapeCallback = Just act }, () ) browserSetMouseCallback' act = atomicModifyIORef iR $ \i -> ( i{ mouseCallback = Just act }, () )- browserResize' w h s = windowResize iw w h >> iReshape iR (Just s) (Size w h)+-- browserResize' w h s = windowResize iw w h >> iReshape iR (Just s) (Size w h) keyboardMouseCallback gl' $= iMouse iR _ <- timeoutAdd (timer iR >> return True) msPerFrame _ <- iw `onDestroy` atExit return Browser { browserWindow = iw+ , browserGL = gl' , browserRender = browserRender' , browserAbort = browserAbort' , browserSaveImage = browserSaveImage' , browserSetExitCallback = browserSetExitCallback' , browserSetReshapeCallback = browserSetReshapeCallback' , browserSetMouseCallback = browserSetMouseCallback'- , browserResize = browserResize'+-- , browserResize = browserResize' } fromColours :: Colours -> (Color3 GLfloat, Color3 GLfloat, Color3 GLfloat)@@ -572,13 +616,17 @@ , combineProg = combineProg' , tsheet0 = tsheet0' , tsheet1 = tsheet1'+ , sheetCount0 = 0+ , sheetCount1 = 0+ , sheetCountTarget = maxBound }, ()) reallocateBuffers iR reallocateBuffers :: IORef GruffImage -> IO () reallocateBuffers iR = do s <- readIORef iR- let Size tw' th' = sheetSize s+ let tw' = texels $ sheetSize s+ th' = tw' ts = [tsheet0 s, tsheet1 s] forM_ (ts `zip` [(tw', th'), (tw' * 2, th' * 2)]) $ \(t, (tw, th)) -> do texture Texture2D $= Enabled@@ -591,6 +639,7 @@ textureWrapMode Texture2D T $= (Repeated, ClampToEdge) textureBinding Texture2D $= Nothing texture Texture2D $= Disabled+ atomicModifyIORef iR $ \s' -> (s'{ sheetCount0 = 0, sheetCount1 = 0, sheetCountTarget = maxBound }, ()) newFBO :: IO GLuint newFBO = alloca $ \p -> glGenFramebuffers 1 p >> peek p
src/View.hs view
@@ -51,8 +51,10 @@ diagonal2 :: Window -> Int diagonal2 w = width w * width w + height w * height w +{- resolution :: Window -> Double-resolution w = supersamples w * fromIntegral (windowSize w)+resolution w = sqrt $ supersamples w * fromIntegral (diagonal2 w)+-} pixelLocation :: Window -> Viewport -> Location -> Double -> Double -> Complex Rational pixelLocation w v l x y =@@ -71,7 +73,7 @@ si = sin a x2 = co * x1 + si * y1 y2 = -si * x1 + co * y1- r = 2 * radius l+ r = radius l in (r * x2) :+ (r * y2) data BufferSize = BufferSize@@ -79,11 +81,11 @@ } deriving (Read, Show, Eq) -bufferSize :: Window -> BufferSize-bufferSize w = BufferSize{ texels = max tileSize . roundUp2 . ceiling . resolution $ w }+bufferSize :: Int -> Window -> BufferSize+bufferSize o w = BufferSize{ texels = roundUp2 . ceiling . ((2::Double) ^^ o *) . sqrt . fromIntegral . diagonal2 $ w } roundUp2 :: Int -> Int -- fails for too small and too large inputs-roundUp2 x = last . takeWhile (x >) . iterate (2 *) $ 1+roundUp2 x = head . dropWhile (x >=) . iterate (2 *) $ 1 level :: Location -> Int level = floor . negate . logBase 2 . radius@@ -148,13 +150,13 @@ , (i1 :+ j1, SouthEast `child` q) ] -visibleQuads :: Window -> Viewport -> Location -> Maybe ([(Complex Int, Quad)], [(Complex Int, Quad)])-visibleQuads w v l = do- let b = bufferSize w+visibleQuads :: Window -> Viewport -> Location -> Int -> Maybe ([(Complex Int, Quad)], [(Complex Int, Quad)])+visibleQuads w v l o = do+ let b = bufferSize o w a = orient v co = cos a si = sin a- k = 0.5 ** delta l * supersamples w+ k = 0.5 ** (delta l - fromIntegral o) x1 = k * fromIntegral (width w) y1 = k * fromIntegral (height w) x0 = - x1
src/gruff.hs view
@@ -2,9 +2,10 @@ module Main (main) where import Prelude hiding (catch, log)+--import Control.Concurrent (forkIO) import Control.Monad (forM_, liftM3, when) import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef, writeIORef)-import Data.Maybe (isJust, fromMaybe)+import Data.Maybe (isJust, fromMaybe) -- , mapMaybe) import Graphics.UI.Gtk hiding (get, Region, Size, Window, Viewport) import qualified Graphics.UI.Gtk as GTK import Graphics.UI.Gtk.OpenGL (initGL)@@ -16,9 +17,9 @@ import Paths_gruff (getDataFileName) import Number (R)-import Browser (Browser(..), browserNew)+import Browser (Browser(..), browserNew) -- , browserRenders) import MuAtom (MuAtom(..), MuProgress(..), muFromAddress, MuProgress'(..), muToAddress, MuProgress''(..), muLocate)-import View (Image(..), Location(..), Viewport(..), Window(..), Colours(..), Colour(..), defWindow, defViewport)+import View (Image(..), Location(..), Viewport(..), Window(..), Colours(..), Colour(..), defWindow, defViewport) -- , defColours) import GLUTGtk (glut, Size(Size), postRedisplay) import Logger (logger, LogLevel(Debug)) import qualified Logger as Log@@ -206,6 +207,7 @@ entI e a = do _ <- e g0 `onEntryActivate` (entryGetText (e g0) >>= wrapE g0 gR a >> upI) return ()+{- entI' e a = do _ <- e g0 `onEntryActivate` (do entryGetText (e g0) >>= wrapE g0 gR a@@ -213,6 +215,7 @@ browserResize browser (width (gWindow g)) (height (gWindow g)) (supersamples (gWindow g)) upI) return ()+-} colI cbi cbb cbe a = forM_ [cbi, cbb, cbe] $ \c -> do _ <- c g0 `onColorSet` (do ci <- colorButtonGetColor (cbi g0)@@ -260,7 +263,7 @@ , () ) g <- readIORef gR uEverything g0 g- upI+-- upI butI bHome aHome butI bLoad aLoad but bSave aSave@@ -273,9 +276,14 @@ entME eImagM eImagE aImag entME eSizeM eSizeE aSize entI eRota aRota+ entI eWidth aWidth+ entI eHeight aHeight+ entI eSamples aSamples+{- entI' eWidth aWidth entI' eHeight aHeight entI' eSamples aSamples+-} colI cInterior cBorder cExterior aColours let aExit' = (exit (Log.log lg) stateFile =<< readIORef gR) browserSetExitCallback browser aExit'@@ -288,6 +296,7 @@ refreshGUI g0 g widgetShowAll iw widgetShowAll ww+-- _ <- forkIO $ script browser mainGUI data Gruff = Gruff@@ -317,7 +326,7 @@ { gAddress = parseAngledInternalAddress "1" , gReal = Just 0 , gImag = Just 0- , gSize = Just 1+ , gSize = Just 2 , gRota = Just 0 , gColours = (red, black, white) , gWindow = defWindow@@ -421,7 +430,7 @@ MuSuccess'' mu -> do let g' = g{ gReal = Just . realPart . muNucleus $ mu , gImag = Just . imagPart . muNucleus $ mu- , gSize = Just . (* 4) . muSize $ mu+ , gSize = Just . (* 16) . muSize $ mu , gRota = Just . subtract (pi/2) . muOrient $ mu } progress "Found!"@@ -478,7 +487,7 @@ MuSuccess mu -> do let g' = g{ gReal = Just . realPart . muNucleus $ mu , gImag = Just . imagPart . muNucleus $ mu- , gSize = Just . (* 4) . muSize $ mu+ , gSize = Just . (* 16) . muSize $ mu , gRota = Just . subtract (pi/2) . muOrient $ mu } progress "Done!"@@ -556,3 +565,95 @@ fromColor :: Color -> Colour fromColor (Color r g b) = Colour (fromIntegral r / 65535) (fromIntegral g / 65535) (fromIntegral b / 65535)++{-+strings :: [String]+strings = [ "1 1/3 " ++ (unwords . map show . scanl (+) 3) (replicate m 1 ++ replicate (n - m) 2) | m <- [0 .. n - 1] ]+ ++ [ "1 1/3 " ++ (unwords . map show . scanl (+) 3) (replicate m 2 ++ replicate (n - m) 1) | m <- [0 .. n - 1] ]+ where n = 21 -- 15++script :: Browser -> IO ()+script b = browserRenders b images'++filename :: String -> Int -> String+filename s n = (reverse . take 4 . (++ "0000") . reverse . show) n ++ "__" ++ map filechar s ++ ".ppm"+ where filechar ' ' = '_'+ filechar '/' = '-'+ filechar c = c++findMu :: AngledInternalAddress -> Maybe MuAtom+findMu a = case last (muFromAddress a) of+ MuSuccess m -> Just m+ _ -> Nothing++scene :: (String, Int) -> Maybe (View.Image, String)+scene (s, n) = do+ a <- parseAngledInternalAddress s+ m <- findMu a+ let cx :+ cy = muNucleus m+ f = filename s n+ i = Image+ { imageLocation = Location{ center = toRational cx :+ toRational cy, radius = muSize m * 8 } -- 6+ , imageViewport = viewportDVD{ orient = muOrient m - pi / 2 }+ , imageWindow = windowDVD+ , imageColours = defColours+ }+ return (i, f)++windowDVD :: View.Window+windowDVD = Window{ width = 1080, height = 576, supersamples = 2 }++viewportDVD :: View.Viewport+viewportDVD = Viewport{ aspect = 1080/576, orient = 0 }++images :: [(View.Image, String)]+images = mapMaybe scene (score `zip` [0..])++kick, snare :: Int -> [String]+kick n = [ "1 2 " ++ (unwords . map show . take m . scanl (+) (3 :: Int) . repeat) 1 | m <- [n, n - 1 .. 1] ]+snare n = [ "1 2 " ++ (unwords . map show . take (2 * m) . filter (\x -> x `mod` 3 /= 0)) [(3 :: Int) ..] | m <- [n, n - 1 .. 1] ]++score :: [String]+score = concat+ [ kick 21+ , snare 32+ , kick 20+ , snare 11+ , kick 8+ , kick 8+ , kick 5+ , snare 32+ , kick 20+ , snare 11+ ]++images' :: [(View.Image, String)]+images' = {-drop 49 . -} map frame . take 1200 $ [0 ..]++frame :: Int -> (View.Image, String)+frame n = (img, (reverse . take 4 . (++ "0000") . reverse . show) n ++ ".ppm")+ where+ t = fromIntegral n / 25+ r = 4 * 2 ** negate t+ a = c0a * t / 48+ img = Image+ { imageLocation = Location{ center = c0x :+ c0y, radius = r }+ , imageViewport = viewportHD{ orient = a }+ , imageWindow = windowHD+ , imageColours = defColours+ }++windowHD :: View.Window+windowHD = Window{ width = 1920, height = 1080, supersamples = 5 }++viewportHD :: View.Viewport+viewportHD = Viewport{ aspect = 1920/1080, orient = 0 }++c0x, c0y :: Rational+c0x = -1.249654501250026535305564290406342641357245044812024384076696041016544263255410210573644817965421364058466642283047810126900443791060999959661828661014819271180654290498245641835647321878524004347784493127325654028742057010462138345395182421783064045422740334072967042829523926305969086019115635907387177+c0y = 2.62964442060409470886060596680625049122647005543488791701840256388323764840498489578760240122036234473238581692886963706321452429283848754125169118310897168288049653283033547961422702057918968158064636527854942947851448063883186797623535011066501329732840334929404268157442692714476422929470502521447535e-2++c0a :: Double+c0a = -2.651738023427396++-}