From f379514a9da033e941aba038ef96bc1ed8b96c8b Mon Sep 17 00:00:00 2001 From: Darren Smith <1234633+darrenks@users.noreply.github.com> Date: Wed, 14 Sep 2022 15:32:31 -0600 Subject: [PATCH 1/5] Fix slowness issue with large integers example of issue before fix: import Data.Function.Memoize f :: (Integer, Integer) -> Integer f = memoFix (\f (b,a)->if a>0 then f (b,a-1) else b) main=print $ f (10^10000, 10) --- src/Data/Function/Memoize.hs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Data/Function/Memoize.hs b/src/Data/Function/Memoize.hs index 83455a6..f44c68a 100644 --- a/src/Data/Function/Memoize.hs +++ b/src/Data/Function/Memoize.hs @@ -158,9 +158,21 @@ data BinaryTreeCache v --- --- 'Integer' memoization --- +signedBitSize = (finiteBitSize (0 :: Int) - 1) :: Int +maxInt = fromIntegral (maxBound :: Int) :: Integer + +toIntBase :: Integer -> [Int] +toIntBase 0 = [] +toIntBase i | i <= maxInt && i >= fromIntegral (minBound :: Int) = + [fromInteger i] +toIntBase i = fromInteger (i .&. maxInt) : toIntBase (i `shiftR` signedBitSize) + +fromIntBase :: [Int] -> Integer +fromIntBase [] = 0 +fromIntBase (x:xs) = fromIntBase xs `shiftL` signedBitSize + fromIntegral x instance Memoizable Integer where - memoize f = integerLookup (f <$> theIntegers) + memoize f = memoize (f . fromIntBase) . toIntBase -- | An integer cache stores a value for 0 and separate caches for the -- positive and negative integers. From 12d19954df460a5149e31fe159e7587ecd5790b6 Mon Sep 17 00:00:00 2001 From: "Jesse A. Tov" Date: Fri, 16 Sep 2022 11:45:53 -0400 Subject: [PATCH 2/5] Rename some things and remove dead code. --- src/Data/Function/Memoize.hs | 71 +++++++++--------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/src/Data/Function/Memoize.hs b/src/Data/Function/Memoize.hs index f44c68a..955e0aa 100644 --- a/src/Data/Function/Memoize.hs +++ b/src/Data/Function/Memoize.hs @@ -158,64 +158,27 @@ data BinaryTreeCache v --- --- 'Integer' memoization --- -signedBitSize = (finiteBitSize (0 :: Int) - 1) :: Int -maxInt = fromIntegral (maxBound :: Int) :: Integer -toIntBase :: Integer -> [Int] -toIntBase 0 = [] -toIntBase i | i <= maxInt && i >= fromIntegral (minBound :: Int) = - [fromInteger i] -toIntBase i = fromInteger (i .&. maxInt) : toIntBase (i `shiftR` signedBitSize) +signedBitSize :: Int +signedBitSize = finiteBitSize (0 :: Int) - 1 -fromIntBase :: [Int] -> Integer -fromIntBase [] = 0 -fromIntBase (x:xs) = fromIntBase xs `shiftL` signedBitSize + fromIntegral x +minInt, maxInt :: Integer +minInt = fromIntegral (minBound :: Int) +maxInt = fromIntegral (maxBound :: Int) -instance Memoizable Integer where - memoize f = memoize (f . fromIntBase) . toIntBase - --- | An integer cache stores a value for 0 and separate caches for the --- positive and negative integers. -data IntegerCache v - = IntegerCache { - icZero ∷ v, - icNegative, icPositive ∷ PosIntCache v - } - deriving Functor - --- | A positive integer cache is represented as a little-endian bitwise --- trie -type PosIntCache v = BinaryTreeCache v - -theIntegers ∷ IntegerCache Integer -theIntegers - = IntegerCache { - icZero = 0, - icNegative = negate <$> thePosInts, - icPositive = thePosInts - } +encodeInteger :: Integer -> [Int] +encodeInteger 0 = [] +encodeInteger i | minInt <= i && i <= maxInt + = [fromInteger i] +encodeInteger i = fromInteger (i .&. maxInt) : encodeInteger (i `shiftR` signedBitSize) -thePosInts ∷ PosIntCache Integer -thePosInts = - BinaryTreeCache { - btValue = 1, - btLeft = fmap (* 2) thePosInts, - btRight = fmap (succ . (* 2)) thePosInts - } - -integerLookup ∷ IntegerCache v → Integer → v -integerLookup cache n = - case n `compare` 0 of - EQ → icZero cache - GT → posIntLookup (icPositive cache) n - LT → posIntLookup (icNegative cache) (negate n) - --- PRECONDITION: @n@ is a positive 'Integer' -posIntLookup ∷ PosIntCache v → Integer → v -posIntLookup cache 1 = btValue cache -posIntLookup cache n - | even n = posIntLookup (btLeft cache) (n `div` 2) - | otherwise = posIntLookup (btRight cache) (n `div` 2) +decodeInteger :: [Int] -> Integer +decodeInteger = foldr op 0 + where + op x xs' = fromIntegral x + xs' `shiftL` signedBitSize + +instance Memoizable Integer where + memoize f = memoize (f . decodeInteger) . encodeInteger --- --- Enumerable types using binary search trees From 2aa85f4ff3d91b39fbcdea21bdc489f0857d90a1 Mon Sep 17 00:00:00 2001 From: "Jesse A. Tov" Date: Fri, 16 Sep 2022 11:57:41 -0400 Subject: [PATCH 3/5] Add needed imports. --- src/Data/Function/Memoize.hs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Data/Function/Memoize.hs b/src/Data/Function/Memoize.hs index 955e0aa..6d72927 100644 --- a/src/Data/Function/Memoize.hs +++ b/src/Data/Function/Memoize.hs @@ -46,6 +46,7 @@ import Debug.Trace import Data.Function.Memoize.Class import Data.Function.Memoize.TH +import Data.Bits (shiftL, shiftR, finiteBitSize, (.&.)) import qualified Data.Complex as Complex import qualified Data.Ratio as Ratio #ifdef COMPAT_HAS_SOLO @@ -159,26 +160,25 @@ data BinaryTreeCache v --- 'Integer' memoization --- -signedBitSize :: Int -signedBitSize = finiteBitSize (0 :: Int) - 1 - -minInt, maxInt :: Integer -minInt = fromIntegral (minBound :: Int) -maxInt = fromIntegral (maxBound :: Int) +instance Memoizable Integer where + memoize f = memoize (f . decodeInteger) . encodeInteger encodeInteger :: Integer -> [Int] encodeInteger 0 = [] encodeInteger i | minInt <= i && i <= maxInt = [fromInteger i] -encodeInteger i = fromInteger (i .&. maxInt) : encodeInteger (i `shiftR` signedBitSize) +encodeInteger i = fromInteger (i .&. maxInt) : encodeInteger (i `shiftR` intBits) decodeInteger :: [Int] -> Integer -decodeInteger = foldr op 0 - where - op x xs' = fromIntegral x + xs' `shiftL` signedBitSize +decodeInteger = foldr op 0 where + op i i' = fromIntegral i + i' `shiftL` intBits -instance Memoizable Integer where - memoize f = memoize (f . decodeInteger) . encodeInteger +intBits :: Int +intBits = finiteBitSize (0 :: Int) - 1 + +minInt, maxInt :: Integer +minInt = fromIntegral (minBound :: Int) +maxInt = fromIntegral (maxBound :: Int) --- --- Enumerable types using binary search trees From b3093700a052e8f3d8ca581a49716ca4e82af683 Mon Sep 17 00:00:00 2001 From: "Jesse A. Tov" Date: Fri, 16 Sep 2022 12:13:27 -0400 Subject: [PATCH 4/5] Reorder definitions to put things that depend on TH splices after the TH splices --- src/Data/Function/Memoize.hs | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Data/Function/Memoize.hs b/src/Data/Function/Memoize.hs index 6d72927..a3e17b3 100644 --- a/src/Data/Function/Memoize.hs +++ b/src/Data/Function/Memoize.hs @@ -147,7 +147,7 @@ traceMemoize f = memoize (\a → traceShow a (f a)) --- Binary-tree based memo caches --- --- Used for both 'Integer' and arbitrary 'Int'-like types. +-- Used for arbitrary types that are bounded and enumerable: data BinaryTreeCache v = BinaryTreeCache { @@ -156,30 +156,6 @@ data BinaryTreeCache v } deriving Functor ---- ---- 'Integer' memoization ---- - -instance Memoizable Integer where - memoize f = memoize (f . decodeInteger) . encodeInteger - -encodeInteger :: Integer -> [Int] -encodeInteger 0 = [] -encodeInteger i | minInt <= i && i <= maxInt - = [fromInteger i] -encodeInteger i = fromInteger (i .&. maxInt) : encodeInteger (i `shiftR` intBits) - -decodeInteger :: [Int] -> Integer -decodeInteger = foldr op 0 where - op i i' = fromIntegral i + i' `shiftL` intBits - -intBits :: Int -intBits = finiteBitSize (0 :: Int) - 1 - -minInt, maxInt :: Integer -minInt = fromIntegral (minBound :: Int) -maxInt = fromIntegral (maxBound :: Int) - --- --- Enumerable types using binary search trees --- @@ -273,6 +249,30 @@ deriveMemoizable ''(,,,,,,,,,) deriveMemoizable ''(,,,,,,,,,,) deriveMemoizable ''(,,,,,,,,,,,) +--- +--- 'Integer' memoization +--- + +instance Memoizable Integer where + memoize f = memoize (f . decodeInteger) . encodeInteger + +encodeInteger :: Integer -> [Int] +encodeInteger 0 = [] +encodeInteger i | minInt <= i && i <= maxInt + = [fromInteger i] +encodeInteger i = fromInteger (i .&. maxInt) : encodeInteger (i `shiftR` intBits) + +decodeInteger :: [Int] -> Integer +decodeInteger = foldr op 0 where + op i i' = fromIntegral i + i' `shiftL` intBits + +intBits :: Int +intBits = finiteBitSize (0 :: Int) - 1 + +minInt, maxInt :: Integer +minInt = fromIntegral (minBound :: Int) +maxInt = fromIntegral (maxBound :: Int) + --- --- Functions --- From baeb59d2956cdc2874de109f9469e0f83c26ad19 Mon Sep 17 00:00:00 2001 From: "Jesse A. Tov" Date: Fri, 16 Sep 2022 12:34:11 -0400 Subject: [PATCH 5/5] Replace (+) with (.|.) (should be equivalent) --- src/Data/Function/Memoize.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Function/Memoize.hs b/src/Data/Function/Memoize.hs index a3e17b3..13276f2 100644 --- a/src/Data/Function/Memoize.hs +++ b/src/Data/Function/Memoize.hs @@ -46,7 +46,7 @@ import Debug.Trace import Data.Function.Memoize.Class import Data.Function.Memoize.TH -import Data.Bits (shiftL, shiftR, finiteBitSize, (.&.)) +import Data.Bits (shiftL, shiftR, finiteBitSize, (.&.), (.|.)) import qualified Data.Complex as Complex import qualified Data.Ratio as Ratio #ifdef COMPAT_HAS_SOLO @@ -264,7 +264,7 @@ encodeInteger i = fromInteger (i .&. maxInt) : encodeInteger (i `shiftR` intBits decodeInteger :: [Int] -> Integer decodeInteger = foldr op 0 where - op i i' = fromIntegral i + i' `shiftL` intBits + op i i' = fromIntegral i .|. i' `shiftL` intBits intBits :: Int intBits = finiteBitSize (0 :: Int) - 1