module Rattletrap.Encode.CompressedWord ( putCompressedWord ) where import Rattletrap.Type.CompressedWord import qualified Data.Binary.Bits.Put as BinaryBits import qualified Data.Bits as Bits putCompressedWord :: CompressedWord -> BinaryBits.BitPut () putCompressedWord compressedWord = let limit = compressedWordLimit compressedWord value = compressedWordValue compressedWord maxBits = getMaxBits limit in putCompressedWordStep limit value maxBits 0 0 putCompressedWordStep :: Word -> Word -> Int -> Int -> Word -> BinaryBits.BitPut () putCompressedWordStep limit value maxBits position soFar = if position < maxBits then do let x = Bits.shiftL 1 position :: Word if maxBits > 1 && position == maxBits - 1 && soFar + x > limit then pure () else do let bit = Bits.testBit value position BinaryBits.putBool bit let delta = if bit then x else 0 putCompressedWordStep limit value maxBits (position + 1) (soFar + delta) else pure () getMaxBits :: Word -> Int getMaxBits x = let n :: Int n = max 1 (ceiling (logBase (2 :: Double) (fromIntegral (max 1 x)))) in if x < 1024 && x == 2 ^ n then n + 1 else n