import math


def sigmoid(x, offset, width):
    """
    A sigmoid function with a given offset from 0 and rate of change

    By default this function increases as the value of x increases.
    ie. y = 1 / (1+e^(-x)
    To flip the sigmoid so the function decreases as the value of x increases,
    subtract it from 1

    :param x: The value to evaluate over the sigmoid
    :param offset: The offset of the center of the sigmoid from 0
    :param width: The total amount of change (centered around the offset) x must undergo
    to cause the value of the sigmoid to go from 0.018 to 0.982
    :return: A value in [0, 1] that is the value of the sigmoid at x
    """
    # This is the factor that changes how quickly the sigmoid goes from 0 to 1.
    # We divide 8 by it because that is the distance a sigmoid function centered
    # about 0 takes to go from 0.018 to 0.982 (and that is what the 'width' parameter is)
    change_factor = 8 / width
    # Avoid large positive exponents to Help prevent math range errors
    # https://stackoverflow.com/a/36269186
    exponent = change_factor * (offset - x)
    if exponent < 0:
        return 1 / (1 + math.exp(exponent))
    else:
        return 1 - 1 / (1 + math.exp(-exponent))