meta_language/
semantics.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3pub enum TruthValue {
4 True,
5 False,
6 Unknown,
7 Both,
8}
9
10impl TruthValue {
11 #[must_use]
13 pub const fn and(self, other: Self) -> Self {
14 match (self, other) {
15 (Self::False, _) | (_, Self::False) => Self::False,
16 (Self::True, value) | (value, Self::True) => value,
17 (Self::Both, _) | (_, Self::Both) => Self::Both,
18 (Self::Unknown, Self::Unknown) => Self::Unknown,
19 }
20 }
21
22 #[must_use]
24 pub const fn or(self, other: Self) -> Self {
25 match (self, other) {
26 (Self::True, _) | (_, Self::True) => Self::True,
27 (Self::False, value) | (value, Self::False) => value,
28 (Self::Both, _) | (_, Self::Both) => Self::Both,
29 (Self::Unknown, Self::Unknown) => Self::Unknown,
30 }
31 }
32
33 #[must_use]
35 pub const fn negate(self) -> Self {
36 match self {
37 Self::True => Self::False,
38 Self::False => Self::True,
39 Self::Unknown => Self::Unknown,
40 Self::Both => Self::Both,
41 }
42 }
43}
44
45const PROBABILITY_SCALE_BASIS_POINTS: u16 = 10_000;
46const PROBABILITY_SCALE_U32: u32 = 10_000;
47const PROBABILITY_SCALE_U128: u128 = 10_000;
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
51pub struct Probability {
52 basis_points: u16,
53}
54
55impl Probability {
56 pub const ZERO: Self = Self { basis_points: 0 };
58
59 pub const ONE: Self = Self {
61 basis_points: PROBABILITY_SCALE_BASIS_POINTS,
62 };
63
64 #[must_use]
66 pub const fn from_basis_points(basis_points: u16) -> Option<Self> {
67 if basis_points <= PROBABILITY_SCALE_BASIS_POINTS {
68 Some(Self { basis_points })
69 } else {
70 None
71 }
72 }
73
74 #[must_use]
76 pub fn from_ratio(numerator: u64, denominator: u64) -> Option<Self> {
77 if denominator == 0 || numerator > denominator {
78 return None;
79 }
80
81 let scaled = (u128::from(numerator) * PROBABILITY_SCALE_U128 + u128::from(denominator) / 2)
82 / u128::from(denominator);
83 let basis_points =
84 u16::try_from(scaled).expect("scaled probability must fit into basis points");
85
86 Self::from_basis_points(basis_points)
87 }
88
89 #[must_use]
91 pub const fn basis_points(self) -> u16 {
92 self.basis_points
93 }
94
95 #[must_use]
97 pub const fn complement(self) -> Self {
98 Self {
99 basis_points: PROBABILITY_SCALE_BASIS_POINTS - self.basis_points,
100 }
101 }
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
106pub struct ProbabilisticTruthValue {
107 true_probability: Probability,
108}
109
110impl ProbabilisticTruthValue {
111 #[must_use]
113 pub const fn new(true_probability: Probability) -> Self {
114 Self { true_probability }
115 }
116
117 #[must_use]
119 pub fn from_ratio(numerator: u64, denominator: u64) -> Option<Self> {
120 Some(Self::new(Probability::from_ratio(numerator, denominator)?))
121 }
122
123 #[must_use]
125 pub const fn true_probability(self) -> Probability {
126 self.true_probability
127 }
128
129 #[must_use]
131 pub const fn false_probability(self) -> Probability {
132 self.true_probability.complement()
133 }
134
135 #[must_use]
137 pub const fn negate(self) -> Self {
138 Self::new(self.false_probability())
139 }
140
141 #[must_use]
143 pub fn and(self, other: Self) -> Self {
144 Self::new(multiply_probabilities(
145 self.true_probability(),
146 other.true_probability(),
147 ))
148 }
149
150 #[must_use]
152 pub fn or(self, other: Self) -> Self {
153 self.negate().and(other.negate()).negate()
154 }
155}
156
157fn multiply_probabilities(left: Probability, right: Probability) -> Probability {
158 let product = u32::from(left.basis_points()) * u32::from(right.basis_points());
159 let rounded = (product + PROBABILITY_SCALE_U32 / 2) / PROBABILITY_SCALE_U32;
160 let basis_points =
161 u16::try_from(rounded).expect("probability product must fit into basis points");
162
163 Probability::from_basis_points(basis_points).expect("probability product stays in range")
164}