/*
 * 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.
 */

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.

package std.overflow

@Intrinsic
func saturatingAdd<T>(x: T, y: T): T

@Intrinsic
func saturatingSub<T>(x: T, y: T): T

@Intrinsic
func saturatingMul<T>(x: T, y: T): T

@Intrinsic
func saturatingDiv<T>(x: T, y: T): T

@Intrinsic
func saturatingMod<T>(x: T, y: T): T

@Intrinsic
func saturatingPow(x: Int64, y: UInt64): Int64

@Intrinsic
func saturatingInc<T>(x: T): T

@Intrinsic
func saturatingNeg<T>(x: T): T

@Intrinsic
func saturatingDec<T>(x: T): T

public interface SaturatingOp<T> {
    func saturatingAdd(y: T): T

    func saturatingSub(y: T): T

    func saturatingMul(y: T): T

    func saturatingDiv(y: T): T

    func saturatingMod(y: T): T

    func saturatingInc(): T

    func saturatingDec(): T

    func saturatingNeg(): T

    func saturatingShl(y: UInt64): T
    func saturatingShr(y: UInt64): T
}

public interface SaturatingPow {
    func saturatingPow(y: UInt64): Int64
}

extend Int8 <: SaturatingOp<Int8> {
    public func saturatingAdd(y: Int8): Int8 {
        saturatingAdd<Int8>(this, y)
    }
    public func saturatingSub(y: Int8): Int8 {
        saturatingSub<Int8>(this, y)
    }
    public func saturatingMul(y: Int8): Int8 {
        saturatingMul<Int8>(this, y)
    }
    public func saturatingDiv(y: Int8): Int8 {
        saturatingDiv<Int8>(this, y)
    }
    public func saturatingMod(y: Int8): Int8 {
        saturatingMod<Int8>(this, y)
    }
    public func saturatingInc(): Int8 {
        saturatingInc<Int8>(this)
    }
    public func saturatingDec(): Int8 {
        saturatingDec<Int8>(this)
    }
    public func saturatingNeg(): Int8 {
        saturatingNeg<Int8>(this)
    }

    public func saturatingShl(y: UInt64): Int8 {
        if (y >= 8) {
            return this << 7
        }
        return this << y
    }

    public func saturatingShr(y: UInt64): Int8 {
        if (y >= 8) {
            return this >> 7
        }
        return this >> y
    }
}

