1use crate::NQuin;
2
3pub use crate::frame_layout::CONSUMED_BIT;
6
7pub fn consume_quin(q: &mut NQuin) {
8 q.metadata |= CONSUMED_BIT;
9}
10
11pub fn is_consumed(q: &NQuin) -> bool {
12 (q.metadata & CONSUMED_BIT) != 0
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum Connective {
23 Atom,
25 AtomDual,
27 Tensor,
29 Par,
31 Plus,
33 With,
35 One,
37 Bottom,
39 Zero,
41 Top,
43 OfCourse,
45 WhyNot,
47}
48
49impl Connective {
50 pub fn dual(self) -> Connective {
52 use Connective::*;
53 match self {
54 Atom => AtomDual,
55 AtomDual => Atom,
56 Tensor => Par,
57 Par => Tensor,
58 Plus => With,
59 With => Plus,
60 One => Bottom,
61 Bottom => One,
62 Zero => Top,
63 Top => Zero,
64 OfCourse => WhyNot,
65 WhyNot => OfCourse,
66 }
67 }
68
69 pub fn is_multiplicative(self) -> bool {
71 use Connective::*;
72 matches!(self, Tensor | Par | One | Bottom)
73 }
74
75 pub fn is_additive(self) -> bool {
77 use Connective::*;
78 matches!(self, Plus | With | Zero | Top)
79 }
80
81 pub fn is_exponential(self) -> bool {
83 matches!(self, Connective::OfCourse | Connective::WhyNot)
84 }
85
86 pub fn is_reusable(self) -> bool {
89 matches!(self, Connective::OfCourse)
90 }
91}
92
93pub fn can_consume(q: &NQuin, reusable: bool) -> bool {
96 reusable || !is_consumed(q)
97}
98
99pub fn tensor_consume(a: &mut NQuin, a_reusable: bool, b: &mut NQuin, b_reusable: bool) -> bool {
103 if !can_consume(a, a_reusable) || !can_consume(b, b_reusable) {
104 return false;
105 }
106 if !a_reusable {
107 consume_quin(a);
108 }
109 if !b_reusable {
110 consume_quin(b);
111 }
112 true
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum StructuralRule {
123 Weakening,
125 Contraction,
127 Exchange,
129}
130
131pub fn structural_rule_licensed(rule: StructuralRule, reusable: bool) -> bool {
135 match rule {
136 StructuralRule::Exchange => true,
137 StructuralRule::Weakening | StructuralRule::Contraction => reusable,
138 }
139}
140
141pub fn structural_derivation_valid(steps: &[(StructuralRule, bool)]) -> bool {
144 steps
145 .iter()
146 .all(|&(rule, reusable)| structural_rule_licensed(rule, reusable))
147}
148
149pub const MAX_PN_NODES: usize = 32;
158pub const MAX_PN_PARS: usize = 12;
160
161#[inline]
162fn pn_find(parent: &mut [usize; MAX_PN_NODES], mut x: usize) -> usize {
163 while parent[x] != x {
164 parent[x] = parent[parent[x]]; x = parent[x];
166 }
167 x
168}
169
170#[inline]
172fn pn_union(parent: &mut [usize; MAX_PN_NODES], a: usize, b: usize) -> bool {
173 let ra = pn_find(parent, a);
174 let rb = pn_find(parent, b);
175 if ra == rb {
176 return false;
177 }
178 parent[ra] = rb;
179 true
180}
181
182pub fn is_proof_net(
187 n_nodes: usize,
188 fixed_edges: &[(usize, usize)],
189 par_switches: &[((usize, usize), (usize, usize))],
190) -> bool {
191 if n_nodes == 0 || n_nodes > MAX_PN_NODES || par_switches.len() > MAX_PN_PARS {
192 return false;
193 }
194 let k = par_switches.len();
195 for mask in 0u32..(1u32 << k) {
196 let mut parent = [0usize; MAX_PN_NODES];
197 for (i, p) in parent.iter_mut().enumerate().take(n_nodes) {
198 *p = i;
199 }
200 let mut acyclic = true;
201 let mut edge_count = 0usize;
202
203 for &(a, b) in fixed_edges {
204 if a >= n_nodes || b >= n_nodes {
205 return false;
206 }
207 if !pn_union(&mut parent, a, b) {
208 acyclic = false;
209 }
210 edge_count += 1;
211 }
212 for (j, &(ea, eb)) in par_switches.iter().enumerate() {
213 let (a, b) = if (mask >> j) & 1 == 0 { ea } else { eb };
214 if a >= n_nodes || b >= n_nodes {
215 return false;
216 }
217 if !pn_union(&mut parent, a, b) {
218 acyclic = false;
219 }
220 edge_count += 1;
221 }
222
223 if !acyclic || edge_count != n_nodes - 1 {
225 return false;
226 }
227 let root = pn_find(&mut parent, 0);
228 for i in 1..n_nodes {
229 if pn_find(&mut parent, i) != root {
230 return false; }
232 }
233 }
234 true
235}
236
237pub fn zk_gated_consume(q: &mut NQuin, reusable: bool, proof_verified: bool) -> bool {
245 if !proof_verified || !can_consume(q, reusable) {
246 return false;
247 }
248 if !reusable {
249 consume_quin(q);
250 }
251 true
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_consume_quin() {
260 let mut q = NQuin {
261 subject: 0,
262 predicate: 0,
263 object: 0,
264 context: 0,
265 metadata: 0,
266 parity: 0,
267 };
268 assert!(!is_consumed(&q));
269 consume_quin(&mut q);
270 assert!(is_consumed(&q));
271 }
272
273 #[test]
274 fn linear_negation_is_involutive_and_dualises_connectives() {
275 use Connective::*;
276 for c in [
277 Atom, AtomDual, Tensor, Par, Plus, With, One, Bottom, Zero, Top, OfCourse, WhyNot,
278 ] {
279 assert_eq!(c.dual().dual(), c, "(A⊥)⊥ = A for {:?}", c);
280 assert_ne!(c.dual(), c, "no connective is its own dual: {:?}", c);
281 }
282 assert_eq!(Tensor.dual(), Par);
284 assert_eq!(Plus.dual(), With);
285 assert_eq!(One.dual(), Bottom);
286 assert_eq!(Zero.dual(), Top);
287 assert_eq!(OfCourse.dual(), WhyNot);
288 }
289
290 #[test]
291 fn connective_classification_and_reuse() {
292 use Connective::*;
293 assert!(Tensor.is_multiplicative() && !Tensor.is_additive());
294 assert!(With.is_additive() && !With.is_multiplicative());
295 assert!(OfCourse.is_exponential() && WhyNot.is_exponential());
296 assert!(OfCourse.is_reusable());
298 assert!(!Atom.is_reusable());
299 }
300
301 #[test]
302 fn tensor_consumes_both_and_respects_reuse() {
303 let mk = || NQuin {
304 subject: 1,
305 predicate: 2,
306 object: 3,
307 context: 0,
308 metadata: 0,
309 parity: 0,
310 };
311 let mut a = mk();
313 let mut b = mk();
314 assert!(tensor_consume(&mut a, false, &mut b, false));
315 assert!(is_consumed(&a) && is_consumed(&b));
316 assert!(
317 !tensor_consume(&mut a, false, &mut b, false),
318 "linear resources are exhausted"
319 );
320
321 let mut r = mk();
323 let mut s = mk();
324 assert!(tensor_consume(&mut r, true, &mut s, false));
325 assert!(!is_consumed(&r), "reusable resource is not consumed");
326 assert!(is_consumed(&s));
327 assert!(
328 tensor_consume(&mut r, true, &mut s, true),
329 "reusable can satisfy again"
330 );
331 }
332
333 #[test]
334 fn structural_rules_are_controlled() {
335 assert!(!structural_rule_licensed(StructuralRule::Weakening, false));
337 assert!(!structural_rule_licensed(
338 StructuralRule::Contraction,
339 false
340 ));
341 assert!(structural_rule_licensed(StructuralRule::Weakening, true));
342 assert!(structural_rule_licensed(StructuralRule::Contraction, true));
343 assert!(structural_rule_licensed(StructuralRule::Exchange, false));
345 assert!(structural_derivation_valid(&[
347 (StructuralRule::Contraction, true),
348 (StructuralRule::Exchange, false),
349 ]));
350 assert!(!structural_derivation_valid(&[(
352 StructuralRule::Contraction,
353 false
354 )]));
355 }
356
357 #[test]
358 fn danos_regnier_distinguishes_nets_from_non_nets() {
359 assert!(is_proof_net(2, &[(0, 1)], &[]));
361 assert!(!is_proof_net(3, &[(0, 1)], &[]));
363 assert!(!is_proof_net(3, &[(0, 1), (1, 2), (0, 2)], &[]));
365
366 assert!(is_proof_net(3, &[(0, 1)], &[((0, 2), (1, 2))]));
369
370 assert!(!is_proof_net(3, &[(0, 1)], &[((0, 1), (0, 1))]));
373
374 assert!(!is_proof_net(0, &[], &[]));
376 assert!(
377 !is_proof_net(2, &[(0, 5)], &[]),
378 "edge to out-of-range node rejected"
379 );
380 }
381
382 #[test]
383 fn zk_gate_controls_resource_exhaustion() {
384 let mk = || NQuin {
385 subject: 1,
386 predicate: 2,
387 object: 3,
388 context: 0,
389 metadata: 0,
390 parity: 0,
391 };
392 let mut q = mk();
394 assert!(!zk_gated_consume(&mut q, false, false));
395 assert!(!is_consumed(&q));
396 assert!(zk_gated_consume(&mut q, false, true));
398 assert!(is_consumed(&q));
399 assert!(!zk_gated_consume(&mut q, false, true), "already exhausted");
400 let mut r = mk();
402 assert!(zk_gated_consume(&mut r, true, true));
403 assert!(!is_consumed(&r));
404 }
405}