packages feed

moonlight-delta-0.1.0.0: bench/patch/Patch/Hackage.hs

{-# LANGUAGE BangPatterns #-}

module Patch.Hackage
  ( prepareHackagePatchComposeFixture,
    prepareHackagePatchApplyFixture,
    prepareHackagePatchReplayFixture,
    hackagePatchComposeConstruct,
    hackagePatchApplyConstruct,
    hackageReplaySequentially,
    hackagePatchMapWeight,
    toHackagePatchMap,
    hackagePatchMapCompose,
    hackagePatchMapApply,
  )
where

import Control.Exception
  ( throwIO,
  )
import Data.Map.Strict
  ( Map,
  )
import Data.Map.Strict qualified as Map
import BenchSupport
  ( BenchmarkFixtureFailure (..),
    assertBenchmarkAgreement,
    forceBenchmarkFixture,
    maybeIntWeight,
  )
import Patch.Types
import Moonlight.Delta.Patch
  ( Patch,
  )
import Moonlight.Delta.Patch qualified as Patch

prepareHackagePatchComposeFixture :: PatchComposeFixture -> IO HackagePatchComposeFixture
prepareHackagePatchComposeFixture fixture =
  case Patch.compose (pcfNewer fixture) (pcfOlder fixture) of
    Left err ->
      throwIO (BenchmarkFixtureFailure "patch compose hackage projection" (show err))
    Right expectedComposed -> do
      let !hackageFixture =
            HackagePatchComposeFixture
              { hpcfNewer = toHackagePatchMap (pcfNewer fixture),
                hpcfOlder = toHackagePatchMap (pcfOlder fixture)
              }
      assertBenchmarkAgreement
        "patch compose hackage projection"
        (toHackagePatchMap expectedComposed)
        (hackagePatchComposeConstruct hackageFixture)
      forceBenchmarkFixture hackageFixture

prepareHackagePatchApplyFixture :: PatchApplyFixture -> IO HackagePatchApplyFixture
prepareHackagePatchApplyFixture fixture =
  case Patch.apply (pafPatch fixture) (pafState fixture) of
    Left err ->
      throwIO (BenchmarkFixtureFailure "patch apply hackage projection" (show err))
    Right expectedState -> do
      let !hackageFixture =
            HackagePatchApplyFixture
              { hpafState = pafState fixture,
                hpafPatch = toHackagePatchMap (pafPatch fixture)
              }
      assertBenchmarkAgreement
        "patch apply hackage projection"
        expectedState
        (hackagePatchApplyConstruct hackageFixture)
      forceBenchmarkFixture hackageFixture

prepareHackagePatchReplayFixture :: PatchReplayFixture -> IO HackagePatchReplayFixture
prepareHackagePatchReplayFixture fixture =
  case Patch.replay (prfPatches fixture) (prfInitialState fixture) of
    Left err ->
      throwIO (BenchmarkFixtureFailure "patch replay hackage projection" (show err))
    Right expectedState -> do
      let !hackageFixture =
            HackagePatchReplayFixture
              { hprfInitialState = prfInitialState fixture,
                hprfPatches = fmap toHackagePatchMap (prfPatches fixture)
              }
      assertBenchmarkAgreement
        "patch replay hackage projection"
        expectedState
        (hackageReplaySequentially (hprfInitialState hackageFixture) (hprfPatches hackageFixture))
      forceBenchmarkFixture hackageFixture

{-# NOINLINE hackagePatchComposeConstruct #-}
hackagePatchComposeConstruct ::
  HackagePatchComposeFixture ->
  HackagePatchMap
hackagePatchComposeConstruct fixture =
  hackagePatchMapCompose (hpcfNewer fixture) (hpcfOlder fixture)

{-# NOINLINE hackagePatchApplyConstruct #-}
hackagePatchApplyConstruct ::
  HackagePatchApplyFixture ->
  Map Int Int
hackagePatchApplyConstruct fixture =
  hackagePatchMapApply (hpafPatch fixture) (hpafState fixture)

hackageReplaySequentially ::
  Map Int Int ->
  [HackagePatchMap] ->
  Map Int Int
hackageReplaySequentially =
  foldl' (\ !state patchValue -> hackagePatchMapApply patchValue state)

hackagePatchMapWeight :: HackagePatchMap -> Int
hackagePatchMapWeight patchMap =
  Map.foldlWithKey' weighEntry 0 (unHackagePatchMap patchMap)
  where
    weighEntry :: Int -> Int -> Maybe Int -> Int
    weighEntry !total key maybeValue =
      total + key + maybeIntWeight maybeValue

toHackagePatchMap :: Patch Int Int -> HackagePatchMap
toHackagePatchMap =
  HackagePatchMap
    . Map.fromDistinctAscList
    . Patch.foldWithKey
      (\_key rest -> rest)
      (\key after rest -> (key, Just after) : rest)
      (\key _before rest -> (key, Nothing) : rest)
      (\key _before after rest -> (key, Just after) : rest)
      []

hackagePatchMapCompose :: HackagePatchMap -> HackagePatchMap -> HackagePatchMap
hackagePatchMapCompose (HackagePatchMap newer) (HackagePatchMap older) =
  HackagePatchMap (Map.union newer older)

hackagePatchMapApply :: HackagePatchMap -> Map Int Int -> Map Int Int
hackagePatchMapApply (HackagePatchMap patchMap) old =
  let !insertions = Map.mapMaybe id patchMap
      !deletions = Map.mapMaybe hackageDeletion patchMap
   in insertions `Map.union` (old `Map.difference` deletions)

hackageDeletion :: Maybe Int -> Maybe ()
hackageDeletion maybeValue =
  case maybeValue of
    Nothing ->
      Just ()
    Just _ ->
      Nothing