module Data.Number.IReal.UnsafeMemo where

import Data.Number.IReal.Scalable

import Control.Concurrent.MVar (newMVar, readMVar, modifyMVar_)
import System.IO.Unsafe (unsafePerformIO)

-- | Memoizing a function which satisfies the property @f (p-r) = scale (f p) (-r)@ for all @r > 0@.
-- Unsafe; based on ideas from Lennart Augustsson's uglymemo.
unsafeMemo :: Scalable a => (Int -> a) -> Int -> a
unsafeMemo f = unsafePerformIO . unsafePerformIO (memoIO f)


memoIO :: Scalable a => (Int -> a) -> IO (Int -> IO a)
memoIO f = do 
  v <- newMVar Nothing
  return (\p -> 
    if p<0 
    then error ("Excessive precision required: " ++ show (fromIntegral p + 2^32))  
    else do
        m <- readMVar v
        case m of
          Just (q,y)
            |p <= q -> return (scale y (p - q))
          _ ->  do let z = f p 
                   modifyMVar_ v (const (return (Just (p,z))))
                   return z)