#ifndef SZ3_ArithmeticEncoder_HPP
#define SZ3_ArithmeticEncoder_HPP

#include <cassert>
#include <iostream>

#include "SZ3/encoder/Encoder.hpp"
#include "SZ3/utils/ByteUtil.hpp"

namespace SZ3 {
#define ONE_FOURTH \
    (0x40000000000)  // 44 bits are absolutely enough to deal with a large dataset (support at most 16TB per process)
#define ONE_HALF (0x80000000000)
#define THREE_FOURTHS (0xC0000000000)
#define MAX_CODE (0xFFFFFFFFFFF)
#define MAX_INTERVALS 1048576  // the limit to the arithmetic coding (at most 2^(20) intervals)

template <class T>
class ArithmeticEncoder : public concepts::EncoderInterface<T> {
   public:
    struct Prob {
        size_t low;
        size_t high;
        int state;
    };

    struct AriCoder {
        int numOfRealStates;   // the # real states menas the number of states after the optimization of # intervals
        int numOfValidStates;  // the # valid states means the number of non-zero frequency cells (some states/codes
                               // actually didn't appear)
        size_t total_frequency;
        Prob *cumulative_frequency;  // used to encode data more efficiencly
    };

    //        ArithmeticEncoder(int stateNum, bool transform = false) {
    ArithmeticEncoder(bool transform = false) {
        //            assert(stateNum <= 4096 && "StateNum of Arithmetic Encoder should be <= 4096");
        //            ariCoder.numOfRealStates = stateNum;
        ariCoder.numOfRealStates = 0;
        ariCoder.numOfValidStates = 0;
        ariCoder.total_frequency = 0;
        ariCoder.cumulative_frequency = nullptr;
        this->transform = transform;
    }

    ~ArithmeticEncoder() override {
        if (ariCoder.cumulative_frequency != nullptr) {
            free(ariCoder.cumulative_frequency);
        }
    }

    void postprocess_encode() override {
        if (transform) {
            bins_transform.clear();
        }
    }

    void preprocess_decode() override{}

    void postprocess_decode() override{}

    void preprocess_encode(const std::vector<T> &bins, int stateNum) override {
        assert(stateNum <= 4096 && "StateNum of Arithmetic Encoder should be <= 4096");
        ariCoder.numOfRealStates = stateNum;
        const T *s = bins.data();
        size_t length = bins.size();
        if (transform) {
            bins_transform = bins;
            for (size_t i = 0; i < bins_transform.size(); i++) {
                T x = bins_transform[i];
                bins_transform[i] = fabs(x - ariCoder.numOfRealStates / 2) * 2;
                if (x - ariCoder.numOfRealStates / 2 < 0) {
                    bins_transform[i] -= 1;
                }
            }

            s = bins_transform.data();
        }

        size_t i;  // # states is in the range of integer.
        int index = 0;
        size_t *freq = static_cast<size_t *>(malloc(ariCoder.numOfRealStates * sizeof(size_t)));
        memset(freq, 0, ariCoder.numOfRealStates * sizeof(size_t));
        for (i = 0; i < length; i++) {
            index = s[i];
            freq[index]++;
        }

        int counter = 0;
        size_t _sum = 0, sum = 0, freqDiv = 0;
        ariCoder.cumulative_frequency = static_cast<Prob *>(malloc(ariCoder.numOfRealStates * sizeof(Prob)));

        memset(ariCoder.cumulative_frequency, 0, ariCoder.numOfRealStates * sizeof(Prob));

        if (length <= MAX_INTERVALS) {
            for (index = 0; index < ariCoder.numOfRealStates; index++) {
                if (freq[index]) {
                    sum += freq[index];
                    (ariCoder.cumulative_frequency[index]).low = _sum;
                    (ariCoder.cumulative_frequency[index]).high = sum;
                    (ariCoder.cumulative_frequency[index]).state = index;
                    _sum = sum;
                    counter++;
                }
            }
            ariCoder.numOfValidStates = counter;
            ariCoder.total_frequency = sum;
        } else {
            int intvSize = length % MAX_INTERVALS == 0 ? length / MAX_INTERVALS : length / MAX_INTERVALS + 1;
            for (index = 0; index < ariCoder.numOfRealStates; index++) {
                if (freq[index]) {
                    freqDiv =
                        freq[index] / intvSize;  // control the sum of frequency to be no greater than MAX_INTERVALS
                    if (freqDiv == 0) freqDiv = 1;
                    sum += freqDiv;
                    (ariCoder.cumulative_frequency[index]).low = _sum;
                    (ariCoder.cumulative_frequency[index]).high = sum;
                    (ariCoder.cumulative_frequency[index]).state = index;
                    _sum = sum;
                    counter++;
                }
            }
            ariCoder.numOfValidStates = counter;
            ariCoder.total_frequency = sum;
        }

        free(freq);
    }

