1use crate::geometric_algebra::quantum_k_theory::QuantumKClass;
28use crate::schubert::{IntersectionResult, SchubertCalculus, SchubertClass};
29use crate::EnumerativeResult;
30use num_rational::Rational64;
31use std::sync::Arc;
32
33#[cfg(feature = "parallel")]
34use rayon::prelude::*;
35
36#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
44pub struct CapabilityId(pub Arc<str>);
45
46impl CapabilityId {
47 #[must_use]
56 pub fn new(name: impl Into<String>) -> Self {
57 Self(Arc::from(name.into()))
58 }
59
60 #[must_use]
62 pub fn as_str(&self) -> &str {
63 &self.0
64 }
65}
66
67impl std::fmt::Display for CapabilityId {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73impl From<&str> for CapabilityId {
74 fn from(s: &str) -> Self {
75 Self::new(s)
76 }
77}
78
79impl From<String> for CapabilityId {
80 fn from(s: String) -> Self {
81 Self::new(s)
82 }
83}
84
85#[derive(Debug, Clone)]
97pub struct Capability {
98 pub id: CapabilityId,
100 pub name: String,
102 pub schubert_class: SchubertClass,
105 pub requires: Vec<CapabilityId>,
107 pub conflicts: Vec<CapabilityId>,
109}
110
111impl Capability {
112 pub fn new(
121 id: impl Into<String>,
122 name: impl Into<String>,
123 partition: Vec<usize>,
124 grassmannian: (usize, usize),
125 ) -> EnumerativeResult<Self> {
126 let id_str = id.into();
127 Ok(Self {
128 id: CapabilityId::new(id_str),
129 name: name.into(),
130 schubert_class: SchubertClass::new(partition, grassmannian)?,
131 requires: Vec::new(),
132 conflicts: Vec::new(),
133 })
134 }
135
136 #[must_use]
138 pub fn requires(mut self, cap_id: CapabilityId) -> Self {
139 self.requires.push(cap_id);
140 self
141 }
142
143 #[must_use]
145 pub fn requires_all(mut self, cap_ids: impl IntoIterator<Item = CapabilityId>) -> Self {
146 self.requires.extend(cap_ids);
147 self
148 }
149
150 #[must_use]
152 pub fn conflicts_with(mut self, cap_id: CapabilityId) -> Self {
153 self.conflicts.push(cap_id);
154 self
155 }
156
157 #[must_use]
159 pub fn conflicts_with_all(mut self, cap_ids: impl IntoIterator<Item = CapabilityId>) -> Self {
160 self.conflicts.extend(cap_ids);
161 self
162 }
163
164 #[must_use]
172 pub fn codimension(&self) -> usize {
173 self.schubert_class.partition.iter().sum()
174 }
175
176 #[must_use]
178 pub fn has_dependencies(&self) -> bool {
179 !self.requires.is_empty()
180 }
181
182 #[must_use]
184 pub fn has_conflicts(&self) -> bool {
185 !self.conflicts.is_empty()
186 }
187}
188
189#[derive(Debug, Clone)]
201pub struct Namespace {
202 pub grassmannian: (usize, usize),
204 pub position: SchubertClass,
206 pub capabilities: Vec<Capability>,
208 pub name: String,
210}
211
212impl Namespace {
213 #[must_use]
215 pub fn new(name: impl Into<String>, position: SchubertClass) -> Self {
216 Self {
217 grassmannian: position.grassmannian_dim,
218 position,
219 capabilities: Vec::new(),
220 name: name.into(),
221 }
222 }
223
224 pub fn full(name: impl Into<String>, k: usize, n: usize) -> EnumerativeResult<Self> {
234 let position = SchubertClass::new(vec![], (k, n))?;
235 Ok(Self::new(name, position))
236 }
237
238 pub fn grant(&mut self, capability: Capability) -> Result<(), NamespaceError> {
254 for existing in &self.capabilities {
256 if capability.conflicts.contains(&existing.id) {
257 return Err(NamespaceError::Conflict {
258 new: capability.id,
259 existing: existing.id.clone(),
260 });
261 }
262 if existing.conflicts.contains(&capability.id) {
263 return Err(NamespaceError::Conflict {
264 new: capability.id,
265 existing: existing.id.clone(),
266 });
267 }
268 }
269
270 for req in &capability.requires {
272 if !self.capabilities.iter().any(|c| &c.id == req) {
273 return Err(NamespaceError::MissingDependency {
274 capability: capability.id,
275 required: req.clone(),
276 });
277 }
278 }
279
280 self.capabilities.push(capability);
281 Ok(())
282 }
283
284 pub fn grant_all(&mut self, capabilities: Vec<Capability>) -> Result<(), NamespaceError> {
289 let mut remaining = capabilities;
291 let mut progress = true;
292
293 while !remaining.is_empty() && progress {
294 progress = false;
295 let mut still_remaining = Vec::new();
296
297 for cap in remaining {
298 let deps_satisfied = cap.requires.iter().all(|dep| self.has_capability(dep));
299
300 if deps_satisfied {
301 self.grant(cap)?;
302 progress = true;
303 } else {
304 still_remaining.push(cap);
305 }
306 }
307
308 remaining = still_remaining;
309 }
310
311 if !remaining.is_empty() {
312 let first = remaining.into_iter().next().unwrap();
313 return Err(NamespaceError::MissingDependency {
314 capability: first.id,
315 required: first
316 .requires
317 .into_iter()
318 .next()
319 .unwrap_or_else(|| CapabilityId::new("unknown")),
320 });
321 }
322
323 Ok(())
324 }
325
326 pub fn revoke(&mut self, id: &CapabilityId) -> bool {
338 if let Some(pos) = self.capabilities.iter().position(|c| &c.id == id) {
339 let dependents: Vec<CapabilityId> = self
341 .capabilities
342 .iter()
343 .filter(|c| c.requires.contains(id))
344 .map(|c| c.id.clone())
345 .collect();
346
347 if dependents.is_empty() {
348 self.capabilities.remove(pos);
349 return true;
350 }
351 }
352 false
353 }
354
355 #[must_use]
357 pub fn has_capability(&self, id: &CapabilityId) -> bool {
358 self.capabilities.iter().any(|c| &c.id == id)
359 }
360
361 #[must_use]
363 pub fn capability_ids(&self) -> Vec<CapabilityId> {
364 self.capabilities.iter().map(|c| c.id.clone()).collect()
365 }
366
367 #[must_use]
369 pub fn capability_count(&self) -> usize {
370 self.capabilities.len()
371 }
372
373 #[must_use]
387 pub fn count_configurations(&self) -> IntersectionResult {
388 let mut calc = SchubertCalculus::new(self.grassmannian);
389
390 let mut classes = vec![self.position.clone()];
391 for cap in &self.capabilities {
392 classes.push(cap.schubert_class.clone());
393 }
394
395 calc.multi_intersect(&classes)
396 }
397
398 #[must_use]
400 pub fn total_codimension(&self) -> usize {
401 let position_codim: usize = self.position.partition.iter().sum();
402 let cap_codim: usize = self.capabilities.iter().map(|c| c.codimension()).sum();
403 position_codim + cap_codim
404 }
405
406 #[must_use]
408 pub fn would_overdetermine(&self, capability: &Capability) -> bool {
409 let (k, n) = self.grassmannian;
410 let grassmannian_dim = k * (n - k);
411 let new_total = self.total_codimension() + capability.codimension();
412 new_total > grassmannian_dim
413 }
414}
415
416#[derive(Debug, Clone, PartialEq, Eq)]
418pub enum NamespaceError {
419 Conflict {
421 new: CapabilityId,
423 existing: CapabilityId,
425 },
426 MissingDependency {
428 capability: CapabilityId,
430 required: CapabilityId,
432 },
433 InvalidConfiguration,
435}
436
437impl std::fmt::Display for NamespaceError {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 match self {
440 NamespaceError::Conflict { new, existing } => {
441 write!(f, "Capability {} conflicts with existing {}", new, existing)
442 }
443 NamespaceError::MissingDependency {
444 capability,
445 required,
446 } => {
447 write!(
448 f,
449 "Capability {} requires {} which is not present",
450 capability, required
451 )
452 }
453 NamespaceError::InvalidConfiguration => {
454 write!(f, "Invalid namespace configuration")
455 }
456 }
457 }
458}
459
460impl std::error::Error for NamespaceError {}
461
462pub fn namespace_intersection(
472 ns1: &Namespace,
473 ns2: &Namespace,
474) -> EnumerativeResult<NamespaceIntersection> {
475 if ns1.grassmannian != ns2.grassmannian {
476 return Ok(NamespaceIntersection::Incompatible);
477 }
478
479 let mut calc = SchubertCalculus::new(ns1.grassmannian);
480 let result = calc.multi_intersect(&[ns1.position.clone(), ns2.position.clone()]);
481
482 Ok(match result {
483 IntersectionResult::Empty => NamespaceIntersection::Disjoint,
484 IntersectionResult::Finite(1) => NamespaceIntersection::SinglePoint,
485 IntersectionResult::Finite(n) => NamespaceIntersection::FinitePoints(n),
486 IntersectionResult::PositiveDimensional { dimension, .. } => {
487 NamespaceIntersection::Subspace { dimension }
488 }
489 })
490}
491
492#[derive(Debug, Clone, PartialEq, Eq, Default)]
494pub enum NamespaceIntersection {
495 #[default]
497 Incompatible,
498 Disjoint,
500 SinglePoint,
502 FinitePoints(u64),
504 Subspace {
506 dimension: usize,
508 },
509}
510
511pub fn capability_accessible(
520 namespace: &Namespace,
521 capability: &Capability,
522) -> EnumerativeResult<bool> {
523 if namespace.grassmannian != capability.schubert_class.grassmannian_dim {
524 return Ok(false);
525 }
526
527 let mut calc = SchubertCalculus::new(namespace.grassmannian);
528 let result = calc.multi_intersect(&[
529 namespace.position.clone(),
530 capability.schubert_class.clone(),
531 ]);
532
533 Ok(!matches!(result, IntersectionResult::Empty))
534}
535
536#[derive(Debug)]
538pub struct NamespaceBuilder {
539 name: String,
540 grassmannian: (usize, usize),
541 position: Vec<usize>,
542 capabilities: Vec<Capability>,
543}
544
545impl NamespaceBuilder {
546 #[must_use]
548 pub fn new(name: impl Into<String>, k: usize, n: usize) -> Self {
549 Self {
550 name: name.into(),
551 grassmannian: (k, n),
552 position: vec![],
553 capabilities: vec![],
554 }
555 }
556
557 #[must_use]
559 pub fn position(mut self, partition: Vec<usize>) -> Self {
560 self.position = partition;
561 self
562 }
563
564 #[must_use]
566 pub fn with_capability(mut self, capability: Capability) -> Self {
567 self.capabilities.push(capability);
568 self
569 }
570
571 #[must_use]
573 pub fn with_capabilities(mut self, capabilities: impl IntoIterator<Item = Capability>) -> Self {
574 self.capabilities.extend(capabilities);
575 self
576 }
577
578 pub fn build(self) -> EnumerativeResult<Namespace> {
580 let position = SchubertClass::new(self.position, self.grassmannian)?;
581 let mut ns = Namespace::new(self.name, position);
582
583 for cap in self.capabilities {
584 ns.grant(cap).map_err(|e| {
585 crate::EnumerativeError::SchubertError(format!("Failed to grant capability: {}", e))
586 })?;
587 }
588
589 Ok(ns)
590 }
591}
592
593#[cfg(feature = "parallel")]
599pub fn count_configurations_batch(namespaces: &[Namespace]) -> Vec<IntersectionResult> {
600 namespaces
601 .par_iter()
602 .map(|ns| ns.count_configurations())
603 .collect()
604}
605
606#[cfg(feature = "parallel")]
608pub fn capability_accessible_batch(
609 pairs: &[(&Namespace, &Capability)],
610) -> EnumerativeResult<Vec<bool>> {
611 pairs
612 .par_iter()
613 .map(|(ns, cap)| capability_accessible(ns, cap))
614 .collect()
615}
616
617#[cfg(feature = "parallel")]
619pub fn namespace_intersection_batch(
620 pairs: &[(&Namespace, &Namespace)],
621) -> EnumerativeResult<Vec<NamespaceIntersection>> {
622 pairs
623 .par_iter()
624 .map(|(ns1, ns2)| namespace_intersection(ns1, ns2))
625 .collect()
626}
627
628#[derive(Debug, Clone)]
652pub struct QuantumCapability<const P: usize, const Q: usize, const R: usize> {
653 pub capability: Capability,
655 pub k_class: QuantumKClass<P, Q, R>,
657}
658
659impl<const P: usize, const Q: usize, const R: usize> QuantumCapability<P, Q, R> {
660 #[must_use]
664 pub fn from_classical(capability: Capability) -> Self {
665 let k_class = QuantumKClass::structure_sheaf_point();
666 Self {
667 capability,
668 k_class,
669 }
670 }
671
672 #[must_use]
674 pub fn new(capability: Capability, k_class: QuantumKClass<P, Q, R>) -> Self {
675 Self {
676 capability,
677 k_class,
678 }
679 }
680
681 pub fn quantum_entangle(&self, other: &Self) -> EnumerativeResult<QuantumKClass<P, Q, R>> {
692 self.k_class.quantum_product(&other.k_class)
693 }
694
695 #[must_use]
706 pub fn euler_characteristic(&self, ambient_dimension: usize) -> Rational64 {
707 self.k_class.euler_characteristic(ambient_dimension)
708 }
709
710 #[must_use]
723 pub fn amplify(&self, k: i32) -> Self {
724 Self {
725 capability: self.capability.clone(),
726 k_class: self.k_class.adams_operation(k),
727 }
728 }
729}
730
731impl Namespace {
732 #[must_use]
745 pub fn quantum_count_configurations<const P: usize, const Q: usize, const R: usize>(
746 &self,
747 quantum_caps: &[QuantumCapability<P, Q, R>],
748 ) -> (IntersectionResult, Rational64) {
749 let classical = self.count_configurations();
750
751 let (k, n) = self.grassmannian;
753 let ambient_dim = k * (n - k);
754
755 let mut total_euler = Rational64::from(1);
756 for cap in quantum_caps {
757 total_euler *= cap.euler_characteristic(ambient_dim);
758 }
759
760 (classical, total_euler)
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use super::*;
767
768 #[test]
769 fn test_capability_creation() {
770 let cap = Capability::new("read", "Read Access", vec![1], (2, 4)).unwrap();
772 assert_eq!(cap.codimension(), 1);
773 assert_eq!(cap.id, CapabilityId::new("read"));
774 }
775
776 #[test]
777 fn test_capability_id_from() {
778 let id1: CapabilityId = "read".into();
779 let id2: CapabilityId = String::from("write").into();
780 assert_eq!(id1.as_str(), "read");
781 assert_eq!(id2.as_str(), "write");
782 }
783
784 #[test]
785 fn test_namespace_full() {
786 let ns = Namespace::full("test", 2, 4).unwrap();
787 assert_eq!(ns.grassmannian, (2, 4));
788 assert!(ns.position.partition.is_empty());
789 }
790
791 #[test]
792 fn test_namespace_grant() {
793 let mut ns = Namespace::full("test", 2, 4).unwrap();
794 let cap = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
795
796 assert!(ns.grant(cap).is_ok());
797 assert!(ns.has_capability(&CapabilityId::new("read")));
798 }
799
800 #[test]
801 fn test_capability_conflict() {
802 let mut ns = Namespace::full("test", 2, 4).unwrap();
803
804 let read = Capability::new("read", "Read", vec![1], (2, 4))
805 .unwrap()
806 .conflicts_with(CapabilityId::new("write"));
807 let write = Capability::new("write", "Write", vec![1], (2, 4)).unwrap();
808
809 ns.grant(read).unwrap();
810
811 let result = ns.grant(write);
812 assert!(matches!(result, Err(NamespaceError::Conflict { .. })));
813 }
814
815 #[test]
816 fn test_capability_dependency() {
817 let mut ns = Namespace::full("test", 2, 4).unwrap();
818
819 let write = Capability::new("write", "Write", vec![1], (2, 4))
820 .unwrap()
821 .requires(CapabilityId::new("read"));
822
823 let result = ns.grant(write.clone());
825 assert!(matches!(
826 result,
827 Err(NamespaceError::MissingDependency { .. })
828 ));
829
830 let read = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
832 ns.grant(read).unwrap();
833 assert!(ns.grant(write).is_ok());
834 }
835
836 #[test]
837 fn test_namespace_intersection() {
838 let ns1 = Namespace::full("ns1", 2, 4).unwrap();
839 let ns2 = Namespace::full("ns2", 2, 4).unwrap();
840
841 let result = namespace_intersection(&ns1, &ns2).unwrap();
842 assert!(matches!(
844 result,
845 NamespaceIntersection::Subspace { dimension: 4 }
846 ));
847 }
848
849 #[test]
850 fn test_namespace_incompatible() {
851 let ns1 = Namespace::full("ns1", 2, 4).unwrap();
852 let ns2 = Namespace::full("ns2", 3, 6).unwrap();
853
854 let result = namespace_intersection(&ns1, &ns2).unwrap();
855 assert_eq!(result, NamespaceIntersection::Incompatible);
856 }
857
858 #[test]
859 fn test_count_configurations() {
860 let mut ns = Namespace::full("agent", 2, 4).unwrap();
861
862 for i in 0..4 {
865 let cap = Capability::new(format!("cap{}", i), format!("Cap {}", i), vec![1], (2, 4))
866 .unwrap();
867 ns.grant(cap).unwrap();
868 }
869
870 let count = ns.count_configurations();
871 assert_eq!(count, IntersectionResult::Finite(2));
872 }
873
874 #[test]
875 fn test_namespace_builder() {
876 let read = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
877
878 let ns = NamespaceBuilder::new("test", 2, 4)
879 .position(vec![])
880 .with_capability(read)
881 .build()
882 .unwrap();
883
884 assert!(ns.has_capability(&CapabilityId::new("read")));
885 }
886
887 #[test]
888 fn test_capability_accessible() {
889 let ns = Namespace::full("test", 2, 4).unwrap();
890 let cap = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
891
892 assert!(capability_accessible(&ns, &cap).unwrap());
893 }
894
895 #[test]
896 fn test_revoke_capability() {
897 let mut ns = Namespace::full("test", 2, 4).unwrap();
898 let cap = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
899
900 ns.grant(cap).unwrap();
901 assert!(ns.has_capability(&CapabilityId::new("read")));
902
903 assert!(ns.revoke(&CapabilityId::new("read")));
904 assert!(!ns.has_capability(&CapabilityId::new("read")));
905 }
906
907 #[test]
908 fn test_would_overdetermine() {
909 let mut ns = Namespace::full("test", 2, 4).unwrap();
910
911 for i in 0..4 {
913 let cap = Capability::new(format!("cap{}", i), format!("Cap {}", i), vec![1], (2, 4))
914 .unwrap();
915 ns.grant(cap).unwrap();
916 }
917
918 let extra = Capability::new("extra", "Extra", vec![1], (2, 4)).unwrap();
920 assert!(ns.would_overdetermine(&extra));
921 }
922
923 #[test]
924 fn test_grant_all() {
925 let mut ns = Namespace::full("test", 2, 4).unwrap();
926
927 let read = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
928 let write = Capability::new("write", "Write", vec![1], (2, 4))
929 .unwrap()
930 .requires(CapabilityId::new("read"));
931
932 ns.grant_all(vec![write, read]).unwrap();
934
935 assert!(ns.has_capability(&CapabilityId::new("read")));
936 assert!(ns.has_capability(&CapabilityId::new("write")));
937 }
938
939 #[test]
940 fn test_namespace_intersection_default() {
941 let default = NamespaceIntersection::default();
942 assert_eq!(default, NamespaceIntersection::Incompatible);
943 }
944
945 #[test]
948 fn test_quantum_capability_creation() {
949 let cap = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
950 let qcap = QuantumCapability::<4, 0, 0>::from_classical(cap);
951 assert_eq!(qcap.capability.codimension(), 1);
952 }
953
954 #[test]
955 fn test_quantum_entanglement() {
956 let cap1 = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
957 let cap2 = Capability::new("write", "Write", vec![1], (2, 4)).unwrap();
958
959 let qcap1 = QuantumCapability::<4, 0, 0>::from_classical(cap1);
960 let qcap2 = QuantumCapability::<4, 0, 0>::from_classical(cap2);
961
962 let entangled = qcap1.quantum_entangle(&qcap2).unwrap();
963 assert!(entangled.q_power >= 0);
965 }
966
967 #[test]
968 fn test_adams_amplification() {
969 let cap = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
970 let mut qcap = QuantumCapability::<4, 0, 0>::from_classical(cap);
971 qcap.k_class = QuantumKClass::line_bundle(1);
972
973 let amplified = qcap.amplify(2);
974 assert_eq!(
976 *amplified.k_class.chern_character.get(&1).unwrap(),
977 Rational64::from(2)
978 );
979 }
980
981 #[test]
982 fn test_quantum_count_configurations() {
983 let mut ns = Namespace::full("agent", 2, 4).unwrap();
984
985 for i in 0..4 {
987 let cap = Capability::new(format!("cap{}", i), format!("Cap {}", i), vec![1], (2, 4))
988 .unwrap();
989 ns.grant(cap).unwrap();
990 }
991
992 let quantum_caps: Vec<QuantumCapability<4, 0, 0>> = ns
994 .capabilities
995 .iter()
996 .map(|c| QuantumCapability::from_classical(c.clone()))
997 .collect();
998
999 let (classical, _euler) = ns.quantum_count_configurations(&quantum_caps);
1000 assert_eq!(classical, IntersectionResult::Finite(2));
1002 }
1003
1004 #[test]
1005 fn test_quantum_count_empty_caps() {
1006 let ns = Namespace::full("agent", 2, 4).unwrap();
1007 let empty_caps: Vec<QuantumCapability<4, 0, 0>> = vec![];
1008 let (classical, euler) = ns.quantum_count_configurations(&empty_caps);
1009 assert!(matches!(
1011 classical,
1012 IntersectionResult::PositiveDimensional { .. }
1013 ));
1014 assert_eq!(euler, Rational64::from(1));
1016 }
1017
1018 #[test]
1019 fn test_amplify_preserves_capability() {
1020 let cap = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
1021 let mut qcap = QuantumCapability::<4, 0, 0>::from_classical(cap);
1022 qcap.k_class = QuantumKClass::line_bundle(1);
1023
1024 let amplified = qcap.amplify(3);
1025 assert_eq!(amplified.capability.id, qcap.capability.id);
1027 assert_eq!(
1028 amplified.capability.codimension(),
1029 qcap.capability.codimension()
1030 );
1031 assert_eq!(
1033 *amplified.k_class.chern_character.get(&1).unwrap(),
1034 Rational64::from(3)
1035 );
1036 }
1037
1038 #[test]
1039 fn test_quantum_capability_with_explicit_k_class() {
1040 use crate::geometric_algebra::quantum_k_theory::QuantumKClass;
1041
1042 let cap = Capability::new("exec", "Execute", vec![1, 1], (2, 4)).unwrap();
1043 let k_class = QuantumKClass::<4, 0, 0>::line_bundle(2);
1044 let qcap = QuantumCapability::new(cap, k_class);
1045
1046 assert_eq!(qcap.capability.codimension(), 2);
1047 assert_eq!(
1048 *qcap.k_class.chern_character.get(&1).unwrap(),
1049 Rational64::from(2)
1050 );
1051 }
1052}
1053
1054#[cfg(all(test, feature = "parallel"))]
1059mod parallel_tests {
1060 use super::*;
1061
1062 #[test]
1063 fn test_count_configurations_batch() {
1064 let mut ns1 = Namespace::full("ns1", 2, 4).unwrap();
1066 let mut ns2 = Namespace::full("ns2", 2, 4).unwrap();
1067 let ns3 = Namespace::full("ns3", 2, 4).unwrap();
1068
1069 for i in 0..4 {
1071 ns1.grant(
1072 Capability::new(format!("c{}", i), format!("Cap {}", i), vec![1], (2, 4)).unwrap(),
1073 )
1074 .unwrap();
1075 }
1076
1077 for i in 0..2 {
1079 ns2.grant(
1080 Capability::new(format!("d{}", i), format!("Cap {}", i), vec![1], (2, 4)).unwrap(),
1081 )
1082 .unwrap();
1083 }
1084
1085 let namespaces = vec![ns1, ns2, ns3];
1088 let results = count_configurations_batch(&namespaces);
1089
1090 assert_eq!(results.len(), 3);
1091 assert_eq!(results[0], IntersectionResult::Finite(2));
1092 assert!(matches!(
1093 results[1],
1094 IntersectionResult::PositiveDimensional { dimension: 2, .. }
1095 ));
1096 assert!(matches!(
1097 results[2],
1098 IntersectionResult::PositiveDimensional { dimension: 4, .. }
1099 ));
1100 }
1101
1102 #[test]
1103 fn test_capability_accessible_batch() {
1104 let ns1 = Namespace::full("ns1", 2, 4).unwrap();
1105 let ns2 = Namespace::full("ns2", 3, 6).unwrap();
1106
1107 let cap_24 = Capability::new("read", "Read", vec![1], (2, 4)).unwrap();
1108 let cap_36 = Capability::new("write", "Write", vec![1], (3, 6)).unwrap();
1109
1110 let pairs: Vec<(&Namespace, &Capability)> = vec![
1111 (&ns1, &cap_24), (&ns1, &cap_36), (&ns2, &cap_36), (&ns2, &cap_24), ];
1116
1117 let results = capability_accessible_batch(&pairs).unwrap();
1118
1119 assert_eq!(results.len(), 4);
1120 assert!(results[0]); assert!(!results[1]); assert!(results[2]); assert!(!results[3]); }
1125
1126 #[test]
1127 fn test_namespace_intersection_batch() {
1128 let ns1 = Namespace::full("ns1", 2, 4).unwrap();
1129 let ns2 = Namespace::full("ns2", 2, 4).unwrap();
1130 let ns3 = Namespace::full("ns3", 3, 6).unwrap();
1131
1132 let pairs: Vec<(&Namespace, &Namespace)> = vec![
1133 (&ns1, &ns2), (&ns1, &ns3), (&ns2, &ns3), ];
1137
1138 let results = namespace_intersection_batch(&pairs).unwrap();
1139
1140 assert_eq!(results.len(), 3);
1141 assert!(matches!(
1142 results[0],
1143 NamespaceIntersection::Subspace { dimension: 4 }
1144 ));
1145 assert_eq!(results[1], NamespaceIntersection::Incompatible);
1146 assert_eq!(results[2], NamespaceIntersection::Incompatible);
1147 }
1148}