extend Int16 <: SaturatingOp<Int16> {
    public func saturatingAdd(y: Int16): Int16 {
        saturatingAdd<Int16>(this, y)
    }
    public func saturatingSub(y: Int16): Int16 {
        saturatingSub<Int16>(this, y)
    }
    public func saturatingMul(y: Int16): Int16 {
        saturatingMul<Int16>(this, y)
    }
    public func saturatingDiv(y: Int16): Int16 {
        saturatingDiv<Int16>(this, y)
    }
    public func saturatingMod(y: Int16): Int16 {
        saturatingMod<Int16>(this, y)
    }
    public func saturatingInc(): Int16 {
        saturatingInc<Int16>(this)
    }
    public func saturatingDec(): Int16 {
        saturatingDec<Int16>(this)
    }
    public func saturatingNeg(): Int16 {
        saturatingNeg<Int16>(this)
    }

    public func saturatingShl(y: UInt64): Int16 {
        return if (y >= 16) {
            this << 15
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): Int16 {
        return if (y >= 16) {
            this >> 15
        } else {
            this >> y
        }
    }
}

extend Int32 <: SaturatingOp<Int32> {
    public func saturatingAdd(y: Int32): Int32 {
        saturatingAdd<Int32>(this, y)
    }
    public func saturatingSub(y: Int32): Int32 {
        saturatingSub<Int32>(this, y)
    }
    public func saturatingMul(y: Int32): Int32 {
        saturatingMul<Int32>(this, y)
    }
    public func saturatingDiv(y: Int32): Int32 {
        saturatingDiv<Int32>(this, y)
    }
    public func saturatingMod(y: Int32): Int32 {
        saturatingMod<Int32>(this, y)
    }
    public func saturatingInc(): Int32 {
        saturatingInc<Int32>(this)
    }
    public func saturatingDec(): Int32 {
        saturatingDec<Int32>(this)
    }
    public func saturatingNeg(): Int32 {
        saturatingNeg<Int32>(this)
    }

    public func saturatingShl(y: UInt64): Int32 {
        return if (y >= 32) {
            this << 31
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): Int32 {
        return if (y >= 32) {
            this >> 31
        } else {
            this >> y
        }
    }
}

extend Int64 <: SaturatingOp<Int64> & SaturatingPow {
    public func saturatingAdd(y: Int64): Int64 {
        saturatingAdd<Int64>(this, y)
    }
    public func saturatingSub(y: Int64): Int64 {
        saturatingSub<Int64>(this, y)
    }
    public func saturatingMul(y: Int64): Int64 {
        saturatingMul<Int64>(this, y)
    }
    public func saturatingDiv(y: Int64): Int64 {
        saturatingDiv<Int64>(this, y)
    }
    public func saturatingMod(y: Int64): Int64 {
        saturatingMod<Int64>(this, y)
    }
    public func saturatingPow(y: UInt64): Int64 {
        saturatingPow(this, y)
    }
    public func saturatingInc(): Int64 {
        saturatingInc<Int64>(this)
    }
    public func saturatingDec(): Int64 {
        saturatingDec<Int64>(this)
    }
    public func saturatingNeg(): Int64 {
        saturatingNeg<Int64>(this)
    }

    public func saturatingShl(y: UInt64): Int64 {
        return if (y >= 64) {
            this << 63
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): Int64 {
        return if (y >= 64) {
            this >> 63
        } else {
            this >> y
        }
    }
}

extend UInt8 <: SaturatingOp<UInt8> {
    public func saturatingAdd(y: UInt8): UInt8 {
        saturatingAdd<UInt8>(this, y)
    }
    public func saturatingSub(y: UInt8): UInt8 {
        saturatingSub<UInt8>(this, y)
    }
    public func saturatingMul(y: UInt8): UInt8 {
        saturatingMul<UInt8>(this, y)
    }
    public func saturatingDiv(y: UInt8): UInt8 {
        saturatingDiv<UInt8>(this, y)
    }
    public func saturatingMod(y: UInt8): UInt8 {
        saturatingMod<UInt8>(this, y)
    }
    public func saturatingInc(): UInt8 {
        saturatingInc<UInt8>(this)
    }
    public func saturatingDec(): UInt8 {
        saturatingDec<UInt8>(this)
    }
    public func saturatingNeg(): UInt8 {
        saturatingNeg<UInt8>(this)
    }

    public func saturatingShl(y: UInt64): UInt8 {
        return if (y >= 8) {
            this << 7
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): UInt8 {
        return if (y >= 8) {
            this >> 7
        } else {
            this >> y
        }
    }
}

extend UInt16 <: SaturatingOp<UInt16> {
    public func saturatingAdd(y: UInt16): UInt16 {
        saturatingAdd<UInt16>(this, y)
    }
    public func saturatingSub(y: UInt16): UInt16 {
        saturatingSub<UInt16>(this, y)
    }
    public func saturatingMul(y: UInt16): UInt16 {
        saturatingMul<UInt16>(this, y)
    }
    public func saturatingDiv(y: UInt16): UInt16 {
        saturatingDiv<UInt16>(this, y)
    }
    public func saturatingMod(y: UInt16): UInt16 {
        saturatingMod<UInt16>(this, y)
    }
    public func saturatingInc(): UInt16 {
        saturatingInc<UInt16>(this)
    }
    public func saturatingDec(): UInt16 {
        saturatingDec<UInt16>(this)
    }
    public func saturatingNeg(): UInt16 {
        saturatingNeg<UInt16>(this)
    }

    public func saturatingShl(y: UInt64): UInt16 {
        return if (y >= 16) {
            this << 15
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): UInt16 {
        return if (y >= 16) {
            this >> 15
        } else {
            this >> y
        }
    }
}

extend UInt32 <: SaturatingOp<UInt32> {
    public func saturatingAdd(y: UInt32): UInt32 {
        saturatingAdd<UInt32>(this, y)
    }
    public func saturatingSub(y: UInt32): UInt32 {
        saturatingSub<UInt32>(this, y)
    }
    public func saturatingMul(y: UInt32): UInt32 {
        saturatingMul<UInt32>(this, y)
    }
    public func saturatingDiv(y: UInt32): UInt32 {
        saturatingDiv<UInt32>(this, y)
    }
    public func saturatingMod(y: UInt32): UInt32 {
        saturatingMod<UInt32>(this, y)
    }
    public func saturatingInc(): UInt32 {
        saturatingInc<UInt32>(this)
    }
    public func saturatingDec(): UInt32 {
        saturatingDec<UInt32>(this)
    }
    public func saturatingNeg(): UInt32 {
        saturatingNeg<UInt32>(this)
    }

    public func saturatingShl(y: UInt64): UInt32 {
        return if (y >= 32) {
            this << 31
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): UInt32 {
        return if (y >= 32) {
            this >> 31
        } else {
            this >> y
        }
    }
}

extend UInt64 <: SaturatingOp<UInt64> {
    public func saturatingAdd(y: UInt64): UInt64 {
        saturatingAdd<UInt64>(this, y)
    }
    public func saturatingSub(y: UInt64): UInt64 {
        saturatingSub<UInt64>(this, y)
    }
    public func saturatingMul(y: UInt64): UInt64 {
        saturatingMul<UInt64>(this, y)
    }
    public func saturatingDiv(y: UInt64): UInt64 {
        saturatingDiv<UInt64>(this, y)
    }
    public func saturatingMod(y: UInt64): UInt64 {
        saturatingMod<UInt64>(this, y)
    }
    public func saturatingInc(): UInt64 {
        saturatingInc<UInt64>(this)
    }
    public func saturatingDec(): UInt64 {
        saturatingDec<UInt64>(this)
    }
    public func saturatingNeg(): UInt64 {
        saturatingNeg<UInt64>(this)
    }

    public func saturatingShl(y: UInt64): UInt64 {
        return if (y >= 64) {
            this << 63
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): UInt64 {
        return if (y >= 64) {
            this >> 63
        } else {
            this >> y
        }
    }
}

extend IntNative <: SaturatingOp<IntNative> {
    public func saturatingAdd(y: IntNative): IntNative {
        saturatingAdd<IntNative>(this, y)
    }
    public func saturatingSub(y: IntNative): IntNative {
        saturatingSub<IntNative>(this, y)
    }
    public func saturatingMul(y: IntNative): IntNative {
        saturatingMul<IntNative>(this, y)
    }
    public func saturatingDiv(y: IntNative): IntNative {
        saturatingDiv<IntNative>(this, y)
    }
    public func saturatingMod(y: IntNative): IntNative {
        saturatingMod<IntNative>(this, y)
    }
    public func saturatingInc(): IntNative {
        saturatingInc<IntNative>(this)
    }
    public func saturatingDec(): IntNative {
        saturatingDec<IntNative>(this)
    }
    public func saturatingNeg(): IntNative {
        saturatingNeg<IntNative>(this)
    }

    public func saturatingShl(y: UInt64): IntNative {
        if (isNative64) {
            return if (y >= 64) {
                this << 63
            } else {
                this << y
            }
        }
        return if (y >= 32) {
            this << 31
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): IntNative {
        if (isNative64) {
            return if (y >= 64) {
                this >> 63
            } else {
                this >> y
            }
        }
        return if (y >= 32) {
            this >> 31
        } else {
            this >> y
        }
    }
}

extend UIntNative <: SaturatingOp<UIntNative> {
    public func saturatingAdd(y: UIntNative): UIntNative {
        saturatingAdd<UIntNative>(this, y)
    }
    public func saturatingSub(y: UIntNative): UIntNative {
        saturatingSub<UIntNative>(this, y)
    }
    public func saturatingMul(y: UIntNative): UIntNative {
        saturatingMul<UIntNative>(this, y)
    }
    public func saturatingDiv(y: UIntNative): UIntNative {
        saturatingDiv<UIntNative>(this, y)
    }

    public func saturatingMod(y: UIntNative): UIntNative {
        saturatingMod<UIntNative>(this, y)
    }
    public func saturatingInc(): UIntNative {
        saturatingInc<UIntNative>(this)
    }
    public func saturatingDec(): UIntNative {
        saturatingDec<UIntNative>(this)
    }
    public func saturatingNeg(): UIntNative {
        saturatingNeg<UIntNative>(this)
    }

    public func saturatingShl(y: UInt64): UIntNative {
        if (isNative64) {
            return if (y >= 64) {
                this << 63
            } else {
                this << y
            }
        }
        return if (y >= 32) {
            this << 31
        } else {
            this << y
        }
    }

    public func saturatingShr(y: UInt64): UIntNative {
        if (isNative64) {
            return if (y >= 64) {
                this >> 63
            } else {
                this >> y
            }
        }
        return if (y >= 32) {
            this >> 31
        } else {
            this >> y
        }
    }
}