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

com.github.myibu.algorithm.endode.GolombEncoder Maven / Gradle / Ivy

package com.github.myibu.algorithm.endode;

import com.github.myibu.algorithm.data.Bit;
import com.github.myibu.algorithm.data.Bits;

/**
 * Golomb code
 * see https://en.wikipedia.org/wiki/Golomb_coding#Simple_algorithm
 * @author myibu
 * Created on 2021/10/12
 */
public class GolombEncoder implements Encoder {
    /**
     * encode n to binary bits based on argument m
     * @param n the value to encode
     * @param m m, like 5
     * @return the length of encoded bits
     */
    public Bits encode(int n, int m) {
        Bits bits = new Bits();
        // 向下取整
        int q = (int)Math.floor(n * 1.0 / m);
        bits.append(Bits.ofOne(Math.max(0, q))).append(Bits.ofZero(1));
        int r = n % m;
        int k = (int)(Math.ceil(Math.log(m) / Math.log(2)));
        if ((m & 0x01) == 0) {
            return bits.append(encodeToBinary(r, k));
        } else {
            // truncated binary encoding
            if (r < Math.pow(2, k) - m) {
                return bits.append(encodeToTruncatedBinary(r, m));
            } else {
                return bits.append(encodeToTruncatedBinary(r, m));
            }
        }
    }

    public static Bits encodeToTruncatedBinary(int x, int n) {
        // Set k = floor(log2(n)), i.e., k such that 2^k <= n < 2^(k+1).
        int k = 0, t = n;
        while (t > 1) { k++;  t >>= 1; }

        // Set u to the number of unused codewords = 2^(k+1) - n.
        int u = (1 << k+1) - n;

        if (x < u)  return encodeToBinary(x, k);
        else  return encodeToBinary(x+u, k+1);
    }

    public static Bits encodeToBinary(int x, int len) {
        Bits s = new Bits();
        while (x != 0) {
            if ((x & 0x01) == 0)  s = Bits.ofZero().append(s);
            else  s = Bits.ofOne().append(s);
            x >>= 1;
        }
        while (s.length() < len)  s = Bits.ofZero().append(s);
        return s;
    }

    /**
     * decode binary bits to n
     * @param bits encoded binary bits
     * @param m m, like 5
     * @return decoded value
     */
    public int decode(Bits bits, int m) {
        // To decode, read the first k bits.
        // If they encode a value less than u, decoding is complete.
        // Otherwise, read an additional bit and subtract u from the result.
        boolean isRStart = false;
        Bits qb = new Bits(), rb = new Bits();
        for (Bit bit: bits) {
            if (!isRStart && bit == Bit.ZERO) {
                isRStart = true;
                continue;
            }
            if (!isRStart) {
                qb.append(bit);
            } else {
                rb.append(bit);
            }
        }
        int q = qb.length();
        int r = 0;
        if ((m & 0x01) == 0) {
            r = encodeToBinary(rb);
        } else {
            r = decodeTruncatedBinary(rb, m);
        }
        return q * m + r;
    }

    public static int decodeTruncatedBinary(Bits bits, int m) {
        // Set k = floor(log2(n)), i.e., k such that 2^k <= n < 2^(k+1).
        int k = 0, t = m;
        while (t > 1) { k++;  t >>= 1; }
        // Set u to the number of unused codewords = 2^(k+1) - n.
        int u = (1 << k+1) - m;
        int x = encodeToBinary(bits);
        return (x < u) ? x : (x - u);
    }

    public static int encodeToBinary(Bits bits) {
        int x = 0;
        for (int i = 0; i < bits.length(); i++) {
            x += (bits.get(i).value() << (bits.length() - i - 1));
        }
        return x;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy