All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.leialearns.logic.utilities.PrefixFreeBigInt.scala Maven / Gradle / Ivy

The newest version!
package org.leialearns.logic.utilities

import org.slf4j.LoggerFactory
import java.math.BigInteger
import java.io.{IOException, Reader}

object PrefixFreeBigInt {
  val logger = LoggerFactory.getLogger(getClass)
  val one = BigInt.int2bigInt(1)

  /**
    * Returns an ascii form of the binary representation of the given big integer. This form uses the capitals
    * 'O' and 'I' to distinguish them from the numeric digits that are used in composite representations of
    * prefix-free codes.
    *
    * @param n The number to represent
    * @return The string of binary digits
    */
  def toBinary(n: BigInt): String = {
    n toString 2 replace ('0', 'O') replace ('1', 'I')
  }

  /** @see #toBinary(BigInt) */
  def toBinary(n: BigInteger): String = {
    toBinary(new BigInt(n))
  }

  /**
    * Returns an ascii form of the prefix-free encoding of the given number. No prefix-free encoding is a prefix of
    * the prefix-free encoding of another number. The actual bit sequence consisting of the capitals 'O' and 'I' is
    * enriched with digits and symbols to enhance the readability of the code.
    *
    * @param n The number to represent
    * @return The prefix-free code for the given number
    */
  def prefixEncode(n: BigInt): String = {
    logger.debug(s"Start prefix encode big integer: [$n]")
    if (n < 0) {
      throw new IllegalArgumentException("Value should be non-negative")
    }
    def chunks = prefixEncodeChunks(n + 1, 'I', Nil)
    logger.trace(s"Chunks: [$chunks}]")
    def parts = chunks.foldRight("" :: Nil)(appendChunk)
    parts.mkString
  }

  /** @see #prefixEncode(BigInt) */
  def prefixEncode(n: BigInteger): String = {
    prefixEncode(new BigInt(n))
  }

  def descriptionLength(n: BigInt): Int = {
    logger.debug(s"Start description length big integer: [$n]")
    if (n < 0) {
      throw new IllegalArgumentException("Value should be non-negative")
    }
    prefixEncodeChunks(n + 1, 'I', Nil).foldLeft(0)(addChunk)
  }

  private def appendChunk(chunk: Triple[Char,String,BigInt], pieces: List[String]): List[String] = {
    val (lastChunkFlag, remainder, n) = chunk
    val newList = lastChunkFlag.toString :: ":1" :: remainder :: "(" :: n.toString() :: ")" :: pieces
    if (n == one) {
      newList
    } else {
      "/" :: newList
    }
  }

  private def addChunk(accumulator: Int, chunk: Triple[Char,String,BigInt]): Int = {
    accumulator + 1 + chunk._2.length
  }

  private def prefixEncodeChunks(n: BigInt, lastChunkFlag: Char, extra: List[Triple[Char,String,BigInt]]): List[Triple[Char,String,BigInt]] = {
    logger.trace(s"Prefix encode big integer: [$n]")
    if (n == one) {
      (lastChunkFlag, "", n) :: extra
    } else {
      val remainder = toBinary(n).substring(1)
      prefixEncodeChunks(remainder.length, 'O', (lastChunkFlag, remainder, n) :: extra)
    }
  }

  def prefixDecode(reader: Reader): BigInt = {
    var value: BigInt = 0
    var isLength = false
    var length = 0L
    do {
      isLength = readBit(reader) == ZERO
      length = value.longValue()
      value = 1
      for (i <- 0L to length - 1L) {
        value = value << 1
        if (readBit(reader) == ONE) {
          value += 1
        }
      }
    } while (isLength)
    value - 1
  }

  def readBit(reader: Reader): Bit = {
    val n = reader.read()
    if (n == -1) {
      throw new IOException("End of reader")
    }
    n.asInstanceOf[Char] match {
      case 'O' => ZERO
      case 'I' => ONE
      case _ => readBit(reader)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy