/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

package std.overflow

const NATIVE_64_BIT_WIDTH: UInt64 = 64
const NATIVE_32_BIT_WIDTH: UInt64 = 32
const NATIVE_64_MAX_SHIFT: UInt64 = NATIVE_64_BIT_WIDTH - 1
const NATIVE_32_MAX_SHIFT: UInt64 = NATIVE_32_BIT_WIDTH - 1
const NATIVE_64_SHIFT_MASK: UInt64 = 0b111111
const NATIVE_32_SHIFT_MASK: UInt64 = 0b11111

let isNative64 = if (UInt64(!UIntNative(0)) == UInt64.Max) {
    true
} else if (UInt64(!UIntNative(0)) == UInt64(UInt32.Max)) {
    false
} else {
    throw Exception("Unrecognized IntNative")
}

func nativeBitWidth(): UInt64 {
    if (isNative64) {
        NATIVE_64_BIT_WIDTH
    } else {
        NATIVE_32_BIT_WIDTH
    }
}

func nativeMaxShift(): UInt64 {
    if (isNative64) {
        NATIVE_64_MAX_SHIFT
    } else {
        NATIVE_32_MAX_SHIFT
    }
}

func nativeShiftConfig(): (UInt64, UInt64) {
    if (isNative64) {
        (NATIVE_64_BIT_WIDTH, NATIVE_64_SHIFT_MASK)
    } else {
        (NATIVE_32_BIT_WIDTH, NATIVE_32_SHIFT_MASK)
    }
}

public interface CheckedOp<T> {
    func checkedAdd(y: T): ?T
    func checkedSub(y: T): ?T
    func checkedMul(y: T): ?T
    func checkedDiv(y: T): ?T
    func checkedMod(y: T): ?T
    func checkedInc(): ?T
    func checkedDec(): ?T
    func checkedNeg(): ?T
    func checkedShl(y: UInt64): ?T
    func checkedShr(y: UInt64): ?T
}

public interface CheckedPow {
    func checkedPow(y: UInt64): ?Int64
}

func checkedShl(x: Int8, y: UInt64): Option<Int8> {
    if (y >= 8) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: Int16, y: UInt64): Option<Int16> {
    if (y >= 16) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: Int32, y: UInt64): Option<Int32> {
    if (y >= 32) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: Int64, y: UInt64): Option<Int64> {
    if (y >= 64) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: IntNative, y: UInt64): Option<IntNative> {
    let bitWidth = nativeBitWidth()
    if (y >= bitWidth) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: UInt8, y: UInt64): Option<UInt8> {
    if (y >= 8) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: UInt16, y: UInt64): Option<UInt16> {
    if (y >= 16) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: UInt32, y: UInt64): Option<UInt32> {
    if (y >= 32) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: UInt64, y: UInt64): Option<UInt64> {
    if (y >= 64) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShl(x: UIntNative, y: UInt64): Option<UIntNative> {
    let bitWidth = nativeBitWidth()
    if (y >= bitWidth) {
        None
    } else {
        Some(x << y)
    }
}

func checkedShr(x: Int8, y: UInt64): Option<Int8> {
    if (y >= 8) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: Int16, y: UInt64): Option<Int16> {
    if (y >= 16) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: Int32, y: UInt64): Option<Int32> {
    if (y >= 32) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: Int64, y: UInt64): Option<Int64> {
    if (y >= 64) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: IntNative, y: UInt64): Option<IntNative> {
    let bitWidth = nativeBitWidth()
    if (y >= bitWidth) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: UInt8, y: UInt64): Option<UInt8> {
    if (y >= 8) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: UInt16, y: UInt64): Option<UInt16> {
    if (y >= 16) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: UInt32, y: UInt64): Option<UInt32> {
    if (y >= 32) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: UInt64, y: UInt64): Option<UInt64> {
    if (y >= 64) {
        None
    } else {
        Some(x >> y)
    }
}

func checkedShr(x: UIntNative, y: UInt64): Option<UIntNative> {
    let bitWidth = nativeBitWidth()
    if (y >= bitWidth) {
        None
    } else {
        Some(x >> y)
    }
}