/**
 * \file dnn/src/cuda/int_fastdiv.cpp
 * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
 *
 * Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */


#include "src/cuda/int_fastdiv.cuh"
#include <cstring>

using namespace megdnn;
using namespace cuda;

Uint32Fastdiv::Uint32Fastdiv() {
    memset(this, 0, sizeof(Uint32Fastdiv));
}

Uint32Fastdiv& Uint32Fastdiv::operator = (uint32_t d) {
    megdnn_assert(d);
    m_divisor = d;
    MEGDNN_CONSTEXPR uint32_t MAX_U32 = ~0u;
    m_inc_dividend = 0;
    m_divisor_is_not_1 = ~0u;
    if (!(d & (d - 1))) {
        // power of 2
        m_mul = 1u << 31;
        int p = 0;
        while ((1u << p) < d)
            ++ p;
        megdnn_assert((1u << p) == d);
        m_shift = p ? p - 1 : 0;
        if (d == 1)
            m_divisor_is_not_1 = 0;
        return *this;
    }
    auto n_bound = uint64_t(d / 2 + 1) * MAX_U32;
    uint32_t shift = 32;
    while ((1ull << shift) < n_bound)
        ++ shift;
    uint64_t mdst = 1ull << shift;
    int64_t delta = d - mdst % d;
    m_mul = mdst / d + 1;
    if ((uint64_t)delta > d / 2) {
        delta -= d;
        -- m_mul;
        m_inc_dividend = 1;
    }
    megdnn_assert((uint64_t)m_mul * d == mdst + delta);
    megdnn_assert((uint64_t)std::abs(delta) * MAX_U32 < mdst);
    m_shift = shift - 32;
    return *this;
}

// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}
