# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

import numpy as np


def compute_logistic(val: float) -> float:
    v = 1.0 / (1.0 + np.exp(-np.abs(val)))
    return (1.0 - v) if val < 0 else v


logistic = np.vectorize(compute_logistic)


def compute_softmax_zero(values: np.ndarray) -> np.ndarray:
    """The function modifies the input inplace."""
    v_max = values.max()
    exp_neg_v_max = np.exp(-v_max)
    s = 0
    for i, v in enumerate(values):
        if v > 0.0000001 or v < -0.0000001:
            values[i] = np.exp(v - v_max)
        else:
            values[i] *= exp_neg_v_max
        s += values[i]
    if s == 0:
        values[:] = 0.5
    else:
        values[:] /= s
    return values


def softmax_zero(values: np.ndarray) -> np.ndarray:
    """Modifications in place."""
    if len(values.shape) == 1:
        compute_softmax_zero(values)
        return values
    for row in values:
        compute_softmax_zero(row)
    return values


def softmax(values: np.ndarray) -> np.ndarray:
    """Modifications in place."""
    if len(values.shape) == 2:
        v_max = values.max(axis=1, keepdims=1)
        values -= v_max
        np.exp(values, out=values)
        s = values.sum(axis=1, keepdims=1)
        values /= s
        return values
    v_max = values.max()
    values[:] = np.exp(values - v_max)
    this_sum = values.sum()
    values /= this_sum
    return values


def erf_inv(x: float) -> float:
    sgn = -1.0 if x < 0 else 1.0
    x = (1.0 - x) * (1 + x)
    if x == 0:
        return 0
    log = np.log(x)
    v = 2.0 / (np.pi * 0.147) + 0.5 * log
    v2 = 1.0 / 0.147 * log
    v3 = -v + np.sqrt(v * v - v2)
    return sgn * np.sqrt(v3)


def compute_probit(val: float) -> float:
    return 1.41421356 * erf_inv(val * 2 - 1)


probit = np.vectorize(compute_probit)


def expit(x: np.ndarray) -> np.ndarray:
    return (1.0 / (1.0 + np.exp(-x))).astype(x.dtype)
