# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
"""Register TOPI implementations for TE tensor overload hooks."""

from tvm import te
from tvm.te import _te_tensor_overload as _overload
from tvm.tirx import expr as _expr

from . import broadcast as _broadcast
from . import math as _math


def _is_integer(value):
    if isinstance(value, te.Tensor | te.TensorSlice):
        return value.dtype.matches_code(_expr.DataTypeCode.INT)
    return _expr._dtype_is_int(value)


def _binary(op, reflected=False, check_integer=False):
    def implementation(lhs, rhs):
        if not isinstance(lhs, te.Tensor) and not isinstance(rhs, te.Tensor):
            return NotImplemented
        if reflected:
            lhs, rhs = rhs, lhs
        if check_integer and _is_integer(lhs) and _is_integer(rhs):
            raise _expr.div_ambiguity_error()
        return op(lhs, rhs)

    return implementation


_overload.__add__ = _binary(_broadcast.add)
_overload.__radd__ = _binary(_broadcast.add, reflected=True)
_overload.__sub__ = _binary(_broadcast.subtract)
_overload.__rsub__ = _binary(_broadcast.subtract, reflected=True)
_overload.__mul__ = _binary(_broadcast.multiply)
_overload.__rmul__ = _binary(_broadcast.multiply, reflected=True)
_overload.__div__ = _binary(_broadcast.divide, check_integer=True)
_overload.__rdiv__ = _binary(_broadcast.divide, reflected=True, check_integer=True)
_overload.__truediv__ = _overload.__div__
_overload.__rtruediv__ = _overload.__rdiv__


def _astype(value, dtype, span=None):
    if not isinstance(value, te.Tensor):
        return NotImplemented
    return _math.cast(value, dtype, span)


_overload.astype = _astype
