Haskell (85 bytes)
The silly version that maps to at most 54:
import Foreign
p y=2*y.&.y<1
d=filter(p.xor 170)[0..255]
f x=sum[1|y<-d,y<x]
g=(d!!)
f
encodes a byte and g
decodes.
The next version should be much faster, consists of a bit of bit manipulation, but covers the range from 0 to 63 and requires 92 bytes:
(#)=div
(&)=mod
(k%j)e x=k*e(x#j)&k+e(x&j)&k
f=8%16$(#128).(453*)
g=16%8$(737715168#).(16^)
Here is the expanded, hopefully more comprehensible version:
module Compress8to6Bit where
import Data.Bits (xor, shiftL, shiftR, (.|.), (.&.))
import Data.Word (Word8)
import qualified Test.QuickCheck as QC
{-
> map compressNibble [0,2,3,8,10,11,14,15]
[0,7,2,4,3,6,1,5]
-}
compressNibble :: Int -> Int
compressNibble x = shiftR (453*x) 7 .&. 7
compress :: Int -> Int
compress x =
shiftL (compressNibble (shiftR x 4)) 3 .|. compressNibble (x .&. 15)
decompressNibble :: Int -> Int
decompressNibble x = shiftR 0x2BF8A3E0 (x*4) .&. 15
decompress :: Int -> Int
decompress x =
shiftL (decompressNibble (shiftR x 3)) 4 .|. decompressNibble (x .&. 7)
inverseProp :: Word8 -> QC.Property
inverseProp byte =
let k = fromIntegral byte
kx = xor 0xAA k
in kx .&. shiftL kx 1 == 0
QC.==>
k == decompress (compress k)
How?
I found the multiplication trick of Arnauld interesting and found that you can do without modulo but simple shift and masking when using the factor 453.
But after all you could also encode dictionaries for compression and decompression (on a 32-bit machine you need to use Int64 instead of Int):
compressNibble :: Int -> Int
compressNibble x = shiftR 0o7600540300002100 (x*3) .&. 7
decompressNibble :: Int -> Int
decompressNibble x = shiftR 0xFEBA8320 (x*4) .&. 15
This one only needs 16 bit for the dictionary but requires popCount
:
compressNibble :: Int -> Int
compressNibble x = popCount (0x6686 .&. (shiftL 1 x - 1) :: Int)