module Text.Fuzzy.Parallel
( filter, filter',
simpleFilter, simpleFilter',
match, defChunkSize, defMaxResults,
Scored(..)
) where
import Control.Parallel.Strategies (evalList, parList, rseq, using)
import Data.Bits ((.|.))
import Data.Maybe (fromMaybe, mapMaybe)
import qualified Data.Text as T
import qualified Data.Text.Array as TA
import qualified Data.Text.Internal as T
import Prelude hiding (filter)
data Scored a = Scored {forall a. Scored a -> Int
score :: !Int, forall a. Scored a -> a
original:: !a}
deriving (forall a b. a -> Scored b -> Scored a
forall a b. (a -> b) -> Scored a -> Scored b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Scored b -> Scored a
$c<$ :: forall a b. a -> Scored b -> Scored a
fmap :: forall a b. (a -> b) -> Scored a -> Scored b
$cfmap :: forall a b. (a -> b) -> Scored a -> Scored b
Functor, Int -> Scored a -> ShowS
forall a. Show a => Int -> Scored a -> ShowS
forall a. Show a => [Scored a] -> ShowS
forall a. Show a => Scored a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Scored a] -> ShowS
$cshowList :: forall a. Show a => [Scored a] -> ShowS
show :: Scored a -> String
$cshow :: forall a. Show a => Scored a -> String
showsPrec :: Int -> Scored a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Scored a -> ShowS
Show)
{-# INLINABLE match #-}
match :: T.Text
-> T.Text
-> Maybe Int
match :: Text -> Text -> Maybe Int
match (T.Text Array
pArr Int
pOff Int
pLen) (T.Text Array
sArr Int
sOff Int
sLen) = forall {t}. Num t => t -> t -> Int -> Int -> Maybe t
go Int
0 Int
1 Int
pOff Int
sOff
where
pTotal :: Int
pTotal = Int
pOff forall a. Num a => a -> a -> a
+ Int
pLen
sDelta :: Int
sDelta = Int
sOff forall a. Num a => a -> a -> a
+ Int
sLen forall a. Num a => a -> a -> a
- Int
pTotal
go :: t -> t -> Int -> Int -> Maybe t
go !t
totalScore !t
currScore !Int
currPOff !Int
currSOff
| Int
currPOff forall a. Ord a => a -> a -> Bool
>= Int
pTotal
= forall a. a -> Maybe a
Just t
totalScore
| Int
currSOff forall a. Ord a => a -> a -> Bool
> Int
currPOff forall a. Num a => a -> a -> a
+ Int
sDelta
= forall a. Maybe a
Nothing
| Word16
pByte <- Array -> Int -> Word16
TA.unsafeIndex Array
pArr Int
currPOff
, Word16
sByte <- Array -> Int -> Word16
TA.unsafeIndex Array
sArr Int
currSOff
, Word16
pByte forall a. Eq a => a -> a -> Bool
== Word16
sByte Bool -> Bool -> Bool
|| (Int
currPOff forall a. Eq a => a -> a -> Bool
/= Int
pOff Bool -> Bool -> Bool
&& Word16
pByte forall a. Eq a => a -> a -> Bool
== forall {a}. (Ord a, Num a, Bits a) => a -> a
toLowerAscii Word16
sByte)
= let curr :: t
curr = t
currScore forall a. Num a => a -> a -> a
* t
2 forall a. Num a => a -> a -> a
+ t
1 in
t -> t -> Int -> Int -> Maybe t
go (t
totalScore forall a. Num a => a -> a -> a
+ t
curr) t
curr (Int
currPOff forall a. Num a => a -> a -> a
+ Int
1) (Int
currSOff forall a. Num a => a -> a -> a
+ Int
1)
| Bool
otherwise
= t -> t -> Int -> Int -> Maybe t
go t
totalScore t
0 Int
currPOff (Int
currSOff forall a. Num a => a -> a -> a
+ Int
1)
toLowerAscii :: a -> a
toLowerAscii a
w = if (a
w forall a. Num a => a -> a -> a
- a
65) forall a. Ord a => a -> a -> Bool
< a
26 then a
w forall a. Bits a => a -> a -> a
.|. a
0x20 else a
w
defChunkSize :: Int
defChunkSize :: Int
defChunkSize = Int
1000
defMaxResults :: Int
defMaxResults :: Int
defMaxResults = Int
10
{-# INLINABLE simpleFilter #-}
simpleFilter :: Int
-> Int
-> T.Text
-> [T.Text]
-> [Scored T.Text]
simpleFilter :: Int -> Int -> Text -> [Text] -> [Scored Text]
simpleFilter Int
chunk Int
maxRes Text
pattern [Text]
xs =
forall t. Int -> Int -> Text -> [t] -> (t -> Text) -> [Scored t]
filter Int
chunk Int
maxRes Text
pattern [Text]
xs forall a. a -> a
id
filter' :: Int
-> Int
-> T.Text
-> [t]
-> (t -> T.Text)
-> (T.Text -> T.Text -> Maybe Int)
-> [Scored t]
filter' :: forall t.
Int
-> Int
-> Text
-> [t]
-> (t -> Text)
-> (Text -> Text -> Maybe Int)
-> [Scored t]
filter' Int
chunkSize Int
maxRes Text
pattern [t]
ts t -> Text
extract Text -> Text -> Maybe Int
match' = forall t. Int -> Int -> [Scored t] -> [Scored t]
partialSortByAscScore Int
maxRes Int
perfectScore (forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Scored t]]
vss)
where
pattern' :: Text
pattern' = case Text -> Maybe (Char, Text)
T.uncons Text
pattern of
Just (Char
c, Text
rest) -> Char -> Text -> Text
T.cons Char
c (Text -> Text
T.toLower Text
rest)
Maybe (Char, Text)
_ -> Text
pattern
vss :: [[Scored t]]
vss = forall a b. (a -> b) -> [a] -> [b]
map (forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (\t
t -> forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Int -> a -> Scored a
Scored t
t forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> Maybe Int
match' Text
pattern' (t -> Text
extract t
t))) (forall a. Int -> [a] -> [[a]]
chunkList Int
chunkSize [t]
ts)
forall a. a -> Strategy a -> a
`using` forall a. Strategy a -> Strategy [a]
parList (forall a. Strategy a -> Strategy [a]
evalList forall a. Strategy a
rseq)
perfectScore :: Int
perfectScore = forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
pattern) forall a b. (a -> b) -> a -> b
$ Text -> Text -> Maybe Int
match' Text
pattern' Text
pattern'
filter :: Int
-> Int
-> T.Text
-> [t]
-> (t -> T.Text)
-> [Scored t]
filter :: forall t. Int -> Int -> Text -> [t] -> (t -> Text) -> [Scored t]
filter Int
chunkSize Int
maxRes Text
pattern [t]
ts t -> Text
extract =
forall t.
Int
-> Int
-> Text
-> [t]
-> (t -> Text)
-> (Text -> Text -> Maybe Int)
-> [Scored t]
filter' Int
chunkSize Int
maxRes Text
pattern [t]
ts t -> Text
extract Text -> Text -> Maybe Int
match
{-# INLINABLE simpleFilter' #-}
simpleFilter' :: Int
-> Int
-> T.Text
-> [T.Text]
-> (T.Text -> T.Text -> Maybe Int)
-> [Scored T.Text]
simpleFilter' :: Int
-> Int
-> Text
-> [Text]
-> (Text -> Text -> Maybe Int)
-> [Scored Text]
simpleFilter' Int
chunk Int
maxRes Text
pattern [Text]
xs Text -> Text -> Maybe Int
match' =
forall t.
Int
-> Int
-> Text
-> [t]
-> (t -> Text)
-> (Text -> Text -> Maybe Int)
-> [Scored t]
filter' Int
chunk Int
maxRes Text
pattern [Text]
xs forall a. a -> a
id Text -> Text -> Maybe Int
match'
chunkList :: Int -> [a] -> [[a]]
chunkList :: forall a. Int -> [a] -> [[a]]
chunkList Int
chunkSize = forall {a}. [a] -> [[a]]
go
where
go :: [a] -> [[a]]
go [] = []
go [a]
xs = [a]
ys forall a. a -> [a] -> [a]
: [a] -> [[a]]
go [a]
zs
where
([a]
ys, [a]
zs) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
chunkSize [a]
xs
partialSortByAscScore
:: Int
-> Int
-> [Scored t]
-> [Scored t]
partialSortByAscScore :: forall t. Int -> Int -> [Scored t] -> [Scored t]
partialSortByAscScore Int
wantedCount Int
perfectScore [Scored t]
orig = forall {a}. [Scored t] -> SortState a -> [Scored t] -> [Scored t]
loop [Scored t]
orig (forall a. Int -> Int -> Int -> SortState a
SortState forall a. Bounded a => a
minBound Int
perfectScore Int
0) [] where
loop :: [Scored t] -> SortState a -> [Scored t] -> [Scored t]
loop [] st :: SortState a
st@SortState{Int
foundCount :: forall a. SortState a -> Int
scoreWanted :: forall a. SortState a -> Int
bestScoreSeen :: forall a. SortState a -> Int
foundCount :: Int
scoreWanted :: Int
bestScoreSeen :: Int
..} [Scored t]
acc
| Int
foundCount forall a. Eq a => a -> a -> Bool
== Int
wantedCount = forall a. [a] -> [a]
reverse [Scored t]
acc
| Bool
otherwise = if Int
bestScoreSeen forall a. Ord a => a -> a -> Bool
< Int
scoreWanted
then [Scored t] -> SortState a -> [Scored t] -> [Scored t]
loop [Scored t]
orig SortState a
st{scoreWanted :: Int
scoreWanted = Int
bestScoreSeen, bestScoreSeen :: Int
bestScoreSeen = forall a. Bounded a => a
minBound} [Scored t]
acc
else forall a. [a] -> [a]
reverse [Scored t]
acc
loop (Scored t
x : [Scored t]
xs) st :: SortState a
st@SortState{Int
foundCount :: Int
scoreWanted :: Int
bestScoreSeen :: Int
foundCount :: forall a. SortState a -> Int
scoreWanted :: forall a. SortState a -> Int
bestScoreSeen :: forall a. SortState a -> Int
..} [Scored t]
acc
| Int
foundCount forall a. Eq a => a -> a -> Bool
== Int
wantedCount = forall a. [a] -> [a]
reverse [Scored t]
acc
| forall a. Scored a -> Int
score Scored t
x forall a. Eq a => a -> a -> Bool
== Int
scoreWanted
= [Scored t] -> SortState a -> [Scored t] -> [Scored t]
loop [Scored t]
xs SortState a
st{foundCount :: Int
foundCount = Int
foundCountforall a. Num a => a -> a -> a
+Int
1} (Scored t
xforall a. a -> [a] -> [a]
:[Scored t]
acc)
| forall a. Scored a -> Int
score Scored t
x forall a. Ord a => a -> a -> Bool
< Int
scoreWanted Bool -> Bool -> Bool
&& forall a. Scored a -> Int
score Scored t
x forall a. Ord a => a -> a -> Bool
> Int
bestScoreSeen
= [Scored t] -> SortState a -> [Scored t] -> [Scored t]
loop [Scored t]
xs SortState a
st{bestScoreSeen :: Int
bestScoreSeen = forall a. Scored a -> Int
score Scored t
x} [Scored t]
acc
| Bool
otherwise
= [Scored t] -> SortState a -> [Scored t] -> [Scored t]
loop [Scored t]
xs SortState a
st [Scored t]
acc
data SortState a = SortState
{ forall a. SortState a -> Int
bestScoreSeen :: !Int
, forall a. SortState a -> Int
scoreWanted :: !Int
, forall a. SortState a -> Int
foundCount :: !Int
}
deriving Int -> SortState a -> ShowS
forall a. Int -> SortState a -> ShowS
forall a. [SortState a] -> ShowS
forall a. SortState a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SortState a] -> ShowS
$cshowList :: forall a. [SortState a] -> ShowS
show :: SortState a -> String
$cshow :: forall a. SortState a -> String
showsPrec :: Int -> SortState a -> ShowS
$cshowsPrec :: forall a. Int -> SortState a -> ShowS
Show