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