# This file is part of Jlsca, license is GPLv3, see https://www.gnu.org/licenses/gpl-3.0.en.html
#
# Author: Cees-Bart Breunesse


export Leakage,Bit,HW,leak,hw

abstract type Leakage end

function show(io::IO, leakages::Vector{Leakage}) 
  if length(leakages) == 0
    print(io, "No leakages")
  else
    print(io, leakages[1])
    if length(leakages) > 1
      map((x -> (print(io,","); print(io,x))), leakages[2:end])
    end
  end
end

# some leakages for CPA

export ID

"""
Identity leakage
"""
struct ID <: Leakage end

leak(this::ID, x) = x

"""
Bit-wise leakage
"""
struct Bit <: Leakage
  idx::Int
end

show(io::IO, a::Bit) = print(io, "bit$(a.idx)")

leak(this::Bit, intermediate::Unsigned) = UInt8((intermediate >> this.idx) & 1)

"""
Hamming weight leakage
"""
struct HW <: Leakage end

show(io::IO, a::HW) = print(io, "HW")

leak(this::HW, intermediate::Union{UInt8,UInt16,UInt32,UInt128}) = hw(intermediate)

const hw_table = [0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,0x05,0x06,0x06,0x07,0x06,0x07,0x07,0x08]

function hw(x::UInt8)
  return @inbounds hw_table[x + 1]
end

function hw(x::UInt16)
  return hw(UInt8(x>>8)) + hw(UInt8(x & 0xff))
end

function hw(x::UInt32)
  ret::UInt8 = 0
  for i in 0:3
    ret += hw(UInt8((x >> (i*8)) & 0xff))
  end
  return ret
end

function hw(x::UInt128)
  ret::UInt8 = 0
  for i in 0:3
    ret += hw(UInt32((x >> (i*32)) & 0xffffffff))
  end
  return ret
end

