1use crate::DeviceRepr;
4
5pub(crate) const RESIDENT_PROGRAM_FACT_CAPACITY: usize = 4096;
7const RESIDENT_PROGRAM_RULE_CAPACITY: usize = 256;
9
10#[repr(C)]
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct SemanticProgramFact {
14 pub predicate: u32,
15 pub first: u32,
16 pub second: u32,
17}
18
19unsafe impl DeviceRepr for SemanticProgramFact {}
21
22#[repr(C)]
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct SemanticProgramRule {
27 pub head_predicate: u32,
28 pub left_predicate: u32,
29 pub right_predicate: u32,
30 pub body_count: u32,
31 pub head_variables: [u32; 2],
32 pub left_variables: [u32; 2],
33 pub right_variables: [u32; 2],
34}
35
36unsafe impl DeviceRepr for SemanticProgramRule {}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct SemanticProgramAdmission {
42 pub predicate_count: u32,
43 pub initial_facts: Vec<SemanticProgramFact>,
44 pub initial_rules: Vec<SemanticProgramRule>,
45 pub queries: [SemanticProgramFact; 3],
46}
47
48impl SemanticProgramAdmission {
49 pub(crate) fn validate(&self) -> Result<(), &'static str> {
50 if self.predicate_count == 0
51 || self.initial_facts.len() > u32::MAX as usize
52 || self.initial_rules.len() > u32::MAX as usize
53 || self
54 .queries
55 .iter()
56 .chain(&self.initial_facts)
57 .any(|fact| fact.predicate >= self.predicate_count)
58 {
59 return Err("binary program has an invalid predicate or fact count");
60 }
61 if self.initial_facts.len() > RESIDENT_PROGRAM_FACT_CAPACITY {
62 return Err("binary program exceeds resident fact capacity");
63 }
64 if self.initial_rules.len() > RESIDENT_PROGRAM_RULE_CAPACITY - 2 {
65 return Err("binary program leaves no room for two rule edits");
66 }
67 for rule in &self.initial_rules {
68 if !(1..=2).contains(&rule.body_count)
69 || rule.head_predicate >= self.predicate_count
70 || rule.left_predicate >= self.predicate_count
71 || (rule.body_count == 2 && rule.right_predicate >= self.predicate_count)
72 || (rule.body_count == 1
73 && (rule.right_predicate != 0 || rule.right_variables != [0, 0]))
74 {
75 return Err("binary program has an invalid rule predicate or body");
76 }
77 let mut bound = [false; 4];
78 for &variable in rule.left_variables.iter().chain(
79 (rule.body_count == 2)
80 .then_some(&rule.right_variables)
81 .into_iter()
82 .flatten(),
83 ) {
84 let slot = bound
85 .get_mut(variable as usize)
86 .ok_or("binary rule variable exceeds its four-slot domain")?;
87 *slot = true;
88 }
89 if rule
90 .head_variables
91 .iter()
92 .any(|&variable| !bound.get(variable as usize).copied().unwrap_or(false))
93 {
94 return Err("binary rule head contains an unbound variable");
95 }
96 }
97 Ok(())
98 }
99
100 pub(crate) fn words(&self) -> Vec<u64> {
102 let mut words =
103 Vec::with_capacity(12 + self.initial_facts.len() * 3 + self.initial_rules.len() * 10);
104 words.extend([
105 u64::from(self.predicate_count),
106 self.initial_facts.len() as u64,
107 self.initial_rules.len() as u64,
108 ]);
109 for fact in self.queries.iter().chain(&self.initial_facts) {
110 words.extend([
111 u64::from(fact.predicate),
112 u64::from(fact.first),
113 u64::from(fact.second),
114 ]);
115 }
116 for rule in &self.initial_rules {
117 words.extend([
118 u64::from(rule.head_predicate),
119 u64::from(rule.left_predicate),
120 u64::from(rule.right_predicate),
121 u64::from(rule.body_count),
122 u64::from(rule.head_variables[0]),
123 u64::from(rule.head_variables[1]),
124 u64::from(rule.left_variables[0]),
125 u64::from(rule.left_variables[1]),
126 u64::from(rule.right_variables[0]),
127 u64::from(rule.right_variables[1]),
128 ]);
129 }
130 words
131 }
132}
133
134const _: () = {
135 assert!(std::mem::size_of::<SemanticProgramFact>() == 12);
136 assert!(std::mem::size_of::<SemanticProgramRule>() == 40);
137};
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn program_bank_words_match_the_native_decoder_layout() {
145 let program = SemanticProgramAdmission {
146 predicate_count: 2,
147 initial_facts: vec![SemanticProgramFact {
148 predicate: 0,
149 first: 1,
150 second: 3,
151 }],
152 initial_rules: vec![SemanticProgramRule {
153 head_predicate: 1,
154 left_predicate: 0,
155 right_predicate: 0,
156 body_count: 1,
157 head_variables: [0, 1],
158 left_variables: [0, 1],
159 right_variables: [0, 0],
160 }],
161 queries: [
162 SemanticProgramFact {
163 predicate: 1,
164 first: 1,
165 second: 3,
166 },
167 SemanticProgramFact {
168 predicate: 1,
169 first: 2,
170 second: 3,
171 },
172 SemanticProgramFact {
173 predicate: 1,
174 first: 3,
175 second: 4,
176 },
177 ],
178 };
179 assert_eq!(
180 program.words(),
181 [2, 1, 1, 1, 1, 3, 1, 2, 3, 1, 3, 4, 0, 1, 3, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0]
182 );
183 assert_eq!(program.validate(), Ok(()));
184 let mut unsafe_rule = program.clone();
185 unsafe_rule.initial_rules[0].head_variables = [2, 2];
186 assert_eq!(
187 unsafe_rule.validate(),
188 Err("binary rule head contains an unbound variable")
189 );
190 let mut too_many_facts = program.clone();
191 too_many_facts.initial_facts = vec![program.initial_facts[0]; 4097];
192 assert_eq!(
193 too_many_facts.validate(),
194 Err("binary program exceeds resident fact capacity")
195 );
196 let mut too_many_rules = program.clone();
197 too_many_rules.initial_rules = vec![program.initial_rules[0]; 255];
198 assert_eq!(
199 too_many_rules.validate(),
200 Err("binary program leaves no room for two rule edits")
201 );
202 }
203}