diff options
Diffstat (limited to 'src/ByteUtils.scala')
-rw-r--r-- | src/ByteUtils.scala | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/ByteUtils.scala b/src/ByteUtils.scala new file mode 100644 index 0000000..723164d --- /dev/null +++ b/src/ByteUtils.scala @@ -0,0 +1,114 @@ +package ixee.cryptopals.utils + +import FunctionUtils.tup +import ConversionUtils._ + +object ByteUtils { + trait SizedNumeric[T] { + def manifest: Manifest[T] + def byteSize: Int + def bitSize: Int + def fromLong(l: Long): T + def toLong(l: T): Long + def buildFrom[U : SizedNumeric : BitOps](from: U): T = fromLong(from.toLong) + def truncate[U : SizedNumeric : BitOps](from: U): T = buildFrom(from) + override def toString = s"SizedNumeric[${manifest}]" + } + + trait BitOps[T] { + def @>>>(t: T, x: Int): T + def @<<(t: T, x: Int): T + def @|(a: T, b: T): T + def @&(a: T, b: T): T + def @^(a: T, b: T): T + def @+(a: T, b: T): T + def @-(a: T, b: T): T + } + + implicit val byteOps: BitOps[Byte] = new BitOps[Byte] { + def @>>>(t: Byte, x: Int): Byte = (t >>> x).toByte + def @<<(t: Byte, x: Int): Byte = (t << x).toByte + def @|(a: Byte, b: Byte): Byte = (a | b).toByte + def @&(a: Byte, b: Byte): Byte = (a & b).toByte + def @^(a: Byte, b: Byte): Byte = (a ^ b).toByte + def @+(a: Byte, b: Byte): Byte = (a + b).toByte + def @-(a: Byte, b: Byte): Byte = (a - b).toByte + } + + implicit val intOps: BitOps[Int] = new BitOps[Int] { + def @>>>(t: Int, x: Int): Int = t >>> x + def @<<(t: Int, x: Int): Int = (t << x) + def @|(a: Int, b: Int): Int = a | b + def @&(a: Int, b: Int): Int = a & b + def @^(a: Int, b: Int): Int = a ^ b + def @+(a: Int, b: Int): Int = a + b + def @-(a: Int, b: Int): Int = a - b + } + + implicit class WithBitOpts[T : BitOps](x: T) { + def @>>>(b: Int): T = implicitly[BitOps[T]].@>>>(x, b) + def @<<(b: Int): T = implicitly[BitOps[T]].@<<(x, b) + def @|(b: T): T = implicitly[BitOps[T]].@|(x, b) + def @&(b: T): T = implicitly[BitOps[T]].@&(x, b) + def @^(b: T): T = implicitly[BitOps[T]].@^(x, b) + def @+(b: T): T = implicitly[BitOps[T]].@+(x, b) + def @-(b: T): T = implicitly[BitOps[T]].@-(x, b) + } + + implicit class SizedWithByteInfo[T : SizedNumeric : BitOps](x: T) { + def byteSize = implicitly[SizedNumeric[T]].byteSize + def octetSize = byteSize * 2 + def bitSize = implicitly[SizedNumeric[T]].bitSize + def toLong = implicitly[SizedNumeric[T]].toLong(x) + def liftedTo[U : SizedNumeric]: U = { + val uSize = implicitly[SizedNumeric[U]] + val tSize = implicitly[SizedNumeric[T]] + if (uSize.byteSize < tSize.byteSize) { + throw new RuntimeException(s"Target size ($uSize) is smaller than the source size ($tSize)") + } + + uSize.buildFrom(x) + } + def hex: String = s"%0${octetSize}x" format x + def bitsSet: Int = { + var bits = 0 + var num = x + + while (num.toLong > 0) { + bits = bits + 1 + num = num @& (num @- 1.truncatedTo[T]) + } + + bits + } + def truncatedTo[U : SizedNumeric]: U = { + implicitly[SizedNumeric[U]].truncate(x) + } + } + + def sizedNumeric[T : Manifest](bytes: Int)(from: Long => T)(to: T => Long) = new SizedNumeric[T] { + def manifest = implicitly[Manifest[T]] + def byteSize = bytes + def bitSize = bytes * 8 + def toLong(t: T) = to(t) + def fromLong(l: Long) = from(l) + } + + implicit val intSized = sizedNumeric(4) { x: Long => x.toInt } { _.toLong } + implicit val shortized = sizedNumeric(2) { x: Long => x.toShort } { _.toLong } + implicit val byteSized = sizedNumeric(1) { x: Long => x.toByte } { _.toLong } + + implicit class SeqByteOps[T : SizedNumeric : BitOps](seq: Seq[T]) { + def xor(other: Seq[T]): Seq[T] = + seq.zip(other).map(tup(_ @^ _)) + } + + def hammingDistance(a: String, b: String): Int = + hammingDistance(a.asBytes, b.asBytes) + + def hammingDistance[T : BitOps : SizedNumeric](a: Seq[T], b: Seq[T]): Int = + (a xor b).map(_.bitsSet).reduce(_ @+ _) + + def avgHammingDistance[T : BitOps : SizedNumeric](xs: Seq[Seq[T]]): Double = + xs.sliding(2).map({ case Stream(a: Seq[T], b: Seq[T]) => hammingDistance(a, b) }).reduce(_ @+ _) / xs.length.toDouble +} |