    void save(uchar *&p) override {
        int numOfRealStates = ariCoder.numOfRealStates;
        int numOfValidStates = ariCoder.numOfValidStates;
        uint64_t total_frequency = ariCoder.total_frequency;
        Prob *cumulative_frequency = ariCoder.cumulative_frequency;

        // unsigned int outSize = 0;

        int32ToBytes_bigEndian(p, numOfRealStates);
        p += sizeof(int);
        int32ToBytes_bigEndian(p, numOfValidStates);
        p += sizeof(int);
        int64ToBytes_bigEndian(p, total_frequency);
        p += sizeof(uint64_t);
        int i = 0;
        if (total_frequency <= 65536) {
            uint16_t low, high;
            if (numOfRealStates <= 256) {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint16_t>(cumulative_frequency[i].high);
                    if (high != 0)  // if this state cell is not null
                    {
                        low = static_cast<uint16_t>(cumulative_frequency[i].low);
                        int16ToBytes_bigEndian(p, low);
                        p += sizeof(uint16_t);
                        int16ToBytes_bigEndian(p, high);
                        p += sizeof(uint16_t);
                        *(p++) = static_cast<unsigned char>(cumulative_frequency[i].state);
                        // if(((unsigned char)cumulative_frequency[i].state)==129)
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 5;  // 2*sizeof(uint16_t)+1
            } else if (numOfRealStates <= 65536) {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint16_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint16_t>(cumulative_frequency[i].low);
                        int16ToBytes_bigEndian(p, low);
                        p += sizeof(uint16_t);
                        int16ToBytes_bigEndian(p, high);
                        p += sizeof(uint16_t);
                        uint16_t state = static_cast<uint16_t>(cumulative_frequency[i].state);
                        int16ToBytes_bigEndian(p, state);
                        p += sizeof(uint16_t);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 6;
            } else {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint16_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint16_t>(cumulative_frequency[i].low);
                        int16ToBytes_bigEndian(p, low);
                        p += sizeof(uint16_t);
                        int16ToBytes_bigEndian(p, high);
                        p += sizeof(uint16_t);
                        int32ToBytes_bigEndian(p, cumulative_frequency[i].state);
                        p += sizeof(uint32_t);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 8;
            }
        } else if (total_frequency <= 4294967296) {
            uint32_t low, high;
            if (numOfRealStates <= 256) {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint32_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint32_t>(cumulative_frequency[i].low);
                        int32ToBytes_bigEndian(p, low);
                        p += sizeof(uint32_t);
                        int32ToBytes_bigEndian(p, high);
                        p += sizeof(uint32_t);
                        *(p++) = static_cast<unsigned char>(cumulative_frequency[i].state);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 9;
            } else if (numOfRealStates <= 65536) {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint32_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint32_t>(cumulative_frequency[i].low);
                        int32ToBytes_bigEndian(p, low);
                        p += sizeof(uint32_t);
                        int32ToBytes_bigEndian(p, high);
                        p += sizeof(uint32_t);
                        uint16_t state = static_cast<uint16_t>(cumulative_frequency[i].state);
                        int16ToBytes_bigEndian(p, state);
                        p += sizeof(uint16_t);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 10;
            } else {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint32_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint32_t>(cumulative_frequency[i].low);
                        int32ToBytes_bigEndian(p, low);
                        p += sizeof(uint32_t);
                        int32ToBytes_bigEndian(p, high);
                        p += sizeof(uint32_t);
                        int32ToBytes_bigEndian(p, cumulative_frequency[i].state);
                        p += sizeof(uint32_t);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 12;
            }
        } else {
            uint64_t low, high;
            if (numOfRealStates <= 256) {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint64_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint64_t>(cumulative_frequency[i].low);
                        int64ToBytes_bigEndian(p, low);
                        p += sizeof(uint64_t);
                        int64ToBytes_bigEndian(p, high);
                        p += sizeof(uint64_t);
                        *(p++) = static_cast<unsigned char>(cumulative_frequency[i].state);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 17;
            } else if (numOfRealStates <= 65536) {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint64_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint64_t>(cumulative_frequency[i].low);
                        int64ToBytes_bigEndian(p, low);
                        p += sizeof(uint64_t);
                        int64ToBytes_bigEndian(p, high);
                        p += sizeof(uint64_t);
                        uint16_t state = static_cast<uint16_t>(cumulative_frequency[i].state);
                        int16ToBytes_bigEndian(p, state);
                        p += sizeof(uint16_t);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 18;
            } else {
                for (i = 0; i < numOfRealStates; i++) {
                    high = static_cast<uint64_t>(cumulative_frequency[i].high);
                    if (high != 0) {
                        low = static_cast<uint64_t>(cumulative_frequency[i].low);
                        int64ToBytes_bigEndian(p, low);
                        p += sizeof(uint64_t);
                        int64ToBytes_bigEndian(p, high);
                        p += sizeof(uint64_t);
                        int32ToBytes_bigEndian(p, cumulative_frequency[i].state);
                        p += sizeof(uint32_t);
                    }
                }
                // outSize = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 20;
            }
        }
        // return outSize;
    }

    /**
     * Reconstruct AriCoder based on the bytes loaded from compressed data
     * @param AriCoder** ariCoder (ourput)
     * @param unsigned char* bytes (input)
     *
     * @return offset
     * */
    void load(const uchar *&p, size_t &remaining_length) override {
        //        int unpad_ariCoder(AriCoder **ariCoder, unsigned char *bytes) {
        int offset = 0;

        int numOfRealStates = ariCoder.numOfRealStates = bytesToInt32_bigEndian(p);
        p += sizeof(int);
        int numOfValidStates = ariCoder.numOfValidStates = bytesToInt32_bigEndian(p);
        p += sizeof(int);
        size_t total_frequency = ariCoder.total_frequency = bytesToInt64_bigEndian(p);
        p += sizeof(uint64_t);

        ariCoder.cumulative_frequency = static_cast<Prob *>(malloc(ariCoder.numOfRealStates * sizeof(Prob)));
        memset(ariCoder.cumulative_frequency, 0, ariCoder.numOfRealStates * sizeof(Prob));

        int i = 0;
        const uchar *low_p = nullptr, *high_p = nullptr, *state_p = nullptr;
        int state = 0;
        if (total_frequency <= 65536) {
            if (numOfRealStates <= 256) {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint16_t);
                    state_p = high_p + sizeof(uint16_t);
                    state = *state_p;
                    ariCoder.cumulative_frequency[state].low = bytesToInt16_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt16_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + 1;
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 5;  // 2*sizeof(uint16_t)+1
            } else if (numOfRealStates <= 65536) {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint16_t);
                    state_p = high_p + sizeof(uint16_t);
                    state = bytesToInt16_bigEndian(state_p);

                    ariCoder.cumulative_frequency[state].low = bytesToInt16_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt16_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + sizeof(uint16_t);
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 6;
            } else {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint16_t);
                    state_p = high_p + sizeof(uint16_t);
                    state = bytesToInt32_bigEndian(state_p);

                    ariCoder.cumulative_frequency[state].low = bytesToInt16_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt16_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + sizeof(uint32_t);
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 8;
            }
        } else if (total_frequency <= 4294967296) {
            if (numOfRealStates <= 256) {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint32_t);
                    state_p = high_p + sizeof(uint32_t);
                    state = *state_p;

                    ariCoder.cumulative_frequency[state].low = bytesToInt32_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt32_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + 1;
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 9;
            } else if (numOfRealStates <= 65536) {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint32_t);
                    state_p = high_p + sizeof(uint32_t);
                    state = bytesToInt16_bigEndian(state_p);

