def simulate(dt, bias, filtered=True):
    h, minimum = 1.0, 1.0
    violations, infeasible = 0, 0
    alpha, lower_limit, upper_limit = 2.0, -1.0, 1.0
    for _ in range(round(6.0/dt)):
        nominal = -1.0
        if filtered:
            lower = max(lower_limit, -alpha*h)
            if lower > upper_limit:
                infeasible += 1
                u = upper_limit  # diagnostic continuation, not a feasible solution
            else:
                u = max(lower, min(upper_limit, nominal))
        else:
            u = nominal
        h += dt*(u-bias)
        minimum = min(minimum, h)
        violations += int(h < -1e-12)
    return minimum, violations, infeasible

cases = ((0.1, 0.0, False), (0.1, 0.0, True), (0.75, 0.0, True),
         (0.1, 0.2, True), (0.1, 2.0, True))
for dt, bias, filtered in cases:
    minimum, violations, infeasible = simulate(dt, bias, filtered)
    print(f'dt={dt:.2f} bias={bias:.1f} filter={filtered} min_h={minimum:.4f} negative_samples={violations} infeasible={infeasible}')
assert simulate(0.1, 0.0)[0] >= 0
assert simulate(0.75, 0.0)[0] < 0
assert simulate(0.1, 2.0)[2] > 0
