///|
pub(all) enum Preference {
Usual
UShape(Double)
VShape(Double)
Level(Double, Double)
Linear(Double, Double)
Gaussian(Double)
} derive(@debug.Debug)
///|
fn valid_preference(pref : Preference) -> Unit raise ChoiceError {
match pref {
Usual => ()
UShape(q) => {
input_number(q)
if q < 0.0 {
raise Invalid("indifference threshold must be nonnegative")
}
}
VShape(p) | Gaussian(p) => {
input_number(p)
if p <= 0.0 {
raise Invalid("preference scale must be positive")
}
}
Level(q, p) | Linear(q, p) => {
input_number(q)
input_number(p)
if q < 0.0 || p <= q {
raise Invalid("thresholds must satisfy 0 <= q < p")
}
}
}
}
///|
fn preference_unchecked(pref : Preference, difference : Double) -> Double {
if difference <= 0.0 {
return 0.0
}
match pref {
Usual => 1.0
UShape(q) => if difference <= q { 0.0 } else { 1.0 }
VShape(p) => if difference >= p { 1.0 } else { difference / p }
Level(q, p) =>
if difference <= q {
0.0
} else if difference <= p {
0.5
} else {
1.0
}
Linear(q, p) =>
if difference <= q {
0.0
} else if difference >= p {
1.0
} else {
(difference - q) / (p - q)
}
Gaussian(s) => {
let ratio = difference / s
// Avoid squaring a huge ratio and improve accuracy near zero.
if ratio >= 40.0 {
1.0
} else {
-@math.expm1(-0.5 * ratio * ratio)
}
}
}
}
///|
pub fn preference_degree(
pref : Preference,
difference : Double,
) -> Double raise ChoiceError {
valid_preference(pref)
input_number(difference)
preference_unchecked(pref, difference)
}
///|
pub extend Preference with @debug.Debug::{to_repr}