1use super::functions::*;
6use std::collections::HashMap;
7
8pub struct OpSymbol {
10 pub name: String,
11 pub arity: usize,
12}
13impl OpSymbol {
14 pub fn new(name: &str, arity: usize) -> Self {
16 OpSymbol {
17 name: name.to_string(),
18 arity,
19 }
20 }
21 pub fn is_constant(&self) -> bool {
23 self.arity == 0
24 }
25}
26#[allow(dead_code)]
28#[derive(Debug, Clone)]
29pub struct BooleanAlgebra {
30 pub name: String,
31 pub is_atomic: bool,
32 pub is_complete: bool,
33 pub cardinality_description: String,
34}
35impl BooleanAlgebra {
36 #[allow(dead_code)]
37 pub fn power_set(set_name: &str) -> Self {
38 Self {
39 name: format!("P({})", set_name),
40 is_atomic: true,
41 is_complete: true,
42 cardinality_description: format!("2^|{}|", set_name),
43 }
44 }
45 #[allow(dead_code)]
46 pub fn finite(n: u32) -> Self {
47 Self {
48 name: format!("2^{n}"),
49 is_atomic: true,
50 is_complete: true,
51 cardinality_description: format!("2^{n} elements"),
52 }
53 }
54 #[allow(dead_code)]
55 pub fn stone_representation(&self) -> String {
56 format!(
57 "Stone duality: {} <-> Stone space (compact Hausdorff zero-dimensional)",
58 self.name
59 )
60 }
61 #[allow(dead_code)]
62 pub fn is_representable(&self) -> bool {
63 true
64 }
65}
66#[allow(dead_code)]
68#[derive(Debug, Clone)]
69pub struct DistributiveLattice {
70 pub name: String,
71 pub is_finite: bool,
72}
73impl DistributiveLattice {
74 #[allow(dead_code)]
75 pub fn new(name: &str, finite: bool) -> Self {
76 Self {
77 name: name.to_string(),
78 is_finite: finite,
79 }
80 }
81 #[allow(dead_code)]
82 pub fn birkhoff_representation(&self) -> String {
83 if self.is_finite {
84 format!(
85 "Finite dist. lattice {} <-> J({}) (poset of join-irreducibles)",
86 self.name, self.name
87 )
88 } else {
89 format!(
90 "Infinite dist. lattice {} <-> lattice of down-sets",
91 self.name
92 )
93 }
94 }
95}
96#[derive(Clone, Debug, PartialEq)]
98pub enum Term {
99 Var(usize),
101 App { op: String, args: Vec<Term> },
103}
104impl Term {
105 pub fn var(index: usize) -> Self {
107 Term::Var(index)
108 }
109 pub fn apply(op: &str, args: Vec<Term>) -> Self {
111 Term::App {
112 op: op.to_string(),
113 args,
114 }
115 }
116 pub fn constant(op: &str) -> Self {
118 Term::App {
119 op: op.to_string(),
120 args: vec![],
121 }
122 }
123 pub fn depth(&self) -> usize {
125 match self {
126 Term::Var(_) => 0,
127 Term::App { args, .. } => 1 + args.iter().map(|a| a.depth()).max().unwrap_or(0),
128 }
129 }
130 pub fn variables(&self) -> Vec<usize> {
132 let mut vars = Vec::new();
133 self.collect_vars(&mut vars);
134 vars.sort_unstable();
135 vars.dedup();
136 vars
137 }
138 fn collect_vars(&self, acc: &mut Vec<usize>) {
139 match self {
140 Term::Var(i) => acc.push(*i),
141 Term::App { args, .. } => {
142 for a in args {
143 a.collect_vars(acc);
144 }
145 }
146 }
147 }
148 pub fn subst(&self, var_idx: usize, replacement: &Term) -> Term {
150 match self {
151 Term::Var(i) if *i == var_idx => replacement.clone(),
152 Term::Var(_) => self.clone(),
153 Term::App { op, args } => Term::App {
154 op: op.clone(),
155 args: args.iter().map(|a| a.subst(var_idx, replacement)).collect(),
156 },
157 }
158 }
159 pub fn eval(&self, alg: &Algebra, assignment: &[usize]) -> Option<usize> {
161 match self {
162 Term::Var(i) => assignment.get(*i).copied(),
163 Term::App { op, args } => {
164 let evaluated: Option<Vec<usize>> =
165 args.iter().map(|a| a.eval(alg, assignment)).collect();
166 let arg_vals = evaluated?;
167 alg.apply_op(op, &arg_vals)
168 }
169 }
170 }
171}
172pub struct Algebra {
176 pub carrier_size: usize,
177 pub signature: Signature,
178 pub tables: std::collections::HashMap<String, Vec<Vec<usize>>>,
179}
180impl Algebra {
181 pub fn new(carrier: usize, sig: Signature) -> Self {
183 Algebra {
184 carrier_size: carrier,
185 signature: sig,
186 tables: std::collections::HashMap::new(),
187 }
188 }
189 pub fn define_op(&mut self, name: &str, table: Vec<Vec<usize>>) {
191 self.tables.insert(name.to_string(), table);
192 }
193 pub fn apply_op(&self, name: &str, args: &[usize]) -> Option<usize> {
195 let table = self.tables.get(name)?;
196 if args.is_empty() {
197 return table.first().and_then(|row| row.first()).copied();
198 }
199 let current: &Vec<Vec<usize>> = table;
200 let first_arg = *args.first()?;
201 if first_arg >= current.len() {
202 return None;
203 }
204 if args.len() == 1 {
205 return current[first_arg].first().copied();
206 }
207 let second_arg = args[1];
208 let row = current[first_arg].get(second_arg)?;
209 Some(*row)
210 }
211 pub fn is_closed(&self) -> bool {
213 for table in self.tables.values() {
214 for row in table {
215 for &val in row {
216 if val >= self.carrier_size {
217 return false;
218 }
219 }
220 }
221 }
222 true
223 }
224 pub fn find_congruences(&self) -> usize {
226 if self.carrier_size <= 1 {
227 1
228 } else {
229 2
230 }
231 }
232}
233#[allow(dead_code)]
235#[derive(Debug, Clone)]
236pub struct BirkhoffVariety {
237 pub name: String,
238 pub signature_name: String,
239 pub axioms: Vec<String>,
240}
241impl BirkhoffVariety {
242 #[allow(dead_code)]
243 pub fn new(name: &str, sig: &str, axioms: Vec<&str>) -> Self {
244 Self {
245 name: name.to_string(),
246 signature_name: sig.to_string(),
247 axioms: axioms.into_iter().map(String::from).collect(),
248 }
249 }
250 #[allow(dead_code)]
251 pub fn groups() -> Self {
252 Self::new(
253 "Groups",
254 "(*, inv, e)",
255 vec![
256 "x * (y * z) = (x * y) * z",
257 "x * e = x",
258 "e * x = x",
259 "x * inv(x) = e",
260 "inv(x) * x = e",
261 ],
262 )
263 }
264 #[allow(dead_code)]
265 pub fn lattices() -> Self {
266 Self::new(
267 "Lattices",
268 "(meet, join)",
269 vec![
270 "meet(x, x) = x",
271 "join(x, x) = x",
272 "meet(x, y) = meet(y, x)",
273 "join(x, y) = join(y, x)",
274 "meet(x, join(x, y)) = x",
275 "join(x, meet(x, y)) = x",
276 ],
277 )
278 }
279 #[allow(dead_code)]
280 pub fn rings() -> Self {
281 Self::new(
282 "Rings",
283 "(+, *, 0, -, 1)",
284 vec![
285 "x + (y + z) = (x + y) + z",
286 "x + 0 = x",
287 "x + (-x) = 0",
288 "x + y = y + x",
289 "x * (y * z) = (x * y) * z",
290 "x * 1 = x",
291 "1 * x = x",
292 "x * (y + z) = x * y + x * z",
293 "(x + y) * z = x * z + y * z",
294 ],
295 )
296 }
297 #[allow(dead_code)]
298 pub fn is_equationally_definable(&self) -> bool {
299 true
300 }
301 #[allow(dead_code)]
302 pub fn closed_under_hsp(&self) -> bool {
303 true
304 }
305}
306#[allow(dead_code)]
308#[derive(Debug, Clone)]
309pub struct OmegaCategoricalStructure {
310 pub name: String,
311 pub automorphism_group_description: String,
312}
313impl OmegaCategoricalStructure {
314 #[allow(dead_code)]
315 pub fn new(name: &str, aut_group: &str) -> Self {
316 Self {
317 name: name.to_string(),
318 automorphism_group_description: aut_group.to_string(),
319 }
320 }
321 #[allow(dead_code)]
322 pub fn is_oligomorphic(&self) -> bool {
323 true
324 }
325 #[allow(dead_code)]
326 pub fn thomas_conjecture(&self) -> String {
327 "Thomas: Constraint satisfaction for omega-categorical structures is in NP or NP-complete or P"
328 .to_string()
329 }
330}
331#[allow(dead_code)]
333#[derive(Debug, Clone)]
334pub struct QuasiVariety {
335 pub name: String,
336 pub quasi_equations: Vec<String>,
337}
338impl QuasiVariety {
339 #[allow(dead_code)]
340 pub fn new(name: &str, eqs: Vec<&str>) -> Self {
341 Self {
342 name: name.to_string(),
343 quasi_equations: eqs.into_iter().map(String::from).collect(),
344 }
345 }
346 #[allow(dead_code)]
347 pub fn every_variety_is_quasivariety(&self) -> bool {
348 true
349 }
350 #[allow(dead_code)]
351 pub fn is_axiomatized_by_implications(&self) -> bool {
352 true
353 }
354}
355#[derive(Clone, Debug)]
357pub struct RewriteRule {
358 pub lhs: Term,
359 pub rhs: Term,
360 pub name: String,
361}
362impl RewriteRule {
363 pub fn new(name: &str, lhs: Term, rhs: Term) -> Self {
365 RewriteRule {
366 lhs,
367 rhs,
368 name: name.to_string(),
369 }
370 }
371 pub fn match_term<'a>(
374 pattern: &'a Term,
375 target: &'a Term,
376 subst: &mut Vec<(usize, Term)>,
377 ) -> bool {
378 match (pattern, target) {
379 (Term::Var(i), _) => {
380 if let Some((_, existing)) = subst.iter().find(|(v, _)| v == i) {
381 existing == target
382 } else {
383 subst.push((*i, target.clone()));
384 true
385 }
386 }
387 (
388 Term::App {
389 op: op1,
390 args: args1,
391 },
392 Term::App {
393 op: op2,
394 args: args2,
395 },
396 ) => {
397 if op1 != op2 || args1.len() != args2.len() {
398 return false;
399 }
400 for (a1, a2) in args1.iter().zip(args2.iter()) {
401 if !Self::match_term(a1, a2, subst) {
402 return false;
403 }
404 }
405 true
406 }
407 _ => false,
408 }
409 }
410 pub fn apply_subst(term: &Term, subst: &[(usize, Term)]) -> Term {
412 let mut result = term.clone();
413 for (var_idx, replacement) in subst {
414 result = result.subst(*var_idx, replacement);
415 }
416 result
417 }
418 pub fn try_apply_top(&self, t: &Term) -> Option<Term> {
421 let mut subst = Vec::new();
422 if Self::match_term(&self.lhs, t, &mut subst) {
423 Some(Self::apply_subst(&self.rhs, &subst))
424 } else {
425 None
426 }
427 }
428}
429pub struct KnuthBendixCompletion {
433 pub trs: TermRewriteSystem,
434 pub pending: Vec<(Term, Term)>,
436 pub max_steps: usize,
438}
439impl KnuthBendixCompletion {
440 pub fn new(sig: Signature, equations: Vec<(Term, Term)>, max_steps: usize) -> Self {
442 KnuthBendixCompletion {
443 trs: TermRewriteSystem::new(sig),
444 pending: equations,
445 max_steps,
446 }
447 }
448 fn orient(lhs: &Term, rhs: &Term) -> Option<(Term, Term)> {
450 match lhs.depth().cmp(&rhs.depth()) {
451 std::cmp::Ordering::Greater => Some((lhs.clone(), rhs.clone())),
452 std::cmp::Ordering::Less => Some((rhs.clone(), lhs.clone())),
453 std::cmp::Ordering::Equal => {
454 let lv = lhs.variables().len();
455 let rv = rhs.variables().len();
456 if lv > rv {
457 Some((lhs.clone(), rhs.clone()))
458 } else if rv > lv {
459 Some((rhs.clone(), lhs.clone()))
460 } else {
461 None
462 }
463 }
464 }
465 }
466 pub fn run(&mut self) -> bool {
469 for _step in 0..self.max_steps {
470 if self.pending.is_empty() {
471 return true;
472 }
473 let (lhs, rhs) = self.pending.remove(0);
474 let lhs_nf = self.trs.normalize(&lhs, 1000);
475 let rhs_nf = self.trs.normalize(&rhs, 1000);
476 if lhs_nf == rhs_nf {
477 continue;
478 }
479 if let Some((oriented_lhs, oriented_rhs)) = Self::orient(&lhs_nf, &rhs_nf) {
480 let rule = RewriteRule::new("kb_rule", oriented_lhs, oriented_rhs);
481 let mut new_trs = TermRewriteSystem::new(Signature::new());
482 new_trs.rules.clone_from(&self.trs.rules);
483 new_trs.rules.push(rule.clone());
484 let crit_pairs = new_trs.find_critical_pairs();
485 self.trs.add_rule(rule);
486 for (t1, t2) in crit_pairs {
487 self.pending.push((t1, t2));
488 }
489 } else {
490 return false;
491 }
492 }
493 self.pending.is_empty()
494 }
495 pub fn result_rules(&self) -> &[RewriteRule] {
497 &self.trs.rules
498 }
499}
500#[allow(dead_code)]
502#[derive(Debug, Clone)]
503pub struct DirectProductDecomposition {
504 pub algebra: String,
505 pub factors: Vec<String>,
506}
507impl DirectProductDecomposition {
508 #[allow(dead_code)]
509 pub fn new(alg: &str, factors: Vec<&str>) -> Self {
510 Self {
511 algebra: alg.to_string(),
512 factors: factors.into_iter().map(String::from).collect(),
513 }
514 }
515 #[allow(dead_code)]
516 pub fn is_nontrivial(&self) -> bool {
517 self.factors.len() > 1
518 }
519 #[allow(dead_code)]
520 pub fn subdirectly_irreducible_components(&self) -> Vec<String> {
521 self.factors.clone()
522 }
523}
524#[allow(dead_code)]
526#[derive(Debug, Clone)]
527pub struct Clone {
528 pub name: String,
529 pub contains_projections: bool,
530 pub closed_under_composition: bool,
531}
532impl Clone {
533 #[allow(dead_code)]
534 pub fn new(name: &str) -> Self {
535 Self {
536 name: name.to_string(),
537 contains_projections: true,
538 closed_under_composition: true,
539 }
540 }
541 #[allow(dead_code)]
542 pub fn clone_of_algebra(algebra: &str) -> Self {
543 Self::new(&format!("Clo({})", algebra))
544 }
545 #[allow(dead_code)]
546 pub fn galois_connection_description(&self) -> String {
547 format!(
548 "Galois connection: Clo <-> Inv (polymorphisms and invariant relations for {})",
549 self.name
550 )
551 }
552}
553pub struct TermAlgebra {
557 pub signature: Signature,
558 pub num_vars: usize,
559}
560impl TermAlgebra {
561 pub fn new(sig: Signature, num_vars: usize) -> Self {
563 TermAlgebra {
564 signature: sig,
565 num_vars,
566 }
567 }
568 pub fn is_well_formed(&self, term: &Term) -> bool {
570 match term {
571 Term::Var(i) => *i < self.num_vars,
572 Term::App { op, args } => {
573 if let Some(sym) = self.signature.operations.iter().find(|s| s.name == *op) {
574 sym.arity == args.len() && args.iter().all(|a| self.is_well_formed(a))
575 } else {
576 false
577 }
578 }
579 }
580 }
581 pub fn free_term(&self, op_name: &str) -> Option<Term> {
583 let sym = self
584 .signature
585 .operations
586 .iter()
587 .find(|s| s.name == *op_name)?;
588 let args: Vec<Term> = (0..sym.arity).map(Term::var).collect();
589 Some(Term::apply(op_name, args))
590 }
591}
592#[allow(dead_code)]
594#[derive(Debug, Clone)]
595pub struct RelationAlgebra {
596 pub name: String,
597 pub is_representable: bool,
598 pub is_simple: bool,
599}
600impl RelationAlgebra {
601 #[allow(dead_code)]
602 pub fn new(name: &str, rep: bool, simple: bool) -> Self {
603 Self {
604 name: name.to_string(),
605 is_representable: rep,
606 is_simple: simple,
607 }
608 }
609 #[allow(dead_code)]
610 pub fn operators_description(&self) -> String {
611 "(+, *, -, 0, 1, ;, ^, 1')".to_string()
612 }
613 #[allow(dead_code)]
614 pub fn representable_iff_field_of_sets(&self) -> bool {
615 self.is_representable
616 }
617}
618pub struct CongruenceRelation {
621 parent: Vec<usize>,
623 pub carrier_size: usize,
625}
626impl CongruenceRelation {
627 pub fn discrete(carrier_size: usize) -> Self {
629 CongruenceRelation {
630 parent: (0..carrier_size).collect(),
631 carrier_size,
632 }
633 }
634 pub fn total(carrier_size: usize) -> Self {
636 let mut rel = Self::discrete(carrier_size);
637 for i in 1..carrier_size {
638 rel.merge(0, i);
639 }
640 rel
641 }
642 pub fn find(&mut self, x: usize) -> usize {
644 if self.parent[x] != x {
645 let root = self.find(self.parent[x]);
646 self.parent[x] = root;
647 }
648 self.parent[x]
649 }
650 pub fn merge(&mut self, x: usize, y: usize) {
652 let rx = self.find(x);
653 let ry = self.find(y);
654 if rx != ry {
655 self.parent[ry] = rx;
656 }
657 }
658 pub fn are_equiv(&mut self, x: usize, y: usize) -> bool {
660 self.find(x) == self.find(y)
661 }
662 pub fn classes(&mut self) -> Vec<Vec<usize>> {
664 let mut map: std::collections::HashMap<usize, Vec<usize>> =
665 std::collections::HashMap::new();
666 for i in 0..self.carrier_size {
667 let root = self.find(i);
668 map.entry(root).or_default().push(i);
669 }
670 let mut classes: Vec<Vec<usize>> = map.into_values().collect();
671 for cls in &mut classes {
672 cls.sort_unstable();
673 }
674 classes.sort_by_key(|c| c[0]);
675 classes
676 }
677 pub fn num_classes(&mut self) -> usize {
679 self.classes().len()
680 }
681}
682pub struct TermRewriteSystem {
684 pub rules: Vec<RewriteRule>,
685 pub signature: Signature,
686}
687impl TermRewriteSystem {
688 pub fn new(sig: Signature) -> Self {
690 TermRewriteSystem {
691 rules: Vec::new(),
692 signature: sig,
693 }
694 }
695 pub fn add_rule(&mut self, rule: RewriteRule) {
697 self.rules.push(rule);
698 }
699 pub fn rewrite_step(&self, t: &Term) -> Option<Term> {
702 for rule in &self.rules {
703 if let Some(result) = rule.try_apply_top(t) {
704 return Some(result);
705 }
706 }
707 match t {
708 Term::Var(_) => None,
709 Term::App { op, args } => {
710 let mut new_args = args.clone();
711 for (i, arg) in args.iter().enumerate() {
712 if let Some(new_arg) = self.rewrite_step(arg) {
713 new_args[i] = new_arg;
714 return Some(Term::App {
715 op: op.clone(),
716 args: new_args,
717 });
718 }
719 }
720 None
721 }
722 }
723 }
724 pub fn normalize(&self, t: &Term, max_steps: usize) -> Term {
726 let mut current = t.clone();
727 for _ in 0..max_steps {
728 match self.rewrite_step(¤t) {
729 Some(next) => current = next,
730 None => break,
731 }
732 }
733 current
734 }
735 pub fn find_critical_pairs(&self) -> Vec<(Term, Term)> {
739 let mut pairs = Vec::new();
740 for (i, r1) in self.rules.iter().enumerate() {
741 for (j, r2) in self.rules.iter().enumerate() {
742 if i == j {
743 continue;
744 }
745 let mut subst1 = Vec::new();
746 let mut subst2 = Vec::new();
747 if RewriteRule::match_term(&r1.lhs, &r2.lhs, &mut subst1)
748 && RewriteRule::match_term(&r2.lhs, &r1.lhs, &mut subst2)
749 {
750 let t1 = RewriteRule::apply_subst(&r1.rhs, &subst1);
751 let t2 = RewriteRule::apply_subst(&r2.rhs, &subst2);
752 pairs.push((t1, t2));
753 }
754 }
755 }
756 pairs
757 }
758}
759pub struct Signature {
761 pub operations: Vec<OpSymbol>,
762}
763impl Signature {
764 pub fn new() -> Self {
766 Signature {
767 operations: Vec::new(),
768 }
769 }
770 pub fn add_op(&mut self, op: OpSymbol) {
772 self.operations.push(op);
773 }
774 pub fn arities(&self) -> Vec<(String, usize)> {
776 self.operations
777 .iter()
778 .map(|op| (op.name.clone(), op.arity))
779 .collect()
780 }
781 pub fn has_op(&self, name: &str) -> bool {
783 self.operations.iter().any(|op| op.name == name)
784 }
785}
786pub struct EquationalLaw {
788 pub name: String,
789 pub lhs: String,
790 pub rhs: String,
791}
792impl EquationalLaw {
793 pub fn new(name: &str, lhs: &str, rhs: &str) -> Self {
795 EquationalLaw {
796 name: name.to_string(),
797 lhs: lhs.to_string(),
798 rhs: rhs.to_string(),
799 }
800 }
801 pub fn is_trivial(&self) -> bool {
803 self.lhs == self.rhs
804 }
805}
806#[allow(dead_code)]
808#[derive(Debug, Clone)]
809pub struct SimpleRewriteSystem {
810 pub name: String,
811 pub rules: Vec<(String, String)>,
812 pub is_confluent: bool,
813 pub is_terminating: bool,
814}
815impl SimpleRewriteSystem {
816 #[allow(dead_code)]
817 pub fn new(name: &str) -> Self {
818 Self {
819 name: name.to_string(),
820 rules: Vec::new(),
821 is_confluent: false,
822 is_terminating: false,
823 }
824 }
825 #[allow(dead_code)]
826 pub fn add_rule(&mut self, lhs: &str, rhs: &str) {
827 self.rules.push((lhs.to_string(), rhs.to_string()));
828 }
829 #[allow(dead_code)]
830 pub fn is_complete(&self) -> bool {
831 self.is_confluent && self.is_terminating
832 }
833 #[allow(dead_code)]
834 pub fn normal_form_unique(&self) -> bool {
835 self.is_complete()
836 }
837 #[allow(dead_code)]
838 pub fn critical_pairs_description(&self) -> String {
839 format!("Critical pairs for {}: overlaps of LHS of rules", self.name)
840 }
841}
842#[allow(dead_code)]
844#[derive(Debug, Clone)]
845pub struct AlgebraHomomorphism {
846 pub source: String,
847 pub target: String,
848 pub is_injective: bool,
849 pub is_surjective: bool,
850}
851impl AlgebraHomomorphism {
852 #[allow(dead_code)]
853 pub fn new(src: &str, tgt: &str, inj: bool, surj: bool) -> Self {
854 Self {
855 source: src.to_string(),
856 target: tgt.to_string(),
857 is_injective: inj,
858 is_surjective: surj,
859 }
860 }
861 #[allow(dead_code)]
862 pub fn is_isomorphism(&self) -> bool {
863 self.is_injective && self.is_surjective
864 }
865 #[allow(dead_code)]
866 pub fn is_embedding(&self) -> bool {
867 self.is_injective
868 }
869}
870#[allow(dead_code)]
872#[derive(Debug, Clone)]
873pub struct FreeAlgebra {
874 pub variety_name: String,
875 pub num_generators: usize,
876}
877impl FreeAlgebra {
878 #[allow(dead_code)]
879 pub fn new(variety: &str, generators: usize) -> Self {
880 Self {
881 variety_name: variety.to_string(),
882 num_generators: generators,
883 }
884 }
885 #[allow(dead_code)]
886 pub fn universal_property(&self) -> String {
887 format!(
888 "Free {}-algebra on {} generators: any map to any {}-algebra extends uniquely",
889 self.variety_name, self.num_generators, self.variety_name
890 )
891 }
892 #[allow(dead_code)]
893 pub fn word_problem_decidable(&self) -> bool {
894 true
895 }
896}
897#[allow(dead_code)]
899#[derive(Debug, Clone)]
900pub struct MaltsevCondition {
901 pub name: String,
902 pub terms: Vec<String>,
903 pub equations: Vec<String>,
904}
905impl MaltsevCondition {
906 #[allow(dead_code)]
907 pub fn congruence_permutability() -> Self {
908 Self {
909 name: "Congruence permutability".to_string(),
910 terms: vec!["p(x,y,z)".to_string()],
911 equations: vec!["p(x,x,y) = y".to_string(), "p(x,y,y) = x".to_string()],
912 }
913 }
914 #[allow(dead_code)]
915 pub fn congruence_distributivity(n: usize) -> Self {
916 Self {
917 name: format!("Congruence distributivity (Jonsson n={})", n),
918 terms: (0..=n).map(|i| format!("d_{i}(x,y,z)")).collect(),
919 equations: vec![
920 "d_0(x,y,z) = x".to_string(),
921 format!("d_{}(x,y,z) = z", n),
922 "d_i(x,x,z) = d_{i+1}(x,x,z) for even i".to_string(),
923 "d_i(x,z,z) = d_{i+1}(x,z,z) for odd i".to_string(),
924 ],
925 }
926 }
927 #[allow(dead_code)]
928 pub fn characterizes_variety(&self) -> bool {
929 true
930 }
931}
932pub struct Variety {
934 pub name: String,
935 pub laws: Vec<EquationalLaw>,
936 pub signature: Signature,
937}
938impl Variety {
939 pub fn new(name: &str, sig: Signature) -> Self {
941 Variety {
942 name: name.to_string(),
943 laws: Vec::new(),
944 signature: sig,
945 }
946 }
947 pub fn add_law(&mut self, law: EquationalLaw) {
949 self.laws.push(law);
950 }
951 pub fn is_group_variety(&self) -> bool {
954 self.includes_law("assoc") && self.includes_law("identity") && self.includes_law("inverse")
955 }
956 pub fn includes_law(&self, name: &str) -> bool {
958 self.laws.iter().any(|l| l.name == name)
959 }
960}
961#[allow(dead_code)]
963#[derive(Debug, Clone)]
964pub struct CongruenceLattice {
965 pub algebra_name: String,
966 pub is_distributive: bool,
967 pub is_modular: bool,
968}
969impl CongruenceLattice {
970 #[allow(dead_code)]
971 pub fn new(name: &str, dist: bool, modular: bool) -> Self {
972 Self {
973 algebra_name: name.to_string(),
974 is_distributive: dist,
975 is_modular: modular,
976 }
977 }
978 #[allow(dead_code)]
979 pub fn has_permuting_congruences(&self) -> bool {
980 false
981 }
982 #[allow(dead_code)]
983 pub fn jonssontheorem_description(&self) -> String {
984 format!(
985 "Jonsson's theorem: Subdirectly irreducible members of variety of {}",
986 self.algebra_name
987 )
988 }
989}