module QuickSearch.String.OneShot
    ( oneShot
    , oneShotTopNMatches
    , oneShotMatchesWithThreshold
    , damerauLevenshteinNorm
    , jaro
    , jaroWinkler
    ) where

import           Data.Hashable                  ( Hashable )
import           Data.Text.Metrics              ( damerauLevenshteinNorm
                                                , jaro
                                                , jaroWinkler
                                                )

import           QuickSearch.String             ( Entry(..)
                                                , Match
                                                , QuickSearch
                                                , Score
                                                , Scorer
                                                , buildQuickSearch
                                                , matchesWithThreshold
                                                , topNMatches
                                                )

{- | Turn a match retrieval function into a one-shot batch function.
   Instead of creating a QuickSearch for reuse, this creates it in the
   background and discards it when done.
-}
oneShot
    :: (Hashable uid1, Eq uid1, Hashable uid2, Eq uid2)
    => (  QuickSearch uid2
       -> Int
       -> Scorer
       -> String
       -> [Match Score (Entry String uid2)]
       )
  -- ^ Match retrieval function to be converted into a one-shot
    -> Int  -- ^ The reference number for the match retrieval function.
    -> [(String, uid1)]  -- ^ List of entries to be processed
    -> [(String, uid2)]  -- ^ List of entries making up the search space
    -> Scorer  -- ^ Similarity function with type (Text -> Text -> Ratio Int)
    -> [(Entry String uid1, [Match Score (Entry String uid2)])]
    -- ^ List of entries and their matches.
oneShot :: (QuickSearch uid2
 -> Int -> Scorer -> String -> [Match Int (Entry String uid2)])
-> Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
oneShot QuickSearch uid2
-> Int -> Scorer -> String -> [Match Int (Entry String uid2)]
f Int
n [(String, uid1)]
entries [(String, uid2)]
targets Scorer
scorer =
    let qs :: QuickSearch uid2
qs      = [(String, uid2)] -> QuickSearch uid2
forall uid.
(Hashable uid, Eq uid) =>
[(String, uid)] -> QuickSearch uid
buildQuickSearch [(String, uid2)]
targets
        results :: [[Match Int (Entry String uid2)]]
results = ((String, uid1) -> [Match Int (Entry String uid2)])
-> [(String, uid1)] -> [[Match Int (Entry String uid2)]]
forall a b. (a -> b) -> [a] -> [b]
map (QuickSearch uid2
-> Int -> Scorer -> String -> [Match Int (Entry String uid2)]
f QuickSearch uid2
qs Int
n Scorer
scorer (String -> [Match Int (Entry String uid2)])
-> ((String, uid1) -> String)
-> (String, uid1)
-> [Match Int (Entry String uid2)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, uid1) -> String
forall a b. (a, b) -> a
fst) [(String, uid1)]
entries
    in  [Entry String uid1]
-> [[Match Int (Entry String uid2)]]
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
forall a b. [a] -> [b] -> [(a, b)]
zip (((String, uid1) -> Entry String uid1)
-> [(String, uid1)] -> [Entry String uid1]
forall a b. (a -> b) -> [a] -> [b]
map (String, uid1) -> Entry String uid1
forall name uid. (name, uid) -> Entry name uid
Entry [(String, uid1)]
entries) [[Match Int (Entry String uid2)]]
results

{- | One-shot version of topNMatches. Builds the QuickSearch in the background
   and discards it when finished.
-}
oneShotTopNMatches
    :: (Hashable uid1, Eq uid1, Hashable uid2, Eq uid2)
    => Int  -- ^ N: Number of matches to return
    -> [(String, uid1)]  -- ^ List of entries to be processed
    -> [(String, uid2)]  -- ^ List of entries making up the search space
    -> Scorer  -- ^ Similarity function with type (Text -> Text -> Ratio Int)
    -> [(Entry String uid1, [Match Score (Entry String uid2)])]
  -- ^ List of entries and up to N of the best matches.
oneShotTopNMatches :: Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
oneShotTopNMatches = (QuickSearch uid2
 -> Int -> Scorer -> String -> [Match Int (Entry String uid2)])
-> Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
forall uid1 uid2.
(Hashable uid1, Eq uid1, Hashable uid2, Eq uid2) =>
(QuickSearch uid2
 -> Int -> Scorer -> String -> [Match Int (Entry String uid2)])
-> Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
oneShot QuickSearch uid2
-> Int -> Scorer -> String -> [Match Int (Entry String uid2)]
forall uid.
(Hashable uid, Eq uid) =>
QuickSearch uid
-> Int -> Scorer -> String -> [Match Int (Entry String uid)]
topNMatches

{- | One-shot version of matchesWithThreshold. Builds the QuickSearch in
   the background and discards it when finished.
-}
oneShotMatchesWithThreshold
    :: (Hashable uid1, Eq uid1, Hashable uid2, Eq uid2)
    => Int  -- ^ Score threshold above which to return matches
    -> [(String, uid1)]  -- ^ List of entries to be processed
    -> [(String, uid2)]  -- ^ List of entries making up the search space
    -> Scorer  -- ^ Similarity function with type (Text -> Text -> Ratio Int)
    -> [(Entry String uid1, [Match Score (Entry String uid2)])]
  -- ^ List of entries and their matches above the score threshold.
oneShotMatchesWithThreshold :: Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
oneShotMatchesWithThreshold = (QuickSearch uid2
 -> Int -> Scorer -> String -> [Match Int (Entry String uid2)])
-> Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
forall uid1 uid2.
(Hashable uid1, Eq uid1, Hashable uid2, Eq uid2) =>
(QuickSearch uid2
 -> Int -> Scorer -> String -> [Match Int (Entry String uid2)])
-> Int
-> [(String, uid1)]
-> [(String, uid2)]
-> Scorer
-> [(Entry String uid1, [Match Int (Entry String uid2)])]
oneShot QuickSearch uid2
-> Int -> Scorer -> String -> [Match Int (Entry String uid2)]
forall uid.
(Hashable uid, Eq uid) =>
QuickSearch uid
-> Int -> Scorer -> String -> [Match Int (Entry String uid)]
matchesWithThreshold