                    ariCoder.cumulative_frequency[state].low = bytesToInt32_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt32_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + sizeof(uint16_t);
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 10;
            } else {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint32_t);
                    state_p = high_p + sizeof(uint32_t);
                    state = bytesToInt32_bigEndian(state_p);

                    ariCoder.cumulative_frequency[state].low = bytesToInt32_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt32_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + sizeof(uint32_t);
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 12;
            }
        } else {
            if (numOfRealStates <= 256) {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint64_t);
                    state_p = high_p + sizeof(uint64_t);
                    state = *state_p;

                    ariCoder.cumulative_frequency[state].low = bytesToInt64_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt64_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + 1;
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 17;
            } else if (numOfRealStates <= 65536) {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint64_t);
                    state_p = high_p + sizeof(uint64_t);
                    state = bytesToInt16_bigEndian(state_p);

                    ariCoder.cumulative_frequency[state].low = bytesToInt64_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt64_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + sizeof(uint16_t);
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 18;
            } else {
                for (i = 0; i < numOfValidStates; i++) {
                    low_p = p;
                    high_p = low_p + sizeof(uint64_t);
                    state_p = high_p + sizeof(uint64_t);
                    state = bytesToInt32_bigEndian(state_p);

                    ariCoder.cumulative_frequency[state].low = bytesToInt64_bigEndian(low_p);
                    ariCoder.cumulative_frequency[state].high = bytesToInt64_bigEndian(high_p);
                    ariCoder.cumulative_frequency[state].state = state;

                    p = state_p + sizeof(uint32_t);
                }
                offset = 2 * sizeof(int) + sizeof(uint64_t) + ariCoder.numOfValidStates * 20;
            }
        }
        remaining_length -= offset;
    }

    /**
     * Arithmetic Encoding
     * @param AriCoder *ariCoder (input)
     * @param int *s (input)
     * @param size_t length (input)
     * @param unsigned char *out (output)
     * @param size_t *outSize (output)
     *
     * */
    //        void ari_encode(AriCoder *ariCoder, int *s, size_t length, unsigned char *out, size_t *outSize) {
    size_t encode(const std::vector<T> &bins, uchar *&bytes) override {
        const T *s = transform ? bins_transform.data() : bins.data();
        size_t length = transform ? bins_transform.size() : bins.size();
        //            unsigned char *bytes = out;
        size_t outSize = 0;

        int pending_bits = 0;
        size_t low = 0;
        size_t high = MAX_CODE;
        size_t i = 0, range = 0;
        size_t count = ariCoder.total_frequency;
        int c = 0, lackBits = 0;

        Prob *cumulative_frequency = ariCoder.cumulative_frequency;
        unsigned int buf = 0;

        for (i = 0; i < length; i++) {
            c = s[i];
            Prob p = cumulative_frequency[c];
            range = high - low + 1;
            high = low + (range * p.high / count) - 1;
            low = low + (range * p.low / count);
            for (;;) {
                if (high < ONE_HALF) {
                    buf = output_bit_0_plus_pending(pending_bits);
                    put_codes_to_output(buf, pending_bits + 1, &bytes, &lackBits, &outSize);
                    pending_bits = 0;
                } else if (low >= ONE_HALF) {
                    buf = output_bit_1_plus_pending(pending_bits);
                    put_codes_to_output(buf, pending_bits + 1, &bytes, &lackBits, &outSize);
                    pending_bits = 0;
                } else if (low >= ONE_FOURTH && high < THREE_FOURTHS) {
                    pending_bits++;
                    low -= ONE_FOURTH;
                    high -= ONE_FOURTH;
                } else
                    break;
                high <<= 1;
                high++;
                low <<= 1;
                high &= MAX_CODE;
                low &= MAX_CODE;
            }
        }
        pending_bits++;
        if (low < ONE_FOURTH) {
            buf = output_bit_0_plus_pending(pending_bits);
            put_codes_to_output(buf, pending_bits + 1, &bytes, &lackBits, &outSize);
        } else {
            buf = output_bit_1_plus_pending(pending_bits);
            put_codes_to_output(buf, pending_bits + 1, &bytes, &lackBits, &outSize);
        }
        bytes += 1;
        return outSize;
    }

    /**
     * Arithmetic Decoding algorithm
     * @param AriCoder *ariCoder (input): the encoder with the constructed frequency information
     * @param unsigned char *s (input): the compressed stream of bytes
     * @param size_t s_len (input): the number of bytes in the 'unsigned char *s'
     * @param size_t targetLength (input): the target number of elements in the type array
     * @param int *out (output) : the result (type array decompressed from the stream 's')
     *
     * */
    std::vector<T> decode(const uchar *&bytes, size_t targetLength) override {
        std::vector<T> out(targetLength);

        //        void ari_decode(AriCoder *ariCoder, unsigned char *s, size_t s_len, size_t targetLength, int *out) {
        size_t high = MAX_CODE;
        size_t low = 0, i = 0;
        size_t range = 0, scaled_value = 0;
        size_t total_frequency = ariCoder.total_frequency;
        const uchar *sp = bytes + 5;
        unsigned int offset = 4;
        size_t value = (bytesToInt64_bigEndian(bytes) >> 20);  // alignment with the MAX_CODE
        size_t s_counter = sizeof(int);

        for (i = 0; i < targetLength; i++) {
            range = high - low + 1;
            scaled_value = ((value - low + 1) * ariCoder.total_frequency - 1) / range;
            Prob *p = getCode(scaled_value);
            //                out[i] = p->state;  //output the state to the 'out' array
            if (transform) {
                T x = p->state;  // output the state to the 'out' array
                if (x % 2 == 0) {
                    out[i] = ariCoder.numOfRealStates / 2 + std::ceil(x / 2.0);
                } else {
                    out[i] = ariCoder.numOfRealStates / 2 - std::ceil(x / 2.0);
                }
            } else {
                out[i] = p->state;  // output the state to the 'out' array
            }

            if (i == targetLength - 1) {
                break;
            }
            high = low + (range * p->high) / total_frequency - 1;
            low = low + (range * p->low) / total_frequency;

            for (;;) {
                if (high < ONE_HALF) {
                    // do nothing, bit is a zero
                } else if (low >= ONE_HALF) {
                    value -= ONE_HALF;  // subtract one half from all three code values
                    low -= ONE_HALF;
                    high -= ONE_HALF;
                } else if (low >= ONE_FOURTH && high < THREE_FOURTHS) {
                    value -= ONE_FOURTH;
                    low -= ONE_FOURTH;
                    high -= ONE_FOURTH;
                } else
                    break;
                low <<= 1;
                high <<= 1;
                high++;
                value <<= 1;
                // load one bit from the input byte stream
                //                    if (s_counter < s_len) {
                value += get_bit(sp, offset++);
                if (offset == 8) {
                    sp++;
                    s_counter++;
                    offset = 0;
                }
                //                    }
            }
        }
        bytes += s_counter;
        return out;
    }

    AriCoder ariCoder;

   private:
    bool transform;
    std::vector<T> bins_transform;

    inline void output_bit_1(unsigned int *buf) {
        (*buf) = (*buf) << 1;
        (*buf) |= 1;
    }

    inline void output_bit_0(unsigned int *buf) {
        (*buf) = (*buf) << 1;
        //(*byte) |= 0; //actually doesn't have to set the bit to 0
    }

    // TODO: problematic
    inline unsigned int output_bit_1_plus_pending(int pending_bits) {
        unsigned int buf = 0, pbits = pending_bits;
        output_bit_1(&buf);
        while (pbits--) output_bit_0(&buf);
        buf = buf << (32 - (pending_bits +
                            1));  // alignment to the left leading bit, which would be easier for the final output
        return buf;
    }

    inline unsigned int output_bit_0_plus_pending(int pending_bits) {
        unsigned int buf = 0, pbits = pending_bits;
        // output_bit_0(&buf);
        while (pbits--) output_bit_1(&buf);
        buf = buf << (32 - (pending_bits + 1));  // alignment to the left leading bit
        return buf;
    }

    /**
     * Get the integer code based on Arithmetic Coding Value
     * @param AriCoder *ariCoder (input)
     * @param size_t scaled_value (input)
     *
     * @return Prob* (output)
     *
     * */
    Prob *getCode(size_t scaled_value) {
        int numOfRealStates = ariCoder.numOfRealStates;
        int i = 0;
        Prob *p = ariCoder.cumulative_frequency;
        for (i = 0; i < numOfRealStates; i++, p++) {
            if (scaled_value < p->high) break;
        }
        return p;
    }

    /**
     * Get one bit from the input stream of bytes
     * @param unsigned char* p (input): the current location to be read (byte) of the byte stream
     * @param int offset (input): the offset of the specified byte in the byte stream
     *
     * @return unsigned char (output) : 1 or 0
     * */
    inline unsigned char get_bit(const uchar *p, int offset) { return ((*p) >> (7 - offset)) & 0x01; }

    /**
     * put 'buf_nbBits' bits represented by buf into a long byte stream (the current output byte pointer is p, where
     * offset is the number of bits already filled out for this byte so far)
     * */
    void put_codes_to_output(unsigned int buf, int bitSize, unsigned char **p, int *lackBits, size_t *outSize) {
        int byteSize, byteSizep;
        if (*lackBits == 0) {
            byteSize = bitSize % 8 == 0 ? bitSize / 8
                                        : bitSize / 8 + 1;  // it's equal to the number of bytes involved (for *outSize)
            byteSizep = bitSize >> 3;                       // it's used to move the pointer p for next data
            int32ToBytes_bigEndian(*p, buf);
            (*p) += byteSizep;
            *outSize += byteSize;
            (*lackBits) = bitSize % 8 == 0 ? 0 : 8 - bitSize % 8;
        } else {
            **p = (**p) | static_cast<unsigned char>(buf >> (32 - *lackBits));
            if ((*lackBits) < bitSize) {
                (*p)++;
                int newCode = buf << (*lackBits);
                int32ToBytes_bigEndian(*p, newCode);
                bitSize -= *lackBits;
                byteSizep = bitSize >> 3;  // =bitSize/8
                byteSize = bitSize % 8 == 0 ? byteSizep : byteSizep + 1;
                *p += byteSizep;
                (*outSize) += byteSize;
                (*lackBits) = bitSize % 8 == 0 ? 0 : 8 - bitSize % 8;
            } else {
                (*lackBits) -= bitSize;
                if (*lackBits == 0) (*p)++;
            }
        }
    }
};

}  // namespace SZ3
#endif /* ----- #ifndef _ArithmeticEncoder_H  ----- */
