From d00902b9f5596d7cda2e45eff3782c64323c8f88 Mon Sep 17 00:00:00 2001 From: Johann Rosain Date: Mon, 23 Mar 2026 18:18:12 +0100 Subject: [PATCH 01/23] clean: remove TermForm and unify terms --- src/AST/tptp-native-types.go | 2 - src/AST/ty-syntax.go | 9 +- src/Core/FormListDS.go | 4 + src/Mods/equality/bse/equality_problem.go | 7 +- .../equality/bse/equality_rules_try_apply.go | 4 +- src/Unif/code-trees.go | 89 ++++++----- src/Unif/data_structure.go | 12 ++ src/Unif/machine.go | 4 +- src/Unif/matching.go | 130 +++++++++------ src/Unif/matching_substitutions.go | 65 +++++++- src/Unif/parsing.go | 150 +++++------------- src/Unif/sequence.go | 41 ++++- src/Unif/substitutions_tree.go | 104 ++++++------ 13 files changed, 350 insertions(+), 271 deletions(-) diff --git a/src/AST/tptp-native-types.go b/src/AST/tptp-native-types.go index 320cf3b7..3b4b821d 100644 --- a/src/AST/tptp-native-types.go +++ b/src/AST/tptp-native-types.go @@ -60,8 +60,6 @@ func initTPTPNativeTypes() { tIndividual = MkTyConst("$i") tProp = MkTyConst("$o") - - count_meta = 0 } func TType() Ty { diff --git a/src/AST/ty-syntax.go b/src/AST/ty-syntax.go index 5ea889c8..7f5b28f3 100644 --- a/src/AST/ty-syntax.go +++ b/src/AST/ty-syntax.go @@ -46,7 +46,6 @@ import ( ) var meta_mut sync.Mutex -var count_meta int type TyGenVar interface { isGenVar() @@ -286,6 +285,10 @@ func (p TyPi) VarsLen() int { return p.vars.Len() } +func (p TyPi) Ty() Ty { + return p.ty +} + // Makers func MkTyVar(repr string) Ty { @@ -298,8 +301,8 @@ func MkTyBV(name string, index int) Ty { func MkTyMeta(name string, formula int) Ty { meta_mut.Lock() - meta := TyMeta{name, count_meta, formula} - count_meta += 1 + meta := TyMeta{name, cpt_term, formula} + cpt_term += 1 meta_mut.Unlock() return meta } diff --git a/src/Core/FormListDS.go b/src/Core/FormListDS.go index f9017fa1..1ab41fcd 100644 --- a/src/Core/FormListDS.go +++ b/src/Core/FormListDS.go @@ -90,3 +90,7 @@ func (fl FormListDS) Unify(f AST.Form) (bool, []Unif.MixedSubstitutions) { } return false, []Unif.MixedSubstitutions{} } + +func (fl FormListDS) UnifyTerm(t AST.Term) (bool, []Unif.MixedTermSubstitutions) { + return false, []Unif.MixedTermSubstitutions{} +} diff --git a/src/Mods/equality/bse/equality_problem.go b/src/Mods/equality/bse/equality_problem.go index 803d84e1..7014fe94 100644 --- a/src/Mods/equality/bse/equality_problem.go +++ b/src/Mods/equality/bse/equality_problem.go @@ -136,11 +136,12 @@ func makeEqualityProblem(E Equalities, s AST.Term, t AST.Term, c ConstraintStruc /* Take a list of equalities and build the corresponding code tree */ func makeDataStructFromEqualities(eq Equalities) Unif.DataStructure { - formList := Lib.NewList[AST.Form]() + formList := Lib.NewList[AST.Term]() for _, e := range eq { - formList.Append(Unif.MakerTermForm(e.GetT1()), Unif.MakerTermForm(e.GetT2())) + formList.Append(e.GetT1(), e.GetT2()) } - return Unif.NewNode().MakeDataStruct(Lib.ListCpy(formList), true) + + return Unif.MakeTermUnifProblem(Lib.ListCpy(formList)) } /* Take a list of equalities and build the corresponding assocative map */ diff --git a/src/Mods/equality/bse/equality_rules_try_apply.go b/src/Mods/equality/bse/equality_rules_try_apply.go index cfc5a359..ceba6fa0 100644 --- a/src/Mods/equality/bse/equality_rules_try_apply.go +++ b/src/Mods/equality/bse/equality_rules_try_apply.go @@ -220,7 +220,7 @@ func searchUnifBewteenListAndEq(tl Lib.List[AST.Term], tree Unif.DataStructure) /* Take a (sub)-term t, and retrieve all the term t' unifiable with t */ func checkUnifInTree(t AST.Term, tree Unif.DataStructure) (bool, Lib.List[AST.Term]) { result_list := Lib.NewList[AST.Term]() - res, ms := tree.Unify(Unif.MakerTermForm(t.Copy())) + res, ms := tree.UnifyTerm(t.Copy()) if !res { return false, result_list @@ -232,7 +232,7 @@ func checkUnifInTree(t AST.Term, tree Unif.DataStructure) (bool, Lib.List[AST.Te return fmt.Sprintf("Unif found with: %s", subst.ToString()) }), ) - result_list.Append(subst.GetForm().(Unif.TermForm).GetTerm()) + result_list.Append(subst.Term()) } return result_list.Len() > 0, result_list diff --git a/src/Unif/code-trees.go b/src/Unif/code-trees.go index 6beb3371..3a7e25d1 100644 --- a/src/Unif/code-trees.go +++ b/src/Unif/code-trees.go @@ -58,11 +58,11 @@ func (c CodeBlock) Copy() CodeBlock { type Node struct { value CodeBlock children []*Node - formulas Lib.List[AST.Form] + leafFor Lib.List[Lib.Either[AST.Term, AST.Form]] } func NewNode() *Node { - return &Node{CodeBlock{}, []*Node{}, Lib.NewList[AST.Form]()} + return &Node{CodeBlock{}, []*Node{}, Lib.NewList[Lib.Either[AST.Term, AST.Form]]()} } func (n Node) getValue() CodeBlock { @@ -71,38 +71,48 @@ func (n Node) getValue() CodeBlock { func (n Node) getChildren() []*Node { return CopyNodeList(n.children) } -func (n Node) getFormulas() Lib.List[AST.Form] { - return Lib.ListCpy(n.formulas) -} /* Check if a node is empty */ func (n Node) IsEmpty() bool { return (len(n.value) == 0) } -/* Make data struct */ +func MakeUnifProblem(l Lib.List[AST.Form], is_pos bool) DataStructure { + return NewNode().MakeDataStruct(l, is_pos) +} + +func MakeTermUnifProblem(l Lib.List[AST.Term]) DataStructure { + root := makeNode(nil) + + for _, t := range l.GetSlice() { + root.insert(ParseTerm(transformTerm(t))) + } + + return root +} + func (n Node) MakeDataStruct(fl Lib.List[AST.Form], is_pos bool) DataStructure { return makeCodeTreeFromAtomic(fl, is_pos) } /* Copy a datastruct */ func (n Node) Copy() DataStructure { - return Node{n.getValue(), n.getChildren(), n.getFormulas()} + return Node{n.getValue(), n.getChildren(), n.leafFor.Copy(Lib.EitherCpy[AST.Term, AST.Form])} } /********************/ /* Helper functions */ /********************/ -/* The Node is a leaf when it contains at least one formulae. */ +/* The Node is a leaf whenever one formula or term ends here. */ func (n Node) isLeaf() bool { - return n.getFormulas().Len() > 0 + return n.leafFor.Len() > 0 } -/* Make two code trees (tree_pos and tree_neg) from st.atomic */ func makeCodeTreeFromAtomic(lf Lib.List[AST.Form], is_pos bool) *Node { form := Lib.NewList[AST.Form]() + // fixme: why are we doing this here? for _, f := range lf.GetSlice() { switch nf := f.(type) { case AST.Pred: @@ -116,9 +126,6 @@ func makeCodeTreeFromAtomic(lf Lib.List[AST.Form], is_pos bool) *Node { form.Append(nf.GetForm()) } } - case TermForm: - // EQUALITY - To build a tree of terms - form.Append(nf.Copy()) } } @@ -152,7 +159,7 @@ func makeNode(block CodeBlock) *Node { n := new(Node) n.value = block.Copy() n.children = []*Node{} - n.formulas = Lib.NewList[AST.Form]() + n.leafFor = Lib.NewList[Lib.Either[AST.Term, AST.Form]]() return n } @@ -198,8 +205,17 @@ func (n Node) printAux(tab int) { } if n.isLeaf() { - for _, form := range n.formulas.GetSlice() { - debug(Lib.MkLazy(func() string { return strings.Repeat("\t", tab+1) + form.ToString() })) + for _, tof := range n.leafFor.GetSlice() { + debug( + Lib.MkLazy( + func() string { + return strings.Repeat( + "\t", + tab+1, + ) + tofToString(tof) + }, + ), + ) } } debug(Lib.MkLazy(func() string { return "\n" })) @@ -214,17 +230,17 @@ func (n Node) printAux(tab int) { func (n *Node) insert(sequence Sequence) { if len(n.value) == 0 { n.value = sequence.GetInstructions() - n.formulas = Lib.MkListV(sequence.GetFormula()) + n.leafFor = Lib.MkListV(sequence.GetBase()) } else { - n.followInstructions(sequence.GetInstructions(), sequence.GetFormula()) + n.followInstructions(sequence.GetInstructions(), sequence.GetBase()) } } /* Auxiliary function to follow the sequence of instructions to insert in the Node. */ -func (n *Node) followInstructions(instructions []Instruction, form AST.Form) { +func (n *Node) followInstructions(instructions []Instruction, tof Lib.Either[AST.Term, AST.Form]) { // Initialization of the node we will be working on and of a counter. current := n - oui := 0 + cnt := 0 // For each instruction, there are 2 cases: // * The current instruction is equivalent to the instruction stored in the CodeBlock of the current node at the index of the counter. @@ -234,11 +250,12 @@ func (n *Node) followInstructions(instructions []Instruction, form AST.Form) { // If it's equivalent, there are 2 cases: // * It's the end of the sequence & the end of the CodeBlock. In this case, it's a full match, just add the formulae to the leaf. // * It's the end of the CodeBlock, but not of the sequence. In this case, check if the following instruction matches with any child. - if instr.IsEquivalent(current.value[oui]) { - oui += 1 - if i == len(instructions)-1 && oui == len(current.value) && !Lib.ListMem(form, current.formulas) { - current.formulas.Append(form) - } else if i < len(instructions)-1 && oui == len(current.value) { + if instr.IsEquivalent(current.value[cnt]) { + cnt += 1 + if i == len(instructions)-1 && cnt == len(current.value) && + !current.leafFor.Contains(tof, tofCmp) { + current.leafFor.Append(tof) + } else if i < len(instructions)-1 && cnt == len(current.value) { // If the instruction matches, then continue the algorithm with the child as the current node. // If it doesn't, we have a new leaf with the following instructions of the sequence. @@ -246,13 +263,13 @@ func (n *Node) followInstructions(instructions []Instruction, form AST.Form) { for _, child := range current.children { if instructions[i+1].IsEquivalent(child.value[0]) { current = child - oui = 0 + cnt = 0 found = true } } if !found { newNode := makeNode(instructions[i+1:]) - newNode.formulas = Lib.MkListV(form) + newNode.leafFor = Lib.MkListV(tof) current.children = append(current.children, newNode) break } @@ -261,15 +278,15 @@ func (n *Node) followInstructions(instructions []Instruction, form AST.Form) { // Split the current CodeBlock in 2 parts: // * The first one will contain the remaining instructions of the current CodeBlock, and it will inherit the current's children and formulaes. // * The second one contains the remaining instructions of the sequence plus the formulae. - child1 := makeNode(current.value[oui:]) + child1 := makeNode(current.value[cnt:]) child2 := makeNode(instructions[i:]) - child2.formulas = Lib.MkListV(form) + child2.leafFor = Lib.MkListV(tof) child1.children = current.children - child1.formulas = current.formulas + child1.leafFor = current.leafFor - current.value = current.value[:oui] - current.formulas = Lib.NewList[AST.Form]() + current.value = current.value[:cnt] + current.leafFor = Lib.NewList[Lib.Either[AST.Term, AST.Form]]() current.children = []*Node{child1, child2} break @@ -279,12 +296,12 @@ func (n *Node) followInstructions(instructions []Instruction, form AST.Form) { // It's the end of the sequence, but there are still instructions in the CodeBlock. // In this case, we have to split the CodeBlock in 2 parts. The first one will be a leaf containing the current sequence's formulae. // The second will be the rest of the CodeBlock's instructions, with the current's children and formulaes. - if oui < len(current.value)-1 { - child1 := makeNode(current.value[oui:]) + if cnt < len(current.value)-1 { + child1 := makeNode(current.value[cnt:]) child1.children = current.children - current.value = current.value[:oui] + current.value = current.value[:cnt] current.children = []*Node{child1} - current.formulas = Lib.MkListV(form.Copy()) + current.leafFor = Lib.MkListV(tofCopy(tof)) } } diff --git a/src/Unif/data_structure.go b/src/Unif/data_structure.go index c3d9f2a7..7678be98 100644 --- a/src/Unif/data_structure.go +++ b/src/Unif/data_structure.go @@ -47,6 +47,18 @@ type DataStructure interface { IsEmpty() bool MakeDataStruct(Lib.List[AST.Form], bool) DataStructure InsertFormulaListToDataStructure(Lib.List[AST.Form]) DataStructure + Unify(AST.Form) (bool, []MixedSubstitutions) + UnifyTerm(AST.Term) (bool, []MixedTermSubstitutions) + // FIXME: + // When the unification gets reworked, think a bit more about the exposed interface. + // We want to index on _terms_ while keeping the ability to unify _predicates_. + // (we can easily coerce a predicate to a function) + // We probably want to expose two functions --- one to unify predicates, and the other + // one to unify terms. But maybe we should say that unifying predicates is the "weird" + // case instead of the other way around. + // + // We should also find a more explicit name over `DataStructure`... + Copy() DataStructure } diff --git a/src/Unif/machine.go b/src/Unif/machine.go index 6fa2c0ec..abd835ae 100644 --- a/src/Unif/machine.go +++ b/src/Unif/machine.go @@ -65,7 +65,7 @@ type Machine struct { subst []SubstPair terms Lib.List[AST.Term] meta Substitutions - failure []MatchingSubstitutions + failure []MixMatchSubstitutions topLevelTot int topLevelCount int } @@ -82,7 +82,7 @@ func makeMachine() Machine { subst: []SubstPair{}, terms: Lib.NewList[AST.Term](), meta: Substitutions{}, - failure: []MatchingSubstitutions{}, + failure: []MixMatchSubstitutions{}, topLevelTot: 0, topLevelCount: 0, } diff --git a/src/Unif/matching.go b/src/Unif/matching.go index 2091101a..9ec4a010 100644 --- a/src/Unif/matching.go +++ b/src/Unif/matching.go @@ -56,58 +56,67 @@ func InitDebugger() { /* Helper function to avoid using MakeMachine() outside of this file. */ func (n Node) Unify(formula AST.Form) (bool, []MixedSubstitutions) { machine := makeMachine() - res := machine.unify(n, formula) + var term AST.Term + + if formula_type, is_pred := formula.(AST.Pred); is_pred { + term = transformPred(formula_type) + } else { + Glob.Anomaly("unification", fmt.Sprintf("Expected predicate, got %s", formula.ToString())) + } + + res, matching_substs := machine.unify(n, term) + // As we have transformed type metas to terms, we get everything in a term substitution. // But externally, we want to have a substitution of both (term) metas to terms and (type) metas to types. // We use MixedSubstitution to properly manage things internally. mixed_substs := []MixedSubstitutions{} - for _, subst := range res { + for _, subst := range matching_substs { mixed_substs = append(mixed_substs, subst.toMixed()) } - return !reflect.DeepEqual(machine.failure, res), mixed_substs + + return res, mixed_substs } -/* Tries to find the substitutions needed to unify the formulae with the one described by the sequence of instructions. */ -func (m *Machine) unify(node Node, formula AST.Form) []MatchingSubstitutions { - var result []MatchingSubstitutions - // The formula has to be a predicate. - switch formula_type := formula.(type) { - case AST.Pred: - // Transform the predicate to a function to make the tool work properly - m.terms = Lib.MkListV[AST.Term](AST.MakerFun( - formula_type.GetID(), - Lib.NewList[AST.Ty](), - getFunctionalArguments(formula_type.GetTyArgs(), formula_type.GetArgs()), - )) - result = m.unifyAux(node) - - if !reflect.DeepEqual(m.failure, result) { - filteredResult := []MatchingSubstitutions{} - for _, matchingSubst := range result { - filteredResult = append(filteredResult, - MakeMatchingSubstitutions(matchingSubst.GetForm(), matchingSubst.GetSubst())) - } - result = filteredResult - } - case TermForm: - m.terms = Lib.MkListV(formula_type.GetTerm()) - result = m.unifyAux(node) - default: - result = m.failure +func (n Node) UnifyTerm(t AST.Term) (bool, []MixedTermSubstitutions) { + m := makeMachine() + + res, matching_substs := m.unify( + n, + transformTerm(t), + ) + + mixed_substs := []MixedTermSubstitutions{} + for _, subst := range matching_substs { + mixed_substs = append(mixed_substs, subst.toMixedTerm()) } - return result + return res, mixed_substs +} + +/* Tries to find the substitutions needed to unify the formulae with the one described by the sequence of instructions. */ +func (m *Machine) unify(node Node, t AST.Term) (bool, []MixMatchSubstitutions) { + m.terms = Lib.MkListV(t) + res := m.unifyAux(node) + return !reflect.DeepEqual(m.failure, res), res } /*** Unify aux ***/ -func (m *Machine) unifyAux(node Node) []MatchingSubstitutions { +func (m *Machine) unifyAux(node Node) []MixMatchSubstitutions { for _, instr := range node.value { debug(Lib.MkLazy(func() string { return "------------------------" })) debug(Lib.MkLazy(func() string { return fmt.Sprintf("Instr: %v", instr.ToString()) })) debug(Lib.MkLazy(func() string { return fmt.Sprintf("Meta : %v", m.meta.ToString()) })) - debug(Lib.MkLazy(func() string { return fmt.Sprintf("Subst : %v", SubstPairListToString(m.subst)) })) - debug(Lib.MkLazy(func() string { return fmt.Sprintf("Post : %v", IntPairistToString(m.post)) })) + debug( + Lib.MkLazy( + func() string { return fmt.Sprintf("Subst : %v", SubstPairListToString(m.subst)) }, + ), + ) + debug( + Lib.MkLazy( + func() string { return fmt.Sprintf("Post : %v", IntPairistToString(m.post)) }, + ), + ) debug(Lib.MkLazy(func() string { return fmt.Sprintf("IsLocked : %v", m.isLocked()) })) debug(Lib.MkLazy(func() string { return fmt.Sprintf("HasPushed : %v", m.hasPushed) })) debug(Lib.MkLazy(func() string { return fmt.Sprintf("HasPoped : %v", m.hasPoped) })) @@ -131,7 +140,9 @@ func (m *Machine) unifyAux(node Node) []MatchingSubstitutions { Lib.MkLazy(func() string { return fmt.Sprintf("Cursor: %v/%v", m.q, m.terms.Len()) }), ) debug( - Lib.MkLazy(func() string { return fmt.Sprintf("m.terms[cursor] : %v", m.terms.At(m.q).ToString()) }), + Lib.MkLazy( + func() string { return fmt.Sprintf("m.terms[cursor] : %v", m.terms.At(m.q).ToString()) }, + ), ) debug( Lib.MkLazy(func() string { @@ -173,26 +184,29 @@ func (m *Machine) unifyAux(node Node) []MatchingSubstitutions { } } - matching := []MatchingSubstitutions{} + matching := []MixMatchSubstitutions{} if node.isLeaf() { - for _, f := range node.formulas.GetSlice() { - if reflect.TypeOf(f) == reflect.TypeOf(AST.Pred{}) || reflect.TypeOf(f) == reflect.TypeOf(TermForm{}) { - // Rebuild final substitution between meta and subst - final_subst := computeSubstitutions(CopySubstPairList(m.subst), m.meta.Copy(), f.Copy()) - if !final_subst.Equals(Failure()) { - matching = append(matching, MakeMatchingSubstitutions(f, final_subst)) - } + for _, f := range node.leafFor.GetSlice() { + // Rebuild final substitution between meta and subst + final_subst := computeSubstitutions( + CopySubstPairList(m.subst), + m.meta.Copy(), + tofMetaList(f), + ) + if !final_subst.Equals(Failure()) { + matching = append(matching, MixMatchSubstitutions{tof: f, subst: final_subst}) } } } + matching = append(matching, m.launchChildrenSearch(node)...) return matching } /* Unify on goroutines - to manage die message */ /* TODO : remove when debug ok */ -func (m *Machine) unifyAuxOnGoroutine(n Node, ch chan []MatchingSubstitutions, father_id uint64) { +func (m *Machine) unifyAuxOnGoroutine(n Node, ch chan []MixMatchSubstitutions, father_id uint64) { debug( Lib.MkLazy(func() string { return fmt.Sprintf("Child of %v, Unify Aux", father_id) }), ) @@ -202,23 +216,37 @@ func (m *Machine) unifyAuxOnGoroutine(n Node, ch chan []MatchingSubstitutions, f } /* Launches each child of the current node in a goroutine. */ -func (m *Machine) launchChildrenSearch(node Node) []MatchingSubstitutions { - channels := []chan []MatchingSubstitutions{} +func (m *Machine) launchChildrenSearch(node Node) []MixMatchSubstitutions { + channels := []chan []MixMatchSubstitutions{} for _, c := range node.children { debug( - Lib.MkLazy(func() string { return fmt.Sprintf("Next symbol = %v", c.getValue()[0].ToString()) }), + Lib.MkLazy( + func() string { return fmt.Sprintf("Next symbol = %v", c.getValue()[0].ToString()) }, + ), ) - channels = append(channels, make(chan []MatchingSubstitutions)) + channels = append(channels, make(chan []MixMatchSubstitutions)) } - matching := []MatchingSubstitutions{} + matching := []MixMatchSubstitutions{} for i, n := range node.children { ch := channels[i] st := m.terms.Copy(AST.Term.Copy) ip := CopyIntPairList(m.post) sc := CopySubstPairList(m.subst) - copy := Machine{subst: sc, beginLock: m.beginLock, terms: st, meta: m.meta.Copy(), q: m.q, beginCount: m.beginCount, hasPushed: m.hasPushed, hasPoped: m.hasPoped, post: ip, topLevelTot: m.topLevelTot, topLevelCount: m.topLevelCount} + copy := Machine{ + subst: sc, + beginLock: m.beginLock, + terms: st, + meta: m.meta.Copy(), + q: m.q, + beginCount: m.beginCount, + hasPushed: m.hasPushed, + hasPoped: m.hasPoped, + post: ip, + topLevelTot: m.topLevelTot, + topLevelCount: m.topLevelCount, + } go copy.unifyAuxOnGoroutine(*n, ch, Glob.GetGID()) Glob.IncrGoRoutine(1) @@ -232,7 +260,7 @@ func (m *Machine) launchChildrenSearch(node Node) []MatchingSubstitutions { for cpt_remaining_children > 0 { _, value, _ := reflect.Select(cases) - matching = append(matching, value.Interface().([]MatchingSubstitutions)...) + matching = append(matching, value.Interface().([]MixMatchSubstitutions)...) cpt_remaining_children-- } diff --git a/src/Unif/matching_substitutions.go b/src/Unif/matching_substitutions.go index 7ed91cb5..1cedfd57 100644 --- a/src/Unif/matching_substitutions.go +++ b/src/Unif/matching_substitutions.go @@ -106,11 +106,12 @@ func translateTermRec(term AST.Term) AST.Term { opt_ty := Typing.QueryGlobalEnv(trm.GetName()) switch ty := opt_ty.(type) { case Lib.Some[AST.Ty]: - switch t := ty.Val.(type) { - case AST.TyPi: - for i := 0; i < t.VarsLen(); i++ { - ty_args.Append(AST.TermToTy(trm.GetArgs().At(i))) - } + t := ty.Val + i := 0 + for Glob.Is[AST.TyPi](t) { + ty_args.Append(AST.TermToTy(trm.GetArgs().At(i))) + t = t.(AST.TyPi).Ty() + i += 1 } } args := trm.GetArgs() @@ -268,6 +269,56 @@ func (m MixedSubstitutions) MatchingSubstitutions() MatchingSubstitutions { return MakeMatchingSubstitutions(m.form, m.GetTrmSubsts()) } +type MixMatchSubstitutions struct { + tof Lib.Either[AST.Term, AST.Form] + subst Substitutions +} + +// Pre-requisite: only formulas in the tof +func (s MixMatchSubstitutions) toMatching() MatchingSubstitutions { + switch tof := s.tof.(type) { + case Lib.Left[AST.Term, AST.Form]: + Glob.Anomaly("unification", "expected unification between formulas, got unification between terms") + case Lib.Right[AST.Term, AST.Form]: + return MakeMatchingSubstitutions(tof.Val, s.subst) + } + + Glob.Anomaly("unification", "reached an unreachable case") + return MakeMatchingSubstitutions(AST.MakerTop(), MakeEmptySubstitution()) +} + +func (s MixMatchSubstitutions) toMixed() MixedSubstitutions { + return s.toMatching().toMixed() +} + +type MixedTermSubstitutions struct { + term AST.Term + substs []MixedSubstitution +} + +func (s MixedTermSubstitutions) Term() AST.Term { return s.term } + +func (s MixedTermSubstitutions) ToString() string { + substs_list := Lib.MkListV(s.substs...) + return s.term.ToString() + " {" + Lib.ListToString(substs_list, Lib.WithEmpty("")) + "}" +} + +func (s MixMatchSubstitutions) toMixedTerm() MixedTermSubstitutions { + switch tof := s.tof.(type) { + case Lib.Left[AST.Term, AST.Form]: + substs := []MixedSubstitution{} + for _, subst := range s.subst { + substs = append(substs, translateFromSubst(subst)) + } + return MixedTermSubstitutions{tof.Val, substs} + case Lib.Right[AST.Term, AST.Form]: + Glob.Anomaly("unification", "expected unification between terms, got unification between formulas") + } + + Glob.Anomaly("unification", "reached an unreachable case") + return MixedTermSubstitutions{nil, []MixedSubstitution{}} +} + func translateToSubst(subst MixedSubstitution) Substitution { switch s := subst.s.(type) { case Lib.Left[TySubstitution, Substitution]: @@ -283,7 +334,9 @@ func translateToSubst(subst MixedSubstitution) Substitution { return MakeSubstitution(AST.MakeEmptyMeta(), nil) } -func MergeMixedSubstitutions(substs1, substs2 Lib.List[MixedSubstitution]) (Lib.List[MixedSubstitution], bool) { +func MergeMixedSubstitutions( + substs1, substs2 Lib.List[MixedSubstitution], +) (Lib.List[MixedSubstitution], bool) { translated_substs1 := Lib.ListMap(substs1, translateToSubst).GetSlice() translated_substs2 := Lib.ListMap(substs2, translateToSubst).GetSlice() diff --git a/src/Unif/parsing.go b/src/Unif/parsing.go index c8a80fc6..5e8d080d 100644 --- a/src/Unif/parsing.go +++ b/src/Unif/parsing.go @@ -34,79 +34,30 @@ package Unif import ( "github.com/GoelandProver/Goeland/AST" + "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" ) -type TermForm struct { - index int - t AST.Term +func transformPred(p AST.Pred) AST.Term { + return transformTerm(AST.MakerFun(p.GetID(), p.GetTyArgs(), p.GetArgs())) } -func (t TermForm) ToString() string { return t.ToString() } -func (t TermForm) GetTerm() AST.Term { return t.t.Copy() } -func (t TermForm) Copy() AST.Form { return makeTermForm(t.GetIndex(), t.GetTerm()) } -func (t TermForm) RenameVariables() AST.Form { return t } -func (t TermForm) ReplaceTermByTerm(AST.Term, AST.Term) (AST.Form, bool) { - return t, false -} -func (t TermForm) SubstTy(AST.TyGenVar, AST.Ty) AST.Form { - return t -} -func (t TermForm) GetIndex() int { return t.index } -func (t TermForm) SubstituteVarByMeta(AST.Var, AST.Meta) AST.Form { return t } -func (t TermForm) GetInternalMetas() Lib.List[AST.Meta] { return Lib.NewList[AST.Meta]() } -func (t TermForm) SetInternalMetas(Lib.List[AST.Meta]) AST.Form { return t } -func (t TermForm) GetSubFormulasRecur() Lib.List[AST.Form] { return Lib.NewList[AST.Form]() } -func (t TermForm) GetChildFormulas() Lib.List[AST.Form] { return Lib.NewList[AST.Form]() } - -func (t TermForm) Equals(t2 any) bool { - switch nt := t2.(type) { - case TermForm: - return t.GetTerm().Equals(nt.GetTerm()) - default: - return false - } -} - -func (t TermForm) GetMetas() Lib.Set[AST.Meta] { - switch nt := t.GetTerm().(type) { - case AST.Meta: - return Lib.Singleton(nt) - case AST.Fun: - res := Lib.EmptySet[AST.Meta]() - - for _, m := range nt.GetArgs().GetSlice() { - switch mt := m.(type) { - case AST.Meta: - res = res.Add(mt) - } - } - - return res - default: - return Lib.EmptySet[AST.Meta]() - } -} - -func (t TermForm) GetSubTerms() Lib.List[AST.Term] { - return t.GetTerm().GetSubTerms() -} - -func (t TermForm) ReplaceMetaByTerm(meta AST.Meta, term AST.Term) AST.Form { - return t -} - -func MakerTermForm(t AST.Term) TermForm { - switch trm := t.(type) { +func transformTerm(t AST.Term) AST.Term { + switch term := t.(type) { + case AST.Id, AST.Meta, AST.Var: + return t case AST.Fun: - args := getFunctionalArguments(trm.GetTyArgs(), trm.GetArgs()) - t = AST.MakerFun(trm.GetID(), Lib.NewList[AST.Ty](), args) + args := Lib.ListMap(term.GetTyArgs(), AST.TyToTerm) + args.Append(Lib.ListMap(term.GetArgs(), transformTerm).GetSlice()...) + return AST.MakerFun( + term.GetID(), + Lib.NewList[AST.Ty](), + args, + ) } - return makeTermForm(AST.MakerIndexFormula(), t.Copy()) -} -func makeTermForm(i int, t AST.Term) TermForm { - return TermForm{i, t.Copy()} + Glob.Anomaly("unif parsing", "Unknown term") + return nil } /* Parses a formulae to a sequence of instructions. */ @@ -115,26 +66,17 @@ func ParseFormula(formula AST.Form) Sequence { // The formula has to be a predicate switch formula_type := formula.(type) { case AST.Pred: - instructions := Sequence{formula: formula_type} + instructions := Sequence{base: Lib.MkRight[AST.Term, AST.Form](formula)} - instructions.add(Begin{}) - parsePred(formula_type, &instructions) - instructions.add(End{}) + switch term := transformPred(formula_type).(type) { + case AST.Fun: + instructions.add(Begin{}) + parsePred(formula_type.GetID(), term.GetArgs(), &instructions) + instructions.add(End{}) - return instructions - case TermForm: - instructions := Sequence{formula: formula} - varCount := 0 - postCount := 0 - instructions.add(Begin{}) - parseTerms( - Lib.MkListV(formula_type.GetTerm().Copy()), - &instructions, - Lib.NewList[AST.Meta](), - &varCount, - &postCount, - ) - instructions.add(End{}) + default: + Glob.Anomaly("unification", "error when translating in internal representation") + } return instructions @@ -143,35 +85,15 @@ func ParseFormula(formula AST.Form) Sequence { } } -/* Parses a predicate to machine instructions */ -func getFunctionalArguments(ty_args Lib.List[AST.Ty], trm_args Lib.List[AST.Term]) Lib.List[AST.Term] { - args := Lib.ListMap(ty_args, AST.TyToTerm) - - for _, arg := range trm_args.GetSlice() { - switch term := arg.(type) { - case AST.Meta: - args.Append(arg) - case AST.Fun: - args.Append(AST.MakerFun( - term.GetID(), - Lib.NewList[AST.Ty](), - getFunctionalArguments(term.GetTyArgs(), term.GetArgs()), - )) - } - } - - return args -} - -func parsePred(p AST.Pred, instructions *Sequence) { - instructions.add(makeCheck(p.GetID())) - if !p.GetTyArgs().Empty() || !p.GetArgs().Empty() { +func parsePred(i AST.Id, args Lib.List[AST.Term], instructions *Sequence) { + instructions.add(makeCheck(i)) + if !args.Empty() { instructions.add(Begin{}) instructions.add(Down{}) varCount := 0 postCount := 0 parseTerms( - getFunctionalArguments(p.GetTyArgs(), p.GetArgs()), + args, instructions, Lib.NewList[AST.Meta](), &varCount, @@ -216,7 +138,7 @@ func parseTerms( instructions.add(Right{}) } case AST.Fun: - instructions.add(Begin{}) // TEST 33 + instructions.add(Begin{}) instructions.add(makeCheck(t.GetID())) if downDefined(t.GetArgs()) { @@ -225,18 +147,20 @@ func parseTerms( *postCount++ } instructions.add(Down{}) - subTerms := getFunctionalArguments(t.GetTyArgs(), t.GetArgs()) - subst = parseTerms(subTerms, instructions, subst, varCount, postCount) + if !t.GetTyArgs().Empty() { + Glob.Anomaly("unif parsing", "found type arguments at an unexpected place") + } + subst = parseTerms(t.GetArgs(), instructions, subst, varCount, postCount) if rightDefined(terms, i) { *postCount-- instructions.add(Pop{*postCount}) } instructions.add(makeEnd(t)) } else if rightDefined(terms, i) { - instructions.add(makeEnd(t)) // TEST33 + instructions.add(makeEnd(t)) instructions.add(Right{}) } else { - instructions.add(makeEnd(t)) // TEST33 + instructions.add(makeEnd(t)) } } } @@ -254,6 +178,6 @@ func ParseTerm(term AST.Term) Sequence { &varCount, &postCount, ) - instructions.formula = MakerTermForm(term) + instructions.base = Lib.MkLeft[AST.Term, AST.Form](term) return instructions } diff --git a/src/Unif/sequence.go b/src/Unif/sequence.go index 76a3717a..93ebf324 100644 --- a/src/Unif/sequence.go +++ b/src/Unif/sequence.go @@ -40,13 +40,42 @@ import ( "fmt" "github.com/GoelandProver/Goeland/AST" + "github.com/GoelandProver/Goeland/Glob" + "github.com/GoelandProver/Goeland/Lib" ) /*** Sequence ***/ +func tofToString(tof Lib.Either[AST.Term, AST.Form]) string { + return Lib.EitherToString[AST.Term, AST.Form](tof, "Trm", "Form") +} + +func tofCopy(tof Lib.Either[AST.Term, AST.Form]) Lib.Either[AST.Term, AST.Form] { + return Lib.EitherCpy[AST.Term, AST.Form](tof) +} + +func tofCmp(tof1, tof2 Lib.Either[AST.Term, AST.Form]) bool { + return Lib.EitherEquals[AST.Term, AST.Form](tof1, tof2) +} + +func tofMetaList(tof Lib.Either[AST.Term, AST.Form]) Lib.List[AST.Meta] { + switch tof := tof.(type) { + case Lib.Left[AST.Term, AST.Form]: + return transformTerm(tof.Val).GetMetaList() + case Lib.Right[AST.Term, AST.Form]: + switch f := tof.Val.(type) { + case AST.Pred: + return transformPred(f).GetMetaList() + } + } + + Glob.Anomaly("unification", "Unification has not been launched on terms or on a predicate") + return Lib.NewList[AST.Meta]() +} + type Sequence struct { instructions []Instruction - formula AST.Form + base Lib.Either[AST.Term, AST.Form] } /*** Sequence's methods ***/ @@ -55,22 +84,22 @@ func (s *Sequence) GetInstructions() []Instruction { return CopyInstructionList(s.instructions) } -func (s *Sequence) GetFormula() AST.Form { - return s.formula.Copy() +func (s *Sequence) GetBase() Lib.Either[AST.Term, AST.Form] { + return s.base } func (s *Sequence) add(instr Instruction) { s.instructions = append(s.instructions, instr) } -// ILL TODO: Should not print directly, should return a string that is then printed +// FIXME: Should not print directly, should return a string that is then printed func (s Sequence) Print() { for _, instr := range s.instructions { fmt.Printf("%v", instr) } - fmt.Printf(" - " + s.formula.ToString()) + fmt.Printf(" - " + tofToString(s.base)) } func (s Sequence) Copy() Sequence { - return Sequence{s.GetInstructions(), s.GetFormula()} + return Sequence{s.GetInstructions(), tofCopy(s.base)} } diff --git a/src/Unif/substitutions_tree.go b/src/Unif/substitutions_tree.go index 5a9be9e7..e1d03121 100644 --- a/src/Unif/substitutions_tree.go +++ b/src/Unif/substitutions_tree.go @@ -49,7 +49,11 @@ import ( * MetaToSubs : (meta, term) : meta in formula, term in tree * Merge both of them **/ -func computeSubstitutions(subs []SubstPair, metasToSubs Substitutions, form AST.Form) Substitutions { +func computeSubstitutions( + subs []SubstPair, + metasToSubs Substitutions, + metaList Lib.List[AST.Meta], +) Substitutions { debug( Lib.MkLazy(func() string { return fmt.Sprintf( @@ -57,56 +61,47 @@ func computeSubstitutions(subs []SubstPair, metasToSubs Substitutions, form AST. SubstPairListToString(subs), metasToSubs.ToString()) }), ) - metasFromTreeForm := Lib.NewList[AST.Meta]() treeSubs := Substitutions{} - // Retrieve all the meta of from the tree formula - switch typedForm := form.(type) { - case AST.Pred: - trms := getFunctionalArguments(typedForm.GetTyArgs(), typedForm.GetArgs()) - for _, trm := range trms.GetSlice() { - metasFromTreeForm.Append(trm.GetMetaList().GetSlice()...) - } - case TermForm: - metasFromTreeForm.Append(typedForm.GetTerm().GetMetaList().GetSlice()...) - default: - return Failure() - } - // Transform subst tree into a real substitution for _, value := range subs { - currentMeta := metasFromTreeForm.At(value.GetIndex()) - currentValue := value.GetTerm() - debug( - Lib.MkLazy(func() string { - return fmt.Sprintf( - "Iterate on subst : %v and %v", - currentMeta.ToString(), - currentValue.ToString()) - }), - ) + if value.GetIndex() < metaList.Len() { + currentMeta := metaList.At(value.GetIndex()) + currentValue := value.GetTerm() + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "Iterate on subst : %v and %v", + currentMeta.ToString(), + currentValue.ToString()) + }), + ) - if !currentMeta.Equals(currentValue) { - // Si current_meta a déjà une association dans metas - metaGet, index := metasToSubs.Get(currentMeta) - if HasSubst(metasToSubs, currentMeta) && (index != -1) && !currentValue.Equals(metaGet) { - // On cherche a unifier les deux valeurs - treeSubs.Set(currentMeta, currentValue) - new_unif := AddUnification(currentValue.Copy(), metaGet.Copy(), treeSubs.Copy()) - if new_unif.Equals(Failure()) { - return Failure() - } else { - treeSubs = new_unif - metasToSubs.Remove(index) // Remove from meta + if !currentMeta.Equals(currentValue) { + // Si current_meta a déjà une association dans metas + metaGet, index := metasToSubs.Get(currentMeta) + if HasSubst(metasToSubs, currentMeta) && (index != -1) && + !currentValue.Equals(metaGet) { + // On cherche a unifier les deux valeurs + treeSubs.Set(currentMeta, currentValue) + new_unif := AddUnification(currentValue.Copy(), metaGet.Copy(), treeSubs.Copy()) + if new_unif.Equals(Failure()) { + return Failure() + } else { + treeSubs = new_unif + metasToSubs.Remove(index) // Remove from meta + } + } else { // Ne pas ajouter la susbtitution égalité + treeSubs.Set(currentMeta, currentValue) } - } else { // Ne pas ajouter la susbtitution égalité - treeSubs.Set(currentMeta, currentValue) } } } debug( - Lib.MkLazy(func() string { return fmt.Sprintf("before meta : %v", metasToSubs.ToString()) }), + Lib.MkLazy( + func() string { return fmt.Sprintf("before meta : %v", metasToSubs.ToString()) }, + ), ) // Metas_subst eliminate EliminateMeta(&metasToSubs) @@ -114,10 +109,14 @@ func computeSubstitutions(subs []SubstPair, metasToSubs Substitutions, form AST. if metasToSubs.Equals(Failure()) { return Failure() } - debug(Lib.MkLazy(func() string { return fmt.Sprintf("After meta : %v", metasToSubs.ToString()) })) + debug( + Lib.MkLazy(func() string { return fmt.Sprintf("After meta : %v", metasToSubs.ToString()) }), + ) debug( - Lib.MkLazy(func() string { return fmt.Sprintf("before tree_subst : %v", treeSubs.ToString()) }), + Lib.MkLazy( + func() string { return fmt.Sprintf("before tree_subst : %v", treeSubs.ToString()) }, + ), ) // Tree subst elminate EliminateMeta(&treeSubs) @@ -126,7 +125,9 @@ func computeSubstitutions(subs []SubstPair, metasToSubs Substitutions, form AST. return Failure() } debug( - Lib.MkLazy(func() string { return fmt.Sprintf("after tree_subst : %v", treeSubs.ToString()) }), + Lib.MkLazy( + func() string { return fmt.Sprintf("after tree_subst : %v", treeSubs.ToString()) }, + ), ) // Fusion @@ -152,7 +153,9 @@ func computeSubstitutions(subs []SubstPair, metasToSubs Substitutions, form AST. /* Call addUnification and returns a status - modify m.meta */ func (m *Machine) trySubstituteMeta(i AST.Term, j AST.Term) Status { debug( - Lib.MkLazy(func() string { return fmt.Sprintf("Try substitute : %v and %v", i.ToString(), j.ToString()) }), + Lib.MkLazy( + func() string { return fmt.Sprintf("Try substitute : %v and %v", i.ToString(), j.ToString()) }, + ), ) new_meta := AddUnification(i, j, m.meta.Copy()) if new_meta.Equals(Failure()) { @@ -172,6 +175,9 @@ func AddUnification(term1, term2 AST.Term, subst Substitutions) Substitutions { subst.ToString()) }), ) + term1 = transformTerm(term1) + term2 = transformTerm(term2) + // unify with ct only if the term already has an unification or if there is 2 fun. Just add it and eliminate otherwise. t1v, _ := subst.Get(term1.ToMeta()) t2v, _ := subst.Get(term2.ToMeta()) @@ -213,12 +219,16 @@ func (m *Machine) addUnifications(term1, term2 AST.Term) Status { term2.ToString()) }), ) - meta := tryUnification(term1.Copy(), term2.Copy(), m.meta.Copy()) // Return empty or an array of 1 matching substitution, which is m.meta improved wit (term1, term2) + meta := tryUnification( + term1.Copy(), + term2.Copy(), + m.meta.Copy(), + ) // Return empty or an array of 1 matching substitution, which is m.meta improved wit (term1, term2) if len(meta) == 0 { return Status(ERROR) } else { - m.meta = meta[0].GetSubst() + m.meta = meta[0].subst EliminateMeta(&m.meta) Eliminate(&m.meta) } @@ -227,7 +237,7 @@ func (m *Machine) addUnifications(term1, term2 AST.Term) Status { } /* Tries to unify term1 with term2, depending on the substitutions already found by the parent unification process. */ -func tryUnification(term1, term2 AST.Term, meta Substitutions) []MatchingSubstitutions { +func tryUnification(term1, term2 AST.Term, meta Substitutions) []MixMatchSubstitutions { debug( Lib.MkLazy(func() string { return fmt.Sprintf( From ccddd14a1fd973c7bb57431300c94ea4aa7d7f93 Mon Sep 17 00:00:00 2001 From: Julie Cailler Date: Mon, 13 Apr 2026 18:12:18 +0200 Subject: [PATCH 02/23] dt skeleton --- src/Core/FormListDS.go | 18 +- src/Core/global_unifier.go | 2 +- src/Core/int_subst_and_form.go | 2 +- src/Core/subst_and_form.go | 2 +- src/Core/subst_and_form_and_terms.go | 2 +- src/Core/substitutions_search.go | 2 +- src/Glob/helper.go | 9 + src/Mods/assisted/assistant.go | 2 +- src/Mods/assisted/rules.go | 2 +- src/Mods/dmt/dmt.go | 8 +- src/Mods/dmt/rewrite.go | 2 +- src/Mods/dmt/rewritten.go | 2 +- src/Mods/equality/bse/constraints_list.go | 2 +- src/Mods/equality/bse/constraints_struct.go | 2 +- src/Mods/equality/bse/constraints_type.go | 2 +- src/Mods/equality/bse/equality.go | 2 +- src/Mods/equality/bse/equality_problem.go | 5 +- .../equality/bse/equality_problem_list.go | 2 +- .../equality/bse/equality_rules_reasoning.go | 2 +- .../equality/bse/equality_rules_try_apply.go | 2 +- src/Mods/equality/bse/equality_rules_utils.go | 2 +- .../bse/equality_solve_reasoning_problem.go | 2 +- src/Mods/equality/bse/equality_test.go | 2 +- src/Mods/equality/bse/equality_types.go | 2 +- src/Mods/equality/eqStruct/term_pair.go | 2 +- src/Mods/equality/sateq/problem.go | 2 +- src/Mods/equality/sateq/subsgatherer.go | 2 +- src/Search/child_management.go | 8 +- src/Search/children.go | 16 +- src/Search/destructive.go | 60 ++--- src/Search/exchanges.go | 8 +- src/Search/incremental/rulesManager.go | 5 +- src/Search/incremental/search.go | 2 +- src/Search/incremental/substitution.go | 2 +- src/Search/nonDestructiveSearch.go | 30 +-- src/Search/proof.go | 4 +- src/Search/rules.go | 28 +-- src/Search/search.go | 12 +- src/Search/state.go | 28 +-- src/Unif/{ => codetree}/code-trees.go | 16 +- src/Unif/{ => codetree}/instruction.go | 2 +- src/Unif/{ => codetree}/int_pair.go | 2 +- src/Unif/{ => codetree}/machine.go | 81 ++++++- src/Unif/{ => codetree}/matching.go | 53 ++--- src/Unif/{ => codetree}/parsing.go | 25 +- src/Unif/{ => codetree}/sequence.go | 7 +- src/Unif/{ => codetree}/substitutions_tree.go | 181 ++++---------- .../discrimitation-trees.go} | 38 +-- src/Unif/substitution/data_structure.go | 221 ++++++++++++++++++ .../matching_substitutions.go | 22 +- src/Unif/{ => substitution}/subst_pair.go | 2 +- src/Unif/{ => substitution}/substitution.go | 9 +- .../{ => substitution}/substitutions_type.go | 2 +- src/main.go | 6 +- src/options.go | 8 + 55 files changed, 581 insertions(+), 383 deletions(-) rename src/Unif/{ => codetree}/code-trees.go (96%) rename src/Unif/{ => codetree}/instruction.go (99%) rename src/Unif/{ => codetree}/int_pair.go (99%) rename src/Unif/{ => codetree}/machine.go (76%) rename src/Unif/{ => codetree}/matching.go (85%) rename src/Unif/{ => codetree}/parsing.go (88%) rename src/Unif/{ => codetree}/sequence.go (95%) rename src/Unif/{ => codetree}/substitutions_tree.go (54%) rename src/Unif/{data_structure.go => discriminationtree/discrimitation-trees.go} (60%) create mode 100644 src/Unif/substitution/data_structure.go rename src/Unif/{ => substitution}/matching_substitutions.go (95%) rename src/Unif/{ => substitution}/subst_pair.go (99%) rename src/Unif/{ => substitution}/substitution.go (94%) rename src/Unif/{ => substitution}/substitutions_type.go (99%) diff --git a/src/Core/FormListDS.go b/src/Core/FormListDS.go index 1ab41fcd..fca3ad38 100644 --- a/src/Core/FormListDS.go +++ b/src/Core/FormListDS.go @@ -34,7 +34,7 @@ package Core import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/substitution" ) type FormListDS struct { @@ -48,12 +48,12 @@ func (f FormListDS) GetFL() Lib.List[AST.Form] { /* Data struct */ /* Take a list of formula and return a FormList (Datastructure type) */ -func (f FormListDS) MakeDataStruct(lf Lib.List[AST.Form], is_pos bool) Unif.DataStructure { +func (f FormListDS) MakeDataStruct(lf Lib.List[AST.Form], is_pos bool) subst.DataStructure { return (new(FormListDS)).InsertFormulaListToDataStructure(lf) } /* Insert a list of formula into the given Datastructure (here, FormList) */ -func (f FormListDS) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) Unif.DataStructure { +func (f FormListDS) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) subst.DataStructure { for _, v := range lf.GetSlice() { switch nf := v.(type) { case AST.Pred: @@ -74,7 +74,7 @@ func (f FormListDS) Print() { } } -func (f FormListDS) Copy() Unif.DataStructure { +func (f FormListDS) Copy() subst.DataStructure { return FormListDS{Lib.ListCpy(f.GetFL())} } @@ -82,15 +82,15 @@ func (fl FormListDS) IsEmpty() bool { return fl.GetFL().Empty() } -func (fl FormListDS) Unify(f AST.Form) (bool, []Unif.MixedSubstitutions) { +func (fl FormListDS) Unify(f AST.Form) (bool, []subst.MixedSubstitutions) { for _, element := range fl.GetFL().GetSlice() { if element.Equals(f) { - return true, []Unif.MixedSubstitutions{} + return true, []subst.MixedSubstitutions{} } } - return false, []Unif.MixedSubstitutions{} + return false, []subst.MixedSubstitutions{} } -func (fl FormListDS) UnifyTerm(t AST.Term) (bool, []Unif.MixedTermSubstitutions) { - return false, []Unif.MixedTermSubstitutions{} +func (fl FormListDS) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSubstitutions) { + return false, []subst.MixedTermSubstitutions{} } diff --git a/src/Core/global_unifier.go b/src/Core/global_unifier.go index aea67a62..40e03511 100644 --- a/src/Core/global_unifier.go +++ b/src/Core/global_unifier.go @@ -38,7 +38,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type substitutions = Lib.List[Unif.MixedSubstitution] diff --git a/src/Core/int_subst_and_form.go b/src/Core/int_subst_and_form.go index 3dd0d90e..896b78c1 100644 --- a/src/Core/int_subst_and_form.go +++ b/src/Core/int_subst_and_form.go @@ -40,7 +40,7 @@ import ( "strconv" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type IntSubstAndForm struct { diff --git a/src/Core/subst_and_form.go b/src/Core/subst_and_form.go index e43c4ac0..2b5f9408 100644 --- a/src/Core/subst_and_form.go +++ b/src/Core/subst_and_form.go @@ -43,7 +43,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Stock the substitution and the corresponding list of formulas */ diff --git a/src/Core/subst_and_form_and_terms.go b/src/Core/subst_and_form_and_terms.go index 3268db17..6121cb7f 100644 --- a/src/Core/subst_and_form_and_terms.go +++ b/src/Core/subst_and_form_and_terms.go @@ -38,7 +38,7 @@ package Core import ( "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Stock the substitution and the corresponding list of formulas */ diff --git a/src/Core/substitutions_search.go b/src/Core/substitutions_search.go index a12c709d..e2449f1e 100644 --- a/src/Core/substitutions_search.go +++ b/src/Core/substitutions_search.go @@ -42,7 +42,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Return the list of metavariable from a substitution */ diff --git a/src/Glob/helper.go b/src/Glob/helper.go index fcf7d91c..301953d4 100644 --- a/src/Glob/helper.go +++ b/src/Glob/helper.go @@ -82,6 +82,7 @@ var printVersion = false var allowFlattening = false var type_check = true var list_dbgs = false +var dt = false var IncrEq = false @@ -287,6 +288,10 @@ func ListDebuggers() bool { return list_dbgs } +func GetDt() bool { + return dt +} + /* Setters */ func SetDebug(debug_list string) { if debug_list == "none" { @@ -442,3 +447,7 @@ func SetNoTypeCheck() { func SetListDebuggers() { list_dbgs = true } + +func SetDt() { + dt = true +} diff --git a/src/Mods/assisted/assistant.go b/src/Mods/assisted/assistant.go index d3aa0480..fa57d629 100644 --- a/src/Mods/assisted/assistant.go +++ b/src/Mods/assisted/assistant.go @@ -40,7 +40,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Search" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) var debug Glob.Debugger diff --git a/src/Mods/assisted/rules.go b/src/Mods/assisted/rules.go index cdce6820..57b018a8 100644 --- a/src/Mods/assisted/rules.go +++ b/src/Mods/assisted/rules.go @@ -39,7 +39,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) func ApplyRulesAssisted(fatherId uint64, state Search.State, c Search.Communication, newAtomics Core.FormAndTermsList, nodeID int, originalNodeId int, metaToReintroduce []int) { diff --git a/src/Mods/dmt/dmt.go b/src/Mods/dmt/dmt.go index b97fbebc..1d42559e 100644 --- a/src/Mods/dmt/dmt.go +++ b/src/Mods/dmt/dmt.go @@ -43,7 +43,8 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" + "github.com/GoelandProver/Goeland/Unif/codetree" ) var positiveRewrite map[string]Lib.List[AST.Form] /* Stores rewrites of atoms with positive occurrences */ @@ -82,8 +83,9 @@ func InitPluginTests(polarized, presko bool) { func initPluginGlobalVariables() { positiveRewrite = make(map[string]Lib.List[AST.Form]) negativeRewrite = make(map[string]Lib.List[AST.Form]) - positiveTree = Unif.NewNode() - negativeTree = Unif.NewNode() + // TODO + positiveTree = codetree.NewNode() + negativeTree = codetree.NewNode() registeredAxioms = Lib.NewList[AST.Form]() } diff --git a/src/Mods/dmt/rewrite.go b/src/Mods/dmt/rewrite.go index 6d55a724..a362404d 100644 --- a/src/Mods/dmt/rewrite.go +++ b/src/Mods/dmt/rewrite.go @@ -43,7 +43,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) // ---------------------------------------------------------------------------- diff --git a/src/Mods/dmt/rewritten.go b/src/Mods/dmt/rewritten.go index a68b16f3..4a2c276b 100644 --- a/src/Mods/dmt/rewritten.go +++ b/src/Mods/dmt/rewritten.go @@ -41,7 +41,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) func substitute(form AST.Form, subst Unif.Substitutions) AST.Form { diff --git a/src/Mods/equality/bse/constraints_list.go b/src/Mods/equality/bse/constraints_list.go index 203f66d9..8be2a980 100644 --- a/src/Mods/equality/bse/constraints_list.go +++ b/src/Mods/equality/bse/constraints_list.go @@ -41,7 +41,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type ConstraintList []Constraint diff --git a/src/Mods/equality/bse/constraints_struct.go b/src/Mods/equality/bse/constraints_struct.go index dc5c79e9..fdd73367 100644 --- a/src/Mods/equality/bse/constraints_struct.go +++ b/src/Mods/equality/bse/constraints_struct.go @@ -40,7 +40,7 @@ import ( "fmt" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type ConstraintStruct struct { diff --git a/src/Mods/equality/bse/constraints_type.go b/src/Mods/equality/bse/constraints_type.go index 453456f7..b3955b0c 100644 --- a/src/Mods/equality/bse/constraints_type.go +++ b/src/Mods/equality/bse/constraints_type.go @@ -44,7 +44,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) const ( diff --git a/src/Mods/equality/bse/equality.go b/src/Mods/equality/bse/equality.go index d0c1b2f2..4ddf6e4f 100644 --- a/src/Mods/equality/bse/equality.go +++ b/src/Mods/equality/bse/equality.go @@ -43,7 +43,7 @@ import ( "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" "github.com/GoelandProver/Goeland/Search" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) var debug Glob.Debugger diff --git a/src/Mods/equality/bse/equality_problem.go b/src/Mods/equality/bse/equality_problem.go index 7014fe94..cd9130ca 100644 --- a/src/Mods/equality/bse/equality_problem.go +++ b/src/Mods/equality/bse/equality_problem.go @@ -44,7 +44,8 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" + "github.com/GoelandProver/Goeland/Unif/codetree" ) type EqualityProblem struct { @@ -141,7 +142,7 @@ func makeDataStructFromEqualities(eq Equalities) Unif.DataStructure { formList.Append(e.GetT1(), e.GetT2()) } - return Unif.MakeTermUnifProblem(Lib.ListCpy(formList)) + return codetree.MakeTermUnifProblem(Lib.ListCpy(formList)) } /* Take a list of equalities and build the corresponding assocative map */ diff --git a/src/Mods/equality/bse/equality_problem_list.go b/src/Mods/equality/bse/equality_problem_list.go index fce98743..5dda7d1b 100644 --- a/src/Mods/equality/bse/equality_problem_list.go +++ b/src/Mods/equality/bse/equality_problem_list.go @@ -47,7 +47,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Typing" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type EqualityProblemList []EqualityProblem diff --git a/src/Mods/equality/bse/equality_rules_reasoning.go b/src/Mods/equality/bse/equality_rules_reasoning.go index 4980e7fd..6dc011df 100644 --- a/src/Mods/equality/bse/equality_rules_reasoning.go +++ b/src/Mods/equality/bse/equality_rules_reasoning.go @@ -42,7 +42,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type BasicEqualityStruct struct { diff --git a/src/Mods/equality/bse/equality_rules_try_apply.go b/src/Mods/equality/bse/equality_rules_try_apply.go index ceba6fa0..e4a4ad48 100644 --- a/src/Mods/equality/bse/equality_rules_try_apply.go +++ b/src/Mods/equality/bse/equality_rules_try_apply.go @@ -42,7 +42,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Try left rule */ diff --git a/src/Mods/equality/bse/equality_rules_utils.go b/src/Mods/equality/bse/equality_rules_utils.go index 386b03bc..ac29db5e 100644 --- a/src/Mods/equality/bse/equality_rules_utils.go +++ b/src/Mods/equality/bse/equality_rules_utils.go @@ -42,7 +42,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type answerEP struct { diff --git a/src/Mods/equality/bse/equality_solve_reasoning_problem.go b/src/Mods/equality/bse/equality_solve_reasoning_problem.go index dd50c1f1..ee313493 100644 --- a/src/Mods/equality/bse/equality_solve_reasoning_problem.go +++ b/src/Mods/equality/bse/equality_solve_reasoning_problem.go @@ -42,7 +42,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) /*** Instaniation ***/ diff --git a/src/Mods/equality/bse/equality_test.go b/src/Mods/equality/bse/equality_test.go index 4dcc83dc..ab82b87f 100644 --- a/src/Mods/equality/bse/equality_test.go +++ b/src/Mods/equality/bse/equality_test.go @@ -46,7 +46,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Typing" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" "github.com/GoelandProver/Goeland/equality/eqStruct" ) diff --git a/src/Mods/equality/bse/equality_types.go b/src/Mods/equality/bse/equality_types.go index 482e272d..f5cca6c4 100644 --- a/src/Mods/equality/bse/equality_types.go +++ b/src/Mods/equality/bse/equality_types.go @@ -44,7 +44,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type Equalities []eqStruct.TermPair diff --git a/src/Mods/equality/eqStruct/term_pair.go b/src/Mods/equality/eqStruct/term_pair.go index 0b79f0bb..add9a805 100644 --- a/src/Mods/equality/eqStruct/term_pair.go +++ b/src/Mods/equality/eqStruct/term_pair.go @@ -39,7 +39,7 @@ package eqStruct import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type EqualityStruct interface { diff --git a/src/Mods/equality/sateq/problem.go b/src/Mods/equality/sateq/problem.go index 7c409c6b..e684b984 100644 --- a/src/Mods/equality/sateq/problem.go +++ b/src/Mods/equality/sateq/problem.go @@ -36,7 +36,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" equality "github.com/GoelandProver/Goeland/Mods/equality/bse" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" "github.com/go-air/gini" "github.com/go-air/gini/z" ) diff --git a/src/Mods/equality/sateq/subsgatherer.go b/src/Mods/equality/sateq/subsgatherer.go index 41b30cff..5968e296 100644 --- a/src/Mods/equality/sateq/subsgatherer.go +++ b/src/Mods/equality/sateq/subsgatherer.go @@ -35,7 +35,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) func gatherSubs(truthValues map[Lit]bool, sMapping map[Glob.Pair[*termRecord, *eqClass]]Lit, rMapping map[Glob.Pair[*eqClass, *termRecord]]Lit) (subs []Unif.Substitutions, success bool) { diff --git a/src/Search/child_management.go b/src/Search/child_management.go index 90afaeeb..76a8086f 100644 --- a/src/Search/child_management.go +++ b/src/Search/child_management.go @@ -38,7 +38,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Arguments for waitChildren function & utilitary subfunctions */ @@ -162,7 +162,7 @@ func (ds *destructiveSearch) passSubstToParent(args wcdArgs, proofChildren [][]P Lib.MkLazy(func() string { return fmt.Sprintf( "All children agree on the substitution(s) : %s", - Unif.SubstsToString(Core.GetSubstListFromSubstAndFormList(substs)), + subst.SubstsToString(Core.GetSubstListFromSubstAndFormList(substs)), ) }), ) @@ -180,7 +180,7 @@ func (ds *destructiveSearch) passSubstToParent(args wcdArgs, proofChildren [][]P // Remove all the metas introduced by the current node to only retrieve relevant ones for the parent. resultingSubstsAndForms := []Core.SubstAndForm{} - resultingSubsts := Lib.NewList[Lib.List[Unif.MixedSubstitution]]() + resultingSubsts := Lib.NewList[Lib.List[subst.MixedSubstitution]]() for _, subst := range substs { debug( @@ -283,7 +283,7 @@ func (ds *destructiveSearch) manageOpenedChild(args wcdArgs) { // If the completeness mode is active, then we need to deal with forbidden substitutions. if Glob.GetCompleteness() { forbidden := args.st.GetForbiddenSubsts() - forbidden.Add(Lib.ListEquals[Unif.MixedSubstitution], args.currentSubst.GetSubst()) + forbidden.Add(Lib.ListEquals[subst.MixedSubstitution], args.currentSubst.GetSubst()) args.st.SetForbiddenSubsts(forbidden) } diff --git a/src/Search/children.go b/src/Search/children.go index c83a2846..bbdc7a0d 100644 --- a/src/Search/children.go +++ b/src/Search/children.go @@ -38,7 +38,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Struct result to communicate substitution or a quit order through a channel */ @@ -63,7 +63,7 @@ type Result struct { closed, need_answer bool subst_for_children Core.SubstAndForm subst_list_for_father []Core.SubstAndForm - forbidden Lib.List[Lib.List[Unif.MixedSubstitution]] + forbidden Lib.List[Lib.List[subst.MixedSubstitution]] proof []ProofStruct node_id int original_node_id int @@ -85,8 +85,8 @@ func (r Result) getSubstForChildren() Core.SubstAndForm { func (r Result) getSubstListForFather() []Core.SubstAndForm { return Core.CopySubstAndFormList(r.subst_list_for_father) } -func (r Result) getForbiddenSubsts() Lib.List[Lib.List[Unif.MixedSubstitution]] { - return r.forbidden.Copy(Lib.ListCpy[Unif.MixedSubstitution]) +func (r Result) getForbiddenSubsts() Lib.List[Lib.List[subst.MixedSubstitution]] { + return r.forbidden.Copy(Lib.ListCpy[subst.MixedSubstitution]) } func (r Result) getProof() []ProofStruct { return CopyProofStructList(r.proof) @@ -170,13 +170,13 @@ func sendSubToChildren(children []Communication, s Core.SubstAndForm) { true, s.Copy(), []Core.SubstAndForm{}, - Lib.NewList[Lib.List[Unif.MixedSubstitution]](), + Lib.NewList[Lib.List[subst.MixedSubstitution]](), nil, -1, -1, Core.MakeUnifier()} } } /* Send a substitution to a list of child */ -func sendForbiddenToChildren(children []Communication, s Lib.List[Lib.List[Unif.MixedSubstitution]]) { +func sendForbiddenToChildren(children []Communication, s Lib.List[Lib.List[subst.MixedSubstitution]]) { debug( Lib.MkLazy(func() string { return fmt.Sprintf("Send forbidden to children : %v", len(children)) }), ) @@ -195,7 +195,7 @@ func (ds *destructiveSearch) sendSubToFather(c Communication, closed, need_answe Lib.MkLazy(func() string { return fmt.Sprintf( "Send subst to father : %s, closed : %v, need answer : %v", - Unif.SubstsToString(Core.GetSubstListFromSubstAndFormList(subst_for_father)), + subst.SubstsToString(Core.GetSubstListFromSubstAndFormList(subst_for_father)), closed, need_answer) }), ) @@ -232,7 +232,7 @@ func (ds *destructiveSearch) sendSubToFather(c Communication, closed, need_answe need_answer, Core.MakeEmptySubstAndForm(), Core.CopySubstAndFormList(subst_for_father), - Lib.NewList[Lib.List[Unif.MixedSubstitution]](), + Lib.NewList[Lib.List[subst.MixedSubstitution]](), st.GetProof(), node_id, original_node_id, st.GetGlobUnifier()}: if need_answer { ds.waitFather(father_id, st, c, Core.FusionSubstAndFormListWithoutDouble(subst_for_father, given_substs), node_id, original_node_id, []int{}, meta_to_reintroduce) diff --git a/src/Search/destructive.go b/src/Search/destructive.go index 1ef43a28..b5d76b76 100644 --- a/src/Search/destructive.go +++ b/src/Search/destructive.go @@ -44,7 +44,8 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/dmt" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/codetree" + substitution "github.com/GoelandProver/Goeland/Unif/substitution" ) const ( @@ -62,7 +63,7 @@ type BasicSearchAlgorithm interface { ProofSearch(uint64, State, Communication, Core.SubstAndForm, int, int, []int, bool) DoEndManageBeta(uint64, State, Communication, []Communication, int, int, []int, []int) manageRewriteRules(uint64, State, Communication, Core.FormAndTermsList, int, int, []int) - ManageClosureRule(uint64, *State, Communication, Lib.List[Lib.List[Unif.MixedSubstitution]], Core.FormAndTerms, int, int) (bool, []Core.SubstAndForm) + ManageClosureRule(uint64, *State, Communication, Lib.List[Lib.List[substitution.MixedSubstitution]], Core.FormAndTerms, int, int) (bool, []Core.SubstAndForm) manageResult(c Communication) (Core.Unifier, []ProofStruct, bool) } @@ -99,9 +100,16 @@ func (ds *destructiveSearch) doOneStep(limit int, formula AST.Form) (bool, int) AST.ResetMeta() // proof.ResetProofFile() ResetExchangesFile() + var tp, tn substitution.DataStructure - tp := Unif.NewNode() - tn := Unif.NewNode() + if Glob.GetDt() { + // TODO : replace by DT + tp = codetree.NewNode() + tn = codetree.NewNode() + } else { + tp = codetree.NewNode() + tn = codetree.NewNode() + } state := MakeState(limit, tp, tn, formula) state.SetCurrentProofNodeId(0) @@ -218,7 +226,7 @@ func (ds *destructiveSearch) searchContradictionAfterApplySusbt(father_id uint64 father_id, &st, cha, - subst.Copy(Lib.ListCpy[Unif.MixedSubstitution]), + subst.Copy(Lib.ListCpy[substitution.MixedSubstitution]), f.Copy(), node_id, original_node_id, @@ -245,7 +253,7 @@ func (ds *destructiveSearch) searchContradiction(atomic AST.Form, father_id uint father_id, &st, cha, - subst.Copy(Lib.ListCpy[Unif.MixedSubstitution]), + subst.Copy(Lib.ListCpy[substitution.MixedSubstitution]), fAt, node_id, original_node_id) return true } @@ -259,7 +267,7 @@ func (ds *destructiveSearch) searchContradiction(atomic AST.Form, father_id uint * st : State, the current search State * c : channel to send the answer to the father * s : substitution to apply to the current State -* subst_found : Unif.Substitutions found by this process +* subst_found : subst.Substitutions found by this process **/ func (ds *destructiveSearch) ProofSearch(father_id uint64, st State, cha Communication, s Core.SubstAndForm, node_id int, original_node_id int, meta_to_reintroduce []int, post_dmt_step bool) { debug( @@ -312,7 +320,7 @@ func (ds *destructiveSearch) ProofSearch(father_id uint64, st State, cha Communi Lib.MkLazy(func() string { return fmt.Sprintf( "Current substitutions list: %v", - Unif.SubstsToString(Core.GetSubstListFromSubstAndFormList(st.GetSubstsFound())), + substitution.SubstsToString(Core.GetSubstListFromSubstAndFormList(st.GetSubstsFound())), ) }), ) @@ -331,7 +339,7 @@ func (ds *destructiveSearch) ProofSearch(father_id uint64, st State, cha Communi father_id, &st, cha, - Lib.NewList[Lib.List[Unif.MixedSubstitution]](), + Lib.NewList[Lib.List[substitution.MixedSubstitution]](), f, node_id, original_node_id) return } @@ -522,7 +530,7 @@ func (ds *destructiveSearch) waitFather(father_id uint64, st State, c Communicat ) // Check if the subst was already seen, returns eventually the subst with new formula(s) - if Core.GetSubstListFromSubstAndFormList(given_substs).Contains(answer_father.subst_for_children.GetSubst(), Lib.ListEquals[Unif.MixedSubstitution]) { + if Core.GetSubstListFromSubstAndFormList(given_substs).Contains(answer_father.subst_for_children.GetSubst(), Lib.ListEquals[substitution.MixedSubstitution]) { debug( Lib.MkLazy(func() string { return "This substitution was sent by this child" }), ) @@ -556,7 +564,7 @@ func (ds *destructiveSearch) waitFather(father_id uint64, st State, c Communicat Lib.MkLazy(func() string { return fmt.Sprintf( "Forbidden received : %s", - Unif.SubstsToString(answer_father.getForbiddenSubsts()), + substitution.SubstsToString(answer_father.getForbiddenSubsts()), ) }), ) @@ -565,7 +573,7 @@ func (ds *destructiveSearch) waitFather(father_id uint64, st State, c Communicat Lib.MkLazy(func() string { return fmt.Sprintf( "New forbidden for this state: %s", - Unif.SubstsToString(st.GetForbiddenSubsts()), + substitution.SubstsToString(st.GetForbiddenSubsts()), ) }), ) @@ -627,7 +635,7 @@ func (ds *destructiveSearch) waitFather(father_id uint64, st State, c Communicat ) debug( Lib.MkLazy(func() string { - return fmt.Sprintf("Forbidden : %s", Unif.SubstsToString(st_copy.GetForbiddenSubsts())) + return fmt.Sprintf("Forbidden : %s", substitution.SubstsToString(st_copy.GetForbiddenSubsts())) }), ) go ds.ProofSearch(Glob.GetGID(), st_copy, c2, answer_father.getSubstForChildren(), node_id, original_node_id, new_meta_to_reintroduce, false) @@ -814,7 +822,7 @@ func (ds *destructiveSearch) selectChildren(father Communication, children *[]Co Lib.MkLazy(func() string { return fmt.Sprintf( "Result_subst :%s", - Unif.SubstsToString( + substitution.SubstsToString( Core.GetSubstListFromSubstAndFormList(result_subst), ), ) @@ -843,7 +851,7 @@ func (ds *destructiveSearch) selectChildren(father Communication, children *[]Co Lib.MkLazy(func() string { return fmt.Sprintf( "New result susbt : %s", - Unif.SubstsToString( + substitution.SubstsToString( Core.GetSubstListFromSubstAndFormList(result_subst), ), ) @@ -918,7 +926,7 @@ func (ds *destructiveSearch) selectChildren(father Communication, children *[]Co Lib.MkLazy(func() string { return fmt.Sprintf( "New subst at the end : %s", - Unif.SubstsToString(Core.GetSubstListFromSubstAndFormList(result_subst)), + substitution.SubstsToString(Core.GetSubstListFromSubstAndFormList(result_subst)), ) }), ) @@ -1012,7 +1020,7 @@ func (ds *destructiveSearch) tryRewrite(rewritten []Core.IntSubstAndForm, f Core newRewritten = Core.CopyIntSubstAndFormAndTermsList(newRewritten[1:]) // If we didn't rewrite as itself ? - if Unif.UnifSucceeded(choosenRewritten.GetSaf().GetSubst()) { + if substitution.UnifSucceeded(choosenRewritten.GetSaf().GetSubst()) { // Create a child with the current rewriting rule and make this process to wait for him, // with a list of other subst to try @@ -1062,7 +1070,7 @@ func (ds *destructiveSearch) ManageClosureRule( father_id uint64, st *State, c Communication, - substs Lib.List[Lib.List[Unif.MixedSubstitution]], + substs Lib.List[Lib.List[substitution.MixedSubstitution]], f Core.FormAndTerms, node_id int, original_node_id int, @@ -1072,7 +1080,7 @@ func (ds *destructiveSearch) ManageClosureRule( subst := st.GetAppliedSubst().GetSubst() mm = mm.Union(Core.GetMetaFromSubst(subst)) substs_with_mm, substs_with_mm_uncleared, substs_without_mm := - Core.DispatchSubst(substs.Copy(Lib.ListCpy[Unif.MixedSubstitution]), mm) + Core.DispatchSubst(substs.Copy(Lib.ListCpy[substitution.MixedSubstitution]), mm) unifier := st.GetGlobUnifier() appliedSubst := st.GetAppliedSubst().GetSubst() @@ -1110,13 +1118,13 @@ func (ds *destructiveSearch) ManageClosureRule( Lib.MkLazy(func() string { return fmt.Sprintf( "Contradiction found (without mm) : %v", - Unif.SubstsToString(substs_without_mm)) + substitution.SubstsToString(substs_without_mm)) }), ) if Glob.GetAssisted() && !substs_without_mm.At(0).Empty() { fmt.Printf("The branch can be closed by using a substitution which has no impact elsewhere!\nApplying it automatically : ") - fmt.Printf("%v !\n", Unif.SubstsToString(substs_without_mm)) + fmt.Printf("%v !\n", substitution.SubstsToString(substs_without_mm)) } st.SetSubstsFound([]Core.SubstAndForm{st.GetAppliedSubst()}) @@ -1134,7 +1142,7 @@ func (ds *destructiveSearch) ManageClosureRule( // As no MM is involved, these substitutions can be unified with all the others having an empty subst. for _, subst := range substs_without_mm.GetSlice() { - merge, _ := Unif.MergeMixedSubstitutions(appliedSubst, subst) + merge, _ := substitution.MergeMixedSubstitutions(appliedSubst, subst) unifier.AddSubstitutions(appliedSubst, merge) } st.SetGlobUnifier(unifier) @@ -1157,7 +1165,7 @@ func (ds *destructiveSearch) ManageClosureRule( meta_to_reintroduce := []int{} for _, subst_for_father := range substs_with_mm.GetSlice() { - if !Unif.UnifSucceeded(subst_for_father) { + if !substitution.UnifSucceeded(subst_for_father) { Glob.Anomaly("MCR", fmt.Sprintf( "Error : SubstForFather is failure between : %s and %s \n", Lib.ListToString(subst_for_father, Lib.WithEmpty("(empty substs)")), @@ -1206,7 +1214,7 @@ func (ds *destructiveSearch) ManageClosureRule( Lib.MkLazy(func() string { return fmt.Sprintf( "Send subst(s) with mm to father : %s", - Unif.SubstsToString( + substitution.SubstsToString( Core.GetSubstListFromSubstAndFormList(st.GetSubstsFound()), ), ) @@ -1216,8 +1224,8 @@ func (ds *destructiveSearch) ManageClosureRule( // Add substs_with_mm found with the corresponding subst for i, subst := range substs_with_mm.GetSlice() { - mergeUncleared, _ := Unif.MergeMixedSubstitutions(appliedSubst, substs_with_mm_uncleared.At(i)) - mergeCleared, _ := Unif.MergeMixedSubstitutions(appliedSubst, subst) + mergeUncleared, _ := substitution.MergeMixedSubstitutions(appliedSubst, substs_with_mm_uncleared.At(i)) + mergeCleared, _ := substitution.MergeMixedSubstitutions(appliedSubst, subst) unifier.AddSubstitutions(mergeCleared, mergeUncleared) } st.SetGlobUnifier(unifier) diff --git a/src/Search/exchanges.go b/src/Search/exchanges.go index 3b069cce..6ea32a02 100644 --- a/src/Search/exchanges.go +++ b/src/Search/exchanges.go @@ -44,7 +44,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/substitution" ) var graph_file_name_exchanges = Glob.GetExecPath() + "../../visualization/json/exchanges_output.json" @@ -86,8 +86,8 @@ func ResetExchangesFile() { func makeJsonExchanges( father_uint uint64, st State, - ss_subst Lib.List[Lib.List[Unif.MixedSubstitution]], - subst_received Lib.List[Unif.MixedSubstitution], + ss_subst Lib.List[Lib.List[subst.MixedSubstitution]], + subst_received Lib.List[subst.MixedSubstitution], calling_function string, ) exchanges_struct { // ID @@ -124,7 +124,7 @@ func makeJsonExchanges( ss := "" if !ss_subst.Empty() { - ss += Unif.SubstsToString(ss_subst) + ss += subst.SubstsToString(ss_subst) } // fmt.Printf("Id = %v, version = %v, father = %v, forms = %v, mm = %v, mc = %v, ss = %v, sr = %v\n", id, version, father, forms, mm, mc, ss, sr) diff --git a/src/Search/incremental/rulesManager.go b/src/Search/incremental/rulesManager.go index 38c0bd53..061868f1 100644 --- a/src/Search/incremental/rulesManager.go +++ b/src/Search/incremental/rulesManager.go @@ -5,7 +5,8 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" + "github.com/GoelandProver/Goeland/Unif/codetree" ) type RulesManager struct { @@ -187,7 +188,7 @@ func (rm *RulesManager) tryComplementaryClosureRules() Rule { func (rm *RulesManager) trySubstitutionClosureRules() (applied Rule, subs SubList) { positiveRules, negativeRules := rm.getAtomicsWithoutTopOrBottom() - negTree := new(Unif.Node).MakeDataStruct(negativeRules.GetFormList(), false) + negTree := new(codetree.Node).MakeDataStruct(negativeRules.GetFormList(), false) substitutions := []Unif.MixedSubstitutions{} diff --git a/src/Search/incremental/search.go b/src/Search/incremental/search.go index b606e099..b9001b8f 100644 --- a/src/Search/incremental/search.go +++ b/src/Search/incremental/search.go @@ -6,7 +6,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Search" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type incrementalSearch struct{} diff --git a/src/Search/incremental/substitution.go b/src/Search/incremental/substitution.go index e7985ba8..5f4d7bb4 100644 --- a/src/Search/incremental/substitution.go +++ b/src/Search/incremental/substitution.go @@ -3,7 +3,7 @@ package incremental import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) var anyTerm AST.Term = nil diff --git a/src/Search/nonDestructiveSearch.go b/src/Search/nonDestructiveSearch.go index bfe2edd3..324ab24b 100644 --- a/src/Search/nonDestructiveSearch.go +++ b/src/Search/nonDestructiveSearch.go @@ -41,7 +41,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + substitution "github.com/GoelandProver/Goeland/Unif/substitution" ) type nonDestructiveSearch struct { @@ -54,11 +54,11 @@ func NewNonDestructiveSearch() BasicSearchAlgorithm { return nil } -func getMetas(substs Lib.List[Unif.MixedSubstitution]) Lib.List[AST.Meta] { +func getMetas(substs Lib.List[substitution.MixedSubstitution]) Lib.List[AST.Meta] { metas := Lib.NewList[AST.Meta]() for _, subst := range substs.GetSlice() { switch s := subst.Substitution().(type) { - case Lib.Some[Unif.Substitution]: + case Lib.Some[substitution.Substitution]: metas.Append(s.Val.Key()) } } @@ -132,11 +132,11 @@ func (nds *nonDestructiveSearch) chooseSubstitutionNonDestructive(substs_found_t } /* Take a substitution, returns the id of the formula which introduce the metavariable */ -func (nds *nonDestructiveSearch) catchFormulaToInstantiate(subst_found Lib.List[Unif.MixedSubstitution]) int { +func (nds *nonDestructiveSearch) catchFormulaToInstantiate(subst_found Lib.List[substitution.MixedSubstitution]) int { meta_to_reintroduce := -1 for _, subst := range subst_found.GetSlice() { switch s := subst.Substitution().(type) { - case Lib.Some[Unif.Substitution]: + case Lib.Some[substitution.Substitution]: meta, term := s.Val.Get() if meta.GetFormula() < meta_to_reintroduce || meta_to_reintroduce == -1 { meta_to_reintroduce = meta.GetFormula() @@ -191,7 +191,7 @@ func (nds *nonDestructiveSearch) instantiate(fatherId uint64, state *State, c Co // If ths meta has an affectation in subst, give it // Else, give the previous meta - association_subst := Unif.Substitutions{} + association_subst := substitution.Substitutions{} // Associate new meta with old meta for _, new_meta := range newMetas.Elements().GetSlice() { @@ -212,7 +212,7 @@ func (nds *nonDestructiveSearch) instantiate(fatherId uint64, state *State, c Co if !found { for _, subst := range state.GetAppliedSubst().GetSubst().GetSlice() { switch s := subst.Substitution().(type) { - case Lib.Some[Unif.Substitution]: + case Lib.Some[substitution.Substitution]: original_meta, original_term := s.Val.Get() if !found && original_meta.GetName() == new_meta.GetName() && !found { association_subst.Set(new_meta, original_term) @@ -233,24 +233,24 @@ func (nds *nonDestructiveSearch) instantiate(fatherId uint64, state *State, c Co } } - mixed_assoc_subst := Lib.NewList[Unif.MixedSubstitution]() + mixed_assoc_subst := Lib.NewList[substitution.MixedSubstitution]() for _, subst := range association_subst { - mixed_assoc_subst.Append(Unif.MkMixedFromSubst(subst)) + mixed_assoc_subst.Append(substitution.MkMixedFromSubst(subst)) } - new_subst, same_key := Unif.MergeMixedSubstitutions(mixed_assoc_subst, state.GetAppliedSubst().GetSubst()) + new_subst, same_key := substitution.MergeMixedSubstitutions(mixed_assoc_subst, state.GetAppliedSubst().GetSubst()) if same_key { Glob.PrintInfo("PS", "Same key in S2 and S1") } - if !Unif.UnifSucceeded(new_subst) { + if !substitution.UnifSucceeded(new_subst) { Glob.Anomaly("PS", "MergeSubstitutions return failure") } - new_subst, same_key = Unif.MergeMixedSubstitutions(new_subst, s.GetSubst()) + new_subst, same_key = substitution.MergeMixedSubstitutions(new_subst, s.GetSubst()) if same_key { Glob.PrintInfo("PS", "Same key in S2 and S1") } - if !Unif.UnifSucceeded(new_subst) { + if !substitution.UnifSucceeded(new_subst) { Glob.Anomaly("PS", "MergeSubstitutions return failure") } @@ -271,12 +271,12 @@ func (nds *nonDestructiveSearch) instantiate(fatherId uint64, state *State, c Co state.SetLF(Core.ApplySubstitutionsOnFormAndTermsList(new_subst, state.GetLF())) - ms, same_key := Unif.MergeMixedSubstitutions(state.GetAppliedSubst().GetSubst(), new_subst) + ms, same_key := substitution.MergeMixedSubstitutions(state.GetAppliedSubst().GetSubst(), new_subst) if same_key { Glob.Anomaly("PS", "Same key in S2 and S1") } - if !Unif.UnifSucceeded(ms) { + if !substitution.UnifSucceeded(ms) { Glob.Anomaly("PS", "MergeSubstitutions return failure") } state.SetAppliedSubst(Core.MakeSubstAndForm(ms, s.GetForm())) diff --git a/src/Search/proof.go b/src/Search/proof.go index e7bcecad..7ec557ad 100644 --- a/src/Search/proof.go +++ b/src/Search/proof.go @@ -47,7 +47,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + substitution "github.com/GoelandProver/Goeland/Unif/substitution" ) var path_proof = Glob.GetExecPath() + "proof_output.json" @@ -404,7 +404,7 @@ func RetrieveUninstantiatedMetaFromProof(proofStruct []ProofStruct) Lib.Set[AST. } /* Apply subst on a proof tree */ -func ApplySubstitutionOnProofList(s Lib.List[Unif.MixedSubstitution], proof_list []ProofStruct) []ProofStruct { +func ApplySubstitutionOnProofList(s Lib.List[substitution.MixedSubstitution], proof_list []ProofStruct) []ProofStruct { new_proof_list := []ProofStruct{} for _, p := range proof_list { diff --git a/src/Search/rules.go b/src/Search/rules.go index b9b490b3..eaff2bec 100644 --- a/src/Search/rules.go +++ b/src/Search/rules.go @@ -42,7 +42,7 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + substitution "github.com/GoelandProver/Goeland/Unif/substitution" ) var strToPrintMap map[string]string = map[string]string{ @@ -59,9 +59,9 @@ var strToPrintMap map[string]string = map[string]string{ "EXISTS": "∃", } -func ApplyClosureRules(form AST.Form, state *State) (bool, Lib.List[Lib.List[Unif.MixedSubstitution]]) { +func ApplyClosureRules(form AST.Form, state *State) (bool, Lib.List[Lib.List[substitution.MixedSubstitution]]) { result := false - substitutions := Lib.NewList[Lib.List[Unif.MixedSubstitution]]() + substitutions := Lib.NewList[Lib.List[substitution.MixedSubstitution]]() debug(Lib.MkLazy(func() string { return "Start ACR" })) if searchObviousClosureRule(form) { @@ -73,9 +73,9 @@ func ApplyClosureRules(form AST.Form, state *State) (bool, Lib.List[Lib.List[Uni substFound, substs := searchInequalities(form) if substFound { result = true - mixed_substs := Lib.NewList[Unif.MixedSubstitution]() + mixed_substs := Lib.NewList[substitution.MixedSubstitution]() for _, subst := range substs { - mixed_substs.Append(Unif.MkMixedFromSubst(subst)) + mixed_substs.Append(substitution.MkMixedFromSubst(subst)) } substitutions.Append(mixed_substs) } @@ -106,7 +106,7 @@ func ApplyClosureRules(form AST.Form, state *State) (bool, Lib.List[Lib.List[Uni subst.ToString()) }), ) - substitutions.Add(Lib.ListEquals[Unif.MixedSubstitution], subst.GetSubsts()) + substitutions.Add(Lib.ListEquals[substitution.MixedSubstitution], subst.GetSubsts()) } } } @@ -114,14 +114,14 @@ func ApplyClosureRules(form AST.Form, state *State) (bool, Lib.List[Lib.List[Uni return result, substitutions } -func searchForbidden(state *State, s Unif.MatchingSubstitutions) bool { +func searchForbidden(state *State, s substitution.MatchingSubstitutions) bool { foundForbidden := false for _, substForbidden := range state.GetForbiddenSubsts().GetSlice() { - substs := Unif.Substitutions{} + substs := substitution.Substitutions{} for _, subst := range substForbidden.GetSlice() { switch s := subst.Substitution().(type) { - case Lib.Some[Unif.Substitution]: + case Lib.Some[substitution.Substitution]: substs = append(substs, s.Val) } } @@ -155,8 +155,8 @@ func searchObviousClosureRule(f AST.Form) bool { } /* Search contradiction with inequalities (for example, !(x,a) -> subst(x, a)) */ -func searchInequalities(form AST.Form) (bool, Unif.Substitutions) { - subst := Unif.MakeEmptySubstitution() +func searchInequalities(form AST.Form) (bool, substitution.Substitutions) { + subst := substitution.MakeEmptySubstitution() if formNot, isNot := form.(AST.Not); isNot { if predNeq, isPred := formNot.GetForm().(AST.Pred); isPred { @@ -181,12 +181,12 @@ func searchInequalities(form AST.Form) (bool, Unif.Substitutions) { Lib.MkLazy(func() string { return fmt.Sprintf("Arg 2 : %v", arg_2.ToString()) }), ) - subst = Unif.AddUnification(arg_1, arg_2, subst) + subst = substitution.AddUnification(arg_1, arg_2, subst) debug( Lib.MkLazy(func() string { return fmt.Sprintf("Subst : %v", subst.ToString()) }), ) - if !subst.Equals(Unif.Failure()) { + if !subst.Equals(substitution.Failure()) { return true, subst } } @@ -197,7 +197,7 @@ func searchInequalities(form AST.Form) (bool, Unif.Substitutions) { } /* Search a contradiction between a formula and another in the datastructure */ -func searchClosureRule(f AST.Form, st State) (bool, []Unif.MixedSubstitutions) { +func searchClosureRule(f AST.Form, st State) (bool, []substitution.MixedSubstitutions) { switch nf := f.(type) { case AST.Pred: return st.GetTreeNeg().Unify(f) diff --git a/src/Search/search.go b/src/Search/search.go index c55ab779..9e33f30f 100644 --- a/src/Search/search.go +++ b/src/Search/search.go @@ -43,13 +43,13 @@ import ( "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/substitution" ) type SearchAlgorithm interface { Search(AST.Form, int) bool SetApplyRules(func(uint64, State, Communication, Core.FormAndTermsList, int, int, []int)) - ManageClosureRule(uint64, *State, Communication, Lib.List[Lib.List[Unif.MixedSubstitution]], Core.FormAndTerms, int, int) (bool, []Core.SubstAndForm) + ManageClosureRule(uint64, *State, Communication, Lib.List[Lib.List[subst.MixedSubstitution]], Core.FormAndTerms, int, int) (bool, []Core.SubstAndForm) } var UsedSearch SearchAlgorithm @@ -118,11 +118,11 @@ func printStandardSolution(status string) { fmt.Printf("%s SZS status %v for %v\n", "%", status, Glob.GetProblemName()) } -func retrieveMetaFromSubst(substs Lib.List[Unif.MixedSubstitution]) []int { +func retrieveMetaFromSubst(substs Lib.List[subst.MixedSubstitution]) []int { res := []int{} - for _, subst := range substs.GetSlice() { - switch s := subst.Substitution().(type) { - case Lib.Some[Unif.Substitution]: + for _, s := range substs.GetSlice() { + switch s := s.Substitution().(type) { + case Lib.Some[subst.Substitution]: res = Glob.AppendIfNotContainsInt(res, s.Val.Key().GetFormula()) } } diff --git a/src/Search/state.go b/src/Search/state.go index 8c3a6f85..60d4f7c2 100644 --- a/src/Search/state.go +++ b/src/Search/state.go @@ -44,7 +44,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /****************/ @@ -60,11 +60,11 @@ type State struct { applied_subst Core.SubstAndForm last_applied_subst Core.SubstAndForm // For non destructive case only substs_found []Core.SubstAndForm // Subst found with mm in d, subst for "bactrack" in nd - tree_pos, tree_neg Unif.DataStructure + tree_pos, tree_neg subst.DataStructure proof []ProofStruct current_proof ProofStruct bt_on_formulas bool - forbidden Lib.List[Lib.List[Unif.MixedSubstitution]] + forbidden Lib.List[Lib.List[subst.MixedSubstitution]] unifier Core.Unifier eqStruct eqStruct.EqualityStruct } @@ -113,13 +113,13 @@ func (s State) GetLastAppliedSubst() Core.SubstAndForm { func (st State) GetSubstsFound() []Core.SubstAndForm { return Core.CopySubstAndFormList(st.substs_found) } -func (s State) GetTreePos() Unif.DataStructure { +func (s State) GetTreePos() subst.DataStructure { return s.tree_pos } func (s *State) AddToTreePos(fl Lib.List[AST.Form]) { s.tree_pos = s.tree_pos.InsertFormulaListToDataStructure(fl) } -func (s State) GetTreeNeg() Unif.DataStructure { +func (s State) GetTreeNeg() subst.DataStructure { return s.tree_neg } func (s *State) AddToTreeNeg(fl Lib.List[AST.Form]) { @@ -134,7 +134,7 @@ func (s State) GetCurrentProof() ProofStruct { func (s State) GetBTOnFormulas() bool { return s.bt_on_formulas } -func (s State) GetForbiddenSubsts() Lib.List[Lib.List[Unif.MixedSubstitution]] { +func (s State) GetForbiddenSubsts() Lib.List[Lib.List[subst.MixedSubstitution]] { return s.forbidden } func (s State) GetGlobUnifier() Core.Unifier { @@ -186,10 +186,10 @@ func (st *State) SetLastAppliedSubst(s Core.SubstAndForm) { func (st *State) SetSubstsFound(s []Core.SubstAndForm) { st.substs_found = Core.CopySubstAndFormList(s) } -func (st *State) SetTreePos(d Unif.DataStructure) { +func (st *State) SetTreePos(d subst.DataStructure) { st.tree_pos = d } -func (st *State) SetTreeNeg(d Unif.DataStructure) { +func (st *State) SetTreeNeg(d subst.DataStructure) { st.tree_neg = d } func (st *State) SetProof(p []ProofStruct) { @@ -245,15 +245,15 @@ func (st *State) SetCurrentProofNodeId(i int) { func (st *State) SetBTOnFormulas(b bool) { st.bt_on_formulas = b } -func (st *State) SetForbiddenSubsts(s Lib.List[Lib.List[Unif.MixedSubstitution]]) { - st.forbidden = s.Copy(Lib.ListCpy[Unif.MixedSubstitution]) +func (st *State) SetForbiddenSubsts(s Lib.List[Lib.List[subst.MixedSubstitution]]) { + st.forbidden = s.Copy(Lib.ListCpy[subst.MixedSubstitution]) } func (s *State) SetGlobUnifier(u Core.Unifier) { s.unifier = u.Copy() } /* Maker */ -func MakeState(limit int, tp, tn Unif.DataStructure, f AST.Form) State { +func MakeState(limit int, tp, tn subst.DataStructure, f AST.Form) State { n := 0 if Glob.IsDestructive() { n = limit @@ -285,7 +285,7 @@ func MakeState(limit int, tp, tn Unif.DataStructure, f AST.Form) State { []ProofStruct{}, current_proof, false, - Lib.NewList[Lib.List[Unif.MixedSubstitution]](), + Lib.NewList[Lib.List[subst.MixedSubstitution]](), Core.MakeUnifier(), eqStruct.NewEqStruct()} } @@ -353,7 +353,7 @@ func (st State) Print() { debug(Lib.MkLazy(func() string { return "Subst_found: " })) debug( Lib.MkLazy(func() string { - return Unif.SubstsToString(Core.GetSubstListFromSubstAndFormList(st.GetSubstsFound())) + return subst.SubstsToString(Core.GetSubstListFromSubstAndFormList(st.GetSubstsFound())) }), ) } @@ -368,7 +368,7 @@ func (st State) Print() { debug( Lib.MkLazy(func() string { return st.forbidden.ToString( - func(m Lib.List[Unif.MixedSubstitution]) string { + func(m Lib.List[subst.MixedSubstitution]) string { return Lib.ListToString(m) }, Lib.WithSep(" ; ")) }), diff --git a/src/Unif/code-trees.go b/src/Unif/codetree/code-trees.go similarity index 96% rename from src/Unif/code-trees.go rename to src/Unif/codetree/code-trees.go index 3a7e25d1..8723369c 100644 --- a/src/Unif/code-trees.go +++ b/src/Unif/codetree/code-trees.go @@ -34,13 +34,14 @@ * This file contains all the definitons necessary to make a Code Tree **/ -package Unif +package codetree import ( "strings" "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /*************************/ @@ -77,29 +78,30 @@ func (n Node) IsEmpty() bool { return (len(n.value) == 0) } -func MakeUnifProblem(l Lib.List[AST.Form], is_pos bool) DataStructure { +func MakeUnifProblem(l Lib.List[AST.Form], is_pos bool) subst.DataStructure { return NewNode().MakeDataStruct(l, is_pos) } -func MakeTermUnifProblem(l Lib.List[AST.Term]) DataStructure { +func MakeTermUnifProblem(l Lib.List[AST.Term]) subst.DataStructure { root := makeNode(nil) for _, t := range l.GetSlice() { - root.insert(ParseTerm(transformTerm(t))) + root.insert(ParseTerm(subst.TransformTerm(t))) } return root } -func (n Node) MakeDataStruct(fl Lib.List[AST.Form], is_pos bool) DataStructure { +func (n Node) MakeDataStruct(fl Lib.List[AST.Form], is_pos bool) subst.DataStructure { return makeCodeTreeFromAtomic(fl, is_pos) } /* Copy a datastruct */ -func (n Node) Copy() DataStructure { +func (n Node) Copy() subst.DataStructure { return Node{n.getValue(), n.getChildren(), n.leafFor.Copy(Lib.EitherCpy[AST.Term, AST.Form])} } + /********************/ /* Helper functions */ /********************/ @@ -164,7 +166,7 @@ func makeNode(block CodeBlock) *Node { } /* Insert a lsit of formula into the right tree */ -func (n Node) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) DataStructure { +func (n Node) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) subst.DataStructure { for _, f := range lf.GetSlice() { switch nf := f.Copy().(type) { case AST.Pred: diff --git a/src/Unif/instruction.go b/src/Unif/codetree/instruction.go similarity index 99% rename from src/Unif/instruction.go rename to src/Unif/codetree/instruction.go index f8b7e7c0..2607294c 100644 --- a/src/Unif/instruction.go +++ b/src/Unif/codetree/instruction.go @@ -34,7 +34,7 @@ * This file provides the necessary structures to represents instructions for the machine. **/ -package Unif +package codetree import ( "reflect" diff --git a/src/Unif/int_pair.go b/src/Unif/codetree/int_pair.go similarity index 99% rename from src/Unif/int_pair.go rename to src/Unif/codetree/int_pair.go index f26a8307..61a621d4 100644 --- a/src/Unif/int_pair.go +++ b/src/Unif/codetree/int_pair.go @@ -34,7 +34,7 @@ * This file provides the necessary structures to manipulate pairs of int. **/ -package Unif +package codetree import ( "strconv" diff --git a/src/Unif/machine.go b/src/Unif/codetree/machine.go similarity index 76% rename from src/Unif/machine.go rename to src/Unif/codetree/machine.go index abd835ae..1c393a27 100644 --- a/src/Unif/machine.go +++ b/src/Unif/codetree/machine.go @@ -34,11 +34,13 @@ * This file provides the necessary structures to operate the unification algorithm. **/ -package Unif +package codetree import ( + "fmt" "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Describes the success or failure of the execution of a machine instruction. */ @@ -62,10 +64,10 @@ type Machine struct { hasPushed bool hasPoped bool post []IntPair - subst []SubstPair + subst []subst.SubstPair terms Lib.List[AST.Term] - meta Substitutions - failure []MixMatchSubstitutions + meta subst.Substitutions + failure []subst.MixMatchSubstitutions topLevelTot int topLevelCount int } @@ -79,10 +81,10 @@ func makeMachine() Machine { hasPushed: false, hasPoped: false, post: []IntPair{}, - subst: []SubstPair{}, + subst: []subst.SubstPair{}, terms: Lib.NewList[AST.Term](), - meta: Substitutions{}, - failure: []MixMatchSubstitutions{}, + meta: subst.Substitutions{}, + failure: []subst.MixMatchSubstitutions{}, topLevelTot: 0, topLevelCount: 0, } @@ -216,7 +218,7 @@ func (m *Machine) matchIndexes(t AST.Term, instrTerm AST.Term) Status { /* Checks if the substitution of the metavariable t matches the index of instrTerm. */ func (m *Machine) checkMeta(t AST.Meta, instrTerm AST.Term) Status { - if HasSubst(m.meta, t) { + if subst.HasSubst(m.meta, t) { metaGotten, _ := m.meta.Get(t) unwrapped := m.unwrapMeta(metaGotten) if !unwrapped.IsMeta() && !m.doIndexMatch(unwrapped, instrTerm) { @@ -226,3 +228,66 @@ func (m *Machine) checkMeta(t AST.Meta, instrTerm AST.Term) Status { return Status(SUCCESS) } + +/* Call addUnification and returns a status - modify m.meta */ +func (m *Machine) trySubstituteMeta(i AST.Term, j AST.Term) Status { + debug( + Lib.MkLazy( + func() string { return fmt.Sprintf("Try substitute : %v and %v", i.ToString(), j.ToString()) }, + ), + ) + new_meta := subst.AddUnification(i, j, m.meta.Copy()) + if new_meta.Equals(subst.Failure()) { + return Status(ERROR) + } + m.meta = new_meta + return Status(SUCCESS) +} + +/* Adds the unifications found to the meta substitutions from running the algorithm on term1 and term2. */ +func (m *Machine) addUnifications(term1, term2 AST.Term) Status { + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "add unification : %v and %v", + term1.ToString(), + term2.ToString()) + }), + ) + meta := tryUnification( + term1.Copy(), + term2.Copy(), + m.meta.Copy(), + ) // Return empty or an array of 1 matching substitution, which is m.meta improved wit (term1, term2) + + if len(meta) == 0 { + return Status(ERROR) + } else { + m.meta = meta[0].Subst + subst.EliminateMeta(&m.meta) + subst.Eliminate(&m.meta) + } + + return Status(SUCCESS) +} + + +/* Tries to unify term1 with term2, depending on the substitutions already found by the parent unification process. */ +func tryUnification(term1, term2 AST.Term, meta subst.Substitutions) []subst.MixMatchSubstitutions { + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "Try unification : %v and %v", + term1.ToString(), + term2.ToString()) + }), + ) + aux := makeMachine() + aux.terms = Lib.MkListV(term2) + aux.meta = meta + + // add begin at the start and end at the end ! + tree := makeBranch(ParseTerm(term1.Copy())) + res := aux.unifyAux(*tree) + return res +} \ No newline at end of file diff --git a/src/Unif/matching.go b/src/Unif/codetree/matching.go similarity index 85% rename from src/Unif/matching.go rename to src/Unif/codetree/matching.go index 9ec4a010..6e19ab74 100644 --- a/src/Unif/matching.go +++ b/src/Unif/codetree/matching.go @@ -34,7 +34,7 @@ * This file provides the necessary methods for the unification algorithm. **/ -package Unif +package codetree import ( "fmt" @@ -43,6 +43,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Unif/substitution" ) var debug Glob.Debugger @@ -54,12 +55,12 @@ func InitDebugger() { /*** Unify ***/ /* Helper function to avoid using MakeMachine() outside of this file. */ -func (n Node) Unify(formula AST.Form) (bool, []MixedSubstitutions) { +func (n Node) Unify(formula AST.Form) (bool, []subst.MixedSubstitutions) { machine := makeMachine() var term AST.Term if formula_type, is_pred := formula.(AST.Pred); is_pred { - term = transformPred(formula_type) + term = subst.TransformPred(formula_type) } else { Glob.Anomaly("unification", fmt.Sprintf("Expected predicate, got %s", formula.ToString())) } @@ -69,39 +70,39 @@ func (n Node) Unify(formula AST.Form) (bool, []MixedSubstitutions) { // As we have transformed type metas to terms, we get everything in a term substitution. // But externally, we want to have a substitution of both (term) metas to terms and (type) metas to types. // We use MixedSubstitution to properly manage things internally. - mixed_substs := []MixedSubstitutions{} + mixed_substs := []subst.MixedSubstitutions{} for _, subst := range matching_substs { - mixed_substs = append(mixed_substs, subst.toMixed()) + mixed_substs = append(mixed_substs, subst.ToMixed()) } return res, mixed_substs } -func (n Node) UnifyTerm(t AST.Term) (bool, []MixedTermSubstitutions) { +func (n Node) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSubstitutions) { m := makeMachine() res, matching_substs := m.unify( n, - transformTerm(t), + subst.TransformTerm(t), ) - mixed_substs := []MixedTermSubstitutions{} + mixed_substs := []subst.MixedTermSubstitutions{} for _, subst := range matching_substs { - mixed_substs = append(mixed_substs, subst.toMixedTerm()) + mixed_substs = append(mixed_substs, subst.ToMixedTerm()) } return res, mixed_substs } /* Tries to find the substitutions needed to unify the formulae with the one described by the sequence of instructions. */ -func (m *Machine) unify(node Node, t AST.Term) (bool, []MixMatchSubstitutions) { +func (m *Machine) unify(node Node, t AST.Term) (bool, []subst.MixMatchSubstitutions) { m.terms = Lib.MkListV(t) res := m.unifyAux(node) return !reflect.DeepEqual(m.failure, res), res } /*** Unify aux ***/ -func (m *Machine) unifyAux(node Node) []MixMatchSubstitutions { +func (m *Machine) unifyAux(node Node) []subst.MixMatchSubstitutions { for _, instr := range node.value { debug(Lib.MkLazy(func() string { return "------------------------" })) @@ -109,7 +110,7 @@ func (m *Machine) unifyAux(node Node) []MixMatchSubstitutions { debug(Lib.MkLazy(func() string { return fmt.Sprintf("Meta : %v", m.meta.ToString()) })) debug( Lib.MkLazy( - func() string { return fmt.Sprintf("Subst : %v", SubstPairListToString(m.subst)) }, + func() string { return fmt.Sprintf("Subst : %v", subst.SubstPairListToString(m.subst)) }, ), ) debug( @@ -184,18 +185,18 @@ func (m *Machine) unifyAux(node Node) []MixMatchSubstitutions { } } - matching := []MixMatchSubstitutions{} + matching := []subst.MixMatchSubstitutions{} if node.isLeaf() { for _, f := range node.leafFor.GetSlice() { // Rebuild final substitution between meta and subst final_subst := computeSubstitutions( - CopySubstPairList(m.subst), + subst.CopySubstPairList(m.subst), m.meta.Copy(), tofMetaList(f), ) - if !final_subst.Equals(Failure()) { - matching = append(matching, MixMatchSubstitutions{tof: f, subst: final_subst}) + if !final_subst.Equals(subst.Failure()) { + matching = append(matching, subst.MixMatchSubstitutions{Tof: f, Subst: final_subst}) } } } @@ -206,7 +207,7 @@ func (m *Machine) unifyAux(node Node) []MixMatchSubstitutions { /* Unify on goroutines - to manage die message */ /* TODO : remove when debug ok */ -func (m *Machine) unifyAuxOnGoroutine(n Node, ch chan []MixMatchSubstitutions, father_id uint64) { +func (m *Machine) unifyAuxOnGoroutine(n Node, ch chan []subst.MixMatchSubstitutions, father_id uint64) { debug( Lib.MkLazy(func() string { return fmt.Sprintf("Child of %v, Unify Aux", father_id) }), ) @@ -216,23 +217,23 @@ func (m *Machine) unifyAuxOnGoroutine(n Node, ch chan []MixMatchSubstitutions, f } /* Launches each child of the current node in a goroutine. */ -func (m *Machine) launchChildrenSearch(node Node) []MixMatchSubstitutions { - channels := []chan []MixMatchSubstitutions{} +func (m *Machine) launchChildrenSearch(node Node) []subst.MixMatchSubstitutions { + channels := []chan []subst.MixMatchSubstitutions{} for _, c := range node.children { debug( Lib.MkLazy( func() string { return fmt.Sprintf("Next symbol = %v", c.getValue()[0].ToString()) }, ), ) - channels = append(channels, make(chan []MixMatchSubstitutions)) + channels = append(channels, make(chan []subst.MixMatchSubstitutions)) } - matching := []MixMatchSubstitutions{} + matching := []subst.MixMatchSubstitutions{} for i, n := range node.children { ch := channels[i] st := m.terms.Copy(AST.Term.Copy) ip := CopyIntPairList(m.post) - sc := CopySubstPairList(m.subst) + sc := subst.CopySubstPairList(m.subst) copy := Machine{ subst: sc, @@ -260,7 +261,7 @@ func (m *Machine) launchChildrenSearch(node Node) []MixMatchSubstitutions { for cpt_remaining_children > 0 { _, value, _ := reflect.Select(cases) - matching = append(matching, value.Interface().([]MixMatchSubstitutions)...) + matching = append(matching, value.Interface().([]subst.MixMatchSubstitutions)...) cpt_remaining_children-- } @@ -348,7 +349,7 @@ func (m *Machine) put(instr Put) { if m.isUnlocked() { m.subst = append( m.subst, - MakeSubstPair(instr.GetIndex(), m.terms.At(m.q)), + subst.MakeSubstPair(instr.GetIndex(), m.terms.At(m.q)), ) } } @@ -356,8 +357,8 @@ func (m *Machine) put(instr Put) { /* Algorithm for the instruction Compare. */ func (m *Machine) compare(i int, j int) Status { if m.isUnlocked() { - i := GetSubstAt(m.subst, i) - j := GetSubstAt(m.subst, j) + i := subst.GetSubstAt(m.subst, i) + j := subst.GetSubstAt(m.subst, j) if i != nil && j != nil { i = m.unwrapMeta(i) diff --git a/src/Unif/parsing.go b/src/Unif/codetree/parsing.go similarity index 88% rename from src/Unif/parsing.go rename to src/Unif/codetree/parsing.go index 5e8d080d..4acd548d 100644 --- a/src/Unif/parsing.go +++ b/src/Unif/codetree/parsing.go @@ -30,35 +30,16 @@ * knowledge of the CeCILL license and that you accept its terms. **/ -package Unif +package codetree import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Unif/substitution" ) -func transformPred(p AST.Pred) AST.Term { - return transformTerm(AST.MakerFun(p.GetID(), p.GetTyArgs(), p.GetArgs())) -} - -func transformTerm(t AST.Term) AST.Term { - switch term := t.(type) { - case AST.Id, AST.Meta, AST.Var: - return t - case AST.Fun: - args := Lib.ListMap(term.GetTyArgs(), AST.TyToTerm) - args.Append(Lib.ListMap(term.GetArgs(), transformTerm).GetSlice()...) - return AST.MakerFun( - term.GetID(), - Lib.NewList[AST.Ty](), - args, - ) - } - Glob.Anomaly("unif parsing", "Unknown term") - return nil -} /* Parses a formulae to a sequence of instructions. */ func ParseFormula(formula AST.Form) Sequence { @@ -68,7 +49,7 @@ func ParseFormula(formula AST.Form) Sequence { case AST.Pred: instructions := Sequence{base: Lib.MkRight[AST.Term, AST.Form](formula)} - switch term := transformPred(formula_type).(type) { + switch term := subst.TransformPred(formula_type).(type) { case AST.Fun: instructions.add(Begin{}) parsePred(formula_type.GetID(), term.GetArgs(), &instructions) diff --git a/src/Unif/sequence.go b/src/Unif/codetree/sequence.go similarity index 95% rename from src/Unif/sequence.go rename to src/Unif/codetree/sequence.go index 93ebf324..922de31e 100644 --- a/src/Unif/sequence.go +++ b/src/Unif/codetree/sequence.go @@ -34,7 +34,7 @@ * This file provides the necessary structures to represents a sequences for the machine. **/ -package Unif +package codetree import ( "fmt" @@ -42,6 +42,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /*** Sequence ***/ @@ -61,11 +62,11 @@ func tofCmp(tof1, tof2 Lib.Either[AST.Term, AST.Form]) bool { func tofMetaList(tof Lib.Either[AST.Term, AST.Form]) Lib.List[AST.Meta] { switch tof := tof.(type) { case Lib.Left[AST.Term, AST.Form]: - return transformTerm(tof.Val).GetMetaList() + return subst.TransformTerm(tof.Val).GetMetaList() case Lib.Right[AST.Term, AST.Form]: switch f := tof.Val.(type) { case AST.Pred: - return transformPred(f).GetMetaList() + return subst.TransformPred(f).GetMetaList() } } diff --git a/src/Unif/substitutions_tree.go b/src/Unif/codetree/substitutions_tree.go similarity index 54% rename from src/Unif/substitutions_tree.go rename to src/Unif/codetree/substitutions_tree.go index e1d03121..6a02408c 100644 --- a/src/Unif/substitutions_tree.go +++ b/src/Unif/codetree/substitutions_tree.go @@ -34,13 +34,14 @@ * This file contains the functions needed to subtitute all the meta-variables of a subtitution map. **/ -package Unif +package codetree import ( "fmt" "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Takes each meta of the formula, matches the index to the metas, and add everything to subst */ @@ -50,18 +51,18 @@ import ( * Merge both of them **/ func computeSubstitutions( - subs []SubstPair, - metasToSubs Substitutions, + subs []subst.SubstPair, + metasToSubs subst.Substitutions, metaList Lib.List[AST.Meta], -) Substitutions { +) subst.Substitutions { debug( Lib.MkLazy(func() string { return fmt.Sprintf( "Compute substitution : %v and %v", - SubstPairListToString(subs), metasToSubs.ToString()) + subst.SubstPairListToString(subs), metasToSubs.ToString()) }), ) - treeSubs := Substitutions{} + treeSubs := subst.Substitutions{} // Transform subst tree into a real substitution for _, value := range subs { @@ -80,13 +81,13 @@ func computeSubstitutions( if !currentMeta.Equals(currentValue) { // Si current_meta a déjà une association dans metas metaGet, index := metasToSubs.Get(currentMeta) - if HasSubst(metasToSubs, currentMeta) && (index != -1) && + if subst.HasSubst(metasToSubs, currentMeta) && (index != -1) && !currentValue.Equals(metaGet) { // On cherche a unifier les deux valeurs treeSubs.Set(currentMeta, currentValue) - new_unif := AddUnification(currentValue.Copy(), metaGet.Copy(), treeSubs.Copy()) - if new_unif.Equals(Failure()) { - return Failure() + new_unif := subst.AddUnification(currentValue.Copy(), metaGet.Copy(), treeSubs.Copy()) + if new_unif.Equals(subst.Failure()) { + return subst.Failure() } else { treeSubs = new_unif metasToSubs.Remove(index) // Remove from meta @@ -104,10 +105,10 @@ func computeSubstitutions( ), ) // Metas_subst eliminate - EliminateMeta(&metasToSubs) - Eliminate(&metasToSubs) - if metasToSubs.Equals(Failure()) { - return Failure() + subst.EliminateMeta(&metasToSubs) + subst.Eliminate(&metasToSubs) + if metasToSubs.Equals(subst.Failure()) { + return subst.Failure() } debug( Lib.MkLazy(func() string { return fmt.Sprintf("After meta : %v", metasToSubs.ToString()) }), @@ -119,10 +120,10 @@ func computeSubstitutions( ), ) // Tree subst elminate - EliminateMeta(&treeSubs) - Eliminate(&treeSubs) - if treeSubs.Equals(Failure()) { - return Failure() + subst.EliminateMeta(&treeSubs) + subst.Eliminate(&treeSubs) + if treeSubs.Equals(subst.Failure()) { + return subst.Failure() } debug( Lib.MkLazy( @@ -131,8 +132,8 @@ func computeSubstitutions( ) // Fusion - res, _ := MergeSubstitutions(metasToSubs, treeSubs) - if res.Equals(Failure()) { + res, _ := subst.MergeSubstitutions(metasToSubs, treeSubs) + if res.Equals(subst.Failure()) { return res } @@ -140,8 +141,8 @@ func computeSubstitutions( Lib.MkLazy(func() string { return fmt.Sprintf("after merge : %v", res.ToString()) }), ) - EliminateMeta(&res) - Eliminate(&res) + subst.EliminateMeta(&res) + subst.Eliminate(&res) debug( Lib.MkLazy(func() string { return fmt.Sprintf("after eliminate : %v", res.ToString()) }), @@ -150,148 +151,50 @@ func computeSubstitutions( return res } -/* Call addUnification and returns a status - modify m.meta */ -func (m *Machine) trySubstituteMeta(i AST.Term, j AST.Term) Status { - debug( - Lib.MkLazy( - func() string { return fmt.Sprintf("Try substitute : %v and %v", i.ToString(), j.ToString()) }, - ), - ) - new_meta := AddUnification(i, j, m.meta.Copy()) - if new_meta.Equals(Failure()) { - return Status(ERROR) - } - m.meta = new_meta - return Status(SUCCESS) -} - -func AddUnification(term1, term2 AST.Term, subst Substitutions) Substitutions { +func addUnification(term1, term2 AST.Term, s subst.Substitutions) subst.Substitutions { debug( Lib.MkLazy(func() string { return fmt.Sprintf( "Add unification : %v and %v to %v", term1.ToString(), term2.ToString(), - subst.ToString()) + s.ToString()) }), ) - term1 = transformTerm(term1) - term2 = transformTerm(term2) + term1 = subst.TransformTerm(term1) + term2 = subst.TransformTerm(term2) // unify with ct only if the term already has an unification or if there is 2 fun. Just add it and eliminate otherwise. - t1v, _ := subst.Get(term1.ToMeta()) - t2v, _ := subst.Get(term2.ToMeta()) - if (term1.IsMeta() && HasSubst(subst, term1.ToMeta()) && !t1v.Equals(term2)) || - (term2.IsMeta() && HasSubst(subst, term2.ToMeta()) && !t2v.Equals(term1)) || + t1v, _ := s.Get(term1.ToMeta()) + t2v, _ := s.Get(term2.ToMeta()) + if (term1.IsMeta() && subst.HasSubst(s, term1.ToMeta()) && !t1v.Equals(term2)) || + (term2.IsMeta() && subst.HasSubst(s, term2.ToMeta()) && !t2v.Equals(term1)) || (term1.IsFun() && term2.IsFun()) { m := makeMachine() - m.meta = subst.Copy() + m.meta = s.Copy() if m.addUnifications(term1, term2) == SUCCESS { return m.meta } else { - return Failure() + return subst.Failure() } } else { switch { case term1.IsMeta(): - subst.Set(term1.ToMeta(), term2) - EliminateMeta(&subst) - Eliminate(&subst) - return subst + s.Set(term1.ToMeta(), term2) + subst.EliminateMeta(&s) + subst.Eliminate(&s) + return s case term2.IsMeta(): - subst.Set(term2.ToMeta(), term1) - EliminateMeta(&subst) - Eliminate(&subst) - return subst + s.Set(term2.ToMeta(), term1) + subst.EliminateMeta(&s) + subst.Eliminate(&s) + return s default: - return Failure() + return subst.Failure() } } } -/* Adds the unifications found to the meta substitutions from running the algorithm on term1 and term2. */ -func (m *Machine) addUnifications(term1, term2 AST.Term) Status { - debug( - Lib.MkLazy(func() string { - return fmt.Sprintf( - "add unification : %v and %v", - term1.ToString(), - term2.ToString()) - }), - ) - meta := tryUnification( - term1.Copy(), - term2.Copy(), - m.meta.Copy(), - ) // Return empty or an array of 1 matching substitution, which is m.meta improved wit (term1, term2) - if len(meta) == 0 { - return Status(ERROR) - } else { - m.meta = meta[0].subst - EliminateMeta(&m.meta) - Eliminate(&m.meta) - } - return Status(SUCCESS) -} -/* Tries to unify term1 with term2, depending on the substitutions already found by the parent unification process. */ -func tryUnification(term1, term2 AST.Term, meta Substitutions) []MixMatchSubstitutions { - debug( - Lib.MkLazy(func() string { - return fmt.Sprintf( - "Try unification : %v and %v", - term1.ToString(), - term2.ToString()) - }), - ) - aux := makeMachine() - aux.terms = Lib.MkListV(term2) - aux.meta = meta - - // add begin at the start and end at the end ! - tree := makeBranch(ParseTerm(term1.Copy())) - res := aux.unifyAux(*tree) - return res -} - -/* Merge two valid substitutions */ -func MergeSubstitutions(s1, s2 Substitutions) (Substitutions, bool) { - debug( - Lib.MkLazy(func() string { - return fmt.Sprintf( - "Merge substitution : %v and %v", - s1.ToString(), - s2.ToString()) - }), - ) - res := Substitutions{} - same_key := false - - if s1.IsEmpty() { - return s2, false - } - - if s2.IsEmpty() { - return s1, false - } - - for _, subst := range s1 { - res.Set(subst.Get()) - } - - for _, subst := range s2 { - s2_k, s2_v := subst.Get() - if HasSubst(res, s2_k) { - same_key = true - res = AddUnification(s2_k.Copy(), s2_v.Copy(), res.Copy()) - } else { - res.Set(s2_k.ToMeta(), s2_v) - EliminateMeta(&res) - Eliminate(&res) - } - - } - return res, same_key -} diff --git a/src/Unif/data_structure.go b/src/Unif/discriminationtree/discrimitation-trees.go similarity index 60% rename from src/Unif/data_structure.go rename to src/Unif/discriminationtree/discrimitation-trees.go index 7678be98..3c7f1cfb 100644 --- a/src/Unif/data_structure.go +++ b/src/Unif/discriminationtree/discrimitation-trees.go @@ -31,34 +31,20 @@ **/ /** -* This file contains functions and types which describe the formula's data - structure +* This file contains all the definitons necessary to make a Code Tree **/ -package Unif +package discriminationtree -import ( - "github.com/GoelandProver/Goeland/AST" - "github.com/GoelandProver/Goeland/Lib" -) +// import ( +// "strings" +// +// "github.com/GoelandProver/Goeland/AST" +// "github.com/GoelandProver/Goeland/Lib" +// "github.com/GoelandProver/Goeland/Unif/substitution" +// ) -type DataStructure interface { - Print() - IsEmpty() bool - MakeDataStruct(Lib.List[AST.Form], bool) DataStructure - InsertFormulaListToDataStructure(Lib.List[AST.Form]) DataStructure +/*************************/ +/* Structures definition */ +/*************************/ - Unify(AST.Form) (bool, []MixedSubstitutions) - UnifyTerm(AST.Term) (bool, []MixedTermSubstitutions) - // FIXME: - // When the unification gets reworked, think a bit more about the exposed interface. - // We want to index on _terms_ while keeping the ability to unify _predicates_. - // (we can easily coerce a predicate to a function) - // We probably want to expose two functions --- one to unify predicates, and the other - // one to unify terms. But maybe we should say that unifying predicates is the "weird" - // case instead of the other way around. - // - // We should also find a more explicit name over `DataStructure`... - - Copy() DataStructure -} diff --git a/src/Unif/substitution/data_structure.go b/src/Unif/substitution/data_structure.go new file mode 100644 index 00000000..4ba18a8f --- /dev/null +++ b/src/Unif/substitution/data_structure.go @@ -0,0 +1,221 @@ +/** +* Copyright 2022 by the authors (see AUTHORS). +* +* Goéland is an automated theorem prover for first order logic. +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited +* liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +**/ + +/** +* This file contains functions and types which describe the formula's data + structure +**/ + +package subst + +import ( + "fmt" + "github.com/GoelandProver/Goeland/AST" + "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Glob" +) + +type DataStructure interface { + Print() + IsEmpty() bool + Copy() DataStructure + MakeDataStruct(Lib.List[AST.Form], bool) DataStructure + InsertFormulaListToDataStructure(Lib.List[AST.Form]) DataStructure + + Unify(AST.Form) (bool, []MixedSubstitutions) + UnifyTerm(AST.Term) (bool, []MixedTermSubstitutions) + // FIXME: + // When the unification gets reworked, think a bit more about the exposed interface. + // We want to index on _terms_ while keeping the ability to unify _predicates_. + // (we can easily coerce a predicate to a function) + // We probably want to expose two functions --- one to unify predicates, and the other + // one to unify terms. But maybe we should say that unifying predicates is the "weird" + // case instead of the other way around. + // + // We should also find a more explicit name over `DataStructure`... -> term indexing structure? UnificationStructure +} + +func TransformPred(p AST.Pred) AST.Term { + return TransformTerm(AST.MakerFun(p.GetID(), p.GetTyArgs(), p.GetArgs())) +} + +func TransformTerm(t AST.Term) AST.Term { + switch term := t.(type) { + case AST.Id, AST.Meta, AST.Var: + return t + case AST.Fun: + args := Lib.ListMap(term.GetTyArgs(), AST.TyToTerm) + args.Append(Lib.ListMap(term.GetArgs(), TransformTerm).GetSlice()...) + return AST.MakerFun( + term.GetID(), + Lib.NewList[AST.Ty](), + args, + ) + } + + Glob.Anomaly("unif parsing", "Unknown term") + return nil +} + +/* Merge two valid substitutions */ +func MergeSubstitutions(s1, s2 Substitutions) (Substitutions, bool) { + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "Merge substitution : %v and %v", + s1.ToString(), + s2.ToString()) + }), + ) + res := Substitutions{} + same_key := false + + if s1.IsEmpty() { + return s2, false + } + + if s2.IsEmpty() { + return s1, false + } + + for _, subst := range s1 { + res.Set(subst.Get()) + } + + for _, subst := range s2 { + s2_k, s2_v := subst.Get() + if HasSubst(res, s2_k) { + same_key = true + res = AddUnification(s2_k.Copy(), s2_v.Copy(), res.Copy()) + } else { + res.Set(s2_k.ToMeta(), s2_v) + EliminateMeta(&res) + Eliminate(&res) + } + + } + return res, same_key +} + + +// robinsonUnify implements Robinson's structural unification algorithm on +// Goeland's term representation. It extends the substitution s in place, +// threading it through recursive calls, and returns Failure() on any clash. +// +// Steps: +// 1. Walk both terms through s to their current representative. +// 2. If they are already identical → nothing to do, return s. +// 3. Meta on either side → occur-check, then bind and propagate via Eliminate. +// 4. Fun / Fun with the same head and arity → recurse on each argument pair. +// 5. Any other combination (different heads, different arities, Var, …) → Failure. +func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { + term1 = walkSubst(term1, s) + term2 = walkSubst(term2, s) + + if term1.Equals(term2) { + return s + } + + switch t1 := term1.(type) { + case AST.Meta: + if !OccurCheckValid(t1, term2) { + return Failure() + } + s.Set(t1, term2) + EliminateMeta(&s) + Eliminate(&s) + return s + + case AST.Fun: + switch t2 := term2.(type) { + case AST.Meta: + if !OccurCheckValid(t2, term1) { + return Failure() + } + s.Set(t2, term1) + EliminateMeta(&s) + Eliminate(&s) + return s + + case AST.Fun: + if !t1.GetID().Equals(t2.GetID()) { + return Failure() + } + args1 := t1.GetArgs().GetSlice() + args2 := t2.GetArgs().GetSlice() + if len(args1) != len(args2) { + return Failure() + } + for i := range args1 { + s = robinsonUnify(args1[i].Copy(), args2[i].Copy(), s) + if s.Equals(Failure()) { + return Failure() + } + } + return s + + default: + return Failure() + } + + default: + // Var or any other term kind: not expected after Skolemisation. + return Failure() + } +} + +// walkSubst chases meta-variable bindings in s until reaching an unbound +// meta or a non-meta term. +func walkSubst(t AST.Term, s Substitutions) AST.Term { + for t.IsMeta() { + val, idx := s.Get(t.ToMeta()) + if idx == -1 { + break + } + t = val + } + return t +} + +func AddUnification(term1, term2 AST.Term, subst Substitutions) Substitutions { + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "Add unification : %v and %v to %v", + term1.ToString(), + term2.ToString(), + subst.ToString()) + }), + ) + return robinsonUnify(term1.Copy(), term2.Copy(), subst.Copy()) +} + \ No newline at end of file diff --git a/src/Unif/matching_substitutions.go b/src/Unif/substitution/matching_substitutions.go similarity index 95% rename from src/Unif/matching_substitutions.go rename to src/Unif/substitution/matching_substitutions.go index 1cedfd57..863ae067 100644 --- a/src/Unif/matching_substitutions.go +++ b/src/Unif/substitution/matching_substitutions.go @@ -34,7 +34,7 @@ * This file provides the necessary structures to manipulate matching substitutions **/ -package Unif +package subst import ( "fmt" @@ -253,7 +253,7 @@ func (m MatchingSubstitutions) Print() { m.GetSubst().Print() } -func (m MatchingSubstitutions) toMixed() MixedSubstitutions { +func (m MatchingSubstitutions) ToMixed() MixedSubstitutions { substs := []MixedSubstitution{} for _, subst := range m.subst { substs = append(substs, translateFromSubst(subst)) @@ -270,25 +270,25 @@ func (m MixedSubstitutions) MatchingSubstitutions() MatchingSubstitutions { } type MixMatchSubstitutions struct { - tof Lib.Either[AST.Term, AST.Form] - subst Substitutions + Tof Lib.Either[AST.Term, AST.Form] + Subst Substitutions } // Pre-requisite: only formulas in the tof func (s MixMatchSubstitutions) toMatching() MatchingSubstitutions { - switch tof := s.tof.(type) { + switch tof := s.Tof.(type) { case Lib.Left[AST.Term, AST.Form]: Glob.Anomaly("unification", "expected unification between formulas, got unification between terms") case Lib.Right[AST.Term, AST.Form]: - return MakeMatchingSubstitutions(tof.Val, s.subst) + return MakeMatchingSubstitutions(tof.Val, s.Subst) } Glob.Anomaly("unification", "reached an unreachable case") return MakeMatchingSubstitutions(AST.MakerTop(), MakeEmptySubstitution()) } -func (s MixMatchSubstitutions) toMixed() MixedSubstitutions { - return s.toMatching().toMixed() +func (s MixMatchSubstitutions) ToMixed() MixedSubstitutions { + return s.toMatching().ToMixed() } type MixedTermSubstitutions struct { @@ -303,11 +303,11 @@ func (s MixedTermSubstitutions) ToString() string { return s.term.ToString() + " {" + Lib.ListToString(substs_list, Lib.WithEmpty("")) + "}" } -func (s MixMatchSubstitutions) toMixedTerm() MixedTermSubstitutions { - switch tof := s.tof.(type) { +func (s MixMatchSubstitutions) ToMixedTerm() MixedTermSubstitutions { + switch tof := s.Tof.(type) { case Lib.Left[AST.Term, AST.Form]: substs := []MixedSubstitution{} - for _, subst := range s.subst { + for _, subst := range s.Subst { substs = append(substs, translateFromSubst(subst)) } return MixedTermSubstitutions{tof.Val, substs} diff --git a/src/Unif/subst_pair.go b/src/Unif/substitution/subst_pair.go similarity index 99% rename from src/Unif/subst_pair.go rename to src/Unif/substitution/subst_pair.go index 6e886564..b8d6f3c1 100644 --- a/src/Unif/subst_pair.go +++ b/src/Unif/substitution/subst_pair.go @@ -34,7 +34,7 @@ * This file provides the necessary structures to manipulate pairs of substitutions. **/ -package Unif +package subst import ( "strconv" diff --git a/src/Unif/substitution.go b/src/Unif/substitution/substitution.go similarity index 94% rename from src/Unif/substitution.go rename to src/Unif/substitution/substitution.go index 0d930699..85c9dcc8 100644 --- a/src/Unif/substitution.go +++ b/src/Unif/substitution/substitution.go @@ -34,12 +34,19 @@ * This file provides the necessary structures to manipulate sustitutions **/ -package Unif +package subst import ( "github.com/GoelandProver/Goeland/AST" + "github.com/GoelandProver/Goeland/Glob" ) +var debug Glob.Debugger + +func InitDebugger() { + debug = Glob.CreateDebugger("Subst") +} + type Substitution struct { k AST.Meta v AST.Term diff --git a/src/Unif/substitutions_type.go b/src/Unif/substitution/substitutions_type.go similarity index 99% rename from src/Unif/substitutions_type.go rename to src/Unif/substitution/substitutions_type.go index 7c43b118..9c93ae3b 100644 --- a/src/Unif/substitutions_type.go +++ b/src/Unif/substitution/substitutions_type.go @@ -34,7 +34,7 @@ * This file provides the necessary structures to manipulate sustitutions **/ -package Unif +package subst import ( "fmt" diff --git a/src/main.go b/src/main.go index 4f51ef7d..44221dd2 100644 --- a/src/main.go +++ b/src/main.go @@ -60,7 +60,8 @@ import ( "github.com/GoelandProver/Goeland/Search" "github.com/GoelandProver/Goeland/Search/incremental" "github.com/GoelandProver/Goeland/Typing" - "github.com/GoelandProver/Goeland/Unif" + "github.com/GoelandProver/Goeland/Unif/codetree" + subst "github.com/GoelandProver/Goeland/Unif/substitution" ) var chAssistant chan bool = make(chan bool) @@ -219,7 +220,8 @@ func initDebuggers() { incremental.InitDebugger() Search.InitDebugger() Typing.InitDebugger() - Unif.InitDebugger() + codetree.InitDebugger() + subst.InitDebugger() Engine.InitDebugger() gs3.InitDebugger() } diff --git a/src/options.go b/src/options.go index 21b8b135..e9fcb1b5 100644 --- a/src/options.go +++ b/src/options.go @@ -425,6 +425,14 @@ func buildOptions() { "Lists the available debuggers and exit", func(bool) { Glob.SetListDebuggers() }, func(bool) {}) + (&option[bool]{}).init( + "dt", + false, + "Use discrimination trees instead of code trees", + func(bool) { + Glob.SetDt() + }, + func(bool) {}) } func chronoInit() { From 892cb1ba818c012f513da202f577e5d3047cc9f9 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Thu, 16 Apr 2026 11:36:41 +0200 Subject: [PATCH 03/23] Basic DiscrminationTree with minimal tests --- go.mod | 3 + .../discrimination-trees.go | 265 ++++++ .../discrimitation-trees.go | 50 -- src/Unif/discriminationtree/dt_test.go | 264 ++++++ src/Unif/discriminationtree/dt_test2.go | 844 ++++++++++++++++++ 5 files changed, 1376 insertions(+), 50 deletions(-) create mode 100644 go.mod create mode 100644 src/Unif/discriminationtree/discrimination-trees.go delete mode 100644 src/Unif/discriminationtree/discrimitation-trees.go create mode 100644 src/Unif/discriminationtree/dt_test.go create mode 100644 src/Unif/discriminationtree/dt_test2.go diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..587f030f --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/GoelandProver/Goeland + +go 1.22.2 diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go new file mode 100644 index 00000000..fc944dac --- /dev/null +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -0,0 +1,265 @@ +/** +* Copyright 2022 by the authors (see AUTHORS). +* +* Goéland is an automated theorem prover for first order logic. +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited +* liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +**/ + +/** +* This file contains all the definitons necessary to make a Code Tree +**/ + +package discriminationtree + +import ( + "strings" + + "github.com/GoelandProver/Goeland/AST" + "github.com/GoelandProver/Goeland/Glob" + "github.com/GoelandProver/Goeland/Lib" +) + +/*************************/ +/* Structures definition */ +/*************************/ + +type SymbolType struct { + symbol AST.Term // Term of the node + arity int // Arity of a node +} + +func (t SymbolType) getSymbol() AST.Term { + return t.symbol +} + +func (t SymbolType) getArity() int { + return t.arity +} + +func (s SymbolType) IsEmpty() bool { + return s.symbol == nil && s.arity == -1 +} + +/* Each node of a CodeTree is composed of a sequence of instruction and its children. If it's a leaf, it has formulaes corresponding to the sequence of instructions. */ +type DiscriminationNode struct { + // Unification tout du long + symbol SymbolType // Variable name or function name + children Lib.List[DiscriminationNode] // All the children of the node + leafFor Lib.List[Lib.Either[AST.Term, AST.Form]] // If not empty, contains the where it come from +} + +// Basic Node with no data inside +func NewNode() DiscriminationNode { + return DiscriminationNode{ + symbol: SymbolType{symbol: nil, arity: -1}, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + } +} + +// Node with data +func MakeNodeWithId(id AST.Id, arity int) DiscriminationNode { + return DiscriminationNode{ + symbol: SymbolType{symbol: id, arity: arity}, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + } +} + +// Arity is 0 because it's a variable +func MakeNodeWithMeta(meta AST.Meta) DiscriminationNode { + return DiscriminationNode{ + symbol: SymbolType{symbol: meta, arity: 0}, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + } +} + +func (dNode DiscriminationNode) isConstant() bool { + return dNode.symbol.getArity() == 0 +} + +func (dNode DiscriminationNode) getArity() int { + return dNode.symbol.getArity() +} + +func (dNode DiscriminationNode) isLeaf() bool { + return dNode.children.Len() == 0 +} + +func (dNode DiscriminationNode) getSymbol() AST.Term { + return dNode.symbol.getSymbol() +} + +func (dNode DiscriminationNode) isEmpty() bool { + return dNode.symbol.IsEmpty() +} + +func (dNode DiscriminationNode) isFun() bool { + return dNode.symbol.getSymbol().IsFun() +} + +func (dNode DiscriminationNode) isMeta() bool { + return dNode.symbol.getSymbol().IsMeta() +} + +func FirstElementToSymbolType(t AST.Term) SymbolType { + switch t := t.(type) { + case AST.Fun: // Case function + return SymbolType{t.GetID(), t.GetArgs().Len()} + case AST.Meta: // Case metaVariable + return SymbolType{t, 0} + default: // Not supposed to see something else + Glob.Anomaly("TermToST", "Var or Id") + return SymbolType{nil, -1} + } +} + +func TermToNode(t AST.Term) DiscriminationNode { + switch t := t.(type) { + case AST.Fun: + children := Lib.NewList[DiscriminationNode]() + for _, c := range t.GetArgs().GetSlice() { + children.Append(TermToNode(c)) + } + // Node with all his children + return DiscriminationNode{FirstElementToSymbolType(t), children, Lib.NewList[Lib.Either[AST.Term, AST.Form]]()} + case AST.Meta: + return DiscriminationNode{FirstElementToSymbolType(t), Lib.NewList[DiscriminationNode](), Lib.NewList[Lib.Either[AST.Term, AST.Form]]()} + default: + Glob.Anomaly("TermToST", "Var or Id") + return NewNode() + } +} + +func (dNode DiscriminationNode) DisplayDiscriminationTree() string { + return dNode.displayRec("") +} + +func (dNode DiscriminationNode) displayRec(indent string) string { + + var b strings.Builder + flag := 0 + + // Print the node + if dNode.isEmpty() { + b.WriteString(indent + "[Root/Empty]\n") + } else { + sym := dNode.getSymbol() + if sym != nil { + b.WriteString(indent + "|-- " + sym.ToString() + "\n") + } + } + // Call the children + for _, child := range dNode.children.GetSlice() { + if child.isMeta() { + if flag == 1 { + b.WriteString(child.displayRec(indent)) + } else { + flag = 1 + b.WriteString(child.displayRec(indent + " ")) + } + } else { + flag = 0 + b.WriteString(child.displayRec(indent + " ")) + } + } + + return b.String() +} + +// Parser for a formula : f(a) -> [f,a] +func SequenceParser(t AST.Term) []AST.Term { + + seq := []AST.Term{t} // Add the node before recursive call + + switch term := t.(type) { + case AST.Fun: // Add all the args of the function + for _, arg := range term.GetArgs().GetSlice() { + seq = append(seq, SequenceParser(arg)...) + } + } + + return seq +} + +func (s SymbolType) Equals(target SymbolType) bool { + + // If the arity is different, no need to go further + if s.arity != target.arity { + return false + } + + // call sig.Equals + return s.symbol.Equals(target.symbol) +} + +func (dNode DiscriminationNode) Insert(t AST.Term) DiscriminationNode { + seq := SequenceParser(t) + return dNode.insertRec(seq, t) +} + +func (dNode DiscriminationNode) insertRec(seq []AST.Term, originalTerm AST.Term) DiscriminationNode { + // End of recursion, time to insert + if len(seq) == 0 { + dNode.leafFor.Append(Lib.MkLeft[AST.Term, AST.Form](originalTerm)) + return dNode + } + + // Create Symbol + sym := FirstElementToSymbolType(seq[0]) + foundIndex := -1 + childrenSlice := dNode.children.GetSlice() + + // Looking for already existing child + for i, child := range childrenSlice { + if child.symbol.Equals(sym) { + foundIndex = i // If we find a match we can end this loop + break + } + } + + // Child already exist + if foundIndex != -1 { + // Insert and update the sequence + updatedChild := childrenSlice[foundIndex].insertRec(seq[1:], originalTerm) + dNode.children.Upd(foundIndex, updatedChild) + + // Child doesn't exist + } else { + // Create new Child + newChild := DiscriminationNode{ + symbol: sym, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + } + updatedChild := newChild.insertRec(seq[1:], originalTerm) // Insert the rest of the sequence after the new child + dNode.children.Append(updatedChild) // Update the children of the args node + } + return dNode // Return updated node +} diff --git a/src/Unif/discriminationtree/discrimitation-trees.go b/src/Unif/discriminationtree/discrimitation-trees.go deleted file mode 100644 index 3c7f1cfb..00000000 --- a/src/Unif/discriminationtree/discrimitation-trees.go +++ /dev/null @@ -1,50 +0,0 @@ -/** -* Copyright 2022 by the authors (see AUTHORS). -* -* Goéland is an automated theorem prover for first order logic. -* -* This software is governed by the CeCILL license under French law and -* abiding by the rules of distribution of free software. You can use, -* modify and/ or redistribute the software under the terms of the CeCILL -* license as circulated by CEA, CNRS and INRIA at the following URL -* "http://www.cecill.info". -* -* As a counterpart to the access to the source code and rights to copy, -* modify and redistribute granted by the license, users are provided only -* with a limited warranty and the software's author, the holder of the -* economic rights, and the successive licensors have only limited -* liability. -* -* In this respect, the user's attention is drawn to the risks associated -* with loading, using, modifying and/or developing or reproducing the -* software by the user in light of its specific status of free software, -* that may mean that it is complicated to manipulate, and that also -* therefore means that it is reserved for developers and experienced -* professionals having in-depth computer knowledge. Users are therefore -* encouraged to load and test the software's suitability as regards their -* requirements in conditions enabling the security of their systems and/or -* data to be ensured and, more generally, to use and operate it in the -* same conditions as regards security. -* -* The fact that you are presently reading this means that you have had -* knowledge of the CeCILL license and that you accept its terms. -**/ - -/** -* This file contains all the definitons necessary to make a Code Tree -**/ - -package discriminationtree - -// import ( -// "strings" -// -// "github.com/GoelandProver/Goeland/AST" -// "github.com/GoelandProver/Goeland/Lib" -// "github.com/GoelandProver/Goeland/Unif/substitution" -// ) - -/*************************/ -/* Structures definition */ -/*************************/ - diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go new file mode 100644 index 00000000..a83c7de7 --- /dev/null +++ b/src/Unif/discriminationtree/dt_test.go @@ -0,0 +1,264 @@ +/** +* Copyright 2022 by the authors (see AUTHORS). +* +* Goéland is an automated theorem prover for first order logic. +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited +* liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +**/ + +package discriminationtree + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/GoelandProver/Goeland/AST" + "github.com/GoelandProver/Goeland/Glob" + "github.com/GoelandProver/Goeland/Lib" + "github.com/GoelandProver/Goeland/Typing" + subst "github.com/GoelandProver/Goeland/Unif/substitution" +) + +// Code trees +//var tp, tn Unif.DataStructure + +// Id +var p_id AST.Id +var g_id AST.Id +var f_id AST.Id +var a_id AST.Id +var b_id AST.Id +var c_id AST.Id +var d_id AST.Id +var c1_id AST.Id +var c2_id AST.Id + +// Meta +var x AST.Meta +var y AST.Meta +var z AST.Meta +var z1 AST.Meta +var z2 AST.Meta +var z3 AST.Meta + +// Const +var a AST.Fun +var b AST.Fun +var c AST.Fun +var d AST.Fun +var c1 AST.Fun +var c2 AST.Fun + +// Fun +var gx AST.Fun +var ga AST.Fun +var fx AST.Fun +var fy AST.Fun +var fa AST.Fun +var fb AST.Fun +var fc AST.Fun + +var ggx AST.Fun +var gga AST.Fun +var gfy AST.Fun +var gfa AST.Fun +var fxy AST.Fun +var fyz AST.Fun +var ffx AST.Fun +var fxa AST.Fun +var fay AST.Fun +var fab AST.Fun +var fbc AST.Fun +var fcd AST.Fun + +var gggx AST.Fun + +var f_fxy_z AST.Fun +var f_x_fyz AST.Fun +var f_fab_c AST.Fun +var f_a_fbc AST.Fun + +// Form +var pggab AST.Form +var pac AST.Form +var pa AST.Form +var pb AST.Form +var not_pc AST.Form +var pab AST.Form +var pax AST.Form +var not_pcd AST.Form + +func initTestVariable() { + // Id + p_id = AST.MakerId("P") + g_id = AST.MakerId("g") + f_id = AST.MakerId("f") + a_id = AST.MakerId("a") + b_id = AST.MakerId("b") + c_id = AST.MakerId("c") + d_id = AST.MakerId("d") + c1_id = AST.MakerId("c1") + c2_id = AST.MakerId("c2") + + // Meta + x = AST.MakerMeta("X", -1, AST.TIndividual()) + y = AST.MakerMeta("Y", -1, AST.TIndividual()) + z = AST.MakerMeta("Z", -1, AST.TIndividual()) + z1 = AST.MakerMeta("Z1", -1, AST.TIndividual()) + z2 = AST.MakerMeta("Z2", -1, AST.TIndividual()) + z3 = AST.MakerMeta("Z3", -1, AST.TIndividual()) + + // Const + a = AST.MakerConst(a_id) + b = AST.MakerConst(b_id) + c = AST.MakerConst(c_id) + d = AST.MakerConst(d_id) + c1 = AST.MakerConst(c1_id) + c2 = AST.MakerConst(c2_id) + + // Fun + gx = AST.MakerFun(g_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) + ga = AST.MakerFun(g_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + fx = AST.MakerFun(f_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) + fy = AST.MakerFun(f_id, Lib.MkListV(y.GetTy()), Lib.MkListV[AST.Term](y)) + fa = AST.MakerFun(f_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + fb = AST.MakerFun(f_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) + fc = AST.MakerFun(f_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c)) + + ggx = AST.MakerFun(g_id, gx.GetTyArgs(), Lib.MkListV[AST.Term](gx)) + gga = AST.MakerFun(g_id, ga.GetTyArgs(), Lib.MkListV[AST.Term](ga)) + gfy = AST.MakerFun(g_id, fy.GetTyArgs(), Lib.MkListV[AST.Term](fy)) + gfa = AST.MakerFun(g_id, fa.GetTyArgs(), Lib.MkListV[AST.Term](fa)) + fxy = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), y.GetTy()), Lib.MkListV[AST.Term](x, y)) + fyz = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), z.GetTy()), Lib.MkListV[AST.Term](x, z)) + ffx = AST.MakerFun(f_id, fx.GetTyArgs(), Lib.MkListV[AST.Term](fx)) + + x_a_type_list := Lib.MkListV[AST.Ty](x.GetTy()) + x_a_type_list.Append(a.GetTyArgs().GetSlice()...) + fxa = AST.MakerFun(f_id, x_a_type_list, Lib.MkListV[AST.Term](x, a)) + + a_y_type_list := a.GetTyArgs() + a_y_type_list.Append(y.GetTy()) + fay = AST.MakerFun(f_id, a_y_type_list, Lib.MkListV[AST.Term](a, y)) + + a_b_type_list := a.GetTyArgs() + a_b_type_list.Append(b.GetTyArgs().GetSlice()...) + fab = AST.MakerFun(f_id, a_b_type_list, Lib.MkListV[AST.Term](a, b)) + + bc_type_list := b.GetTyArgs() + bc_type_list.Append(c.GetTyArgs().GetSlice()...) + fbc = AST.MakerFun(f_id, bc_type_list, Lib.MkListV[AST.Term](b, c)) + + cd_type_list := c.GetTyArgs() + cd_type_list.Append(d.GetTyArgs().GetSlice()...) + fcd = AST.MakerFun(f_id, cd_type_list, Lib.MkListV[AST.Term](c, d)) + + gggx = AST.MakerFun(g_id, ggx.GetTyArgs(), Lib.MkListV[AST.Term](ggx)) + + fxy_z_type_list := fxy.GetTyArgs() + fxy_z_type_list.Append(z.GetTy()) + f_fxy_z = AST.MakerFun(f_id, fxy_z_type_list, Lib.MkListV[AST.Term](fxy, z)) + + x_fyz_type_list := Lib.MkListV[AST.Ty](x.GetTy()) + x_fyz_type_list.Append(fyz.GetTyArgs().GetSlice()...) + f_x_fyz = AST.MakerFun(f_id, x_fyz_type_list, Lib.MkListV[AST.Term](x, fyz)) + + fab_c_type_list := fab.GetTyArgs() + fab_c_type_list.Append(c.GetTyArgs().GetSlice()...) + f_fab_c = AST.MakerFun(f_id, fab_c_type_list, Lib.MkListV[AST.Term](fab, c)) + + a_fbc_type_list := a.GetTyArgs() + a_fbc_type_list.Append(fbc.GetTyArgs().GetSlice()...) + f_a_fbc = AST.MakerFun(f_id, a_fbc_type_list, Lib.MkListV[AST.Term](a, fbc)) + + // Predicates + pggab_type_list := gga.GetTyArgs() + pggab_type_list.Append(b.GetTyArgs().GetSlice()...) + pggab = AST.MakerPred(p_id, pggab_type_list, Lib.MkListV[AST.Term](gga, b)) + + pac_type_list := a.GetTyArgs() + pac_type_list.Append(c.GetTyArgs().GetSlice()...) + pac = AST.MakerNot(AST.MakerPred(p_id, pac_type_list, Lib.MkListV[AST.Term](a, c))) + + pa = AST.MakerPred(p_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + + pb = AST.MakerPred(p_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) + + not_pc = AST.MakerNot(AST.MakerPred(p_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c))) + + pab_type_list := a.GetTyArgs() + pab_type_list.Append(b.GetTyArgs().GetSlice()...) + pab = AST.MakerPred(p_id, pab_type_list, Lib.MkListV[AST.Term](a, b)) + + pax_type_list := a.GetTyArgs() + pax_type_list.Append(x.GetTy()) + pax = AST.MakerPred(p_id, pax_type_list, Lib.MkListV[AST.Term](a, x)) + + not_pcd_type_list := c.GetTyArgs() + not_pcd_type_list.Append(d.GetTyArgs().GetSlice()...) + not_pcd = AST.MakerNot(AST.MakerPred(p_id, not_pcd_type_list, Lib.MkListV[AST.Term](c, d))) +} + +/* +func initCodeTreesTests(lf Lib.List[AST.Form]) (Unif.DataStructure, Unif.DataStructure) { + tp = Unif.NewNode() + tn = Unif.NewNode() + tp = tp.MakeDataStruct(lf, true) + tn = tn.MakeDataStruct(lf, false) + return tp, tn +} +*/ + +func initDebuggers() { + AST.InitDebugger() + Typing.InitDebugger() + subst.InitDebugger() +} + +func TestMain(m *testing.M) { + Glob.SetStart(time.Now()) + initDebuggers() + AST.Init() + Typing.Init() + initTestVariable() + Glob.EnableDebug() + code := m.Run() + os.Exit(code) +} + +func TestPrintDiscriminationTree(t *testing.T) { + tree := NewNode() + tree = tree.Insert(ggx) + fmt.Println(tree.DisplayDiscriminationTree()) + + tree2 := NewNode() + tree2 = tree2.Insert(fxy) + fmt.Println(tree2.DisplayDiscriminationTree()) + +} diff --git a/src/Unif/discriminationtree/dt_test2.go b/src/Unif/discriminationtree/dt_test2.go new file mode 100644 index 00000000..c4a074bb --- /dev/null +++ b/src/Unif/discriminationtree/dt_test2.go @@ -0,0 +1,844 @@ +// /** +// * Copyright 2022 by the authors (see AUTHORS). +// * +// * Goéland is an automated theorem prover for first order logic. +// * +// * This software is governed by the CeCILL license under French law and +// * abiding by the rules of distribution of free software. You can use, +// * modify and/ or redistribute the software under the terms of the CeCILL +// * license as circulated by CEA, CNRS and INRIA at the following URL +// * "http://www.cecill.info". +// * +// * As a counterpart to the access to the source code and rights to copy, +// * modify and redistribute granted by the license, users are provided only +// * with a limited warranty and the software's author, the holder of the +// * economic rights, and the successive licensors have only limited +// * liability. +// * +// * In this respect, the user's attention is drawn to the risks associated +// * with loading, using, modifying and/or developing or reproducing the +// * software by the user in light of its specific status of free software, +// * that may mean that it is complicated to manipulate, and that also +// * therefore means that it is reserved for developers and experienced +// * professionals having in-depth computer knowledge. Users are therefore +// * encouraged to load and test the software's suitability as regards their +// * requirements in conditions enabling the security of their systems and/or +// * data to be ensured and, more generally, to use and operate it in the +// * same conditions as regards security. +// * +// * The fact that you are presently reading this means that you have had +// * knowledge of the CeCILL license and that you accept its terms. +// **/ + +package discriminationtree + +// import ( +// "fmt" +// "os" +// "testing" +// "time" + +// "github.com/GoelandProver/Goeland/AST" +// "github.com/GoelandProver/Goeland/Glob" +// "github.com/GoelandProver/Goeland/Lib" +// "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" +// "github.com/GoelandProver/Goeland/Typing" +// "github.com/GoelandProver/Goeland/Unif" +// ) + +// // Code trees +// //var tp, tn Unif.DataStructure + +// // Id +// var p_id AST.Id +// var g_id AST.Id +// var f_id AST.Id +// var a_id AST.Id +// var b_id AST.Id +// var c_id AST.Id +// var d_id AST.Id +// var c1_id AST.Id +// var c2_id AST.Id + +// // Meta +// var x AST.Meta +// var y AST.Meta +// var z AST.Meta +// var z1 AST.Meta +// var z2 AST.Meta +// var z3 AST.Meta + +// // Const +// var a AST.Fun +// var b AST.Fun +// var c AST.Fun +// var d AST.Fun +// var c1 AST.Fun +// var c2 AST.Fun + +// // Fun +// var gx AST.Fun +// var ga AST.Fun +// var fx AST.Fun +// var fy AST.Fun +// var fa AST.Fun +// var fb AST.Fun +// var fc AST.Fun + +// var ggx AST.Fun +// var gga AST.Fun +// var gfy AST.Fun +// var gfa AST.Fun +// var fxy AST.Fun +// var fyz AST.Fun +// var ffx AST.Fun +// var fxa AST.Fun +// var fay AST.Fun +// var fab AST.Fun +// var fbc AST.Fun +// var fcd AST.Fun + +// var gggx AST.Fun + +// var f_fxy_z AST.Fun +// var f_x_fyz AST.Fun +// var f_fab_c AST.Fun +// var f_a_fbc AST.Fun + +// // Equalities +// var eq_x_y AST.Pred +// var eq_x_a AST.Pred +// var eq_y_a AST.Pred +// var eq_z1_c1 AST.Pred +// var eq_z1_c2 AST.Pred +// var eq_z2_c1 AST.Pred +// var eq_z3_c1 AST.Pred +// var eq_gx_fx AST.Pred +// var eq_ggx_fa AST.Pred +// var eq_gfy_y AST.Pred +// var eq_fa_a AST.Pred +// var eq_b_c AST.Pred +// var eq_a_b AST.Pred +// var eq_a_c AST.Pred +// var eq_b_d AST.Pred +// var eq_x_d AST.Pred + +// // Inequalites +// var neq_x_a AST.Form +// var neq_a_b AST.Form +// var neq_a_d AST.Form +// var neq_gggx_x AST.Form +// var neq_fx_a AST.Form +// var neq_fx_x AST.Form +// var neq_fab_fcd AST.Form +// var neq_fb_fc AST.Form + +// // Form +// var pggab AST.Form +// var pac AST.Form +// var pa AST.Form +// var pb AST.Form +// var not_pc AST.Form +// var pab AST.Form +// var pax AST.Form +// var not_pcd AST.Form + +// func initTestVariable() { +// // Id +// p_id = AST.MakerId("P") +// g_id = AST.MakerId("g") +// f_id = AST.MakerId("f") +// a_id = AST.MakerId("a") +// b_id = AST.MakerId("b") +// c_id = AST.MakerId("c") +// d_id = AST.MakerId("d") +// c1_id = AST.MakerId("c1") +// c2_id = AST.MakerId("c2") + +// // Meta +// x = AST.MakerMeta("X", -1, AST.TIndividual()) +// y = AST.MakerMeta("Y", -1, AST.TIndividual()) +// z = AST.MakerMeta("Z", -1, AST.TIndividual()) +// z1 = AST.MakerMeta("Z1", -1, AST.TIndividual()) +// z2 = AST.MakerMeta("Z2", -1, AST.TIndividual()) +// z3 = AST.MakerMeta("Z3", -1, AST.TIndividual()) + +// // Const +// a = AST.MakerConst(a_id) +// b = AST.MakerConst(b_id) +// c = AST.MakerConst(c_id) +// d = AST.MakerConst(d_id) +// c1 = AST.MakerConst(c1_id) +// c2 = AST.MakerConst(c2_id) + +// // Fun +// gx = AST.MakerFun(g_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) +// ga = AST.MakerFun(g_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) +// fx = AST.MakerFun(f_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) +// fy = AST.MakerFun(f_id, Lib.MkListV(y.GetTy()), Lib.MkListV[AST.Term](y)) +// fa = AST.MakerFun(f_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) +// fb = AST.MakerFun(f_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) +// fc = AST.MakerFun(f_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c)) + +// ggx = AST.MakerFun(g_id, gx.GetTyArgs(), Lib.MkListV[AST.Term](gx)) +// gga = AST.MakerFun(g_id, ga.GetTyArgs(), Lib.MkListV[AST.Term](ga)) +// gfy = AST.MakerFun(g_id, fy.GetTyArgs(), Lib.MkListV[AST.Term](fy)) +// gfa = AST.MakerFun(g_id, fa.GetTyArgs(), Lib.MkListV[AST.Term](fa)) +// fxy = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), y.GetTy()), Lib.MkListV[AST.Term](x, y)) +// fyz = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), z.GetTy()), Lib.MkListV[AST.Term](x, z)) +// ffx = AST.MakerFun(f_id, fx.GetTyArgs(), Lib.MkListV[AST.Term](fx)) + +// x_a_type_list := Lib.MkListV[AST.Ty](x.GetTy()) +// x_a_type_list.Append(a.GetTyArgs().GetSlice()...) +// fxa = AST.MakerFun(f_id, x_a_type_list, Lib.MkListV[AST.Term](x, a)) + +// a_y_type_list := a.GetTyArgs() +// a_y_type_list.Append(y.GetTy()) +// fay = AST.MakerFun(f_id, a_y_type_list, Lib.MkListV[AST.Term](a, y)) + +// a_b_type_list := a.GetTyArgs() +// a_b_type_list.Append(b.GetTyArgs().GetSlice()...) +// fab = AST.MakerFun(f_id, a_b_type_list, Lib.MkListV[AST.Term](a, b)) + +// bc_type_list := b.GetTyArgs() +// bc_type_list.Append(c.GetTyArgs().GetSlice()...) +// fbc = AST.MakerFun(f_id, bc_type_list, Lib.MkListV[AST.Term](b, c)) + +// cd_type_list := c.GetTyArgs() +// cd_type_list.Append(d.GetTyArgs().GetSlice()...) +// fcd = AST.MakerFun(f_id, cd_type_list, Lib.MkListV[AST.Term](c, d)) + +// gggx = AST.MakerFun(g_id, ggx.GetTyArgs(), Lib.MkListV[AST.Term](ggx)) + +// fxy_z_type_list := fxy.GetTyArgs() +// fxy_z_type_list.Append(z.GetTy()) +// f_fxy_z = AST.MakerFun(f_id, fxy_z_type_list, Lib.MkListV[AST.Term](fxy, z)) + +// x_fyz_type_list := Lib.MkListV[AST.Ty](x.GetTy()) +// x_fyz_type_list.Append(fyz.GetTyArgs().GetSlice()...) +// f_x_fyz = AST.MakerFun(f_id, x_fyz_type_list, Lib.MkListV[AST.Term](x, fyz)) + +// fab_c_type_list := fab.GetTyArgs() +// fab_c_type_list.Append(c.GetTyArgs().GetSlice()...) +// f_fab_c = AST.MakerFun(f_id, fab_c_type_list, Lib.MkListV[AST.Term](fab, c)) + +// a_fbc_type_list := a.GetTyArgs() +// a_fbc_type_list.Append(fbc.GetTyArgs().GetSlice()...) +// f_a_fbc = AST.MakerFun(f_id, a_fbc_type_list, Lib.MkListV[AST.Term](a, fbc)) + +// // Equalities + +// eq_x_y = AST.MakerPred(AST.Id_eq, Lib.MkListV(x.GetTy(), y.GetTy()), Lib.MkListV[AST.Term](x, y)) +// eq_x_a = AST.MakerPred(AST.Id_eq, x_a_type_list, Lib.MkListV[AST.Term](x, a)) + +// y_a_type_list := Lib.MkListV[AST.Ty](y.GetTy()) +// y_a_type_list.Append(a.GetTyArgs().GetSlice()...) +// eq_y_a = AST.MakerPred(AST.Id_eq, y_a_type_list, Lib.MkListV[AST.Term](y, a)) + +// z_c1_type_list := Lib.MkListV[AST.Ty](z.GetTy()) +// z_c1_type_list.Append(c1.GetTyArgs().GetSlice()...) + +// eq_z1_c1 = AST.MakerPred(AST.Id_eq, z_c1_type_list, Lib.MkListV[AST.Term](z, c1)) + +// z1_c2_type_list := Lib.MkListV[AST.Ty](z1.GetTy()) +// z1_c2_type_list.Append(c2.GetTyArgs().GetSlice()...) +// eq_z1_c2 = AST.MakerPred(AST.Id_eq, z1_c2_type_list, Lib.MkListV[AST.Term](z1, c2)) + +// z2_c1_type_list := Lib.MkListV[AST.Ty](z2.GetTy()) +// z2_c1_type_list.Append(c1.GetTyArgs().GetSlice()...) +// eq_z2_c1 = AST.MakerPred(AST.Id_eq, z2_c1_type_list, Lib.MkListV[AST.Term](z2, c1)) + +// z3_c1_type_list := Lib.MkListV[AST.Ty](z3.GetTy()) +// z3_c1_type_list.Append(c1.GetTyArgs().GetSlice()...) +// eq_z3_c1 = AST.MakerPred(AST.Id_eq, z3_c1_type_list, Lib.MkListV[AST.Term](z3, c1)) + +// ggx_fa_type_list := ggx.GetTyArgs() +// ggx_fa_type_list.Append(fa.GetTyArgs().GetSlice()...) +// eq_ggx_fa = AST.MakerPred(AST.Id_eq, ggx_fa_type_list, Lib.MkListV[AST.Term](ggx, fa)) + +// gfy_y_type_list := gfy.GetTyArgs() +// gfy_y_type_list.Append(y.GetTy()) +// eq_gfy_y = AST.MakerPred(AST.Id_eq, gfy_y_type_list, Lib.MkListV[AST.Term](gfy, y)) + +// gx_fx_type_list := gx.GetTyArgs() +// gx_fx_type_list.Append(fx.GetTyArgs().GetSlice()...) +// eq_gx_fx = AST.MakerPred(AST.Id_eq, gx_fx_type_list, Lib.MkListV[AST.Term](gx, fx)) + +// fa_a_type_list := fa.GetTyArgs() +// fa_a_type_list.Append(a.GetTyArgs().GetSlice()...) +// eq_fa_a = AST.MakerPred(AST.Id_eq, fa_a_type_list, Lib.MkListV[AST.Term](fa, a)) + +// a_b_type_list2 := a.GetTyArgs() +// a_b_type_list2.Append(b.GetTyArgs().GetSlice()...) +// eq_a_b = AST.MakerPred(AST.Id_eq, a_b_type_list2, Lib.MkListV[AST.Term](a, b)) + +// b_c_type_list := b.GetTyArgs() +// b_c_type_list.Append(c.GetTyArgs().GetSlice()...) +// eq_b_c = AST.MakerPred(AST.Id_eq, b_c_type_list, Lib.MkListV[AST.Term](b, c)) + +// a_c_type_list := a.GetTyArgs() +// a_c_type_list.Append(c.GetTyArgs().GetSlice()...) +// eq_a_c = AST.MakerPred(AST.Id_eq, a_c_type_list, Lib.MkListV[AST.Term](a, c)) + +// b_d_type_list := b.GetTyArgs() +// b_d_type_list.Append(d.GetTyArgs().GetSlice()...) +// eq_b_d = AST.MakerPred(AST.Id_eq, b_d_type_list, Lib.MkListV[AST.Term](b, d)) + +// x_d_type_list := Lib.MkListV[AST.Ty](x.GetTy()) +// x_d_type_list.Append(d.GetTyArgs().GetSlice()...) +// eq_x_d = AST.MakerPred(AST.Id_eq, x_d_type_list, Lib.MkListV[AST.Term](x, d)) + +// // Inequalities +// neq_x_a_type_list := Lib.MkListV[AST.Ty](x.GetTy()) +// neq_x_a_type_list.Append(a.GetTyArgs().GetSlice()...) +// neq_x_a = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_x_a_type_list, Lib.MkListV[AST.Term](x, a))) + +// neq_a_b_type_list := a.GetTyArgs() +// neq_a_b_type_list.Append(b.GetTyArgs().GetSlice()...) +// neq_a_b = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_a_b_type_list, Lib.MkListV[AST.Term](a, b))) + +// neq_a_d_type_list := a.GetTyArgs() +// neq_a_d_type_list.Append(d.GetTyArgs().GetSlice()...) +// neq_a_d = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_a_d_type_list, Lib.MkListV[AST.Term](a, d))) + +// neq_gggx_x_type_list := gggx.GetTyArgs() +// neq_gggx_x_type_list.Append(x.GetTy()) +// neq_gggx_x = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_gggx_x_type_list, Lib.MkListV[AST.Term](gggx, x))) + +// neq_fx_a_type_list := fx.GetTyArgs() +// neq_fx_a_type_list.Append(a.GetTyArgs().GetSlice()...) +// neq_fx_a = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fx_a_type_list, Lib.MkListV[AST.Term](fx, a))) + +// neq_fx_x_type_list := fx.GetTyArgs() +// neq_fx_x_type_list.Append(x.GetTy()) +// neq_fx_x = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fx_x_type_list, Lib.MkListV[AST.Term](fx, x))) + +// neq_fab_fcd_type_list := fab.GetTyArgs() +// neq_fab_fcd_type_list.Append(fcd.GetTyArgs().GetSlice()...) +// neq_fab_fcd = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fab_fcd_type_list, Lib.MkListV[AST.Term](fab, fcd))) + +// neq_fb_fc_type_list := fb.GetTyArgs() +// neq_fb_fc_type_list.Append(fc.GetTyArgs().GetSlice()...) +// neq_fb_fc = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fb_fc_type_list, Lib.MkListV[AST.Term](fb, fc))) + +// // Predicates +// pggab_type_list := gga.GetTyArgs() +// pggab_type_list.Append(b.GetTyArgs().GetSlice()...) +// pggab = AST.MakerPred(p_id, pggab_type_list, Lib.MkListV[AST.Term](gga, b)) + +// pac_type_list := a.GetTyArgs() +// pac_type_list.Append(c.GetTyArgs().GetSlice()...) +// pac = AST.MakerNot(AST.MakerPred(p_id, pac_type_list, Lib.MkListV[AST.Term](a, c))) + +// pa = AST.MakerPred(p_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + +// pb = AST.MakerPred(p_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) + +// not_pc = AST.MakerNot(AST.MakerPred(p_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c))) + +// pab_type_list := a.GetTyArgs() +// pab_type_list.Append(b.GetTyArgs().GetSlice()...) +// pab = AST.MakerPred(p_id, pab_type_list, Lib.MkListV[AST.Term](a, b)) + +// pax_type_list := a.GetTyArgs() +// pax_type_list.Append(x.GetTy()) +// pax = AST.MakerPred(p_id, pax_type_list, Lib.MkListV[AST.Term](a, x)) + +// not_pcd_type_list := c.GetTyArgs() +// not_pcd_type_list.Append(d.GetTyArgs().GetSlice()...) +// not_pcd = AST.MakerNot(AST.MakerPred(p_id, not_pcd_type_list, Lib.MkListV[AST.Term](c, d))) +// } + +// func initCodeTreesTests(lf Lib.List[AST.Form]) (Unif.DataStructure, Unif.DataStructure) { +// tp = Unif.NewNode() +// tn = Unif.NewNode() +// tp = tp.MakeDataStruct(lf, true) +// tn = tn.MakeDataStruct(lf, false) +// return tp, tn +// } + +// func initDebuggers() { +// AST.InitDebugger() +// InitDebugger() +// Typing.InitDebugger() +// Unif.InitDebugger() +// } + +// func TestMain(m *testing.M) { +// Glob.SetStart(time.Now()) +// initDebuggers() +// AST.Init() +// Typing.Init() +// initTestVariable() +// Glob.EnableDebug() +// code := m.Run() +// os.Exit(code) +// } + +// /* Test apply substitution */ + +// func TestAS(t *testing.T) { +// /** +// * Problème : <[X = Y], X, Y> +// * Substitution : (Y, a) +// **/ + +// // Original problem +// lf := Lib.MkListV[AST.Form](eq_x_y) +// tp, tn = initCodeTreesTests(lf) +// eq := retrieveEqualities(tp.Copy()) +// ep := makeEqualityProblem(eq, x, y, makeEmptyConstraintStruct()) + +// // Expected problem +// lf2 := Lib.MkListV[AST.Form](eq_x_a) +// tp, tn = initCodeTreesTests(lf2) +// eq2 := retrieveEqualities(tp.Copy()) +// expected_ep := makeEqualityProblem(eq2, x, a, makeEmptyConstraintStruct()) + +// s := Unif.MakeEmptySubstitution() +// s.Set(y, a) +// new_ep := ep.applySubstitution(s) + +// debug( +// Lib.MkLazy(func() string { return fmt.Sprintf("Current EP : %v", new_ep.ToString()) }), +// ) + +// debug( +// Lib.MkLazy(func() string { return fmt.Sprintf("Expected : %v", expected_ep.ToString()) }), +// ) +// } + +// /*** Test constraints ***/ +// func TestConstraints1(t *testing.T) { +// /* Not consistent */ +// tp_ffx_x := eqStruct.MakeTermPair(ffx, x) +// constraint_ffx_x := MakeConstraint(PREC, tp_ffx_x) +// cs := makeEmptyConstraintStruct() +// append := cs.appendIfConsistent(constraint_ffx_x) + +// if append || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected not consistent and empty PREC list", append, cs.getPrec().toString()) +// } +// } + +// func TestConstraints2(t *testing.T) { +// /* Consistent but useless */ +// tp_x_ffx := eqStruct.MakeTermPair(x, ffx) +// constraint_x_ffx := MakeConstraint(PREC, tp_x_ffx) +// cs := makeEmptyConstraintStruct() +// append := cs.appendIfConsistent(constraint_x_ffx) + +// if !append || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) +// } +// } + +// func TestConstraints3(t *testing.T) { +// /* Consistent and relevant */ + +// tp_fx_a := eqStruct.MakeTermPair(fx, a) +// constraint_fx_a := MakeConstraint(PREC, tp_fx_a) +// cs := makeEmptyConstraintStruct() + +// append := cs.appendIfConsistent(constraint_fx_a) +// if !append || len(cs.getPrec()) != 1 || !cs.getPrec()[0].equals(constraint_fx_a) { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and %v", append, cs.getPrec().toString(), constraint_fx_a.toString()) +// } +// } + +// func TestConstraints4(t *testing.T) { +// /* First constraint is consistent, second is not consistent with the first one */ +// /* +// * On accepte les cas comme f(f(x)) < a et a < f(x) +// */ + +// tp_fx_a := eqStruct.MakeTermPair(fx, a) +// constraint_fx_a := MakeConstraint(PREC, tp_fx_a) +// cs := makeEmptyConstraintStruct() + +// res_constraint_1 := cs.appendIfConsistent(constraint_fx_a) +// if !res_constraint_1 || len(cs.getPrec()) != 1 || !cs.getPrec()[0].equals(constraint_fx_a) { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and %v", res_constraint_1, cs.getPrec().toString(), constraint_fx_a.toString()) +// } + +// tp_a_fx := eqStruct.MakeTermPair(a, fx) +// constraint_a_fx := MakeConstraint(PREC, tp_a_fx) +// res_constraint_2 := cs.appendIfConsistent(constraint_a_fx) +// if res_constraint_2 || len(cs.getPrec()) != 1 || !cs.getPrec()[0].equals(constraint_fx_a) { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected not consistent and %v", res_constraint_2, cs.getPrec().toString(), constraint_fx_a.toString()) +// } + +// } + +// func TestConstraints5(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// /* Not consistent */ +// tp_ffabc_fafbc := eqStruct.MakeTermPair(f_fab_c, f_a_fbc) +// constraint_ffabc_fafbc := MakeConstraint(PREC, tp_ffabc_fafbc) +// res_constraint_1 := cs.appendIfConsistent(constraint_ffabc_fafbc) +// if res_constraint_1 || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected not consistent and empty PREC list", res_constraint_1, cs.getPrec().toString()) +// } + +// /* Consistent but not relevant */ +// tp_fafbc_ffabc := eqStruct.MakeTermPair(f_a_fbc, f_fab_c) +// constraint_fafbc_ffabc := MakeConstraint(PREC, tp_fafbc_ffabc) +// res_constraint_2 := cs.appendIfConsistent(constraint_fafbc_ffabc) +// if !res_constraint_2 || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", res_constraint_1, cs.getPrec().toString()) +// } +// } + +// func TestConstaintes6(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// /* consistent but not relevant */ +// tp_fxfyz_ffxyz := eqStruct.MakeTermPair(f_x_fyz, f_fxy_z) +// constraint_fafbc_ffabc := MakeConstraint(PREC, tp_fxfyz_ffxyz) +// append := cs.appendIfConsistent(constraint_fafbc_ffabc) +// if !append || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) +// } +// } + +// func TestConstaintes7(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// /* consistent, should return X,a and Y, b */ +// tp_fxy_fab := eqStruct.MakeTermPair(fxy, fab) +// constraint_fxy_fab := MakeConstraint(EQ, tp_fxy_fab) +// // append := +// cs.appendIfConsistent(constraint_fxy_fab) +// /* +// if !append || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) +// } +// */ +// } + +// func TestConstaintes8(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// /* consistent, should return X,a and Y, b */ +// tp_fxa_fay := eqStruct.MakeTermPair(fxa, fay) +// constraint_fxa_fay := MakeConstraint(EQ, tp_fxa_fay) +// // append := +// cs.appendIfConsistent(constraint_fxa_fay) +// /* +// if !append || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) +// } +// */ +// } + +// func TestConstaintes9(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// /* consistent, should return X,a and Y, b */ +// tp_gga_ggx := eqStruct.MakeTermPair(gga, ggx) +// constraint_gga_ggx := MakeConstraint(PREC, tp_gga_ggx) +// // append := +// cs.appendIfConsistent(constraint_gga_ggx) +// /* +// if !append || len(cs.getPrec()) > 0 { +// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) +// } +// */ +// } + +// // --------------------------------------------------------------------------- +// // LPO / PREC edge cases +// // --------------------------------------------------------------------------- + +// // Two identical deferred constraints: the second must be accepted (idempotent). +// // f(X) ≺ a added twice → still only one entry in prec list. +// func TestConstraints_Idempotent(t *testing.T) { +// tp_fx_a := eqStruct.MakeTermPair(fx, a) +// c := MakeConstraint(PREC, tp_fx_a) +// cs := makeEmptyConstraintStruct() + +// res1 := cs.appendIfConsistent(c) +// res2 := cs.appendIfConsistent(c) // duplicate + +// if !res1 || !res2 { +// t.Fatalf("Both insertions should return true for a duplicate, got %v %v", res1, res2) +// } +// if len(cs.getPrec()) != 1 { +// t.Fatalf("Duplicate constraint should not grow the prec list; got %v", cs.getPrec().toString()) +// } +// } + +// // Two distinct deferred constraints that are compatible: both must be accepted. +// // f(X) ≺ a and g(Y) ≺ b — different metas, no conflict. +// func TestConstraints_TwoCompatibleDeferred(t *testing.T) { +// c1 := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) +// c2 := MakeConstraint(PREC, eqStruct.MakeTermPair(fy, b)) +// cs := makeEmptyConstraintStruct() + +// if !cs.appendIfConsistent(c1) { +// t.Fatalf("c1 should be consistent") +// } +// if !cs.appendIfConsistent(c2) { +// t.Fatalf("c2 should be consistent with c1") +// } +// if len(cs.getPrec()) != 2 { +// t.Fatalf("Expected 2 deferred constraints, got %v", cs.getPrec().toString()) +// } +// } + +// // g(g(g(X))) ≺ X is an occur-check violation in LPO (X appears inside gggx). +// // Must be rejected. +// func TestConstraints_OccurCheckPREC(t *testing.T) { +// tp := eqStruct.MakeTermPair(gggx, x) +// c := MakeConstraint(PREC, tp) +// cs := makeEmptyConstraintStruct() + +// if cs.appendIfConsistent(c) { +// t.Fatalf("ggg(X) ≺ X should be rejected (occur-check)") +// } +// } + +// // Ground PREC that is trivially satisfied and does not interact with any +// // deferred constraint: a ≺ f(a). Pure ground, f > a, no metas. +// // Expected: consistent, not added to prec list (ground/comparable). +// func TestConstraints_GroundSatisfied(t *testing.T) { +// tp := eqStruct.MakeTermPair(a, fa) +// c := MakeConstraint(PREC, tp) +// cs := makeEmptyConstraintStruct() + +// if !cs.appendIfConsistent(c) { +// t.Fatalf("a ≺ f(a) should be consistent (ground, f>a)") +// } +// if len(cs.getPrec()) != 0 { +// t.Fatalf("Ground comparable constraint should not be deferred; prec=%v", cs.getPrec().toString()) +// } +// } + +// // Ground PREC that is violated: f(a) ≺ a. f > a, so f(a) > a in LPO. +// // Expected: rejected. +// func TestConstraints_GroundViolated(t *testing.T) { +// tp := eqStruct.MakeTermPair(fa, a) +// c := MakeConstraint(PREC, tp) +// cs := makeEmptyConstraintStruct() + +// if cs.appendIfConsistent(c) { +// t.Fatalf("f(a) ≺ a should be rejected (ground, f>a so f(a)>a)") +// } +// } + +// // Three-way cycle: X ≺ f(X) is fine, but then adding f(X) ≺ X must fail. +// func TestConstraints_Cycle(t *testing.T) { +// c_x_fx := MakeConstraint(PREC, eqStruct.MakeTermPair(x, fx)) +// c_fx_x := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, x)) +// cs := makeEmptyConstraintStruct() + +// // X ≺ f(X): X occurs inside f(X), so this is detected as comparable and +// // satisfied (occur-check direction), prec list stays empty. +// if !cs.appendIfConsistent(c_x_fx) { +// t.Fatalf("X ≺ f(X) should be consistent") +// } +// // f(X) ≺ X: occur-check in reverse → must be rejected. +// if cs.appendIfConsistent(c_fx_x) { +// t.Fatalf("f(X) ≺ X should be rejected after X ≺ f(X)") +// } +// } + +// // --------------------------------------------------------------------------- +// // EQ edge cases +// // --------------------------------------------------------------------------- + +// // EQ constraint with already-equal ground terms: a ≃ a → trivially consistent. +// func TestConstraintsEQ_SameTerm(t *testing.T) { +// c := MakeConstraint(EQ, eqStruct.MakeTermPair(a, a)) +// cs := makeEmptyConstraintStruct() + +// if !cs.appendIfConsistent(c) { +// t.Fatalf("a ≃ a should be consistent") +// } +// } + +// // EQ constraint between two distinct ground constants: a ≃ b → not unifiable. +// func TestConstraintsEQ_GroundConflict(t *testing.T) { +// c := MakeConstraint(EQ, eqStruct.MakeTermPair(a, b)) +// cs := makeEmptyConstraintStruct() + +// if cs.appendIfConsistent(c) { +// t.Fatalf("a ≃ b should be rejected (a ≠ b ground)") +// } +// } + +// // EQ constraint X ≃ a followed by a PREC constraint f(X) ≺ a. +// // After substituting X→a, f(X) becomes f(a), and f(a) ≺ a is ground-violated. +// // Expected: the PREC is rejected. +// func TestConstraints_EQThenPREC_Conflict(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) +// if !cs.appendIfConsistent(cEQ) { +// t.Fatalf("X ≃ a should be accepted") +// } + +// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) +// if cs.appendIfConsistent(cPREC) { +// t.Fatalf("f(X) ≺ a with X→a means f(a) ≺ a, which is violated — should be rejected") +// } +// } + +// // EQ constraint X ≃ a followed by a PREC constraint a ≺ f(X). +// // After substituting X→a, a ≺ f(a) is ground-satisfied. +// // Expected: the PREC is accepted. +// func TestConstraints_EQThenPREC_Satisfied(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) +// if !cs.appendIfConsistent(cEQ) { +// t.Fatalf("X ≃ a should be accepted") +// } + +// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(a, fx)) +// if !cs.appendIfConsistent(cPREC) { +// t.Fatalf("a ≺ f(X) with X→a means a ≺ f(a), which is satisfied — should be accepted") +// } +// } + +// // Deferred PREC f(X) ≺ a, then EQ X ≃ a. +// // Applying X→a to the deferred constraint gives f(a) ≺ a — violated. +// // The EQ must be rejected because it breaks the stored PREC constraint. +// func TestConstraints_PRECThenEQ_Conflict(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) +// if !cs.appendIfConsistent(cPREC) { +// t.Fatalf("f(X) ≺ a should be deferred") +// } + +// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) +// if cs.appendIfConsistent(cEQ) { +// t.Fatalf("X ≃ a should be rejected: it instantiates f(X) ≺ a to f(a) ≺ a which is violated") +// } +// } + +// // Two conflicting EQ constraints: X ≃ a then X ≃ b. +// // Second should be rejected because the substitution already maps X to a. +// func TestConstraintsEQ_ConflictingSubst(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// c1 := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) +// c2 := MakeConstraint(EQ, eqStruct.MakeTermPair(x, b)) + +// if !cs.appendIfConsistent(c1) { +// t.Fatalf("X ≃ a should be accepted") +// } +// if cs.appendIfConsistent(c2) { +// t.Fatalf("X ≃ b should be rejected: X is already bound to a") +// } +// } + +// // Two compatible EQ constraints on different metas: X ≃ a then Y ≃ b. +// // Both should be accepted and the substitution should contain both bindings. +// func TestConstraintsEQ_CompatibleSubst(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// c1 := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) +// c2 := MakeConstraint(EQ, eqStruct.MakeTermPair(y, b)) + +// if !cs.appendIfConsistent(c1) { +// t.Fatalf("X ≃ a should be accepted") +// } +// if !cs.appendIfConsistent(c2) { +// t.Fatalf("Y ≃ b should be accepted alongside X ≃ a") +// } + +// s := cs.getSubst() +// xBound := false +// yBound := false +// for _, pair := range s { +// m, t := pair.Get() +// if m.Equals(x) && t.Equals(a) { +// xBound = true +// } +// if m.Equals(y) && t.Equals(b) { +// yBound = true +// } +// } +// if !xBound || !yBound { +// t.Fatalf("Expected substitution {X→a, Y→b}, got %v", s.ToString()) +// } +// } + +// // Substitution applied to a PREC that remains comparable after instantiation, +// // but in the satisfying direction: deferred f(X) ≺ g(a), then X ≃ a. +// // After X→a: f(a) ≺ g(a). f < g so f(a) < g(a) in LPO — satisfied. +// // Expected: EQ accepted, prec list cleared (constraint resolved). +// func TestConstraints_PRECResolvedByEQ(t *testing.T) { +// cs := makeEmptyConstraintStruct() + +// // f(X) ≺ g(a): f < g, but X is free → deferred +// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, ga)) +// if !cs.appendIfConsistent(cPREC) { +// t.Fatalf("f(X) ≺ g(a) should be deferred as consistent") +// } +// if len(cs.getPrec()) != 1 { +// t.Fatalf("f(X) ≺ g(a) should be in the prec list, got %v", cs.getPrec().toString()) +// } + +// // X ≃ a: should be accepted; after applying, the deferred PREC is satisfied. +// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) +// if !cs.appendIfConsistent(cEQ) { +// t.Fatalf("X ≃ a should be accepted; it resolves f(X) ≺ g(a) to f(a) ≺ g(a) which holds") +// } +// } + +// // Empty constraint struct — isEmpty must hold. +// func TestConstraintStruct_Empty(t *testing.T) { +// cs := makeEmptyConstraintStruct() +// if !cs.isEmpty() { +// t.Fatalf("Fresh constraint struct should be empty") +// } +// } + +// // After a successful PREC insertion the struct is no longer empty. +// func TestConstraintStruct_NotEmptyAfterInsert(t *testing.T) { +// cs := makeEmptyConstraintStruct() +// c := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) +// cs.appendIfConsistent(c) +// if cs.isEmpty() { +// t.Fatalf("Struct should not be empty after inserting a deferred constraint") +// } +// } + +// // copy() must produce a deep copy: mutating the copy must not affect the original. +// func TestConstraintStruct_Copy(t *testing.T) { +// cs := makeEmptyConstraintStruct() +// c := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) +// cs.appendIfConsistent(c) + +// csCopy := cs.copy() + +// // Add a new constraint only to the copy. +// c2 := MakeConstraint(PREC, eqStruct.MakeTermPair(fy, b)) +// csCopy.appendIfConsistent(c2) + +// if len(cs.getPrec()) != 1 { +// t.Fatalf("Original prec list should still have 1 element after mutating the copy; got %v", cs.getPrec().toString()) +// } +// if len(csCopy.getPrec()) != 2 { +// t.Fatalf("Copy prec list should have 2 elements; got %v", csCopy.getPrec().toString()) +// } +// } + +// // A substitution that maps X to itself (identity) should be treated as empty/trivial. +// func TestConstraintsEQ_IdentitySubst(t *testing.T) { +// cs := makeEmptyConstraintStruct() +// s := Unif.MakeEmptySubstitution() +// s.Set(x, x) +// cs.setSubst(s) + +// // f(X) ≺ a with a substitution that maps X→X: effectively no change. +// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) +// if !cs.appendIfConsistent(cPREC) { +// t.Fatalf("f(X) ≺ a should still be deferred as consistent with identity subst") +// } + +// } From db79bdd16d7c043dd25bb30b9f867c2ecbf837c8 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Thu, 23 Apr 2026 16:24:27 +0200 Subject: [PATCH 04/23] Temporary implement of discriminationTree with few issues --- .../discrimination-trees.go | 460 ++++++++++++--- src/Unif/discriminationtree/dt_test.go | 551 +++++++++++++++++- src/Unif/substitution/data_structure.go | 41 +- 3 files changed, 942 insertions(+), 110 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index fc944dac..c1d77e5c 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -37,13 +37,17 @@ package discriminationtree import ( + "fmt" "strings" "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" + subst "github.com/GoelandProver/Goeland/Unif/substitution" ) +var substitutions map[AST.Term]AST.Term + /*************************/ /* Structures definition */ /*************************/ @@ -57,20 +61,23 @@ func (t SymbolType) getSymbol() AST.Term { return t.symbol } -func (t SymbolType) getArity() int { +func (t SymbolType) GetArity() int { return t.arity } -func (s SymbolType) IsEmpty() bool { +func (s SymbolType) IsNil() bool { return s.symbol == nil && s.arity == -1 } +func makeSymbolType(t AST.Term, arity int) SymbolType { + return SymbolType{t, arity} +} + /* Each node of a CodeTree is composed of a sequence of instruction and its children. If it's a leaf, it has formulaes corresponding to the sequence of instructions. */ type DiscriminationNode struct { - // Unification tout du long - symbol SymbolType // Variable name or function name - children Lib.List[DiscriminationNode] // All the children of the node - leafFor Lib.List[Lib.Either[AST.Term, AST.Form]] // If not empty, contains the where it come from + symbol SymbolType // Contain the AST.Term and Arity + children Lib.List[DiscriminationNode] // All the children of the node + leafFor Lib.List[AST.Pred] // If not empty, contains the where it come from } // Basic Node with no data inside @@ -78,7 +85,7 @@ func NewNode() DiscriminationNode { return DiscriminationNode{ symbol: SymbolType{symbol: nil, arity: -1}, children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + leafFor: Lib.NewList[AST.Pred](), } } @@ -87,7 +94,7 @@ func MakeNodeWithId(id AST.Id, arity int) DiscriminationNode { return DiscriminationNode{ symbol: SymbolType{symbol: id, arity: arity}, children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + leafFor: Lib.NewList[AST.Pred](), } } @@ -96,38 +103,82 @@ func MakeNodeWithMeta(meta AST.Meta) DiscriminationNode { return DiscriminationNode{ symbol: SymbolType{symbol: meta, arity: 0}, children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + leafFor: Lib.NewList[AST.Pred](), + } +} + +func MakeNodeWithSym(sym SymbolType) DiscriminationNode { + return DiscriminationNode{ + symbol: sym, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[AST.Pred](), } } -func (dNode DiscriminationNode) isConstant() bool { - return dNode.symbol.getArity() == 0 +func (dNode *DiscriminationNode) setSymbol(symbol SymbolType) { + dNode.symbol = symbol } -func (dNode DiscriminationNode) getArity() int { - return dNode.symbol.getArity() +func (dNode DiscriminationNode) getChildren() Lib.List[DiscriminationNode] { + return dNode.children } -func (dNode DiscriminationNode) isLeaf() bool { - return dNode.children.Len() == 0 +func (dNode DiscriminationNode) GetArity() int { + return dNode.symbol.GetArity() } func (dNode DiscriminationNode) getSymbol() AST.Term { return dNode.symbol.getSymbol() } -func (dNode DiscriminationNode) isEmpty() bool { - return dNode.symbol.IsEmpty() +func (dNode DiscriminationNode) IsEmpty() bool { + return dNode.symbol.IsNil() } -func (dNode DiscriminationNode) isFun() bool { - return dNode.symbol.getSymbol().IsFun() +func (dNode DiscriminationNode) ToString() string { + return dNode.getSymbol().ToString() } -func (dNode DiscriminationNode) isMeta() bool { - return dNode.symbol.getSymbol().IsMeta() +//[-----PARSER-----] + +func parseFormula(formula AST.Form) Lib.List[SymbolType] { + res := Lib.NewList[SymbolType]() + // The formula has to be a predicate + switch formula_type := formula.(type) { + case AST.Pred: + first_element := makeSymbolType(formula_type.GetID(), formula_type.GetArgs().Len()) + res.Append(first_element) + for _, arg := range formula_type.GetArgs().GetSlice() { + arg_list := parseTerm(arg) + res.Append(arg_list.GetSlice()...) + } + return res + default: + return Lib.NewList[SymbolType]() + } +} + +// Parser for a formula : f(x,y) -> [f,x,y], a -> [a], x -> [x] +func parseTerm(t AST.Term) Lib.List[SymbolType] { + res := Lib.NewList[SymbolType]() + // The formula has to be a predicate + + switch term := t.(type) { + case AST.Fun: // Add all the args of the function + first_element := makeSymbolType(term.GetID(), term.GetArgs().Len()) // Add the node before recursive call + res.Append(first_element) + for _, arg := range term.GetArgs().GetSlice() { + res.Append(parseTerm(arg).GetSlice()...) + } + case AST.Meta: + res.Append(makeSymbolType(term, 0)) + } + + return res } +//[---FIN PARSER---] + func FirstElementToSymbolType(t AST.Term) SymbolType { switch t := t.(type) { case AST.Fun: // Case function @@ -147,119 +198,358 @@ func TermToNode(t AST.Term) DiscriminationNode { for _, c := range t.GetArgs().GetSlice() { children.Append(TermToNode(c)) } + // Node with all his children - return DiscriminationNode{FirstElementToSymbolType(t), children, Lib.NewList[Lib.Either[AST.Term, AST.Form]]()} + return DiscriminationNode{FirstElementToSymbolType(t), children, Lib.NewList[AST.Pred]()} case AST.Meta: - return DiscriminationNode{FirstElementToSymbolType(t), Lib.NewList[DiscriminationNode](), Lib.NewList[Lib.Either[AST.Term, AST.Form]]()} + return DiscriminationNode{FirstElementToSymbolType(t), Lib.NewList[DiscriminationNode](), Lib.NewList[AST.Pred]()} default: Glob.Anomaly("TermToST", "Var or Id") return NewNode() } } -func (dNode DiscriminationNode) DisplayDiscriminationTree() string { - return dNode.displayRec("") +func (dNode DiscriminationNode) Print() { + for _, child := range dNode.children.GetSlice() { + child.displayRec(2) // Magic Number + } } -func (dNode DiscriminationNode) displayRec(indent string) string { +func (dNode DiscriminationNode) displayRec(indent int) { - var b strings.Builder - flag := 0 + prefix := strings.Repeat(" ", indent-1) + " |-- " - // Print the node - if dNode.isEmpty() { - b.WriteString(indent + "[Root/Empty]\n") - } else { - sym := dNode.getSymbol() - if sym != nil { - b.WriteString(indent + "|-- " + sym.ToString() + "\n") - } + if indent == 2 { + prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " } - // Call the children - for _, child := range dNode.children.GetSlice() { - if child.isMeta() { - if flag == 1 { - b.WriteString(child.displayRec(indent)) - } else { - flag = 1 - b.WriteString(child.displayRec(indent + " ")) - } - } else { - flag = 0 - b.WriteString(child.displayRec(indent + " ")) + + fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) + + if dNode.leafFor.Len() > 0 { + leafPrefix := strings.Repeat(" ", indent) + " [=> " + for _, pred := range dNode.leafFor.GetSlice() { + fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) } } - return b.String() + for _, child := range dNode.children.GetSlice() { + child.displayRec(indent + 1) + } } -// Parser for a formula : f(a) -> [f,a] -func SequenceParser(t AST.Term) []AST.Term { +func (dNode DiscriminationNode) GetASTAtDepth(depth int) []SymbolType { - seq := []AST.Term{t} // Add the node before recursive call - - switch term := t.(type) { - case AST.Fun: // Add all the args of the function - for _, arg := range term.GetArgs().GetSlice() { - seq = append(seq, SequenceParser(arg)...) + res := []SymbolType{} + if depth == 0 { + if !dNode.IsEmpty() { + res = append(res, dNode.symbol) } + return res } - return seq + for _, child := range dNode.children.GetSlice() { + res = append(res, child.GetASTAtDepth(depth-1)...) + } + + return res + } +// Equals between tow SymbolType func (s SymbolType) Equals(target SymbolType) bool { - // If the arity is different, no need to go further - if s.arity != target.arity { - return false + if ok := s.getSymbol().Equals(target.getSymbol()); ok { + if s.GetArity() != target.GetArity() { + Glob.Anomaly("Pred Error", "Same predicat but different arity") + } else { + return true + } } - - // call sig.Equals - return s.symbol.Equals(target.symbol) + return false } -func (dNode DiscriminationNode) Insert(t AST.Term) DiscriminationNode { - seq := SequenceParser(t) - return dNode.insertRec(seq, t) +func (dNode DiscriminationNode) Insert(p AST.Pred) DiscriminationNode { + sym_list := parseFormula(p) + return dNode.insertRec(sym_list, p) } -func (dNode DiscriminationNode) insertRec(seq []AST.Term, originalTerm AST.Term) DiscriminationNode { +func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm AST.Pred) DiscriminationNode { + // End of recursion, time to insert - if len(seq) == 0 { - dNode.leafFor.Append(Lib.MkLeft[AST.Term, AST.Form](originalTerm)) + if seq.Len() == 0 { + Exist := false + for _, pred := range dNode.leafFor.GetSlice() { + if pred.Equals(originalTerm) { + Exist = true + break + } + } + + if !Exist { + dNode.leafFor.Append(originalTerm) + } return dNode + } // Create Symbol - sym := FirstElementToSymbolType(seq[0]) + sym := seq.At(0) foundIndex := -1 childrenSlice := dNode.children.GetSlice() // Looking for already existing child + var ok bool for i, child := range childrenSlice { - if child.symbol.Equals(sym) { - foundIndex = i // If we find a match we can end this loop + if ok = child.symbol.Equals(sym); ok { // Set ok to True + foundIndex = i break + } } // Child already exist - if foundIndex != -1 { + if ok { + // Insert and update the sequence - updatedChild := childrenSlice[foundIndex].insertRec(seq[1:], originalTerm) - dNode.children.Upd(foundIndex, updatedChild) + updatedChild := childrenSlice[foundIndex].insertRec(seq.RemoveAt(0), originalTerm) + dNode.children.Upd(foundIndex, updatedChild) // Update children[foundIntex] = updateChild + // if Child doesn't exist + } else { + + newChild := MakeNodeWithSym(sym) // Create a new Node with the new SymbolType + updatedChild := newChild.insertRec(seq.RemoveAt(0), originalTerm) // Insert the rest of the sequence after the new child + dNode.children.Append(updatedChild) // Update the children of the args node + } + return dNode // Return updated node +} + +func GetSubTermLength(seq []SymbolType) int { + + if len(seq) == 0 { + return 0 + } + + needed := 1 + index := 0 + + for needed > 0 && index < len(seq) { + sym := seq[index] + needed = needed - 1 + sym.arity // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... + index++ + } + fmt.Println("longeur terme", index) + return index + +} + +func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQuery []SymbolType) Lib.List[AST.Pred] { + + res := Lib.NewList[AST.Pred]() + + // End of recursion + if needed == 0 { + return dNode.retrieveRec(remainingQuery) + } + + for _, child := range dNode.children.GetSlice() { + newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term + matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery) + res.Append(matches.GetSlice()...) + } + + return res + +} + +func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) Lib.List[AST.Pred] { + seq := parseFormula(t).GetSlice() + return dNode.retrieveRec(seq) +} + +func VerifyPossiblesSub(Met AST.Term, Substitute AST.Term) bool { + + // Source - https://stackoverflow.com/a/2050629 + // Posted by marketer, modified by community. See post 'Timeline' for change history + // Retrieved 2026-04-23, License - CC BY-SA 4.0 - // Child doesn't exist + if _, ok := substitutions[Met]; ok { + substitutions[Met] = Substitute + return true } else { - // Create new Child - newChild := DiscriminationNode{ - symbol: sym, - children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[Lib.Either[AST.Term, AST.Form]](), + return false + } + +} + +func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] { + + fmt.Println("Execution de RetrieveREc") + res := Lib.NewList[AST.Pred]() + if len(seq) == 0 { // End of recursion + fmt.Println("Fin de la recursion de retrieveRec") + res.Append(dNode.leafFor.GetSlice()...) // Append leafFor of this node + return res + } + + symQuery := seq[0] // First Element + + for _, child := range dNode.children.GetSlice() { + + fmt.Println("Recherche en cours avec", child.ToString()) + + isExactMatch := child.symbol.Equals(symQuery) + + // Exact Match + if isExactMatch { + + fmt.Println("Exact Match") + matches := child.retrieveRec(seq[1:]) // Exact Match -> Search next element + res.Append(matches.GetSlice()...) + } + + symChild := child.getSymbol() + + // Case the child is a AST.Meta + if symChild != nil && symChild.IsMeta() && !isExactMatch { + + // We noticed that the term of the dNode is a Meta + // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term + // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 + skip := GetSubTermLength(seq) + fmt.Println("Longueur du skip si dNode.child est une Meta", skip) + if skip <= len(seq) { // Security to prevent segfault + matches := child.retrieveRec(seq[skip:]) + res.Append(matches.GetSlice()...) + } + + // First element is a meta + } else if symQuery.getSymbol() != nil && symQuery.getSymbol().IsMeta() && !isExactMatch { + // Reverse of the situation with the previous if. + // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify + // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode + fmt.Println("Skip si le term de la sequence est une meta", child.GetArity()) + matches := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:]) + res.Append(matches.GetSlice()...) } - updatedChild := newChild.insertRec(seq[1:], originalTerm) // Insert the rest of the sequence after the new child - dNode.children.Append(updatedChild) // Update the children of the args node } - return dNode // Return updated node + + return res +} + +func (dNode DiscriminationNode) Copy() subst.DataStructure { + + newChildMaster := Lib.NewList[DiscriminationNode]() + for _, child := range dNode.children.GetSlice() { + newChild := child.Copy().(DiscriminationNode) + newChildMaster.Append(newChild) + } + + newLeafFor := Lib.ListCpy(dNode.leafFor) + + return DiscriminationNode{symbol: dNode.symbol, children: newChildMaster, leafFor: newLeafFor} + +} + +func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) subst.DataStructure { + fmt.Println("Form") + for _, f := range lf.GetSlice() { + fmt.Println("element f", f.ToString()) + switch nf := f.Copy().(type) { + case AST.Pred: + fmt.Println("Cas Pred") + dNode.Insert(nf) + case AST.Not: + fmt.Println("Cas not") + switch newForm := nf.GetForm().(type) { // Get the type AST.Form + case AST.Pred: + fmt.Println("Cas not apres cast pour Pred", newForm) + dNode.Insert(newForm) + } + } + } + return dNode +} + +// TODO +func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { + + candidates := dNode.RetrieveUnifiables(inputFormula) + var mixed []subst.MixedSubstitutions + var found bool + + // Robinson required Pred, so we verify + predFormula, isPred := inputFormula.(AST.Pred) + if !isPred { + Glob.Anomaly("DiscriminationTree Unify", "Expected a predicate") + return false, nil + } + + // For Robinson + queryTerm := subst.TransformPred(predFormula) + + fmt.Println("taille candidat", len(candidates.GetSlice())) + + for _, possibleMatch := range candidates.GetSlice() { + + fmt.Println("candiat trouve", possibleMatch.ToString()) + possibleMatchTerm := subst.TransformPred(possibleMatch) // Pred -> Term for Robinson + emptySubst := subst.Substitutions{} + fmt.Println("PossibleMatchTerm : ", possibleMatchTerm.ToString()) + + fmt.Println("Param Robinson", possibleMatchTerm.ToString(), ",", queryTerm.ToString(), ",", emptySubst.ToString()) + finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, emptySubst) // Call Robinson + + if finalSubst.Equals(subst.Failure()) { + fmt.Println("-------------------------") + fmt.Println("Echec Substitution") + fmt.Println("-------------------------") + } + + if !finalSubst.Equals(subst.Failure()) { + fmt.Println("Sustitution") + found = true + matching := subst.MakeMatchingSubstitutions(possibleMatch, finalSubst) // constructor + mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return + for _, elem := range mixed { + fmt.Println("element", elem.ToString()) + } + } + } + return found, mixed +} + +// TODO +func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSubstitutions) { + + var mixed []subst.MixedTermSubstitutions + var found bool + + seq := parseTerm(t).GetSlice() + candidates := dNode.retrieveRec(seq) + + for _, possibleMatch := range candidates.GetSlice() { + + candidateTerm := subst.TransformPred(possibleMatch) + emptySubst := subst.Substitutions{} + finalSubst := subst.AddUnification(candidateTerm, t, emptySubst) // Call Robinson + + if !finalSubst.Equals(subst.Failure()) { + found = true + + mixMatch := subst.MixMatchSubstitutions{ + Tof: Lib.MkLeft[AST.Term, AST.Form](candidateTerm), + Subst: finalSubst, + } + mixed = append(mixed, mixMatch.ToMixedTerm()) + } + + } + + return found, mixed + +} + +// TODO ? +func (dNode DiscriminationNode) MakeDataStruct(Formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { + return dNode.InsertFormulaListToDataStructure(Formulas) } diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index a83c7de7..7b0d96d1 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -34,6 +34,7 @@ package discriminationtree import ( "fmt" + "log" "os" "testing" "time" @@ -58,6 +59,7 @@ var c_id AST.Id var d_id AST.Id var c1_id AST.Id var c2_id AST.Id +var PR_id AST.Id // Meta var x AST.Meta @@ -111,9 +113,24 @@ var pa AST.Form var pb AST.Form var not_pc AST.Form var pab AST.Form +var pabc AST.Form +var pba AST.Form +var pca AST.Form var pax AST.Form +var pay AST.Form +var pxy AST.Form +var pxx AST.Form +var px AST.Form +var py AST.Form +var pfx AST.Form +var pafx AST.Form +var pafy AST.Form + var not_pcd AST.Form +var PRa AST.Form +var PRb AST.Form + func initTestVariable() { // Id p_id = AST.MakerId("P") @@ -125,6 +142,7 @@ func initTestVariable() { d_id = AST.MakerId("d") c1_id = AST.MakerId("c1") c2_id = AST.MakerId("c2") + PR_id = AST.MakerId("PR") // Meta x = AST.MakerMeta("X", -1, AST.TIndividual()) @@ -206,23 +224,71 @@ func initTestVariable() { pac_type_list.Append(c.GetTyArgs().GetSlice()...) pac = AST.MakerNot(AST.MakerPred(p_id, pac_type_list, Lib.MkListV[AST.Term](a, c))) - pa = AST.MakerPred(p_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) - - pb = AST.MakerPred(p_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) - not_pc = AST.MakerNot(AST.MakerPred(p_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c))) pab_type_list := a.GetTyArgs() pab_type_list.Append(b.GetTyArgs().GetSlice()...) pab = AST.MakerPred(p_id, pab_type_list, Lib.MkListV[AST.Term](a, b)) + pabc_type_list := a.GetTyArgs() + pabc_type_list.Append(b.GetTyArgs().GetSlice()...) + pabc_type_list.Append(c.GetTyArgs().GetSlice()...) + pabc = AST.MakerPred(p_id, pabc_type_list, Lib.MkListV[AST.Term](a, b, c)) + + pba_type_list := b.GetTyArgs() + pba_type_list.Append(a.GetTyArgs().GetSlice()...) + pba = AST.MakerPred(p_id, pba_type_list, Lib.MkListV[AST.Term](b, a)) + + pca_type_list := c.GetTyArgs() + pca_type_list.Append(a.GetTyArgs().GetSlice()...) + pca = AST.MakerPred(p_id, pca_type_list, Lib.MkListV[AST.Term](c, a)) + pax_type_list := a.GetTyArgs() pax_type_list.Append(x.GetTy()) pax = AST.MakerPred(p_id, pax_type_list, Lib.MkListV[AST.Term](a, x)) + pay_type_list := a.GetTyArgs() + pay_type_list.Append(y.GetTy()) + pay = AST.MakerPred(p_id, pay_type_list, Lib.MkListV[AST.Term](a, y)) + + pxy_type_list := Lib.NewList[AST.Ty]() + pxy_type_list.Append(x.GetTy()) + pxy_type_list.Append(y.GetTy()) + pxy = AST.MakerPred(p_id, pxy_type_list, Lib.MkListV[AST.Term](x, y)) + + pxx_type_list := Lib.NewList[AST.Ty]() + pxx_type_list.Append(x.GetTy()) + pxx_type_list.Append(x.GetTy()) + pxx = AST.MakerPred(p_id, pxx_type_list, Lib.MkListV[AST.Term](x, x)) + + px_type_list := Lib.NewList[AST.Ty]() + px_type_list.Append(x.GetTy()) + px = AST.MakerPred(p_id, px_type_list, Lib.MkListV[AST.Term](x)) + + py_type_list := Lib.NewList[AST.Ty]() + py_type_list.Append(y.GetTy()) + py = AST.MakerPred(p_id, py_type_list, Lib.MkListV[AST.Term](y)) + + pfx_type_list := Lib.MkListV[AST.Ty](AST.TIndividual()) + pfx = AST.MakerPred(p_id, pfx_type_list, Lib.MkListV[AST.Term](fx)) + + pafy_type_list := a.GetTyArgs() + pafy_type_list.Append(fy.GetTyArgs().GetSlice()...) + pafy = AST.MakerPred(p_id, pafy_type_list, Lib.MkListV[AST.Term](a, fy)) + + pafx_type_list := a.GetTyArgs() + pafx_type_list.Append(fx.GetTyArgs().GetSlice()...) + pafx = AST.MakerPred(p_id, pafx_type_list, Lib.MkListV[AST.Term](a, fx)) + not_pcd_type_list := c.GetTyArgs() not_pcd_type_list.Append(d.GetTyArgs().GetSlice()...) not_pcd = AST.MakerNot(AST.MakerPred(p_id, not_pcd_type_list, Lib.MkListV[AST.Term](c, d))) + + pa = AST.MakerPred(p_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + pb = AST.MakerPred(p_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) + + PRa = AST.MakerPred(PR_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + PRb = AST.MakerPred(PR_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) } /* @@ -252,13 +318,482 @@ func TestMain(m *testing.M) { os.Exit(code) } +func TestFirstElementToSymbol(t *testing.T) { + + tree := NewNode() // NewNode create a symbolType with arity == -1. Arity will be 0 if create normaly -> lead to false negative + argsA := pa.GetSubTerms() + termA := argsA.At(0) + resultA := FirstElementToSymbolType(termA) + tree.setSymbol(resultA) + + if tree.GetArity() == -1 { + Glob.Anomaly("Arity Error", "Wrong Arity") + } else { + fmt.Println("OK") + } + + tree2 := NewNode() + argsB := pb.GetSubTerms() + termB := argsB.At(0) + resultB := FirstElementToSymbolType(termB) + tree2.setSymbol(resultB) + + if tree2.GetArity() == -1 { + Glob.Anomaly("Arity Error", "Wrong Arity") + } else { + fmt.Println("OK") + } + + argsC := gga.GetArgs() + resultC := FirstElementToSymbolType(argsC.At(0)) + if resultC.GetArity() == -1 { + Glob.Anomaly("Arity Error", "Wrong Arity") + } else { + fmt.Println("OK") + } + + func() { + defer func() { + if err := recover(); err != nil { + log.Println("panic occurred:", err) + } else { + fmt.Println("Supposed to throw a Error") + } + }() + + argsD := c_id + resultD := FirstElementToSymbolType(argsD) + println("Not supposed to see this ", resultD.symbol) // Required or Go panic due variable not used. However if you see this print : Bon Courage + + }() + +} + +func TestTermToNode(t *testing.T) { + + tree := NewNode() + tree = TermToNode(ggx) + tmp := tree.GetArity() + + if tmp != 1 { + Glob.Anomaly("Element number", "Wrong number of element") + } + + tree2 := NewNode() + tree2 = TermToNode(fbc) + tmp2 := tree2.GetArity() + if tmp2 != 2 { + Glob.Anomaly("Element number", "Wrong number of element") + } +} + +func TestInsert(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + tree = tree.Insert(pafx.(AST.Pred)) + + fmt.Println("-------------PANIC EXPECTED------------- ") + func() { + defer func() { + if err := recover(); err != nil { + log.Println("panic occurred:", err) + } + }() + + tree = tree.Insert(pabc.(AST.Pred)) // pabc supposed to throw a error + }() + fmt.Println("-----------END PANIC EXPECTED----------- ") + tree.Print() + + println() + println() + println() + + tree2 := NewNode() + tree2 = tree2.Insert(pfx.(AST.Pred)) + tree2.Print() + +} + func TestPrintDiscriminationTree(t *testing.T) { + tree := NewNode() - tree = tree.Insert(ggx) - fmt.Println(tree.DisplayDiscriminationTree()) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(PRa.(AST.Pred)) + tree = tree.Insert(PRb.(AST.Pred)) + tree.Print() + + fmt.Println() + fmt.Println() + + tree = tree.Insert(pa.(AST.Pred)) + tree.Print() +} + +func TestPrintSamePredicatCheck(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(PRa.(AST.Pred)) + tree = tree.Insert(PRb.(AST.Pred)) + tree.Print() + +} + +func TestPrintDoublonCheck(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree.Print() + +} + +func TestParser(t *testing.T) { + + seqList := parseTerm(fxy) + seq := seqList.GetSlice() + + if len(seq) != 3 { + t.Fatalf("Got %d elements", len(seq)) + } + + var tmp []string + for _, sym := range seq { + tmp = append(tmp, sym.getSymbol().ToString()) + } + fmt.Printf(" Sequence Parsed : % v\n", tmp) + + seqList = parseTerm(f_fxy_z) + seq = seqList.GetSlice() + + if len(seq) != 5 { + t.Fatalf("Got %d elements", len(seq)) + } + + for _, sym := range seq { + tmp = append(tmp, sym.getSymbol().ToString()) + } + fmt.Printf(" Sequence Parsed : %v\n", tmp) + + seqList = parseTerm(f_x_fyz) + seq = seqList.GetSlice() + + if len(seq) != 5 { + t.Fatalf("Got %d elements", len(seq)) + } + + for _, sym := range seq { + tmp = append(tmp, sym.getSymbol().ToString()) + } + fmt.Printf(" Sequence Parsed : %v\n", tmp) + +} + +func TestRetrieve(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) // Cast + tree = tree.Insert(pay.(AST.Pred)) + tree.Print() + results := tree.RetrieveUnifiables(pab) + if results.Len() == 0 { + fmt.Println("C'est la merde") + } else { + fmt.Printf("Match : %d \n", results.Len()) + } + for _, pred := range results.GetSlice() { + fmt.Println(pred.ToString()) + } + +} + +func TestEquals(t *testing.T) { + + ok := x.Equals(x) + if !ok { + t.Fatalf("Equals Test with failled") + } + +} + +func TestGetSubTermLength(t *testing.T) { + + seq := parseTerm(ggx).GetSlice() + var1 := (GetSubTermLength(seq)) + if var1 != 3 { + t.Fatalf("Error SubTerLength with 2functions & 1Meta ") + } + + seq2 := parseTerm(fxy).GetSlice() + var2 := (GetSubTermLength(seq2)) + if var2 != 3 { + t.Fatalf("Error SubTerLength with 1function & 2Meta") + } + + seq3 := parseTerm(gx).GetSlice() + var3 := (GetSubTermLength(seq3)) + if var3 != 2 { + t.Fatalf("Error SubTerLength with 1function & 1Meta") + } + + seq4 := parseTerm(ga).GetSlice() + var4 := (GetSubTermLength(seq4)) + if var4 != 2 { + t.Fatalf("Error SubTerLength with 1function & 1cst") + } + + seq5 := parseTerm(gggx).GetSlice() + var5 := (GetSubTermLength(seq5)) + if var5 != 4 { + t.Fatalf("Error SubTerLength with 1function & 3Meta") + } + +} + +func TestSkipTreeTermAndContinue(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + tree = tree.Insert(pggab.(AST.Pred)) + + nodeP := tree.getChildren().GetSlice()[0] + + remainingQuery := parseTerm(b).GetSlice() + + results := Lib.NewList[AST.Pred]() + + for _, child := range nodeP.getChildren().GetSlice() { + matches := child.SkipTreeTermAndContinue(child.GetArity(), remainingQuery) + results.Append(matches.GetSlice()...) + } + + if results.Len() != 2 { + t.Fatalf("error") + } + + for _, res := range results.GetSlice() { + fmt.Println("=>", res.ToString()) + } +} + +func TestRetrieveUnifiables(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + res := tree.RetrieveUnifiables(pay) + + if res.Len() == 0 { + t.Fatalf("No Match") + } + + for _, pred := range res.GetSlice() { + + fmt.Println(len(res.GetSlice())) // Doublon + fmt.Println("=>", pred.ToString()) + } +} + +func TestCopy(t *testing.T) { + + tree1 := NewNode() + tree2 := tree1.Copy() + + tree1 = tree1.Insert(pax.(AST.Pred)) + + res2 := tree2.IsEmpty() + + if !res2 { + t.Fatalf("Tree2 is not a copy, it s only a pointer to tree1") + } + +} + +func TestUnify(t *testing.T) { + + fmt.Println("-----TEST-----") + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + + var val bool + var mix []subst.MixedSubstitutions + val, mix = tree.Unify(pay) + + for _, elem := range mix { + if val { + fmt.Println(elem.ToString()) + } + } + fmt.Println("---END TEST---") + fmt.Println() + + fmt.Println("-----TEST-----") + fmt.Println("-----MPTY-----") tree2 := NewNode() - tree2 = tree2.Insert(fxy) - fmt.Println(tree2.DisplayDiscriminationTree()) + tree2 = tree2.Insert(pa.(AST.Pred)) + var mix2 []subst.MixedSubstitutions + _, mix2 = tree2.Unify(pb) + + if len(mix2) != 0 { + t.Fatalf("can't Unify Predicat(Cst) and Predicat(Cst)") + } + + fmt.Println("---END TEST---") + fmt.Println() + + fmt.Println("-----TEST-----") + + tree3 := NewNode() + tree3 = tree3.Insert(pa.(AST.Pred)) + var mix3 []subst.MixedSubstitutions + _, mix3 = tree3.Unify(pa) + + for _, elem := range mix3 { + fmt.Println(elem.ToString()) + } + + fmt.Println("---END TEST---") + fmt.Println() + + fmt.Println("-----TEST-----") + fmt.Println() + tree6 := NewNode() + tree6 = tree6.Insert(px.(AST.Pred)) + var mix6 []subst.MixedSubstitutions + _, mix6 = tree6.Unify(py) + for _, elem := range mix6 { + fmt.Println(elem.ToString()) + } + fmt.Println("---END TEST---") + + fmt.Println("-----TEST-----") + tree7 := NewNode() + tree7 = tree7.Insert(px.(AST.Pred)) + var mix7 []subst.MixedSubstitutions + _, mix7 = tree7.Unify(py) + for _, elem := range mix7 { + fmt.Println(elem.ToString()) + } + fmt.Println("---END TEST---") + +} + +func TestDeCon0(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pab) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon4(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pay) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon1(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pa) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } + +} + +func TestDeCon2(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(py) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon3(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pafy) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon5(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pafy) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon6(t *testing.T) { + tree4 := NewNode() + tree4 = tree4.Insert(pxy.(AST.Pred)) + var mix4 []subst.MixedSubstitutions + _, mix4 = tree4.Unify(pab) + for _, elem := range mix4 { + fmt.Println(elem.ToString()) + } +} + +func TestDecCon7(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pab) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon8(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pxx) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestDeCon9(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pca.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pxy) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } +} + +func TestMakeDataStruct(t *testing.T) { + tree := NewNode() + formulas := Lib.NewList[AST.Form]() + formulas.Append(pxy) + tree.MakeDataStruct(formulas, true) + tree.Print() } diff --git a/src/Unif/substitution/data_structure.go b/src/Unif/substitution/data_structure.go index 4ba18a8f..7c226502 100644 --- a/src/Unif/substitution/data_structure.go +++ b/src/Unif/substitution/data_structure.go @@ -39,9 +39,10 @@ package subst import ( "fmt" + "github.com/GoelandProver/Goeland/AST" - "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Glob" + "github.com/GoelandProver/Goeland/Lib" ) type DataStructure interface { @@ -65,7 +66,7 @@ type DataStructure interface { } func TransformPred(p AST.Pred) AST.Term { - return TransformTerm(AST.MakerFun(p.GetID(), p.GetTyArgs(), p.GetArgs())) + return TransformTerm(AST.MakerFun(p.GetID(), Lib.NewList[AST.Ty](), p.GetArgs())) } func TransformTerm(t AST.Term) AST.Term { @@ -126,7 +127,6 @@ func MergeSubstitutions(s1, s2 Substitutions) (Substitutions, bool) { return res, same_key } - // robinsonUnify implements Robinson's structural unification algorithm on // Goeland's term representation. It extends the substitution s in place, // threading it through recursive calls, and returns Failure() on any clash. @@ -140,59 +140,67 @@ func MergeSubstitutions(s1, s2 Substitutions) (Substitutions, bool) { func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { term1 = walkSubst(term1, s) term2 = walkSubst(term2, s) - + if term1.Equals(term2) { + fmt.Println("Equals ok") return s } - + switch t1 := term1.(type) { case AST.Meta: if !OccurCheckValid(t1, term2) { + fmt.Println("Failure OccurCheck META T1 ") return Failure() } s.Set(t1, term2) EliminateMeta(&s) Eliminate(&s) return s - + case AST.Fun: switch t2 := term2.(type) { case AST.Meta: if !OccurCheckValid(t2, term1) { + fmt.Println("Failure OccurCheck META T2 ") return Failure() } s.Set(t2, term1) EliminateMeta(&s) Eliminate(&s) return s - + case AST.Fun: if !t1.GetID().Equals(t2.GetID()) { + fmt.Println("Failure Equals Fun ") return Failure() } - args1 := t1.GetArgs().GetSlice() - args2 := t2.GetArgs().GetSlice() - if len(args1) != len(args2) { + args1 := t1.GetArgs() + args2 := t2.GetArgs() + fmt.Printf("%v\n", Lib.ListToString(args1)) + fmt.Printf("%v\n", Lib.ListToString(args2)) + if args1.Len() != args2.Len() { + fmt.Println("Failure Longueur args ") return Failure() } - for i := range args1 { - s = robinsonUnify(args1[i].Copy(), args2[i].Copy(), s) + for i := range args1.GetSlice() { + s = robinsonUnify(args1.At(i).Copy(), args2.At(i).Copy(), s) if s.Equals(Failure()) { + fmt.Println("Failure RobinsonJspQuoi ") return Failure() } } return s - + default: return Failure() } - + default: // Var or any other term kind: not expected after Skolemisation. return Failure() } } - + // walkSubst chases meta-variable bindings in s until reaching an unbound // meta or a non-meta term. func walkSubst(t AST.Term, s Substitutions) AST.Term { @@ -205,7 +213,7 @@ func walkSubst(t AST.Term, s Substitutions) AST.Term { } return t } - + func AddUnification(term1, term2 AST.Term, subst Substitutions) Substitutions { debug( Lib.MkLazy(func() string { @@ -218,4 +226,3 @@ func AddUnification(term1, term2 AST.Term, subst Substitutions) Substitutions { ) return robinsonUnify(term1.Copy(), term2.Copy(), subst.Copy()) } - \ No newline at end of file From 6ebd40f254015f6b6ae8b2e5a56b0175abe295a5 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Fri, 24 Apr 2026 10:13:17 +0200 Subject: [PATCH 05/23] Fix Tests and remove debugging Printf --- .../discrimination-trees.go | 62 +-- src/Unif/discriminationtree/dt_test.go | 468 ++++++++---------- src/Unif/substitution/data_structure.go | 8 +- 3 files changed, 211 insertions(+), 327 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index c1d77e5c..c9c87f05 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -46,8 +46,6 @@ import ( subst "github.com/GoelandProver/Goeland/Unif/substitution" ) -var substitutions map[AST.Term]AST.Term - /*************************/ /* Structures definition */ /*************************/ @@ -89,24 +87,6 @@ func NewNode() DiscriminationNode { } } -// Node with data -func MakeNodeWithId(id AST.Id, arity int) DiscriminationNode { - return DiscriminationNode{ - symbol: SymbolType{symbol: id, arity: arity}, - children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[AST.Pred](), - } -} - -// Arity is 0 because it's a variable -func MakeNodeWithMeta(meta AST.Meta) DiscriminationNode { - return DiscriminationNode{ - symbol: SymbolType{symbol: meta, arity: 0}, - children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[AST.Pred](), - } -} - func MakeNodeWithSym(sym SymbolType) DiscriminationNode { return DiscriminationNode{ symbol: sym, @@ -284,12 +264,10 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm break } } - if !Exist { dNode.leafFor.Append(originalTerm) } return dNode - } // Create Symbol @@ -303,7 +281,6 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm if ok = child.symbol.Equals(sym); ok { // Set ok to True foundIndex = i break - } } @@ -337,7 +314,6 @@ func GetSubTermLength(seq []SymbolType) int { needed = needed - 1 + sym.arity // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... index++ } - fmt.Println("longeur terme", index) return index } @@ -366,27 +342,10 @@ func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) Lib.List[AST.Pred return dNode.retrieveRec(seq) } -func VerifyPossiblesSub(Met AST.Term, Substitute AST.Term) bool { - - // Source - https://stackoverflow.com/a/2050629 - // Posted by marketer, modified by community. See post 'Timeline' for change history - // Retrieved 2026-04-23, License - CC BY-SA 4.0 - - if _, ok := substitutions[Met]; ok { - substitutions[Met] = Substitute - return true - } else { - return false - } - -} - func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] { - fmt.Println("Execution de RetrieveREc") res := Lib.NewList[AST.Pred]() if len(seq) == 0 { // End of recursion - fmt.Println("Fin de la recursion de retrieveRec") res.Append(dNode.leafFor.GetSlice()...) // Append leafFor of this node return res } @@ -395,14 +354,11 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] for _, child := range dNode.children.GetSlice() { - fmt.Println("Recherche en cours avec", child.ToString()) - isExactMatch := child.symbol.Equals(symQuery) // Exact Match if isExactMatch { - fmt.Println("Exact Match") matches := child.retrieveRec(seq[1:]) // Exact Match -> Search next element res.Append(matches.GetSlice()...) } @@ -416,7 +372,6 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 skip := GetSubTermLength(seq) - fmt.Println("Longueur du skip si dNode.child est une Meta", skip) if skip <= len(seq) { // Security to prevent segfault matches := child.retrieveRec(seq[skip:]) res.Append(matches.GetSlice()...) @@ -427,7 +382,6 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] // Reverse of the situation with the previous if. // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode - fmt.Println("Skip si le term de la sequence est une meta", child.GetArity()) matches := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:]) res.Append(matches.GetSlice()...) } @@ -457,13 +411,13 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST switch nf := f.Copy().(type) { case AST.Pred: fmt.Println("Cas Pred") - dNode.Insert(nf) + dNode = dNode.Insert(nf) case AST.Not: fmt.Println("Cas not") switch newForm := nf.GetForm().(type) { // Get the type AST.Form case AST.Pred: fmt.Println("Cas not apres cast pour Pred", newForm) - dNode.Insert(newForm) + dNode = dNode.Insert(newForm) } } } @@ -487,16 +441,10 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe // For Robinson queryTerm := subst.TransformPred(predFormula) - fmt.Println("taille candidat", len(candidates.GetSlice())) - for _, possibleMatch := range candidates.GetSlice() { - fmt.Println("candiat trouve", possibleMatch.ToString()) possibleMatchTerm := subst.TransformPred(possibleMatch) // Pred -> Term for Robinson emptySubst := subst.Substitutions{} - fmt.Println("PossibleMatchTerm : ", possibleMatchTerm.ToString()) - - fmt.Println("Param Robinson", possibleMatchTerm.ToString(), ",", queryTerm.ToString(), ",", emptySubst.ToString()) finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, emptySubst) // Call Robinson if finalSubst.Equals(subst.Failure()) { @@ -506,13 +454,9 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe } if !finalSubst.Equals(subst.Failure()) { - fmt.Println("Sustitution") found = true matching := subst.MakeMatchingSubstitutions(possibleMatch, finalSubst) // constructor mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return - for _, elem := range mixed { - fmt.Println("element", elem.ToString()) - } } } return found, mixed @@ -549,7 +493,7 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu } -// TODO ? func (dNode DiscriminationNode) MakeDataStruct(Formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { + // Gerer cas possitif ou negatif return dNode.InsertFormulaListToDataStructure(Formulas) } diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 7b0d96d1..d64e7dc3 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -106,15 +106,45 @@ var f_x_fyz AST.Fun var f_fab_c AST.Fun var f_a_fbc AST.Fun +// // Equalities +// var eq_x_y AST.Pred +// var eq_x_a AST.Pred +// var eq_y_a AST.Pred +// var eq_z1_c1 AST.Pred +// var eq_z1_c2 AST.Pred +// var eq_z2_c1 AST.Pred +// var eq_z3_c1 AST.Pred +// var eq_gx_fx AST.Pred +// var eq_ggx_fa AST.Pred +// var eq_gfy_y AST.Pred +// var eq_fa_a AST.Pred +// var eq_b_c AST.Pred +// var eq_a_b AST.Pred +// var eq_a_c AST.Pred +// var eq_b_d AST.Pred +// var eq_x_d AST.Pred + +// // Inequalites +// var neq_x_a AST.Form +// var neq_a_b AST.Form +// var neq_a_d AST.Form +// var neq_gggx_x AST.Form +// var neq_fx_a AST.Form +// var neq_fx_x AST.Form +// var neq_fab_fcd AST.Form +// var neq_fb_fc AST.Form + // Form var pggab AST.Form var pac AST.Form var pa AST.Form var pb AST.Form + var not_pc AST.Form var pab AST.Form var pabc AST.Form var pba AST.Form + var pca AST.Form var pax AST.Form var pay AST.Form @@ -161,134 +191,55 @@ func initTestVariable() { c2 = AST.MakerConst(c2_id) // Fun - gx = AST.MakerFun(g_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) - ga = AST.MakerFun(g_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) + gx = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x)) + ga = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) fx = AST.MakerFun(f_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) fy = AST.MakerFun(f_id, Lib.MkListV(y.GetTy()), Lib.MkListV[AST.Term](y)) - fa = AST.MakerFun(f_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) - fb = AST.MakerFun(f_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) - fc = AST.MakerFun(f_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c)) - - ggx = AST.MakerFun(g_id, gx.GetTyArgs(), Lib.MkListV[AST.Term](gx)) - gga = AST.MakerFun(g_id, ga.GetTyArgs(), Lib.MkListV[AST.Term](ga)) - gfy = AST.MakerFun(g_id, fy.GetTyArgs(), Lib.MkListV[AST.Term](fy)) - gfa = AST.MakerFun(g_id, fa.GetTyArgs(), Lib.MkListV[AST.Term](fa)) - fxy = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), y.GetTy()), Lib.MkListV[AST.Term](x, y)) - fyz = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), z.GetTy()), Lib.MkListV[AST.Term](x, z)) - ffx = AST.MakerFun(f_id, fx.GetTyArgs(), Lib.MkListV[AST.Term](fx)) - - x_a_type_list := Lib.MkListV[AST.Ty](x.GetTy()) - x_a_type_list.Append(a.GetTyArgs().GetSlice()...) - fxa = AST.MakerFun(f_id, x_a_type_list, Lib.MkListV[AST.Term](x, a)) - - a_y_type_list := a.GetTyArgs() - a_y_type_list.Append(y.GetTy()) - fay = AST.MakerFun(f_id, a_y_type_list, Lib.MkListV[AST.Term](a, y)) - - a_b_type_list := a.GetTyArgs() - a_b_type_list.Append(b.GetTyArgs().GetSlice()...) - fab = AST.MakerFun(f_id, a_b_type_list, Lib.MkListV[AST.Term](a, b)) - - bc_type_list := b.GetTyArgs() - bc_type_list.Append(c.GetTyArgs().GetSlice()...) - fbc = AST.MakerFun(f_id, bc_type_list, Lib.MkListV[AST.Term](b, c)) - - cd_type_list := c.GetTyArgs() - cd_type_list.Append(d.GetTyArgs().GetSlice()...) - fcd = AST.MakerFun(f_id, cd_type_list, Lib.MkListV[AST.Term](c, d)) - - gggx = AST.MakerFun(g_id, ggx.GetTyArgs(), Lib.MkListV[AST.Term](ggx)) - - fxy_z_type_list := fxy.GetTyArgs() - fxy_z_type_list.Append(z.GetTy()) - f_fxy_z = AST.MakerFun(f_id, fxy_z_type_list, Lib.MkListV[AST.Term](fxy, z)) - - x_fyz_type_list := Lib.MkListV[AST.Ty](x.GetTy()) - x_fyz_type_list.Append(fyz.GetTyArgs().GetSlice()...) - f_x_fyz = AST.MakerFun(f_id, x_fyz_type_list, Lib.MkListV[AST.Term](x, fyz)) - - fab_c_type_list := fab.GetTyArgs() - fab_c_type_list.Append(c.GetTyArgs().GetSlice()...) - f_fab_c = AST.MakerFun(f_id, fab_c_type_list, Lib.MkListV[AST.Term](fab, c)) - - a_fbc_type_list := a.GetTyArgs() - a_fbc_type_list.Append(fbc.GetTyArgs().GetSlice()...) - f_a_fbc = AST.MakerFun(f_id, a_fbc_type_list, Lib.MkListV[AST.Term](a, fbc)) + fa = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) + fb = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) + fc = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c)) + + ggx = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gx)) + gga = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](ga)) + gfy = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fy)) + gfa = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fa)) + fxy = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, y)) + fyz = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, z)) + ffx = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fx)) + + fxa = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, a)) + fay = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, y)) + fab = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) + fbc = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, c)) + fcd = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, d)) + gggx = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](ggx)) + f_fxy_z = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fxy, z)) + f_x_fyz = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, fyz)) + f_fab_c = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fab, c)) + f_a_fbc = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fbc)) // Predicates - pggab_type_list := gga.GetTyArgs() - pggab_type_list.Append(b.GetTyArgs().GetSlice()...) - pggab = AST.MakerPred(p_id, pggab_type_list, Lib.MkListV[AST.Term](gga, b)) - - pac_type_list := a.GetTyArgs() - pac_type_list.Append(c.GetTyArgs().GetSlice()...) - pac = AST.MakerNot(AST.MakerPred(p_id, pac_type_list, Lib.MkListV[AST.Term](a, c))) - - not_pc = AST.MakerNot(AST.MakerPred(p_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c))) - - pab_type_list := a.GetTyArgs() - pab_type_list.Append(b.GetTyArgs().GetSlice()...) - pab = AST.MakerPred(p_id, pab_type_list, Lib.MkListV[AST.Term](a, b)) - - pabc_type_list := a.GetTyArgs() - pabc_type_list.Append(b.GetTyArgs().GetSlice()...) - pabc_type_list.Append(c.GetTyArgs().GetSlice()...) - pabc = AST.MakerPred(p_id, pabc_type_list, Lib.MkListV[AST.Term](a, b, c)) - - pba_type_list := b.GetTyArgs() - pba_type_list.Append(a.GetTyArgs().GetSlice()...) - pba = AST.MakerPred(p_id, pba_type_list, Lib.MkListV[AST.Term](b, a)) - - pca_type_list := c.GetTyArgs() - pca_type_list.Append(a.GetTyArgs().GetSlice()...) - pca = AST.MakerPred(p_id, pca_type_list, Lib.MkListV[AST.Term](c, a)) - - pax_type_list := a.GetTyArgs() - pax_type_list.Append(x.GetTy()) - pax = AST.MakerPred(p_id, pax_type_list, Lib.MkListV[AST.Term](a, x)) - - pay_type_list := a.GetTyArgs() - pay_type_list.Append(y.GetTy()) - pay = AST.MakerPred(p_id, pay_type_list, Lib.MkListV[AST.Term](a, y)) - - pxy_type_list := Lib.NewList[AST.Ty]() - pxy_type_list.Append(x.GetTy()) - pxy_type_list.Append(y.GetTy()) - pxy = AST.MakerPred(p_id, pxy_type_list, Lib.MkListV[AST.Term](x, y)) - - pxx_type_list := Lib.NewList[AST.Ty]() - pxx_type_list.Append(x.GetTy()) - pxx_type_list.Append(x.GetTy()) - pxx = AST.MakerPred(p_id, pxx_type_list, Lib.MkListV[AST.Term](x, x)) - - px_type_list := Lib.NewList[AST.Ty]() - px_type_list.Append(x.GetTy()) - px = AST.MakerPred(p_id, px_type_list, Lib.MkListV[AST.Term](x)) - - py_type_list := Lib.NewList[AST.Ty]() - py_type_list.Append(y.GetTy()) - py = AST.MakerPred(p_id, py_type_list, Lib.MkListV[AST.Term](y)) - - pfx_type_list := Lib.MkListV[AST.Ty](AST.TIndividual()) - pfx = AST.MakerPred(p_id, pfx_type_list, Lib.MkListV[AST.Term](fx)) - - pafy_type_list := a.GetTyArgs() - pafy_type_list.Append(fy.GetTyArgs().GetSlice()...) - pafy = AST.MakerPred(p_id, pafy_type_list, Lib.MkListV[AST.Term](a, fy)) - - pafx_type_list := a.GetTyArgs() - pafx_type_list.Append(fx.GetTyArgs().GetSlice()...) - pafx = AST.MakerPred(p_id, pafx_type_list, Lib.MkListV[AST.Term](a, fx)) - - not_pcd_type_list := c.GetTyArgs() - not_pcd_type_list.Append(d.GetTyArgs().GetSlice()...) - not_pcd = AST.MakerNot(AST.MakerPred(p_id, not_pcd_type_list, Lib.MkListV[AST.Term](c, d))) - - pa = AST.MakerPred(p_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) - pb = AST.MakerPred(p_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) - - PRa = AST.MakerPred(PR_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) - PRb = AST.MakerPred(PR_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) + pggab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gga, b)) + pac = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, c))) + not_pc = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c))) + pab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) + pabc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b, c)) + pba = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, a)) + pca = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, a)) + pax = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, x)) + pay = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, y)) + pxy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, y)) + pxx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, x)) + px = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x)) + py = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y)) + pfx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fx)) + pafy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fy)) + pafx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fx)) + not_pcd = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, d))) + pa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) + pb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) + PRa = AST.MakerPred(PR_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) + PRb = AST.MakerPred(PR_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) } /* @@ -352,6 +303,7 @@ func TestFirstElementToSymbol(t *testing.T) { fmt.Println("OK") } + fmt.Println("-----EXPECTED PANIC-----") func() { defer func() { if err := recover(); err != nil { @@ -366,6 +318,7 @@ func TestFirstElementToSymbol(t *testing.T) { println("Not supposed to see this ", resultD.symbol) // Required or Go panic due variable not used. However if you see this print : Bon Courage }() + fmt.Println("---END EXPECTED PANIC---") } @@ -455,14 +408,15 @@ func TestPrintDoublonCheck(t *testing.T) { func TestParser(t *testing.T) { + var tmp []string + var tmp2 []string + var tmp3 []string + seqList := parseTerm(fxy) seq := seqList.GetSlice() - if len(seq) != 3 { t.Fatalf("Got %d elements", len(seq)) } - - var tmp []string for _, sym := range seq { tmp = append(tmp, sym.getSymbol().ToString()) } @@ -470,27 +424,23 @@ func TestParser(t *testing.T) { seqList = parseTerm(f_fxy_z) seq = seqList.GetSlice() - if len(seq) != 5 { t.Fatalf("Got %d elements", len(seq)) } - for _, sym := range seq { - tmp = append(tmp, sym.getSymbol().ToString()) + tmp2 = append(tmp2, sym.getSymbol().ToString()) } - fmt.Printf(" Sequence Parsed : %v\n", tmp) + fmt.Printf(" Sequence Parsed : %v\n", tmp2) seqList = parseTerm(f_x_fyz) seq = seqList.GetSlice() - if len(seq) != 5 { t.Fatalf("Got %d elements", len(seq)) } - for _, sym := range seq { - tmp = append(tmp, sym.getSymbol().ToString()) + tmp3 = append(tmp3, sym.getSymbol().ToString()) } - fmt.Printf(" Sequence Parsed : %v\n", tmp) + fmt.Printf(" Sequence Parsed : %v\n", tmp3) } @@ -519,6 +469,16 @@ func TestEquals(t *testing.T) { t.Fatalf("Equals Test with failled") } + ok2 := pxx.Equals(pxx) + if !ok2 { + t.Fatalf("Equals Test with failled") + } + + ok3 := fab.Equals(fab) + if !ok3 { + t.Fatalf("Equals Test with failled") + } + } func TestGetSubTermLength(t *testing.T) { @@ -562,9 +522,7 @@ func TestSkipTreeTermAndContinue(t *testing.T) { tree = tree.Insert(pggab.(AST.Pred)) nodeP := tree.getChildren().GetSlice()[0] - remainingQuery := parseTerm(b).GetSlice() - results := Lib.NewList[AST.Pred]() for _, child := range nodeP.getChildren().GetSlice() { @@ -585,10 +543,11 @@ func TestRetrieveUnifiables(t *testing.T) { tree := NewNode() tree = tree.Insert(pax.(AST.Pred)) + tree = tree.Insert(pba.(AST.Pred)) res := tree.RetrieveUnifiables(pay) - if res.Len() == 0 { - t.Fatalf("No Match") + if res.Len() != 1 { + t.Fatalf("Should be only 1") } for _, pred := range res.GetSlice() { @@ -602,11 +561,8 @@ func TestCopy(t *testing.T) { tree1 := NewNode() tree2 := tree1.Copy() - tree1 = tree1.Insert(pax.(AST.Pred)) - res2 := tree2.IsEmpty() - if !res2 { t.Fatalf("Tree2 is not a copy, it s only a pointer to tree1") } @@ -615,53 +571,74 @@ func TestCopy(t *testing.T) { func TestUnify(t *testing.T) { - fmt.Println("-----TEST-----") + fmt.Println("-----TEST 01 -----") tree := NewNode() tree = tree.Insert(pax.(AST.Pred)) - - var val bool var mix []subst.MixedSubstitutions - val, mix = tree.Unify(pay) - + _, mix = tree.Unify(pay) for _, elem := range mix { - if val { - fmt.Println(elem.ToString()) - } + fmt.Println(elem.ToString()) } - fmt.Println("---END TEST---") + fmt.Println("-----END TEST-----") fmt.Println() - fmt.Println("-----TEST-----") - fmt.Println("-----MPTY-----") + fmt.Println("-----TEST 02 -----") + tree1 := NewNode() + tree1 = tree1.Insert(pax.(AST.Pred)) + var mix1 []subst.MixedSubstitutions + _, mix1 = tree1.Unify(pab) + for _, elem := range mix1 { + fmt.Println(elem.ToString()) + } + fmt.Println("-----END TEST-----") + fmt.Println() + fmt.Println("-----TEST 03 -----") + fmt.Println("----- EMPTY -----") tree2 := NewNode() tree2 = tree2.Insert(pa.(AST.Pred)) var mix2 []subst.MixedSubstitutions _, mix2 = tree2.Unify(pb) - if len(mix2) != 0 { t.Fatalf("can't Unify Predicat(Cst) and Predicat(Cst)") } - - fmt.Println("---END TEST---") + fmt.Println("-----END TEST-----") fmt.Println() - fmt.Println("-----TEST-----") - + fmt.Println("-----TEST 04 -----") tree3 := NewNode() tree3 = tree3.Insert(pa.(AST.Pred)) var mix3 []subst.MixedSubstitutions _, mix3 = tree3.Unify(pa) - for _, elem := range mix3 { fmt.Println(elem.ToString()) } + fmt.Println("-----END TEST-----") + fmt.Println() - fmt.Println("---END TEST---") + fmt.Println("-----TEST 05 -----") + tree4 := NewNode() + tree4 = tree4.Insert(pax.(AST.Pred)) + var mix4 []subst.MixedSubstitutions + _, mix4 = tree4.Unify(pafy) + for _, elem := range mix4 { + fmt.Println(elem.ToString()) + } + fmt.Println("-----END TEST-----") fmt.Println() - fmt.Println("-----TEST-----") + fmt.Println("-----TEST 06 -----") + tree5 := NewNode() + tree5 = tree5.Insert(pafx.(AST.Pred)) + var mix5 []subst.MixedSubstitutions + _, mix5 = tree5.Unify(pafy) + for _, elem5 := range mix5 { + fmt.Println(elem5.ToString()) + } + fmt.Println("-----END TEST-----") fmt.Println() + + fmt.Println("-----TEST 07 -----") tree6 := NewNode() tree6 = tree6.Insert(px.(AST.Pred)) var mix6 []subst.MixedSubstitutions @@ -669,131 +646,100 @@ func TestUnify(t *testing.T) { for _, elem := range mix6 { fmt.Println(elem.ToString()) } - fmt.Println("---END TEST---") + fmt.Println("-----END TEST-----") + fmt.Println() - fmt.Println("-----TEST-----") + fmt.Println("-----TEST 08 -----") tree7 := NewNode() - tree7 = tree7.Insert(px.(AST.Pred)) + tree7 = tree7.Insert(pxy.(AST.Pred)) var mix7 []subst.MixedSubstitutions - _, mix7 = tree7.Unify(py) + _, mix7 = tree7.Unify(pab) for _, elem := range mix7 { fmt.Println(elem.ToString()) } - fmt.Println("---END TEST---") - -} - -func TestDeCon0(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pab) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } -} - -func TestDeCon4(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pay) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } -} - -func TestDeCon1(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pa) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } - -} - -func TestDeCon2(t *testing.T) { + fmt.Println("-----END TEST-----") + fmt.Println() - tree := NewNode() - tree = tree.Insert(px.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(py) - for _, elem := range mix { - fmt.Println(elem.ToString()) + fmt.Println("-----TEST 09 -----") + tree8 := NewNode() + tree8 = tree8.Insert(pxx.(AST.Pred)) + var mix8 []subst.MixedSubstitutions + _, mix8 = tree8.Unify(pab) + if len(mix8) != 0 { + t.Fatalf("Got %d elements instead of 0", len(mix8)) } -} -func TestDeCon3(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pafy) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } -} + fmt.Println("-----END TEST-----") + fmt.Println() -func TestDeCon5(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pafx.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pafy) - for _, elem := range mix { - fmt.Println(elem.ToString()) + fmt.Println("-----TEST 10 -----") + tree9 := NewNode() + tree9 = tree9.Insert(pba.(AST.Pred)) + tree9 = tree9.Insert(pab.(AST.Pred)) + var mix9 []subst.MixedSubstitutions + _, mix9 = tree9.Unify(pxx) + if len(mix9) != 0 { + t.Fatalf("Got %d elements instead of 0", len(mix9)) } -} + fmt.Println("-----END TEST-----") + fmt.Println() -func TestDeCon6(t *testing.T) { - tree4 := NewNode() - tree4 = tree4.Insert(pxy.(AST.Pred)) - var mix4 []subst.MixedSubstitutions - _, mix4 = tree4.Unify(pab) - for _, elem := range mix4 { + fmt.Println("-----TEST 11 -----") + tree10 := NewNode() + tree10 = tree10.Insert(pb.(AST.Pred)) + tree10 = tree10.Insert(pa.(AST.Pred)) + tree10 = tree10.Insert(pfx.(AST.Pred)) + var mix10 []subst.MixedSubstitutions + _, mix10 = tree10.Unify(py) + for _, elem := range mix10 { fmt.Println(elem.ToString()) } -} + fmt.Println("-----END TEST-----") + fmt.Println() -func TestDecCon7(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pxx.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pab) - for _, elem := range mix { - fmt.Println(elem.ToString()) + fmt.Println("-----TEST 12 -----") + tree11 := NewNode() + tree11 = tree11.Insert(pab.(AST.Pred)) + var mix11 []subst.MixedSubstitutions + _, mix11 = tree11.Unify(pxx) + if len(mix11) != 0 { + t.Fatalf("Got %d elements instead of 0", len(mix11)) } -} + fmt.Println("-----END TEST-----") + fmt.Println() -func TestDeCon8(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pba.(AST.Pred)) - tree = tree.Insert(pab.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pxx) - for _, elem := range mix { + fmt.Println("-----TEST 13 -----") + tree12 := NewNode() + tree12 = tree12.Insert(pggab.(AST.Pred)) + var mix12 []subst.MixedSubstitutions + _, mix12 = tree12.Unify(pxy) + for _, elem := range mix12 { fmt.Println(elem.ToString()) } -} + fmt.Println("-----END TEST-----") + fmt.Println() -func TestDeCon9(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pba.(AST.Pred)) - tree = tree.Insert(pca.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pxy) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } } func TestMakeDataStruct(t *testing.T) { tree := NewNode() formulas := Lib.NewList[AST.Form]() formulas.Append(pxy) - tree.MakeDataStruct(formulas, true) - tree.Print() + tree2 := tree.MakeDataStruct(formulas, true) + tree2.Print() + + tree3 := NewNode() + formulas2 := Lib.NewList[AST.Form]() + formulas2.Append(pab) + formulas2.Append(pac) + formulas2.Append(pba) + tree4 := tree3.MakeDataStruct(formulas2, true) + tree4.Print() + + tree5 := NewNode() + formulas3 := Lib.NewList[AST.Form]() + formulas3.Append(pac) + tree6 := tree5.MakeDataStruct(formulas3, true) + tree6.Print() } diff --git a/src/Unif/substitution/data_structure.go b/src/Unif/substitution/data_structure.go index 7c226502..d9eb23d6 100644 --- a/src/Unif/substitution/data_structure.go +++ b/src/Unif/substitution/data_structure.go @@ -66,7 +66,7 @@ type DataStructure interface { } func TransformPred(p AST.Pred) AST.Term { - return TransformTerm(AST.MakerFun(p.GetID(), Lib.NewList[AST.Ty](), p.GetArgs())) + return TransformTerm(AST.MakerFun(p.GetID(), p.GetTyArgs(), p.GetArgs())) } func TransformTerm(t AST.Term) AST.Term { @@ -142,14 +142,12 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { term2 = walkSubst(term2, s) if term1.Equals(term2) { - fmt.Println("Equals ok") return s } switch t1 := term1.(type) { case AST.Meta: if !OccurCheckValid(t1, term2) { - fmt.Println("Failure OccurCheck META T1 ") return Failure() } s.Set(t1, term2) @@ -161,7 +159,6 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { switch t2 := term2.(type) { case AST.Meta: if !OccurCheckValid(t2, term1) { - fmt.Println("Failure OccurCheck META T2 ") return Failure() } s.Set(t2, term1) @@ -171,7 +168,6 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { case AST.Fun: if !t1.GetID().Equals(t2.GetID()) { - fmt.Println("Failure Equals Fun ") return Failure() } args1 := t1.GetArgs() @@ -179,13 +175,11 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { fmt.Printf("%v\n", Lib.ListToString(args1)) fmt.Printf("%v\n", Lib.ListToString(args2)) if args1.Len() != args2.Len() { - fmt.Println("Failure Longueur args ") return Failure() } for i := range args1.GetSlice() { s = robinsonUnify(args1.At(i).Copy(), args2.At(i).Copy(), s) if s.Equals(Failure()) { - fmt.Println("Failure RobinsonJspQuoi ") return Failure() } } From cd80f6c740537c57324aa02d678d621ec8e304c0 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Mon, 27 Apr 2026 09:25:10 +0200 Subject: [PATCH 06/23] Additionnal Test + Commit before update function retrieve --- .../discrimination-trees.go | 70 ++-- src/Unif/discriminationtree/dt_test.go | 338 +++++++++++++++++- 2 files changed, 356 insertions(+), 52 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index c9c87f05..7c335f7f 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -240,7 +240,8 @@ func (s SymbolType) Equals(target SymbolType) bool { if ok := s.getSymbol().Equals(target.getSymbol()); ok { if s.GetArity() != target.GetArity() { - Glob.Anomaly("Pred Error", "Same predicat but different arity") + fmt.Printf("Symbol Arity : %d, Target Arity : %d", s.GetArity(), target.GetArity()) + Glob.Anomaly("Pred Error", "Same predicat but different arity ") } else { return true } @@ -318,8 +319,9 @@ func GetSubTermLength(seq []SymbolType) int { } -func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQuery []SymbolType) Lib.List[AST.Pred] { +func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQuery []SymbolType) (subs []subst.Substitution, preds Lib.List[AST.Pred]) { + var monTableau []subst.Substitution res := Lib.NewList[AST.Pred]() // End of recursion @@ -329,25 +331,29 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue for _, child := range dNode.children.GetSlice() { newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term - matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery) + childSubs, matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery) + monTableau = append(monTableau, childSubs...) res.Append(matches.GetSlice()...) } - return res + return monTableau, res } -func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) Lib.List[AST.Pred] { +func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) (subs []subst.Substitution, preds Lib.List[AST.Pred]) { seq := parseFormula(t).GetSlice() - return dNode.retrieveRec(seq) + subs, preds = dNode.retrieveRec(seq) + return subs, preds } -func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] { +func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) ([]subst.Substitution, Lib.List[AST.Pred]) { + var monTableau []subst.Substitution res := Lib.NewList[AST.Pred]() + if len(seq) == 0 { // End of recursion res.Append(dNode.leafFor.GetSlice()...) // Append leafFor of this node - return res + return monTableau, res } symQuery := seq[0] // First Element @@ -356,10 +362,9 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] isExactMatch := child.symbol.Equals(symQuery) - // Exact Match - if isExactMatch { - - matches := child.retrieveRec(seq[1:]) // Exact Match -> Search next element + if isExactMatch { // Exact Match + childSubs, matches := child.retrieveRec(seq[1:]) // Exact Match -> Search next element + monTableau = append(monTableau, childSubs...) res.Append(matches.GetSlice()...) } @@ -373,7 +378,10 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 skip := GetSubTermLength(seq) if skip <= len(seq) { // Security to prevent segfault - matches := child.retrieveRec(seq[skip:]) + + monTableau = append(monTableau, subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol())) + childSubs, matches := child.retrieveRec(seq[skip:]) + monTableau = append(monTableau, childSubs...) res.Append(matches.GetSlice()...) } @@ -382,12 +390,14 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) Lib.List[AST.Pred] // Reverse of the situation with the previous if. // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode - matches := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:]) + monTableau = append(monTableau, subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild)) // Create a new substitution + childSubs, matches := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:]) + monTableau = append(monTableau, childSubs...) res.Append(matches.GetSlice()...) } } - return res + return monTableau, res } func (dNode DiscriminationNode) Copy() subst.DataStructure { @@ -399,7 +409,6 @@ func (dNode DiscriminationNode) Copy() subst.DataStructure { } newLeafFor := Lib.ListCpy(dNode.leafFor) - return DiscriminationNode{symbol: dNode.symbol, children: newChildMaster, leafFor: newLeafFor} } @@ -424,10 +433,9 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST return dNode } -// TODO func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { - candidates := dNode.RetrieveUnifiables(inputFormula) + valSubst, candidates := dNode.RetrieveUnifiables(inputFormula) var mixed []subst.MixedSubstitutions var found bool @@ -440,20 +448,18 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe // For Robinson queryTerm := subst.TransformPred(predFormula) + initialSubst := subst.Substitutions(valSubst) for _, possibleMatch := range candidates.GetSlice() { - possibleMatchTerm := subst.TransformPred(possibleMatch) // Pred -> Term for Robinson - emptySubst := subst.Substitutions{} - finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, emptySubst) // Call Robinson + possibleMatchTerm := subst.TransformPred(possibleMatch) // Pred -> Term for Robinson + finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson if finalSubst.Equals(subst.Failure()) { fmt.Println("-------------------------") - fmt.Println("Echec Substitution") + fmt.Println("Substitution FAILURE") fmt.Println("-------------------------") - } - - if !finalSubst.Equals(subst.Failure()) { + } else { found = true matching := subst.MakeMatchingSubstitutions(possibleMatch, finalSubst) // constructor mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return @@ -462,15 +468,13 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe return found, mixed } -// TODO func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSubstitutions) { var mixed []subst.MixedTermSubstitutions var found bool seq := parseTerm(t).GetSlice() - candidates := dNode.retrieveRec(seq) - + _, candidates := dNode.retrieveRec(seq) for _, possibleMatch := range candidates.GetSlice() { candidateTerm := subst.TransformPred(possibleMatch) @@ -479,21 +483,21 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu if !finalSubst.Equals(subst.Failure()) { found = true - mixMatch := subst.MixMatchSubstitutions{ Tof: Lib.MkLeft[AST.Term, AST.Form](candidateTerm), Subst: finalSubst, } mixed = append(mixed, mixMatch.ToMixedTerm()) + } else { + fmt.Println("-------------------------") + fmt.Println("Substitution FAILURE") + fmt.Println("-------------------------") } - } - return found, mixed - } func (dNode DiscriminationNode) MakeDataStruct(Formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { - // Gerer cas possitif ou negatif + // Gerer cas positif ou negatif return dNode.InsertFormulaListToDataStructure(Formulas) } diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index d64e7dc3..f4226016 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -193,8 +193,8 @@ func initTestVariable() { // Fun gx = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x)) ga = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) - fx = AST.MakerFun(f_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) - fy = AST.MakerFun(f_id, Lib.MkListV(y.GetTy()), Lib.MkListV[AST.Term](y)) + fx = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x)) + fy = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y)) fa = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) fb = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) fc = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c)) @@ -449,15 +449,18 @@ func TestRetrieve(t *testing.T) { tree := NewNode() tree = tree.Insert(pax.(AST.Pred)) // Cast tree = tree.Insert(pay.(AST.Pred)) - tree.Print() - results := tree.RetrieveUnifiables(pab) + test, results := tree.RetrieveUnifiables(pab) if results.Len() == 0 { fmt.Println("C'est la merde") } else { fmt.Printf("Match : %d \n", results.Len()) } + for _, pred := range results.GetSlice() { - fmt.Println(pred.ToString()) + fmt.Println("List de Pred : ", pred.ToString()) + } + for _, pred := range test { + fmt.Println("List de substitution", pred.ToString()) } } @@ -526,7 +529,7 @@ func TestSkipTreeTermAndContinue(t *testing.T) { results := Lib.NewList[AST.Pred]() for _, child := range nodeP.getChildren().GetSlice() { - matches := child.SkipTreeTermAndContinue(child.GetArity(), remainingQuery) + _, matches := child.SkipTreeTermAndContinue(child.GetArity(), remainingQuery) results.Append(matches.GetSlice()...) } @@ -544,7 +547,7 @@ func TestRetrieveUnifiables(t *testing.T) { tree := NewNode() tree = tree.Insert(pax.(AST.Pred)) tree = tree.Insert(pba.(AST.Pred)) - res := tree.RetrieveUnifiables(pay) + _, res := tree.RetrieveUnifiables(pay) if res.Len() != 1 { t.Fatalf("Should be only 1") @@ -579,6 +582,9 @@ func TestUnify(t *testing.T) { for _, elem := range mix { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -590,6 +596,9 @@ func TestUnify(t *testing.T) { for _, elem := range mix1 { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -613,6 +622,9 @@ func TestUnify(t *testing.T) { for _, elem := range mix3 { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should return empty list") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -624,6 +636,9 @@ func TestUnify(t *testing.T) { for _, elem := range mix4 { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -635,6 +650,9 @@ func TestUnify(t *testing.T) { for _, elem5 := range mix5 { fmt.Println(elem5.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -646,6 +664,9 @@ func TestUnify(t *testing.T) { for _, elem := range mix6 { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -657,30 +678,38 @@ func TestUnify(t *testing.T) { for _, elem := range mix7 { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } fmt.Println("-----END TEST-----") fmt.Println() fmt.Println("-----TEST 09 -----") + fmt.Println("-----EXPECTED FAILURE -----") + tree8 := NewNode() tree8 = tree8.Insert(pxx.(AST.Pred)) - var mix8 []subst.MixedSubstitutions - _, mix8 = tree8.Unify(pab) - if len(mix8) != 0 { - t.Fatalf("Got %d elements instead of 0", len(mix8)) + val1, _ := tree8.Unify(pab) + fmt.Println("-----EXPECTED FAILURE -----") + + if val1 { + t.Fatalf("This test must fail") } fmt.Println("-----END TEST-----") fmt.Println() fmt.Println("-----TEST 10 -----") + fmt.Println("-----EXPECTED FAILURE -----") + tree9 := NewNode() tree9 = tree9.Insert(pba.(AST.Pred)) tree9 = tree9.Insert(pab.(AST.Pred)) - var mix9 []subst.MixedSubstitutions - _, mix9 = tree9.Unify(pxx) - if len(mix9) != 0 { - t.Fatalf("Got %d elements instead of 0", len(mix9)) + val2, _ := tree9.Unify(pxx) + if val2 { + t.Fatalf(" This test must fail ") } + fmt.Println("-----EXPECTED FAILURE -----") fmt.Println("-----END TEST-----") fmt.Println() @@ -698,13 +727,15 @@ func TestUnify(t *testing.T) { fmt.Println() fmt.Println("-----TEST 12 -----") + fmt.Println("-----EXPECTED FAILURE -----") + tree11 := NewNode() tree11 = tree11.Insert(pab.(AST.Pred)) - var mix11 []subst.MixedSubstitutions - _, mix11 = tree11.Unify(pxx) - if len(mix11) != 0 { - t.Fatalf("Got %d elements instead of 0", len(mix11)) + val11, _ := tree11.Unify(pxx) + if val11 { + t.Fatalf("This test must fail ") } + fmt.Println("-----EXPECTED FAILURE -----") fmt.Println("-----END TEST-----") fmt.Println() @@ -716,6 +747,260 @@ func TestUnify(t *testing.T) { for _, elem := range mix12 { fmt.Println(elem.ToString()) } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") + } + fmt.Println("-----END TEST-----") + fmt.Println() + +} + +func TestUnifyTerm(t *testing.T) { + + fmt.Println("-----TEST 01 -----") + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) + + var val bool + var mix []subst.MixedTermSubstitutions + val, mix = tree.UnifyTerm(queryTerm) + + if val { + for _, elem := range mix { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 02 -----") + tree2 := NewNode() + tree2 = tree2.Insert(pax.(AST.Pred)) + queryTerm2 := subst.TransformPred(pab.(AST.Pred)) + + var val2 bool + var mix2 []subst.MixedTermSubstitutions + val2, mix2 = tree2.UnifyTerm(queryTerm2) + + if val2 { + for _, elem := range mix2 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 03 -----") + tree3 := NewNode() + tree3 = tree3.Insert(pa.(AST.Pred)) + queryTerm3 := subst.TransformPred(pb.(AST.Pred)) + + var val3 bool + val3, _ = tree3.UnifyTerm(queryTerm3) + + if val3 { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 04 -----") + tree4 := NewNode() + tree4 = tree4.Insert(pa.(AST.Pred)) + queryTerm4 := subst.TransformPred(pa.(AST.Pred)) + + var val4 bool + var mix4 []subst.MixedTermSubstitutions + val4, mix4 = tree4.UnifyTerm(queryTerm4) + + if val4 { + for _, elem := range mix4 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 05 -----") + tree5 := NewNode() + tree5 = tree5.Insert(pax.(AST.Pred)) + queryTerm5 := subst.TransformPred(pafy.(AST.Pred)) + + var val5 bool + var mix5 []subst.MixedTermSubstitutions + val5, mix5 = tree5.UnifyTerm(queryTerm5) + + if val5 { + for _, elem := range mix5 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 06 -----") + tree6 := NewNode() + tree6 = tree6.Insert(pafx.(AST.Pred)) + queryTerm6 := subst.TransformPred(pafy.(AST.Pred)) + + var val6 bool + var mix6 []subst.MixedTermSubstitutions + val6, mix6 = tree6.UnifyTerm(queryTerm6) + + if val6 { + for _, elem := range mix6 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 07 -----") + tree7 := NewNode() + tree7 = tree7.Insert(px.(AST.Pred)) + queryTerm7 := subst.TransformPred(py.(AST.Pred)) + + var val7 bool + var mix7 []subst.MixedTermSubstitutions + val7, mix7 = tree7.UnifyTerm(queryTerm7) + + if val7 { + for _, elem := range mix7 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 08 -----") + tree8 := NewNode() + tree8 = tree8.Insert(pxy.(AST.Pred)) + queryTerm8 := subst.TransformPred(pab.(AST.Pred)) + + var val8 bool + var mix8 []subst.MixedTermSubstitutions + val8, mix8 = tree8.UnifyTerm(queryTerm8) + + if val8 { + for _, elem := range mix8 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 09 -----") + fmt.Println("-----EXPECTED FAILURE -----") + + tree9 := NewNode() + tree9 = tree9.Insert(pxx.(AST.Pred)) + queryTerm9 := subst.TransformPred(pab.(AST.Pred)) + + var val9 bool + val9, _ = tree9.UnifyTerm(queryTerm9) + + if val9 { + t.Fatalf("Unify Failure") + } + fmt.Println("-----EXPECTED FAILURE -----") + + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 10 -----") + fmt.Println("-----EXPECTED FAILURE -----") + + tree10 := NewNode() + tree10 = tree10.Insert(pba.(AST.Pred)) + tree10 = tree10.Insert(pab.(AST.Pred)) + queryTerm10 := subst.TransformPred(pxx.(AST.Pred)) + + var val10 bool + var mix10 []subst.MixedTermSubstitutions + val10, mix10 = tree10.UnifyTerm(queryTerm10) + + if val10 { + t.Fatalf("Got %d elements instead of 0", len(mix10)) + } else { + fmt.Println("Unify Failure (Expected)") + } + fmt.Println("-----EXPECTED FAILURE -----") + + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 11 -----") + tree11 := NewNode() + tree11 = tree11.Insert(pb.(AST.Pred)) + tree11 = tree11.Insert(pa.(AST.Pred)) + tree11 = tree11.Insert(pfx.(AST.Pred)) + queryTerm11 := subst.TransformPred(py.(AST.Pred)) + + var val11 bool + var mix11 []subst.MixedTermSubstitutions + val11, mix11 = tree11.UnifyTerm(queryTerm11) + + if val11 { + for _, elem := range mix11 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 12 -----") + fmt.Println("-----EXPECTED FAILURE -----") + + tree12 := NewNode() + tree12 = tree12.Insert(pab.(AST.Pred)) + queryTerm12 := subst.TransformPred(pxx.(AST.Pred)) + + var val12 bool + var mix12 []subst.MixedTermSubstitutions + val12, mix12 = tree12.UnifyTerm(queryTerm12) + + if val12 { + t.Fatalf("Got %d elements instead of 0", len(mix12)) + } else { + fmt.Println("Unify Failure (Expected)") + } + fmt.Println("-----EXPECTED FAILURE -----") + fmt.Println("-----END TEST-----") + fmt.Println() + + fmt.Println("-----TEST 13 -----") + tree13 := NewNode() + tree13 = tree13.Insert(pggab.(AST.Pred)) + queryTerm13 := subst.TransformPred(pxy.(AST.Pred)) + + var val13 bool + var mix13 []subst.MixedTermSubstitutions + val13, mix13 = tree13.UnifyTerm(queryTerm13) + + if val13 { + for _, elem := range mix13 { + fmt.Println(" ->", elem.ToString()) + } + } else { + t.Fatalf("Unify Failure") + } fmt.Println("-----END TEST-----") fmt.Println() @@ -743,3 +1028,18 @@ func TestMakeDataStruct(t *testing.T) { tree6.Print() } + +func TestRetrieveFail(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + test, elem := tree.RetrieveUnifiables(pab) + + for _, elem := range test { + fmt.Println("Substs : ", elem.ToString()) + } + for _, elembis := range elem.GetSlice() { + fmt.Println("Pred : ", elembis.ToString()) + } + +} From e4176395dceab23d720074a7692f1d9b9944d027 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Tue, 28 Apr 2026 16:03:30 +0200 Subject: [PATCH 07/23] Implementation of all the basics function for the discrimination Tree + Test for all functions --- .../discrimination-trees.go | 224 ++++++---- src/Unif/discriminationtree/dt_test.go | 383 ++++++++++++------ 2 files changed, 424 insertions(+), 183 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 7c335f7f..a22ee6cd 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -99,10 +99,6 @@ func (dNode *DiscriminationNode) setSymbol(symbol SymbolType) { dNode.symbol = symbol } -func (dNode DiscriminationNode) getChildren() Lib.List[DiscriminationNode] { - return dNode.children -} - func (dNode DiscriminationNode) GetArity() int { return dNode.symbol.GetArity() } @@ -119,7 +115,40 @@ func (dNode DiscriminationNode) ToString() string { return dNode.getSymbol().ToString() } -//[-----PARSER-----] +type CandidatResult struct { + Pred AST.Pred + Subs subst.Substitutions +} + +func (Candidat CandidatResult) getPred() AST.Pred { + return Candidat.Pred +} + +func (Candidat CandidatResult) GetSubs() subst.Substitutions { + return Candidat.Subs +} + +func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { + return CandidatResult{ + Pred: p, + Subs: sub, + } +} + +func MakeCandidatResultWithPred(p AST.Pred) CandidatResult { + return CandidatResult{ + Pred: p, + Subs: subst.Substitutions{}, + } +} + +/*****************************/ +/* End Structures definition */ +/*****************************/ + +/*****************************/ +/*********** Parse ***********/ +/*****************************/ func parseFormula(formula AST.Form) Lib.List[SymbolType] { res := Lib.NewList[SymbolType]() @@ -157,7 +186,13 @@ func parseTerm(t AST.Term) Lib.List[SymbolType] { return res } -//[---FIN PARSER---] +/*****************************/ +/********* End Parse *********/ +/*****************************/ + +/*****************************/ +/********* Transform *********/ +/*****************************/ func FirstElementToSymbolType(t AST.Term) SymbolType { switch t := t.(type) { @@ -189,6 +224,10 @@ func TermToNode(t AST.Term) DiscriminationNode { } } +/*****************************/ +/******* End Transform *******/ +/*****************************/ + func (dNode DiscriminationNode) Print() { for _, child := range dNode.children.GetSlice() { child.displayRec(2) // Magic Number @@ -217,25 +256,7 @@ func (dNode DiscriminationNode) displayRec(indent int) { } } -func (dNode DiscriminationNode) GetASTAtDepth(depth int) []SymbolType { - - res := []SymbolType{} - if depth == 0 { - if !dNode.IsEmpty() { - res = append(res, dNode.symbol) - } - return res - } - - for _, child := range dNode.children.GetSlice() { - res = append(res, child.GetASTAtDepth(depth-1)...) - } - - return res - -} - -// Equals between tow SymbolType +// Equals between two SymbolType func (s SymbolType) Equals(target SymbolType) bool { if ok := s.getSymbol().Equals(target.getSymbol()); ok { @@ -319,53 +340,54 @@ func GetSubTermLength(seq []SymbolType) int { } -func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQuery []SymbolType) (subs []subst.Substitution, preds Lib.List[AST.Pred]) { +func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []CandidatResult { - var monTableau []subst.Substitution - res := Lib.NewList[AST.Pred]() + var subs []CandidatResult // End of recursion if needed == 0 { - return dNode.retrieveRec(remainingQuery) + return dNode.retrieveRec(remainingQuery, substitutions) } for _, child := range dNode.children.GetSlice() { newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term - childSubs, matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery) - monTableau = append(monTableau, childSubs...) - res.Append(matches.GetSlice()...) + matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery, substitutions) + subs = append(subs, matches...) } - return monTableau, res + return subs } -func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) (subs []subst.Substitution, preds Lib.List[AST.Pred]) { +func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { seq := parseFormula(t).GetSlice() - subs, preds = dNode.retrieveRec(seq) - return subs, preds + Env := subst.Substitutions{} + return dNode.retrieveRec(seq, Env) } -func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) ([]subst.Substitution, Lib.List[AST.Pred]) { +func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { - var monTableau []subst.Substitution - res := Lib.NewList[AST.Pred]() + var results []CandidatResult + // Voir pour la suite, est-ce necessaire de faire ceci alors que Robinson a déjà vérifier les blocs précédent, notamment celui juste avant de ce rendre compte que cette partie va fonctionner ou non if len(seq) == 0 { // End of recursion - res.Append(dNode.leafFor.GetSlice()...) // Append leafFor of this node - return monTableau, res + for _, p := range dNode.leafFor.GetSlice() { + results = append(results, MakeCandidat(p, currentEnv)) + } + return results } symQuery := seq[0] // First Element + // fmt.Println("RetrieveRec SymQuery", symQuery.getSymbol().ToString()) + for _, child := range dNode.children.GetSlice() { isExactMatch := child.symbol.Equals(symQuery) - + // fmt.Println("Exact Match", child.getSymbol()) if isExactMatch { // Exact Match - childSubs, matches := child.retrieveRec(seq[1:]) // Exact Match -> Search next element - monTableau = append(monTableau, childSubs...) - res.Append(matches.GetSlice()...) + matches := child.retrieveRec(seq[1:], currentEnv) // Exact Match -> Search next element + results = append(results, matches...) } symChild := child.getSymbol() @@ -377,12 +399,32 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) ([]subst.Substitut // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 skip := GetSubTermLength(seq) + + // fmt.Println("Longueur du skip", skip) + if skip <= len(seq) { // Security to prevent segfault - monTableau = append(monTableau, subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol())) - childSubs, matches := child.retrieveRec(seq[skip:]) - monTableau = append(monTableau, childSubs...) - res.Append(matches.GetSlice()...) + var mergedSub subst.Substitutions + if skip == 1 { + currentSub := subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol()) + tmp3 := subst.Substitutions{currentSub} + // Ok Commat Idoms doesn't works because ?????????????????????????????? + if len(currentEnv) == 0 { + mergedSub = tmp3 + } else { + mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) + } + + } else { + mergedSub = currentEnv + } + + // Verify + if !mergedSub.Equals(subst.Failure()) { + matches := child.retrieveRec(seq[skip:], mergedSub) + results = append(results, matches...) + } + } // First element is a meta @@ -390,14 +432,35 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType) ([]subst.Substitut // Reverse of the situation with the previous if. // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode - monTableau = append(monTableau, subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild)) // Create a new substitution - childSubs, matches := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:]) - monTableau = append(monTableau, childSubs...) - res.Append(matches.GetSlice()...) + + var mergedSub subst.Substitutions + if child.GetArity() == 0 { + currentSub := subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild) // Create a new substitution + tmp3 := subst.Substitutions{currentSub} + if len(currentEnv) == 0 { + mergedSub = tmp3 + } else { + mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) + } + } else { + mergedSub = currentEnv + } + + if !mergedSub.Equals(subst.Failure()) { + childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) + results = append(results, childResults...) + + // for _, elem := range results { + // fmt.Println("elem pred Query meta", elem.getPred().ToString()) + // fmt.Println("elem pred Query meta", elem.GetSubs().ToString()) + // } + } + + } else { + continue } } - - return monTableau, res + return results } func (dNode DiscriminationNode) Copy() subst.DataStructure { @@ -425,7 +488,7 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST fmt.Println("Cas not") switch newForm := nf.GetForm().(type) { // Get the type AST.Form case AST.Pred: - fmt.Println("Cas not apres cast pour Pred", newForm) + fmt.Println("Cas not apres cast pour Pred", newForm.ToString()) dNode = dNode.Insert(newForm) } } @@ -435,7 +498,7 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { - valSubst, candidates := dNode.RetrieveUnifiables(inputFormula) + candidates := dNode.RetrieveUnifiables(inputFormula) var mixed []subst.MixedSubstitutions var found bool @@ -446,23 +509,26 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe return false, nil } - // For Robinson - queryTerm := subst.TransformPred(predFormula) - initialSubst := subst.Substitutions(valSubst) + queryTerm := subst.TransformPred(predFormula) // For Robinson - for _, possibleMatch := range candidates.GetSlice() { + for _, possibleMatch := range candidates { - possibleMatchTerm := subst.TransformPred(possibleMatch) // Pred -> Term for Robinson + initialSubst := subst.Substitutions{} + possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term for Robinson finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson + // fmt.Println("Unify initialSubst", initialSubst.ToString()) + // fmt.Println("Unify possibleMatchTerm", possibleMatchTerm.ToString()) + // fmt.Println("Unify finalSubst", finalSubst.ToString()) + if finalSubst.Equals(subst.Failure()) { fmt.Println("-------------------------") fmt.Println("Substitution FAILURE") fmt.Println("-------------------------") } else { found = true - matching := subst.MakeMatchingSubstitutions(possibleMatch, finalSubst) // constructor - mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return + matching := subst.MakeMatchingSubstitutions(inputFormula, finalSubst) // constructor + mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return } } return found, mixed @@ -474,17 +540,35 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu var found bool seq := parseTerm(t).GetSlice() - _, candidates := dNode.retrieveRec(seq) - for _, possibleMatch := range candidates.GetSlice() { - candidateTerm := subst.TransformPred(possibleMatch) + // for _, elem := range seq { + // fmt.Println("seq", elem.getSymbol().ToString()) + // } + + candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) + + // for _, elem := range candidates { + // fmt.Println("element", elem.getPred().ToString()) + // } + + for _, possibleMatch := range candidates { + + // fmt.Println("Candidat", possibleMatch.Pred.ToString(), possibleMatch.Subs.ToString()) + + candidateTerm := subst.TransformPred(possibleMatch.getPred()) emptySubst := subst.Substitutions{} - finalSubst := subst.AddUnification(candidateTerm, t, emptySubst) // Call Robinson + // fmt.Println("=> candidateTerm : ", candidateTerm.ToString()) + // fmt.Println("=> t : ", t.ToString()) + // fmt.Println("=> EmptySubset : ", emptySubst.ToString()) + + finalSubst := subst.AddUnification(t, candidateTerm, emptySubst) // Call Robinson + + // fmt.Println("finalSubst", finalSubst.ToString()) if !finalSubst.Equals(subst.Failure()) { found = true mixMatch := subst.MixMatchSubstitutions{ - Tof: Lib.MkLeft[AST.Term, AST.Form](candidateTerm), + Tof: Lib.MkLeft[AST.Term, AST.Form](t), Subst: finalSubst, } mixed = append(mixed, mixMatch.ToMixedTerm()) @@ -498,6 +582,6 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu } func (dNode DiscriminationNode) MakeDataStruct(Formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { - // Gerer cas positif ou negatif + // Gérer cas positif ou negatif return dNode.InsertFormulaListToDataStructure(Formulas) } diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index f4226016..7641fb9f 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -136,7 +136,7 @@ var f_a_fbc AST.Fun // Form var pggab AST.Form -var pac AST.Form +var not_pac AST.Form var pa AST.Form var pb AST.Form @@ -152,9 +152,11 @@ var pxy AST.Form var pxx AST.Form var px AST.Form var py AST.Form +var pxc AST.Form var pfx AST.Form var pafx AST.Form var pafy AST.Form +var pfac AST.Form var not_pcd AST.Form @@ -220,7 +222,7 @@ func initTestVariable() { // Predicates pggab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gga, b)) - pac = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, c))) + not_pac = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, c))) not_pc = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c))) pab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) pabc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b, c)) @@ -232,9 +234,11 @@ func initTestVariable() { pxx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, x)) px = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x)) py = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y)) + pxc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, c)) pfx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fx)) pafy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fy)) pafx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fx)) + pfac = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fa, c)) not_pcd = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, d))) pa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) pb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) @@ -269,7 +273,7 @@ func TestMain(m *testing.M) { os.Exit(code) } -func TestFirstElementToSymbol(t *testing.T) { +func TestFirstElementToSymbolType(t *testing.T) { tree := NewNode() // NewNode create a symbolType with arity == -1. Arity will be 0 if create normaly -> lead to false negative argsA := pa.GetSubTerms() @@ -406,7 +410,35 @@ func TestPrintDoublonCheck(t *testing.T) { } -func TestParser(t *testing.T) { +func TestParseFormula(t *testing.T) { + + tmp := parseFormula(pax) + for _, value := range tmp.GetSlice() { + if value.getSymbol().ToString() == "P" { + if value.GetArity() != 2 { + t.Fatalf("Arrity Error") + } + } else if value.getSymbol().ToString() == "a" { + if value.GetArity() != 0 { + t.Fatalf("Arrity Error") + } + } else if value.getSymbol().ToString() == "X" { + if value.GetArity() != 0 { + t.Fatalf("Arrity Error") + } + } else { + t.Fatalf("Supposed to have only \"P\", \"a\" or \"X\" ") + } + } + + tmp2 := parseFormula(not_pac) + for _, value := range tmp2.GetSlice() { + fmt.Println(value.symbol.ToMeta()) + } + +} + +func TestParseTerm(t *testing.T) { var tmp []string var tmp2 []string @@ -447,20 +479,47 @@ func TestParser(t *testing.T) { func TestRetrieve(t *testing.T) { tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) // Cast + tree = tree.Insert(pax.(AST.Pred)) tree = tree.Insert(pay.(AST.Pred)) - test, results := tree.RetrieveUnifiables(pab) - if results.Len() == 0 { - fmt.Println("C'est la merde") + results := tree.RetrieveUnifiables(pab) + if len(results) == 0 { + t.Fatalf("Returned 0 element") } else { - fmt.Printf("Match : %d \n", results.Len()) + fmt.Printf("returned %d element", len(results)) } - for _, pred := range results.GetSlice() { - fmt.Println("List de Pred : ", pred.ToString()) + for _, result := range results { + fmt.Println("Pred : ", result.getPred().ToString()) + for _, element := range result.GetSubs() { + fmt.Println("Subs : ", element.ToString()) + } } - for _, pred := range test { - fmt.Println("List de substitution", pred.ToString()) + fmt.Println() + + tree2 := NewNode() + tree2 = tree2.Insert(pax.(AST.Pred)) + results2 := tree2.RetrieveUnifiables(pba) + if len(results2) != 0 { + t.Fatalf(" Not supposed to have Unifiable element") + } + + fmt.Println("----- EMPTY -----") + fmt.Println("----- EMPTY -----") + + fmt.Println() + + tree3 := NewNode() + tree3 = tree3.Insert(pxy.(AST.Pred)) + results3 := tree3.RetrieveUnifiables(pab) + if len(results3) != 1 { + t.Fatalf("Supposed to have 2 unifiables") + } + + for _, result := range results3 { + fmt.Println("Pred : ", result.getPred().ToString()) + for _, element := range result.GetSubs() { + fmt.Println("Subs : ", element.ToString()) + } } } @@ -522,24 +581,47 @@ func TestSkipTreeTermAndContinue(t *testing.T) { tree := NewNode() tree = tree.Insert(pab.(AST.Pred)) - tree = tree.Insert(pggab.(AST.Pred)) - - nodeP := tree.getChildren().GetSlice()[0] - remainingQuery := parseTerm(b).GetSlice() - results := Lib.NewList[AST.Pred]() + needed := 1 + emptyQuery := []SymbolType{} + emptyEnv := subst.Substitutions{} + results := tree.SkipTreeTermAndContinue(needed, emptyQuery, emptyEnv) + if len(results) != 1 { + t.Fatalf(" Expected 1 Element, got %d", len(results)) + } - for _, child := range nodeP.getChildren().GetSlice() { - _, matches := child.SkipTreeTermAndContinue(child.GetArity(), remainingQuery) - results.Append(matches.GetSlice()...) + tree2 := NewNode() + tree2 = tree2.Insert(px.(AST.Pred)) + needed2 := 1 + emptyQuery2 := []SymbolType{} + emptyEnv2 := subst.Substitutions{} + results2 := tree2.SkipTreeTermAndContinue(needed2, emptyQuery2, emptyEnv2) + if len(results2) != 1 { + t.Fatalf(" Expected 1 Element, got %d", len(results2)) } - if results.Len() != 2 { - t.Fatalf("error") + tree3 := NewNode() + tree3 = tree3.Insert(pba.(AST.Pred)) + tree3 = tree3.Insert(pab.(AST.Pred)) + needed3 := 1 + emptyQuery3 := []SymbolType{} + emptyEnv3 := subst.Substitutions{} + results3 := tree3.SkipTreeTermAndContinue(needed3, emptyQuery3, emptyEnv3) + if len(results3) != 2 { + t.Fatalf(" Expected 2 Element, got %d", len(results3)) } - for _, res := range results.GetSlice() { - fmt.Println("=>", res.ToString()) + tree4 := NewNode() + tree4 = tree4.Insert(pba.(AST.Pred)) + tree4 = tree4.Insert(pab.(AST.Pred)) + tree4 = tree4.Insert(pca.(AST.Pred)) + needed4 := 1 + emptyQuery4 := []SymbolType{} + emptyEnv4 := subst.Substitutions{} + results4 := tree4.SkipTreeTermAndContinue(needed4, emptyQuery4, emptyEnv4) + if len(results4) != 3 { + t.Fatalf(" Expected 3 Element, got %d", len(results4)) } + } func TestRetrieveUnifiables(t *testing.T) { @@ -547,17 +629,30 @@ func TestRetrieveUnifiables(t *testing.T) { tree := NewNode() tree = tree.Insert(pax.(AST.Pred)) tree = tree.Insert(pba.(AST.Pred)) - _, res := tree.RetrieveUnifiables(pay) - - if res.Len() != 1 { + candidat := tree.RetrieveUnifiables(pay) + if len(candidat) != 1 { t.Fatalf("Should be only 1") } - for _, pred := range res.GetSlice() { + tree = tree.Insert(pafx.(AST.Pred)) + candidat = tree.RetrieveUnifiables(pay) + if len(candidat) != 2 { + t.Fatalf("Should be only 2") + } - fmt.Println(len(res.GetSlice())) // Doublon - fmt.Println("=>", pred.ToString()) + tree = tree.Insert(pafy.(AST.Pred)) + candidat = tree.RetrieveUnifiables(pay) + if len(candidat) != 3 { + t.Fatalf("Should be only 3") } + + tree2 := NewNode() + tree2 = tree2.Insert(pa.(AST.Pred)) + candidat2 := tree2.RetrieveUnifiables(pb) + if len(candidat2) != 0 { + t.Fatalf("Should be 0 because pa and pb can't be unified") + } + } func TestCopy(t *testing.T) { @@ -569,7 +664,6 @@ func TestCopy(t *testing.T) { if !res2 { t.Fatalf("Tree2 is not a copy, it s only a pointer to tree1") } - } func TestUnify(t *testing.T) { @@ -585,6 +679,12 @@ func TestUnify(t *testing.T) { if len(mix) != 1 { t.Fatalf("Should have a found 1 unification") } + for _, elem := range mix { + if elem.GetForm().ToString() != "P(a, Y)" { + t.Fatalf("Fatal Failure, shouhd have P(a, Y)") + } + } + fmt.Println("-----END TEST-----") fmt.Println() @@ -596,9 +696,15 @@ func TestUnify(t *testing.T) { for _, elem := range mix1 { fmt.Println(elem.ToString()) } - if len(mix) != 1 { + if len(mix1) != 1 { t.Fatalf("Should have a found 1 unification") } + for _, elem := range mix1 { + if elem.GetForm().ToString() != "P(a, b)" { + t.Fatalf("Form must be P(a, b)") + } + } + fmt.Println("-----END TEST-----") fmt.Println() @@ -622,9 +728,14 @@ func TestUnify(t *testing.T) { for _, elem := range mix3 { fmt.Println(elem.ToString()) } - if len(mix) != 1 { + if len(mix3) != 1 { t.Fatalf("Should return empty list") } + for _, elem := range mix3 { + if elem.GetForm().ToString() != "P(a)" { + t.Fatalf("Must be P(a)") + } + } fmt.Println("-----END TEST-----") fmt.Println() @@ -636,9 +747,14 @@ func TestUnify(t *testing.T) { for _, elem := range mix4 { fmt.Println(elem.ToString()) } - if len(mix) != 1 { + if len(mix4) != 1 { t.Fatalf("Should have a found 1 unification") } + for _, elem := range mix4 { + if elem.GetForm().ToString() != "P(a, f(Y))" { + t.Fatalf("Return must be P(a, f(Y))") + } + } fmt.Println("-----END TEST-----") fmt.Println() @@ -650,9 +766,14 @@ func TestUnify(t *testing.T) { for _, elem5 := range mix5 { fmt.Println(elem5.ToString()) } - if len(mix) != 1 { + if len(mix5) != 1 { t.Fatalf("Should have a found 1 unification") } + for _, elem := range mix5 { + if elem.GetForm().ToString() != "P(a, f(Y))" { + t.Fatalf("Must return P(a, f(Y))") + } + } fmt.Println("-----END TEST-----") fmt.Println() @@ -664,9 +785,15 @@ func TestUnify(t *testing.T) { for _, elem := range mix6 { fmt.Println(elem.ToString()) } - if len(mix) != 1 { + if len(mix6) != 1 { t.Fatalf("Should have a found 1 unification") } + for _, elem := range mix6 { + fmt.Println(elem.GetForm().ToString()) + if elem.GetForm().ToString() != "P(Y)" { + t.Fatalf("Must return P(Y)") + } + } fmt.Println("-----END TEST-----") fmt.Println() @@ -678,9 +805,14 @@ func TestUnify(t *testing.T) { for _, elem := range mix7 { fmt.Println(elem.ToString()) } - if len(mix) != 1 { + if len(mix7) != 1 { t.Fatalf("Should have a found 1 unification") } + for _, elem := range mix7 { + if elem.GetForm().ToString() != "P(a, b)" { + t.Fatalf("Must return P(a, b)") + } + } fmt.Println("-----END TEST-----") fmt.Println() @@ -689,10 +821,13 @@ func TestUnify(t *testing.T) { tree8 := NewNode() tree8 = tree8.Insert(pxx.(AST.Pred)) - val1, _ := tree8.Unify(pab) + val8, mix8 := tree8.Unify(pab) fmt.Println("-----EXPECTED FAILURE -----") - if val1 { + if len(mix8) != 0 { + fmt.Println("return must Be empty ") + } + if val8 { t.Fatalf("This test must fail") } @@ -705,10 +840,13 @@ func TestUnify(t *testing.T) { tree9 := NewNode() tree9 = tree9.Insert(pba.(AST.Pred)) tree9 = tree9.Insert(pab.(AST.Pred)) - val2, _ := tree9.Unify(pxx) - if val2 { + val9, mix9 := tree9.Unify(pxx) + if val9 { t.Fatalf(" This test must fail ") } + if len(mix9) != 0 { + t.Fatal("Return must be empty") + } fmt.Println("-----EXPECTED FAILURE -----") fmt.Println("-----END TEST-----") fmt.Println() @@ -720,8 +858,14 @@ func TestUnify(t *testing.T) { tree10 = tree10.Insert(pfx.(AST.Pred)) var mix10 []subst.MixedSubstitutions _, mix10 = tree10.Unify(py) + + if len(mix10) != 3 { + t.Fatalf("Size must be 3") + } for _, elem := range mix10 { - fmt.Println(elem.ToString()) + if elem.GetForm().ToString() != "P(Y)" { + t.Fatalf("Return must be P(Y)") + } } fmt.Println("-----END TEST-----") fmt.Println() @@ -731,10 +875,14 @@ func TestUnify(t *testing.T) { tree11 := NewNode() tree11 = tree11.Insert(pab.(AST.Pred)) - val11, _ := tree11.Unify(pxx) + val11, mix11 := tree11.Unify(pxx) if val11 { t.Fatalf("This test must fail ") } + if len(mix11) != 0 { + t.Fatalf("Size of mix11 must be empty") + } + fmt.Println("-----EXPECTED FAILURE -----") fmt.Println("-----END TEST-----") fmt.Println() @@ -745,9 +893,11 @@ func TestUnify(t *testing.T) { var mix12 []subst.MixedSubstitutions _, mix12 = tree12.Unify(pxy) for _, elem := range mix12 { - fmt.Println(elem.ToString()) + if elem.GetForm().ToString() != "P(X, Y)" { + t.Fatalf("Return must be P(X, Y)") + } } - if len(mix) != 1 { + if len(mix12) != 1 { t.Fatalf("Should have a found 1 unification") } fmt.Println("-----END TEST-----") @@ -781,17 +931,16 @@ func TestUnifyTerm(t *testing.T) { tree2 = tree2.Insert(pax.(AST.Pred)) queryTerm2 := subst.TransformPred(pab.(AST.Pred)) - var val2 bool var mix2 []subst.MixedTermSubstitutions - val2, mix2 = tree2.UnifyTerm(queryTerm2) + _, mix2 = tree2.UnifyTerm(queryTerm2) - if val2 { - for _, elem := range mix2 { - fmt.Println(" ->", elem.ToString()) - } - } else { + if len(mix2) != 1 { t.Fatalf("Unify Failure") } + for _, elem := range mix2 { + fmt.Println(elem.ToString()) + } + fmt.Println("-----END TEST-----") fmt.Println() @@ -801,8 +950,12 @@ func TestUnifyTerm(t *testing.T) { queryTerm3 := subst.TransformPred(pb.(AST.Pred)) var val3 bool - val3, _ = tree3.UnifyTerm(queryTerm3) + var mix3 []subst.MixedTermSubstitutions + val3, mix3 = tree3.UnifyTerm(queryTerm3) + if len(mix3) != 0 { + t.Fatalf("Must return null because it can't be unified") + } if val3 { t.Fatalf("Unify Failure") } @@ -818,32 +971,32 @@ func TestUnifyTerm(t *testing.T) { var mix4 []subst.MixedTermSubstitutions val4, mix4 = tree4.UnifyTerm(queryTerm4) - if val4 { - for _, elem := range mix4 { - fmt.Println(" ->", elem.ToString()) - } - } else { + if !val4 { t.Fatalf("Unify Failure") } + for _, elem := range mix4 { + if elem.ToString() != "P(a) {}" { + t.Fatalf("Must be P(a) {}") + } + } + fmt.Println("-----END TEST-----") fmt.Println() fmt.Println("-----TEST 05 -----") tree5 := NewNode() - tree5 = tree5.Insert(pax.(AST.Pred)) - queryTerm5 := subst.TransformPred(pafy.(AST.Pred)) + tree5 = tree5.Insert(pab.(AST.Pred)) + queryTerm5 := subst.TransformPred(pay.(AST.Pred)) - var val5 bool var mix5 []subst.MixedTermSubstitutions - val5, mix5 = tree5.UnifyTerm(queryTerm5) + _, mix5 = tree5.UnifyTerm(queryTerm5) - if val5 { - for _, elem := range mix5 { - fmt.Println(" ->", elem.ToString()) + for _, elem := range mix5 { + if elem.Term().ToString() != "P(a, Y)" { + t.Fatalf("Return must be P(a, Y) ") } - } else { - t.Fatalf("Unify Failure") } + fmt.Println("-----END TEST-----") fmt.Println() @@ -852,17 +1005,15 @@ func TestUnifyTerm(t *testing.T) { tree6 = tree6.Insert(pafx.(AST.Pred)) queryTerm6 := subst.TransformPred(pafy.(AST.Pred)) - var val6 bool var mix6 []subst.MixedTermSubstitutions - val6, mix6 = tree6.UnifyTerm(queryTerm6) + _, mix6 = tree6.UnifyTerm(queryTerm6) - if val6 { - for _, elem := range mix6 { - fmt.Println(" ->", elem.ToString()) + for _, elem := range mix6 { + if elem.Term().ToString() != "P(a, f(Y))" { + t.Fatalf("Must return P(a, f(Y))") } - } else { - t.Fatalf("Unify Failure") } + fmt.Println("-----END TEST-----") fmt.Println() @@ -871,17 +1022,15 @@ func TestUnifyTerm(t *testing.T) { tree7 = tree7.Insert(px.(AST.Pred)) queryTerm7 := subst.TransformPred(py.(AST.Pred)) - var val7 bool var mix7 []subst.MixedTermSubstitutions - val7, mix7 = tree7.UnifyTerm(queryTerm7) + _, mix7 = tree7.UnifyTerm(queryTerm7) - if val7 { - for _, elem := range mix7 { - fmt.Println(" ->", elem.ToString()) + for _, elem := range mix7 { + if elem.Term().ToString() != "P(Y)" { + t.Fatalf("Must return P(Y)") } - } else { - t.Fatalf("Unify Failure") } + fmt.Println("-----END TEST-----") fmt.Println() @@ -890,16 +1039,13 @@ func TestUnifyTerm(t *testing.T) { tree8 = tree8.Insert(pxy.(AST.Pred)) queryTerm8 := subst.TransformPred(pab.(AST.Pred)) - var val8 bool var mix8 []subst.MixedTermSubstitutions - val8, mix8 = tree8.UnifyTerm(queryTerm8) + _, mix8 = tree8.UnifyTerm(queryTerm8) - if val8 { - for _, elem := range mix8 { - fmt.Println(" ->", elem.ToString()) + for _, elem := range mix8 { + if elem.Term().ToString() != "P(a, b)" { + t.Fatalf("Must return P(a, b)") } - } else { - t.Fatalf("Unify Failure") } fmt.Println("-----END TEST-----") fmt.Println() @@ -951,17 +1097,15 @@ func TestUnifyTerm(t *testing.T) { tree11 = tree11.Insert(pfx.(AST.Pred)) queryTerm11 := subst.TransformPred(py.(AST.Pred)) - var val11 bool var mix11 []subst.MixedTermSubstitutions - val11, mix11 = tree11.UnifyTerm(queryTerm11) + _, mix11 = tree11.UnifyTerm(queryTerm11) - if val11 { - for _, elem := range mix11 { - fmt.Println(" ->", elem.ToString()) + for _, elem := range mix11 { + if elem.Term().ToString() != "P(Y)" { + t.Fatalf("Unify Failure") } - } else { - t.Fatalf("Unify Failure") } + fmt.Println("-----END TEST-----") fmt.Println() @@ -981,6 +1125,10 @@ func TestUnifyTerm(t *testing.T) { } else { fmt.Println("Unify Failure (Expected)") } + if len(mix12) != 0 { + t.Fatalf("Got %d elements instead of 0", len(mix12)) + + } fmt.Println("-----EXPECTED FAILURE -----") fmt.Println("-----END TEST-----") fmt.Println() @@ -990,17 +1138,15 @@ func TestUnifyTerm(t *testing.T) { tree13 = tree13.Insert(pggab.(AST.Pred)) queryTerm13 := subst.TransformPred(pxy.(AST.Pred)) - var val13 bool var mix13 []subst.MixedTermSubstitutions - val13, mix13 = tree13.UnifyTerm(queryTerm13) + _, mix13 = tree13.UnifyTerm(queryTerm13) - if val13 { - for _, elem := range mix13 { - fmt.Println(" ->", elem.ToString()) + for _, elem := range mix13 { + if elem.Term().ToString() != "P(X, Y)" { + t.Fatalf("Must return P(X, Y)") } - } else { - t.Fatalf("Unify Failure") } + fmt.Println("-----END TEST-----") fmt.Println() @@ -1016,30 +1162,41 @@ func TestMakeDataStruct(t *testing.T) { tree3 := NewNode() formulas2 := Lib.NewList[AST.Form]() formulas2.Append(pab) - formulas2.Append(pac) + formulas2.Append(not_pac) formulas2.Append(pba) tree4 := tree3.MakeDataStruct(formulas2, true) tree4.Print() tree5 := NewNode() formulas3 := Lib.NewList[AST.Form]() - formulas3.Append(pac) + formulas3.Append(not_pac) tree6 := tree5.MakeDataStruct(formulas3, true) tree6.Print() } -func TestRetrieveFail(t *testing.T) { +func TestMaVieEllePueSaMere(t *testing.T) { + fmt.Println("-----TEST 01 -----") tree := NewNode() - tree = tree.Insert(pxx.(AST.Pred)) - test, elem := tree.RetrieveUnifiables(pab) - - for _, elem := range test { - fmt.Println("Substs : ", elem.ToString()) + tree = tree.Insert(pab.(AST.Pred)) + var mix []subst.MixedSubstitutions + _, mix = tree.Unify(pay) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } + if len(mix) != 1 { + t.Fatalf("Should have a found 1 unification") } - for _, elembis := range elem.GetSlice() { - fmt.Println("Pred : ", elembis.ToString()) + for _, elem := range mix { + if elem.GetForm().ToString() != "P(a, Y)" { + t.Fatalf("Fatal Failure, shouhd have P(a, Y)") + } } + fmt.Println("-----END TEST-----") + fmt.Println() + + tree.Print() + } From f82a32800acca417aa9c818dfad36f7c3678c87a Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Wed, 29 Apr 2026 13:50:41 +0200 Subject: [PATCH 08/23] Basic DiscriminationTree implementation --- .../discrimination-trees.go | 220 ++++++++++++------ src/Unif/discriminationtree/dt_test.go | 94 ++++---- 2 files changed, 193 insertions(+), 121 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index a22ee6cd..42de6629 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -71,6 +71,20 @@ func makeSymbolType(t AST.Term, arity int) SymbolType { return SymbolType{t, arity} } +// Equals between two SymbolType +func (s SymbolType) Equals(target SymbolType) bool { + + if ok := s.getSymbol().Equals(target.getSymbol()); ok { + if s.GetArity() != target.GetArity() { + fmt.Printf("Symbol Arity : %d, Target Arity : %d", s.GetArity(), target.GetArity()) + Glob.Anomaly("Pred Error", "Same predicat but different arity ") + } else { + return true + } + } + return false +} + /* Each node of a CodeTree is composed of a sequence of instruction and its children. If it's a leaf, it has formulaes corresponding to the sequence of instructions. */ type DiscriminationNode struct { symbol SymbolType // Contain the AST.Term and Arity @@ -78,7 +92,7 @@ type DiscriminationNode struct { leafFor Lib.List[AST.Pred] // If not empty, contains the where it come from } -// Basic Node with no data inside +// Basic Node. Create a SymbolType{nil, -1} and empty list for children and leafFor func NewNode() DiscriminationNode { return DiscriminationNode{ symbol: SymbolType{symbol: nil, arity: -1}, @@ -87,6 +101,7 @@ func NewNode() DiscriminationNode { } } +// Basic Node with SymbolType and no empty list for children and leafFor func MakeNodeWithSym(sym SymbolType) DiscriminationNode { return DiscriminationNode{ symbol: sym, @@ -95,6 +110,23 @@ func MakeNodeWithSym(sym SymbolType) DiscriminationNode { } } +func MakeNodeWithSymAndleaf(sym SymbolType, leaf Lib.List[AST.Pred]) DiscriminationNode { + return DiscriminationNode{ + symbol: sym, + children: Lib.NewList[DiscriminationNode](), + leafFor: leaf, + } +} + +// Basic Node with SymbolType, children and empty List for leafFor +func MakeNodeWithSymAndChildren(sym SymbolType, children Lib.List[DiscriminationNode]) DiscriminationNode { + return DiscriminationNode{ + symbol: sym, + children: children, + leafFor: Lib.NewList[AST.Pred](), + } +} + func (dNode *DiscriminationNode) setSymbol(symbol SymbolType) { dNode.symbol = symbol } @@ -107,17 +139,23 @@ func (dNode DiscriminationNode) getSymbol() AST.Term { return dNode.symbol.getSymbol() } -func (dNode DiscriminationNode) IsEmpty() bool { - return dNode.symbol.IsNil() +func (dNode DiscriminationNode) getChildren() Lib.List[DiscriminationNode] { + return dNode.children +} + +func (dNode DiscriminationNode) getLeafFor() Lib.List[AST.Pred] { + return dNode.leafFor } func (dNode DiscriminationNode) ToString() string { return dNode.getSymbol().ToString() } +// Struct with a Pred and a associated substitution. Used for Robinson type CandidatResult struct { - Pred AST.Pred - Subs subst.Substitutions + Pred AST.Pred // Predicat + Subs subst.Substitutions // The associated substitution + } func (Candidat CandidatResult) getPred() AST.Pred { @@ -135,13 +173,6 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { } } -func MakeCandidatResultWithPred(p AST.Pred) CandidatResult { - return CandidatResult{ - Pred: p, - Subs: subst.Substitutions{}, - } -} - /*****************************/ /* End Structures definition */ /*****************************/ @@ -150,6 +181,8 @@ func MakeCandidatResultWithPred(p AST.Pred) CandidatResult { /*********** Parse ***********/ /*****************************/ +// Parse a AST.Form formula to a List of SymbolType. +// e.g AST.Form == pax => [p(arity :2),a(arity:0), x(arity:0)] func parseFormula(formula AST.Form) Lib.List[SymbolType] { res := Lib.NewList[SymbolType]() // The formula has to be a predicate @@ -194,6 +227,9 @@ func parseTerm(t AST.Term) Lib.List[SymbolType] { /********* Transform *********/ /*****************************/ +// Case t is a Function => SymbolType{t.ID, t.getArgs} +// Case t is a Meta => SymbolType{t.ID, 0} +// Else Glob.Anomaly func FirstElementToSymbolType(t AST.Term) SymbolType { switch t := t.(type) { case AST.Fun: // Case function @@ -208,16 +244,14 @@ func FirstElementToSymbolType(t AST.Term) SymbolType { func TermToNode(t AST.Term) DiscriminationNode { switch t := t.(type) { - case AST.Fun: + case AST.Fun: // Accumulate all the term of the AST.Term then create a Node with all the children children := Lib.NewList[DiscriminationNode]() for _, c := range t.GetArgs().GetSlice() { children.Append(TermToNode(c)) } - - // Node with all his children - return DiscriminationNode{FirstElementToSymbolType(t), children, Lib.NewList[AST.Pred]()} - case AST.Meta: - return DiscriminationNode{FirstElementToSymbolType(t), Lib.NewList[DiscriminationNode](), Lib.NewList[AST.Pred]()} + return MakeNodeWithSymAndChildren(FirstElementToSymbolType(t), children) + case AST.Meta: // Node with T as SymbolType and empty children / leafFor + return MakeNodeWithSym(FirstElementToSymbolType(t)) default: Glob.Anomaly("TermToST", "Var or Id") return NewNode() @@ -228,53 +262,18 @@ func TermToNode(t AST.Term) DiscriminationNode { /******* End Transform *******/ /*****************************/ -func (dNode DiscriminationNode) Print() { - for _, child := range dNode.children.GetSlice() { - child.displayRec(2) // Magic Number - } -} - -func (dNode DiscriminationNode) displayRec(indent int) { - - prefix := strings.Repeat(" ", indent-1) + " |-- " - - if indent == 2 { - prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " - } - - fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) - - if dNode.leafFor.Len() > 0 { - leafPrefix := strings.Repeat(" ", indent) + " [=> " - for _, pred := range dNode.leafFor.GetSlice() { - fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) - } - } - - for _, child := range dNode.children.GetSlice() { - child.displayRec(indent + 1) - } -} - -// Equals between two SymbolType -func (s SymbolType) Equals(target SymbolType) bool { - - if ok := s.getSymbol().Equals(target.getSymbol()); ok { - if s.GetArity() != target.GetArity() { - fmt.Printf("Symbol Arity : %d, Target Arity : %d", s.GetArity(), target.GetArity()) - Glob.Anomaly("Pred Error", "Same predicat but different arity ") - } else { - return true - } - } - return false -} +/*****************************/ +/*********** Insrt ***********/ +/*****************************/ +// Insert a AST.Pred in the tree. If using a AST.term, it have to be cast when inserting ( tree = tree.Insert(px.(AST.pred)) ) +// Call the parser then the auxiliary function func (dNode DiscriminationNode) Insert(p AST.Pred) DiscriminationNode { sym_list := parseFormula(p) return dNode.insertRec(sym_list, p) } +// Auxiliary function for insert. func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm AST.Pred) DiscriminationNode { // End of recursion, time to insert @@ -294,6 +293,12 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm // Create Symbol sym := seq.At(0) + + // fmt.Println("Symbol : ", sym.getSymbol().ToString()) + // fmt.Println("Meta : ", sym.getSymbol().IsMeta()) + // fmt.Println("Fun : ", sym.getSymbol().IsFun()) + // fmt.Println("Cst : ", sym.getSymbol().IsFun() && sym.GetArity() == 0) + foundIndex := -1 childrenSlice := dNode.children.GetSlice() @@ -306,22 +311,29 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm } } - // Child already exist - if ok { + if ok { // Child already exist // Insert and update the sequence updatedChild := childrenSlice[foundIndex].insertRec(seq.RemoveAt(0), originalTerm) dNode.children.Upd(foundIndex, updatedChild) // Update children[foundIntex] = updateChild - // if Child doesn't exist - } else { - newChild := MakeNodeWithSym(sym) // Create a new Node with the new SymbolType + } else { // if Child doesn't exist + + newChild := MakeNodeWithSym(sym) // Create a new Node with the new SymbolType and his leafFor updatedChild := newChild.insertRec(seq.RemoveAt(0), originalTerm) // Insert the rest of the sequence after the new child dNode.children.Append(updatedChild) // Update the children of the args node } - return dNode // Return updated node + return dNode } +/*****************************/ +/********* End insrt *********/ +/*****************************/ + +/*****************************/ +/********** Retriev **********/ +/*****************************/ + func GetSubTermLength(seq []SymbolType) int { if len(seq) == 0 { @@ -354,7 +366,6 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery, substitutions) subs = append(subs, matches...) } - return subs } @@ -385,11 +396,13 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S isExactMatch := child.symbol.Equals(symQuery) // fmt.Println("Exact Match", child.getSymbol()) + if isExactMatch { // Exact Match matches := child.retrieveRec(seq[1:], currentEnv) // Exact Match -> Search next element results = append(results, matches...) } + // Depending of the symbol of the child, there is 2 possilities, either the child is a meta, ether the sym is a meta symChild := child.getSymbol() // Case the child is a AST.Meta @@ -463,6 +476,42 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S return results } +/*****************************/ +/* DataStruct implementation */ +/*****************************/ + +func (dNode DiscriminationNode) Print() { + for _, child := range dNode.children.GetSlice() { + child.displayRec(2) // Magic Number (Set the indent but bellow 2 the display is horrible and above 2 is bugget for ??? reason) + } +} + +func (dNode DiscriminationNode) displayRec(indent int) { + + prefix := strings.Repeat(" ", indent-1) + " |-- " + + if indent == 2 { + prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " + } + + fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) + + if dNode.leafFor.Len() > 0 { + leafPrefix := strings.Repeat(" ", indent) + " [=> " + for _, pred := range dNode.leafFor.GetSlice() { + fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) + } + } + + for _, child := range dNode.children.GetSlice() { + child.displayRec(indent + 1) + } +} + +func (dNode DiscriminationNode) IsEmpty() bool { + return dNode.symbol.IsNil() +} + func (dNode DiscriminationNode) Copy() subst.DataStructure { newChildMaster := Lib.NewList[DiscriminationNode]() @@ -476,19 +525,43 @@ func (dNode DiscriminationNode) Copy() subst.DataStructure { } +func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { + + form := Lib.NewList[AST.Form]() + + // fixme: why are we doing this here? + for _, f := range formulas.GetSlice() { + switch nf := f.(type) { + case AST.Pred: + if is_pos { + form.Append(nf.Copy()) + } + case AST.Not: + switch nf.GetForm().(type) { + case AST.Pred: + if !(is_pos) { + form.Append(nf.GetForm()) + } + } + } + } + + return dNode.InsertFormulaListToDataStructure(form) +} + func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) subst.DataStructure { - fmt.Println("Form") + //fmt.Println("Form") for _, f := range lf.GetSlice() { - fmt.Println("element f", f.ToString()) + //fmt.Println("element f", f.ToString()) switch nf := f.Copy().(type) { case AST.Pred: - fmt.Println("Cas Pred") + //fmt.Println("Cas Pred") dNode = dNode.Insert(nf) case AST.Not: - fmt.Println("Cas not") + //fmt.Println("Cas not") switch newForm := nf.GetForm().(type) { // Get the type AST.Form case AST.Pred: - fmt.Println("Cas not apres cast pour Pred", newForm.ToString()) + //fmt.Println("Cas not apres cast pour Pred", newForm.ToString()) dNode = dNode.Insert(newForm) } } @@ -580,8 +653,3 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu } return found, mixed } - -func (dNode DiscriminationNode) MakeDataStruct(Formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { - // Gérer cas positif ou negatif - return dNode.InsertFormulaListToDataStructure(Formulas) -} diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 7641fb9f..892937ad 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -246,16 +246,6 @@ func initTestVariable() { PRb = AST.MakerPred(PR_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) } -/* -func initCodeTreesTests(lf Lib.List[AST.Form]) (Unif.DataStructure, Unif.DataStructure) { - tp = Unif.NewNode() - tn = Unif.NewNode() - tp = tp.MakeDataStruct(lf, true) - tn = tn.MakeDataStruct(lf, false) - return tp, tn -} -*/ - func initDebuggers() { AST.InitDebugger() Typing.InitDebugger() @@ -413,7 +403,11 @@ func TestPrintDoublonCheck(t *testing.T) { func TestParseFormula(t *testing.T) { tmp := parseFormula(pax) + + fmt.Println(tmp.GetSlice()) + for _, value := range tmp.GetSlice() { + fmt.Println(value.getSymbol().ToString()) if value.getSymbol().ToString() == "P" { if value.GetArity() != 2 { t.Fatalf("Arrity Error") @@ -1153,50 +1147,60 @@ func TestUnifyTerm(t *testing.T) { } func TestMakeDataStruct(t *testing.T) { - tree := NewNode() - formulas := Lib.NewList[AST.Form]() - formulas.Append(pxy) - tree2 := tree.MakeDataStruct(formulas, true) - tree2.Print() - tree3 := NewNode() - formulas2 := Lib.NewList[AST.Form]() - formulas2.Append(pab) - formulas2.Append(not_pac) - formulas2.Append(pba) - tree4 := tree3.MakeDataStruct(formulas2, true) - tree4.Print() + pac_form := not_pac.(AST.Not).GetForm().(AST.Pred) - tree5 := NewNode() - formulas3 := Lib.NewList[AST.Form]() - formulas3.Append(not_pac) - tree6 := tree5.MakeDataStruct(formulas3, true) - tree6.Print() + fmt.Println("Test positive tree") -} + tree1 := NewNode() + formulas1 := Lib.NewList[AST.Form]() + formulas1.Append(pab) // + => Insert + formulas1.Append(not_pac) // - => Ignore + formulas1.Append(pba) // + => Insert -func TestMaVieEllePueSaMere(t *testing.T) { + resultTree1 := tree1.MakeDataStruct(formulas1, true).(DiscriminationNode) - fmt.Println("-----TEST 01 -----") - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pay) - for _, elem := range mix { - fmt.Println(elem.ToString()) + // Vérifications + if len(resultTree1.RetrieveUnifiables(pab)) == 0 { + t.Fatalf(" Tree must contain pab") } - if len(mix) != 1 { - t.Fatalf("Should have a found 1 unification") + if len(resultTree1.RetrieveUnifiables(pba)) == 0 { + t.Fatalf(" Tree must contain pba ") } - for _, elem := range mix { - if elem.GetForm().ToString() != "P(a, Y)" { - t.Fatalf("Fatal Failure, shouhd have P(a, Y)") - } + if len(resultTree1.RetrieveUnifiables(pac_form)) != 0 { + t.Fatalf(" Tree mustn't contain pac because it's negative in the positive tree") } - fmt.Println("-----END TEST-----") - fmt.Println() + fmt.Println("Test negative tree") - tree.Print() + tree2 := NewNode() + formulas2 := Lib.NewList[AST.Form]() + formulas2.Append(pab) // + => Ignored + formulas2.Append(not_pac) // - => Insert + formulas2.Append(pba) // + => Ignored + + resultTree2 := tree2.MakeDataStruct(formulas2, false).(DiscriminationNode) + + // Vérifications + if len(resultTree2.RetrieveUnifiables(pac_form)) == 0 { + t.Fatalf(" Tree must contain not_pac ") + } + if len(resultTree2.RetrieveUnifiables(pab)) != 0 { + t.Fatalf(" Tree musn't contain pab because it's positive in the negative tree") + } + if len(resultTree2.RetrieveUnifiables(pba)) != 0 { + t.Fatalf(" Tree mustn't contain pba because it's positive in the negative tree") + } + + resultTree1.Print() + resultTree2.Print() + +} + +func TestMaVieEllePueSaMere(t *testing.T) { + + metaIdentication(pax) + + metaIdentication(pabc) } From 114382d96d8a62e752c7dd693a0bb8cebe17f0e6 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Wed, 29 Apr 2026 13:52:13 +0200 Subject: [PATCH 09/23] Removing debug print --- .../discrimination-trees.go | 47 +------------------ src/Unif/discriminationtree/dt_test.go | 8 ---- 2 files changed, 1 insertion(+), 54 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 42de6629..845c1a4a 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -293,12 +293,6 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm // Create Symbol sym := seq.At(0) - - // fmt.Println("Symbol : ", sym.getSymbol().ToString()) - // fmt.Println("Meta : ", sym.getSymbol().IsMeta()) - // fmt.Println("Fun : ", sym.getSymbol().IsFun()) - // fmt.Println("Cst : ", sym.getSymbol().IsFun() && sym.GetArity() == 0) - foundIndex := -1 childrenSlice := dNode.children.GetSlice() @@ -380,7 +374,6 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S var results []CandidatResult - // Voir pour la suite, est-ce necessaire de faire ceci alors que Robinson a déjà vérifier les blocs précédent, notamment celui juste avant de ce rendre compte que cette partie va fonctionner ou non if len(seq) == 0 { // End of recursion for _, p := range dNode.leafFor.GetSlice() { results = append(results, MakeCandidat(p, currentEnv)) @@ -390,13 +383,9 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S symQuery := seq[0] // First Element - // fmt.Println("RetrieveRec SymQuery", symQuery.getSymbol().ToString()) - for _, child := range dNode.children.GetSlice() { isExactMatch := child.symbol.Equals(symQuery) - // fmt.Println("Exact Match", child.getSymbol()) - if isExactMatch { // Exact Match matches := child.retrieveRec(seq[1:], currentEnv) // Exact Match -> Search next element results = append(results, matches...) @@ -413,15 +402,13 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 skip := GetSubTermLength(seq) - // fmt.Println("Longueur du skip", skip) - if skip <= len(seq) { // Security to prevent segfault var mergedSub subst.Substitutions if skip == 1 { currentSub := subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol()) tmp3 := subst.Substitutions{currentSub} - // Ok Commat Idoms doesn't works because ?????????????????????????????? + // Ok Commat Idoms doesn't works so we use this if len(currentEnv) == 0 { mergedSub = tmp3 } else { @@ -462,11 +449,6 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S if !mergedSub.Equals(subst.Failure()) { childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) results = append(results, childResults...) - - // for _, elem := range results { - // fmt.Println("elem pred Query meta", elem.getPred().ToString()) - // fmt.Println("elem pred Query meta", elem.GetSubs().ToString()) - // } } } else { @@ -529,7 +511,6 @@ func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_p form := Lib.NewList[AST.Form]() - // fixme: why are we doing this here? for _, f := range formulas.GetSlice() { switch nf := f.(type) { case AST.Pred: @@ -550,18 +531,13 @@ func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_p } func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST.Form]) subst.DataStructure { - //fmt.Println("Form") for _, f := range lf.GetSlice() { - //fmt.Println("element f", f.ToString()) switch nf := f.Copy().(type) { case AST.Pred: - //fmt.Println("Cas Pred") dNode = dNode.Insert(nf) case AST.Not: - //fmt.Println("Cas not") switch newForm := nf.GetForm().(type) { // Get the type AST.Form case AST.Pred: - //fmt.Println("Cas not apres cast pour Pred", newForm.ToString()) dNode = dNode.Insert(newForm) } } @@ -590,10 +566,6 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term for Robinson finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson - // fmt.Println("Unify initialSubst", initialSubst.ToString()) - // fmt.Println("Unify possibleMatchTerm", possibleMatchTerm.ToString()) - // fmt.Println("Unify finalSubst", finalSubst.ToString()) - if finalSubst.Equals(subst.Failure()) { fmt.Println("-------------------------") fmt.Println("Substitution FAILURE") @@ -613,31 +585,14 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu var found bool seq := parseTerm(t).GetSlice() - - // for _, elem := range seq { - // fmt.Println("seq", elem.getSymbol().ToString()) - // } - candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) - // for _, elem := range candidates { - // fmt.Println("element", elem.getPred().ToString()) - // } - for _, possibleMatch := range candidates { - // fmt.Println("Candidat", possibleMatch.Pred.ToString(), possibleMatch.Subs.ToString()) - candidateTerm := subst.TransformPred(possibleMatch.getPred()) emptySubst := subst.Substitutions{} - // fmt.Println("=> candidateTerm : ", candidateTerm.ToString()) - // fmt.Println("=> t : ", t.ToString()) - // fmt.Println("=> EmptySubset : ", emptySubst.ToString()) - finalSubst := subst.AddUnification(t, candidateTerm, emptySubst) // Call Robinson - // fmt.Println("finalSubst", finalSubst.ToString()) - if !finalSubst.Equals(subst.Failure()) { found = true mixMatch := subst.MixMatchSubstitutions{ diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 892937ad..4b19e8be 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -1196,11 +1196,3 @@ func TestMakeDataStruct(t *testing.T) { resultTree2.Print() } - -func TestMaVieEllePueSaMere(t *testing.T) { - - metaIdentication(pax) - - metaIdentication(pabc) - -} From aaffcc666b90304a143264ff71885b2d31bb5e0a Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Thu, 30 Apr 2026 17:49:44 +0200 Subject: [PATCH 10/23] Implementing single thread perfect tree --- .../discriminationtree/ContextNormalizer.go | 75 ++ .../discrimination-trees.go | 49 +- src/Unif/discriminationtree/dt_test.go | 148 ++- src/Unif/discriminationtree/dt_test2.go | 844 ------------------ 4 files changed, 214 insertions(+), 902 deletions(-) create mode 100644 src/Unif/discriminationtree/ContextNormalizer.go delete mode 100644 src/Unif/discriminationtree/dt_test2.go diff --git a/src/Unif/discriminationtree/ContextNormalizer.go b/src/Unif/discriminationtree/ContextNormalizer.go new file mode 100644 index 00000000..2384918f --- /dev/null +++ b/src/Unif/discriminationtree/ContextNormalizer.go @@ -0,0 +1,75 @@ +/** +* Copyright 2022 by the authors (see AUTHORS). +* +* Goéland is an automated theorem prover for first order logic. +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited +* liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +**/ + +/** +* This file contains all the definitons necessary to make a Code Tree +**/ + +package discriminationtree + +import ( + "fmt" + + "github.com/GoelandProver/Goeland/AST" +) + +// Implement the perfect tree link between a Meta and newMeta +type NormalizerContext struct { + counter int // Counter for the naming + mapping map[string]AST.Meta // Association a variable name to a normalizedVariable name +} + +// New Instance +func NewContext() *NormalizerContext { + return &NormalizerContext{ + counter: 0, + mapping: make(map[string]AST.Meta), + } +} + +func (ctx *NormalizerContext) GetNormalizedMeta(originalMeta AST.Meta) AST.Meta { + + originalName := originalMeta.GetName() // Get meeta Name + + // Contains check + if fakeMeta, exists := ctx.mapping[originalName]; exists { + return fakeMeta + } + + // Create new name + ctx.counter++ + newName := fmt.Sprintf("v%d", ctx.counter) // Create the Meta name + newMeta := AST.MakeMeta(ctx.counter, 0, newName, 0, originalMeta.GetTy()) + ctx.mapping[originalName] = newMeta // Add to the map + + return newMeta +} diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 845c1a4a..37e79c23 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -181,17 +181,19 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { /*********** Parse ***********/ /*****************************/ -// Parse a AST.Form formula to a List of SymbolType. -// e.g AST.Form == pax => [p(arity :2),a(arity:0), x(arity:0)] func parseFormula(formula AST.Form) Lib.List[SymbolType] { res := Lib.NewList[SymbolType]() - // The formula has to be a predicate + ctx := NewContext() // Context gonna start all the transformations ( X == v1, Y == v2, ...) + switch formula_type := formula.(type) { case AST.Pred: + // Add First element ( Predicat ) first_element := makeSymbolType(formula_type.GetID(), formula_type.GetArgs().Len()) res.Append(first_element) + + // Call the parse on each element of the predicat for _, arg := range formula_type.GetArgs().GetSlice() { - arg_list := parseTerm(arg) + arg_list := parseTerm(arg, ctx) res.Append(arg_list.GetSlice()...) } return res @@ -200,23 +202,37 @@ func parseFormula(formula AST.Form) Lib.List[SymbolType] { } } -// Parser for a formula : f(x,y) -> [f,x,y], a -> [a], x -> [x] -func parseTerm(t AST.Term) Lib.List[SymbolType] { +func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { res := Lib.NewList[SymbolType]() - // The formula has to be a predicate switch term := t.(type) { - case AST.Fun: // Add all the args of the function - first_element := makeSymbolType(term.GetID(), term.GetArgs().Len()) // Add the node before recursive call + + // if term is a function or cst, add and call his args + case AST.Fun: + first_element := makeSymbolType(term.GetID(), term.GetArgs().Len()) res.Append(first_element) for _, arg := range term.GetArgs().GetSlice() { - res.Append(parseTerm(arg).GetSlice()...) + res.Append(parseTerm(arg, ctx).GetSlice()...) } + + // Case meta, we have to transform it case AST.Meta: - res.Append(makeSymbolType(term, 0)) - } + originalName := term.GetName() // Name of the meta + _, exists := ctx.mapping[originalName] // Contains + if !exists { // If the meta is unknow + ctx.counter++ + newName := fmt.Sprintf("v%d", ctx.counter) // v + int. e.g v1,v2,v3,... + fakeMeta := AST.MakeMeta(ctx.counter, 0, newName, 0, term.GetTy()) + ctx.mapping[originalName] = fakeMeta // Update the mapping : X -> v1 or Y -> v2 .... ) + } + + normalizedMeta := ctx.mapping[originalName] // Return the transformed name association to the originalName before adding + res.Append(makeSymbolType(normalizedMeta, 0)) // Add the new Meta to the return slice + + } return res + } /*****************************/ @@ -408,7 +424,7 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S if skip == 1 { currentSub := subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol()) tmp3 := subst.Substitutions{currentSub} - // Ok Commat Idoms doesn't works so we use this + // Ok Commat Idoms doesn't works because ?????????????????????????????? if len(currentEnv) == 0 { mergedSub = tmp3 } else { @@ -429,10 +445,10 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S // First element is a meta } else if symQuery.getSymbol() != nil && symQuery.getSymbol().IsMeta() && !isExactMatch { + // Reverse of the situation with the previous if. // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode - var mergedSub subst.Substitutions if child.GetArity() == 0 { currentSub := subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild) // Create a new substitution @@ -475,7 +491,6 @@ func (dNode DiscriminationNode) displayRec(indent int) { if indent == 2 { prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " } - fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) if dNode.leafFor.Len() > 0 { @@ -484,7 +499,6 @@ func (dNode DiscriminationNode) displayRec(indent int) { fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) } } - for _, child := range dNode.children.GetSlice() { child.displayRec(indent + 1) } @@ -511,6 +525,7 @@ func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_p form := Lib.NewList[AST.Form]() + // fixme: why are we doing this here? for _, f := range formulas.GetSlice() { switch nf := f.(type) { case AST.Pred: @@ -584,7 +599,7 @@ func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSu var mixed []subst.MixedTermSubstitutions var found bool - seq := parseTerm(t).GetSlice() + seq := parseTerm(t, nil).GetSlice() candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) for _, possibleMatch := range candidates { diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 4b19e8be..62515b87 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -86,6 +86,11 @@ var fa AST.Fun var fb AST.Fun var fc AST.Fun +var gax AST.Fun +var gxb AST.Fun +var gyb AST.Fun +var gab AST.Fun +var gxc AST.Fun var ggx AST.Fun var gga AST.Fun var gfy AST.Fun @@ -105,34 +110,16 @@ var f_fxy_z AST.Fun var f_x_fyz AST.Fun var f_fab_c AST.Fun var f_a_fbc AST.Fun - -// // Equalities -// var eq_x_y AST.Pred -// var eq_x_a AST.Pred -// var eq_y_a AST.Pred -// var eq_z1_c1 AST.Pred -// var eq_z1_c2 AST.Pred -// var eq_z2_c1 AST.Pred -// var eq_z3_c1 AST.Pred -// var eq_gx_fx AST.Pred -// var eq_ggx_fa AST.Pred -// var eq_gfy_y AST.Pred -// var eq_fa_a AST.Pred -// var eq_b_c AST.Pred -// var eq_a_b AST.Pred -// var eq_a_c AST.Pred -// var eq_b_d AST.Pred -// var eq_x_d AST.Pred - -// // Inequalites -// var neq_x_a AST.Form -// var neq_a_b AST.Form -// var neq_a_d AST.Form -// var neq_gggx_x AST.Form -// var neq_fx_a AST.Form -// var neq_fx_x AST.Form -// var neq_fab_fcd AST.Form -// var neq_fb_fc AST.Form +var f_x_x AST.Fun +var f_y_y AST.Fun +var f_z_z AST.Fun +var f_gax_c AST.Fun +var f_gxb_y AST.Fun +var f_gyb_z AST.Fun +var f_gab_a AST.Fun +var f_gxc_b AST.Fun +var f_z_y AST.Fun +var f_x_y AST.Fun // Form var pggab AST.Form @@ -154,10 +141,21 @@ var px AST.Form var py AST.Form var pxc AST.Form var pfx AST.Form +var pfy AST.Form var pafx AST.Form var pafy AST.Form var pfac AST.Form +var pfgaxc AST.Form +var pfgxby AST.Form +var pfgybz AST.Form +var pfgaba AST.Form +var pfgxcb AST.Form +var pfzy AST.Form +var pfxy AST.Form +var pfxx AST.Form +var pfzz AST.Form + var not_pcd AST.Form var PRa AST.Form @@ -209,6 +207,12 @@ func initTestVariable() { fyz = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, z)) ffx = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fx)) + gax = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, x)) + gxb = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, b)) + gyb = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, b)) + gab = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) + gxc = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, c)) + fxa = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, a)) fay = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, y)) fab = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) @@ -219,6 +223,17 @@ func initTestVariable() { f_x_fyz = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, fyz)) f_fab_c = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fab, c)) f_a_fbc = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fbc)) + f_x_x = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, x)) + f_y_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, y)) + f_z_z = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](z, z)) + f_gax_c = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gax, c)) + f_gxb_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gxb, y)) + f_gyb_z = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gyb, z)) + f_gab_a = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gab, a)) + + f_gxc_b = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gxc, b)) + f_z_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](z, y)) + f_x_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, y)) // Predicates pggab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gga, b)) @@ -236,9 +251,21 @@ func initTestVariable() { py = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y)) pxc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, c)) pfx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fx)) + pfy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fy)) pafy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fy)) pafx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fx)) pfac = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fa, c)) + + pfgaxc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gax_c)) + pfgxby = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gxb_y)) + pfgybz = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gyb_z)) + pfgaba = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gab_a)) + pfgxcb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gxc_b)) + pfzy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_z_y)) + pfxy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_x_y)) + pfxx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_x_x)) + pfzz = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_z_z)) + not_pcd = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, d))) pa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) pb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) @@ -340,6 +367,7 @@ func TestInsert(t *testing.T) { tree = tree.Insert(pba.(AST.Pred)) tree = tree.Insert(pab.(AST.Pred)) tree = tree.Insert(pafx.(AST.Pred)) + tree = tree.Insert(pafy.(AST.Pred)) fmt.Println("-------------PANIC EXPECTED------------- ") func() { @@ -362,6 +390,15 @@ func TestInsert(t *testing.T) { tree2 = tree2.Insert(pfx.(AST.Pred)) tree2.Print() + println() + println() + println() + + tree3 := NewNode() + tree3 = tree3.Insert(pfx.(AST.Pred)) + tree3 = tree3.Insert(pfy.(AST.Pred)) + tree3.Print() + } func TestPrintDiscriminationTree(t *testing.T) { @@ -400,6 +437,22 @@ func TestPrintDoublonCheck(t *testing.T) { } +func TestPrintHugeTree(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pfgaxc.(AST.Pred)) + tree = tree.Insert(pfgxby.(AST.Pred)) + tree = tree.Insert(pfgybz.(AST.Pred)) + tree = tree.Insert(pfgaba.(AST.Pred)) + tree = tree.Insert(pfgxcb.(AST.Pred)) + tree = tree.Insert(pfzy.(AST.Pred)) + tree = tree.Insert(pfxy.(AST.Pred)) + tree = tree.Insert(pfxx.(AST.Pred)) + tree = tree.Insert(pfzz.(AST.Pred)) + tree.Print() + +} + func TestParseFormula(t *testing.T) { tmp := parseFormula(pax) @@ -416,18 +469,22 @@ func TestParseFormula(t *testing.T) { if value.GetArity() != 0 { t.Fatalf("Arrity Error") } - } else if value.getSymbol().ToString() == "X" { + } else if value.getSymbol().ToString() == "v1" { if value.GetArity() != 0 { t.Fatalf("Arrity Error") } } else { - t.Fatalf("Supposed to have only \"P\", \"a\" or \"X\" ") + t.Fatalf("Supposed to have only \"P\", \"a\" or \"v1\" ") } } - tmp2 := parseFormula(not_pac) - for _, value := range tmp2.GetSlice() { - fmt.Println(value.symbol.ToMeta()) + tmp2 := parseFormula(pay) + for _, elem := range tmp2.GetSlice() { + tmp.Append(elem) + } + + for _, elem := range tmp.GetSlice() { + fmt.Println("elem", elem.getSymbol().ToString()) } } @@ -438,7 +495,7 @@ func TestParseTerm(t *testing.T) { var tmp2 []string var tmp3 []string - seqList := parseTerm(fxy) + seqList := parseTerm(fxy, nil) seq := seqList.GetSlice() if len(seq) != 3 { t.Fatalf("Got %d elements", len(seq)) @@ -448,7 +505,7 @@ func TestParseTerm(t *testing.T) { } fmt.Printf(" Sequence Parsed : % v\n", tmp) - seqList = parseTerm(f_fxy_z) + seqList = parseTerm(f_fxy_z, nil) seq = seqList.GetSlice() if len(seq) != 5 { t.Fatalf("Got %d elements", len(seq)) @@ -458,7 +515,7 @@ func TestParseTerm(t *testing.T) { } fmt.Printf(" Sequence Parsed : %v\n", tmp2) - seqList = parseTerm(f_x_fyz) + seqList = parseTerm(f_x_fyz, nil) seq = seqList.GetSlice() if len(seq) != 5 { t.Fatalf("Got %d elements", len(seq)) @@ -539,31 +596,31 @@ func TestEquals(t *testing.T) { func TestGetSubTermLength(t *testing.T) { - seq := parseTerm(ggx).GetSlice() + seq := parseTerm(ggx, nil).GetSlice() var1 := (GetSubTermLength(seq)) if var1 != 3 { t.Fatalf("Error SubTerLength with 2functions & 1Meta ") } - seq2 := parseTerm(fxy).GetSlice() + seq2 := parseTerm(fxy, nil).GetSlice() var2 := (GetSubTermLength(seq2)) if var2 != 3 { t.Fatalf("Error SubTerLength with 1function & 2Meta") } - seq3 := parseTerm(gx).GetSlice() + seq3 := parseTerm(gx, nil).GetSlice() var3 := (GetSubTermLength(seq3)) if var3 != 2 { t.Fatalf("Error SubTerLength with 1function & 1Meta") } - seq4 := parseTerm(ga).GetSlice() + seq4 := parseTerm(ga, nil).GetSlice() var4 := (GetSubTermLength(seq4)) if var4 != 2 { t.Fatalf("Error SubTerLength with 1function & 1cst") } - seq5 := parseTerm(gggx).GetSlice() + seq5 := parseTerm(gggx, nil).GetSlice() var5 := (GetSubTermLength(seq5)) if var5 != 4 { t.Fatalf("Error SubTerLength with 1function & 3Meta") @@ -1196,3 +1253,12 @@ func TestMakeDataStruct(t *testing.T) { resultTree2.Print() } + +func TestCaMarchePas(t *testing.T) { + + tree3 := NewNode() + tree3 = tree3.Insert(pfx.(AST.Pred)) + tree3 = tree3.Insert(pfy.(AST.Pred)) + tree3.Print() + +} diff --git a/src/Unif/discriminationtree/dt_test2.go b/src/Unif/discriminationtree/dt_test2.go deleted file mode 100644 index c4a074bb..00000000 --- a/src/Unif/discriminationtree/dt_test2.go +++ /dev/null @@ -1,844 +0,0 @@ -// /** -// * Copyright 2022 by the authors (see AUTHORS). -// * -// * Goéland is an automated theorem prover for first order logic. -// * -// * This software is governed by the CeCILL license under French law and -// * abiding by the rules of distribution of free software. You can use, -// * modify and/ or redistribute the software under the terms of the CeCILL -// * license as circulated by CEA, CNRS and INRIA at the following URL -// * "http://www.cecill.info". -// * -// * As a counterpart to the access to the source code and rights to copy, -// * modify and redistribute granted by the license, users are provided only -// * with a limited warranty and the software's author, the holder of the -// * economic rights, and the successive licensors have only limited -// * liability. -// * -// * In this respect, the user's attention is drawn to the risks associated -// * with loading, using, modifying and/or developing or reproducing the -// * software by the user in light of its specific status of free software, -// * that may mean that it is complicated to manipulate, and that also -// * therefore means that it is reserved for developers and experienced -// * professionals having in-depth computer knowledge. Users are therefore -// * encouraged to load and test the software's suitability as regards their -// * requirements in conditions enabling the security of their systems and/or -// * data to be ensured and, more generally, to use and operate it in the -// * same conditions as regards security. -// * -// * The fact that you are presently reading this means that you have had -// * knowledge of the CeCILL license and that you accept its terms. -// **/ - -package discriminationtree - -// import ( -// "fmt" -// "os" -// "testing" -// "time" - -// "github.com/GoelandProver/Goeland/AST" -// "github.com/GoelandProver/Goeland/Glob" -// "github.com/GoelandProver/Goeland/Lib" -// "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" -// "github.com/GoelandProver/Goeland/Typing" -// "github.com/GoelandProver/Goeland/Unif" -// ) - -// // Code trees -// //var tp, tn Unif.DataStructure - -// // Id -// var p_id AST.Id -// var g_id AST.Id -// var f_id AST.Id -// var a_id AST.Id -// var b_id AST.Id -// var c_id AST.Id -// var d_id AST.Id -// var c1_id AST.Id -// var c2_id AST.Id - -// // Meta -// var x AST.Meta -// var y AST.Meta -// var z AST.Meta -// var z1 AST.Meta -// var z2 AST.Meta -// var z3 AST.Meta - -// // Const -// var a AST.Fun -// var b AST.Fun -// var c AST.Fun -// var d AST.Fun -// var c1 AST.Fun -// var c2 AST.Fun - -// // Fun -// var gx AST.Fun -// var ga AST.Fun -// var fx AST.Fun -// var fy AST.Fun -// var fa AST.Fun -// var fb AST.Fun -// var fc AST.Fun - -// var ggx AST.Fun -// var gga AST.Fun -// var gfy AST.Fun -// var gfa AST.Fun -// var fxy AST.Fun -// var fyz AST.Fun -// var ffx AST.Fun -// var fxa AST.Fun -// var fay AST.Fun -// var fab AST.Fun -// var fbc AST.Fun -// var fcd AST.Fun - -// var gggx AST.Fun - -// var f_fxy_z AST.Fun -// var f_x_fyz AST.Fun -// var f_fab_c AST.Fun -// var f_a_fbc AST.Fun - -// // Equalities -// var eq_x_y AST.Pred -// var eq_x_a AST.Pred -// var eq_y_a AST.Pred -// var eq_z1_c1 AST.Pred -// var eq_z1_c2 AST.Pred -// var eq_z2_c1 AST.Pred -// var eq_z3_c1 AST.Pred -// var eq_gx_fx AST.Pred -// var eq_ggx_fa AST.Pred -// var eq_gfy_y AST.Pred -// var eq_fa_a AST.Pred -// var eq_b_c AST.Pred -// var eq_a_b AST.Pred -// var eq_a_c AST.Pred -// var eq_b_d AST.Pred -// var eq_x_d AST.Pred - -// // Inequalites -// var neq_x_a AST.Form -// var neq_a_b AST.Form -// var neq_a_d AST.Form -// var neq_gggx_x AST.Form -// var neq_fx_a AST.Form -// var neq_fx_x AST.Form -// var neq_fab_fcd AST.Form -// var neq_fb_fc AST.Form - -// // Form -// var pggab AST.Form -// var pac AST.Form -// var pa AST.Form -// var pb AST.Form -// var not_pc AST.Form -// var pab AST.Form -// var pax AST.Form -// var not_pcd AST.Form - -// func initTestVariable() { -// // Id -// p_id = AST.MakerId("P") -// g_id = AST.MakerId("g") -// f_id = AST.MakerId("f") -// a_id = AST.MakerId("a") -// b_id = AST.MakerId("b") -// c_id = AST.MakerId("c") -// d_id = AST.MakerId("d") -// c1_id = AST.MakerId("c1") -// c2_id = AST.MakerId("c2") - -// // Meta -// x = AST.MakerMeta("X", -1, AST.TIndividual()) -// y = AST.MakerMeta("Y", -1, AST.TIndividual()) -// z = AST.MakerMeta("Z", -1, AST.TIndividual()) -// z1 = AST.MakerMeta("Z1", -1, AST.TIndividual()) -// z2 = AST.MakerMeta("Z2", -1, AST.TIndividual()) -// z3 = AST.MakerMeta("Z3", -1, AST.TIndividual()) - -// // Const -// a = AST.MakerConst(a_id) -// b = AST.MakerConst(b_id) -// c = AST.MakerConst(c_id) -// d = AST.MakerConst(d_id) -// c1 = AST.MakerConst(c1_id) -// c2 = AST.MakerConst(c2_id) - -// // Fun -// gx = AST.MakerFun(g_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) -// ga = AST.MakerFun(g_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) -// fx = AST.MakerFun(f_id, Lib.MkListV(x.GetTy()), Lib.MkListV[AST.Term](x)) -// fy = AST.MakerFun(f_id, Lib.MkListV(y.GetTy()), Lib.MkListV[AST.Term](y)) -// fa = AST.MakerFun(f_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) -// fb = AST.MakerFun(f_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) -// fc = AST.MakerFun(f_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c)) - -// ggx = AST.MakerFun(g_id, gx.GetTyArgs(), Lib.MkListV[AST.Term](gx)) -// gga = AST.MakerFun(g_id, ga.GetTyArgs(), Lib.MkListV[AST.Term](ga)) -// gfy = AST.MakerFun(g_id, fy.GetTyArgs(), Lib.MkListV[AST.Term](fy)) -// gfa = AST.MakerFun(g_id, fa.GetTyArgs(), Lib.MkListV[AST.Term](fa)) -// fxy = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), y.GetTy()), Lib.MkListV[AST.Term](x, y)) -// fyz = AST.MakerFun(f_id, Lib.MkListV(x.GetTy(), z.GetTy()), Lib.MkListV[AST.Term](x, z)) -// ffx = AST.MakerFun(f_id, fx.GetTyArgs(), Lib.MkListV[AST.Term](fx)) - -// x_a_type_list := Lib.MkListV[AST.Ty](x.GetTy()) -// x_a_type_list.Append(a.GetTyArgs().GetSlice()...) -// fxa = AST.MakerFun(f_id, x_a_type_list, Lib.MkListV[AST.Term](x, a)) - -// a_y_type_list := a.GetTyArgs() -// a_y_type_list.Append(y.GetTy()) -// fay = AST.MakerFun(f_id, a_y_type_list, Lib.MkListV[AST.Term](a, y)) - -// a_b_type_list := a.GetTyArgs() -// a_b_type_list.Append(b.GetTyArgs().GetSlice()...) -// fab = AST.MakerFun(f_id, a_b_type_list, Lib.MkListV[AST.Term](a, b)) - -// bc_type_list := b.GetTyArgs() -// bc_type_list.Append(c.GetTyArgs().GetSlice()...) -// fbc = AST.MakerFun(f_id, bc_type_list, Lib.MkListV[AST.Term](b, c)) - -// cd_type_list := c.GetTyArgs() -// cd_type_list.Append(d.GetTyArgs().GetSlice()...) -// fcd = AST.MakerFun(f_id, cd_type_list, Lib.MkListV[AST.Term](c, d)) - -// gggx = AST.MakerFun(g_id, ggx.GetTyArgs(), Lib.MkListV[AST.Term](ggx)) - -// fxy_z_type_list := fxy.GetTyArgs() -// fxy_z_type_list.Append(z.GetTy()) -// f_fxy_z = AST.MakerFun(f_id, fxy_z_type_list, Lib.MkListV[AST.Term](fxy, z)) - -// x_fyz_type_list := Lib.MkListV[AST.Ty](x.GetTy()) -// x_fyz_type_list.Append(fyz.GetTyArgs().GetSlice()...) -// f_x_fyz = AST.MakerFun(f_id, x_fyz_type_list, Lib.MkListV[AST.Term](x, fyz)) - -// fab_c_type_list := fab.GetTyArgs() -// fab_c_type_list.Append(c.GetTyArgs().GetSlice()...) -// f_fab_c = AST.MakerFun(f_id, fab_c_type_list, Lib.MkListV[AST.Term](fab, c)) - -// a_fbc_type_list := a.GetTyArgs() -// a_fbc_type_list.Append(fbc.GetTyArgs().GetSlice()...) -// f_a_fbc = AST.MakerFun(f_id, a_fbc_type_list, Lib.MkListV[AST.Term](a, fbc)) - -// // Equalities - -// eq_x_y = AST.MakerPred(AST.Id_eq, Lib.MkListV(x.GetTy(), y.GetTy()), Lib.MkListV[AST.Term](x, y)) -// eq_x_a = AST.MakerPred(AST.Id_eq, x_a_type_list, Lib.MkListV[AST.Term](x, a)) - -// y_a_type_list := Lib.MkListV[AST.Ty](y.GetTy()) -// y_a_type_list.Append(a.GetTyArgs().GetSlice()...) -// eq_y_a = AST.MakerPred(AST.Id_eq, y_a_type_list, Lib.MkListV[AST.Term](y, a)) - -// z_c1_type_list := Lib.MkListV[AST.Ty](z.GetTy()) -// z_c1_type_list.Append(c1.GetTyArgs().GetSlice()...) - -// eq_z1_c1 = AST.MakerPred(AST.Id_eq, z_c1_type_list, Lib.MkListV[AST.Term](z, c1)) - -// z1_c2_type_list := Lib.MkListV[AST.Ty](z1.GetTy()) -// z1_c2_type_list.Append(c2.GetTyArgs().GetSlice()...) -// eq_z1_c2 = AST.MakerPred(AST.Id_eq, z1_c2_type_list, Lib.MkListV[AST.Term](z1, c2)) - -// z2_c1_type_list := Lib.MkListV[AST.Ty](z2.GetTy()) -// z2_c1_type_list.Append(c1.GetTyArgs().GetSlice()...) -// eq_z2_c1 = AST.MakerPred(AST.Id_eq, z2_c1_type_list, Lib.MkListV[AST.Term](z2, c1)) - -// z3_c1_type_list := Lib.MkListV[AST.Ty](z3.GetTy()) -// z3_c1_type_list.Append(c1.GetTyArgs().GetSlice()...) -// eq_z3_c1 = AST.MakerPred(AST.Id_eq, z3_c1_type_list, Lib.MkListV[AST.Term](z3, c1)) - -// ggx_fa_type_list := ggx.GetTyArgs() -// ggx_fa_type_list.Append(fa.GetTyArgs().GetSlice()...) -// eq_ggx_fa = AST.MakerPred(AST.Id_eq, ggx_fa_type_list, Lib.MkListV[AST.Term](ggx, fa)) - -// gfy_y_type_list := gfy.GetTyArgs() -// gfy_y_type_list.Append(y.GetTy()) -// eq_gfy_y = AST.MakerPred(AST.Id_eq, gfy_y_type_list, Lib.MkListV[AST.Term](gfy, y)) - -// gx_fx_type_list := gx.GetTyArgs() -// gx_fx_type_list.Append(fx.GetTyArgs().GetSlice()...) -// eq_gx_fx = AST.MakerPred(AST.Id_eq, gx_fx_type_list, Lib.MkListV[AST.Term](gx, fx)) - -// fa_a_type_list := fa.GetTyArgs() -// fa_a_type_list.Append(a.GetTyArgs().GetSlice()...) -// eq_fa_a = AST.MakerPred(AST.Id_eq, fa_a_type_list, Lib.MkListV[AST.Term](fa, a)) - -// a_b_type_list2 := a.GetTyArgs() -// a_b_type_list2.Append(b.GetTyArgs().GetSlice()...) -// eq_a_b = AST.MakerPred(AST.Id_eq, a_b_type_list2, Lib.MkListV[AST.Term](a, b)) - -// b_c_type_list := b.GetTyArgs() -// b_c_type_list.Append(c.GetTyArgs().GetSlice()...) -// eq_b_c = AST.MakerPred(AST.Id_eq, b_c_type_list, Lib.MkListV[AST.Term](b, c)) - -// a_c_type_list := a.GetTyArgs() -// a_c_type_list.Append(c.GetTyArgs().GetSlice()...) -// eq_a_c = AST.MakerPred(AST.Id_eq, a_c_type_list, Lib.MkListV[AST.Term](a, c)) - -// b_d_type_list := b.GetTyArgs() -// b_d_type_list.Append(d.GetTyArgs().GetSlice()...) -// eq_b_d = AST.MakerPred(AST.Id_eq, b_d_type_list, Lib.MkListV[AST.Term](b, d)) - -// x_d_type_list := Lib.MkListV[AST.Ty](x.GetTy()) -// x_d_type_list.Append(d.GetTyArgs().GetSlice()...) -// eq_x_d = AST.MakerPred(AST.Id_eq, x_d_type_list, Lib.MkListV[AST.Term](x, d)) - -// // Inequalities -// neq_x_a_type_list := Lib.MkListV[AST.Ty](x.GetTy()) -// neq_x_a_type_list.Append(a.GetTyArgs().GetSlice()...) -// neq_x_a = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_x_a_type_list, Lib.MkListV[AST.Term](x, a))) - -// neq_a_b_type_list := a.GetTyArgs() -// neq_a_b_type_list.Append(b.GetTyArgs().GetSlice()...) -// neq_a_b = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_a_b_type_list, Lib.MkListV[AST.Term](a, b))) - -// neq_a_d_type_list := a.GetTyArgs() -// neq_a_d_type_list.Append(d.GetTyArgs().GetSlice()...) -// neq_a_d = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_a_d_type_list, Lib.MkListV[AST.Term](a, d))) - -// neq_gggx_x_type_list := gggx.GetTyArgs() -// neq_gggx_x_type_list.Append(x.GetTy()) -// neq_gggx_x = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_gggx_x_type_list, Lib.MkListV[AST.Term](gggx, x))) - -// neq_fx_a_type_list := fx.GetTyArgs() -// neq_fx_a_type_list.Append(a.GetTyArgs().GetSlice()...) -// neq_fx_a = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fx_a_type_list, Lib.MkListV[AST.Term](fx, a))) - -// neq_fx_x_type_list := fx.GetTyArgs() -// neq_fx_x_type_list.Append(x.GetTy()) -// neq_fx_x = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fx_x_type_list, Lib.MkListV[AST.Term](fx, x))) - -// neq_fab_fcd_type_list := fab.GetTyArgs() -// neq_fab_fcd_type_list.Append(fcd.GetTyArgs().GetSlice()...) -// neq_fab_fcd = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fab_fcd_type_list, Lib.MkListV[AST.Term](fab, fcd))) - -// neq_fb_fc_type_list := fb.GetTyArgs() -// neq_fb_fc_type_list.Append(fc.GetTyArgs().GetSlice()...) -// neq_fb_fc = AST.MakerNot(AST.MakerPred(AST.Id_eq, neq_fb_fc_type_list, Lib.MkListV[AST.Term](fb, fc))) - -// // Predicates -// pggab_type_list := gga.GetTyArgs() -// pggab_type_list.Append(b.GetTyArgs().GetSlice()...) -// pggab = AST.MakerPred(p_id, pggab_type_list, Lib.MkListV[AST.Term](gga, b)) - -// pac_type_list := a.GetTyArgs() -// pac_type_list.Append(c.GetTyArgs().GetSlice()...) -// pac = AST.MakerNot(AST.MakerPred(p_id, pac_type_list, Lib.MkListV[AST.Term](a, c))) - -// pa = AST.MakerPred(p_id, a.GetTyArgs(), Lib.MkListV[AST.Term](a)) - -// pb = AST.MakerPred(p_id, b.GetTyArgs(), Lib.MkListV[AST.Term](b)) - -// not_pc = AST.MakerNot(AST.MakerPred(p_id, c.GetTyArgs(), Lib.MkListV[AST.Term](c))) - -// pab_type_list := a.GetTyArgs() -// pab_type_list.Append(b.GetTyArgs().GetSlice()...) -// pab = AST.MakerPred(p_id, pab_type_list, Lib.MkListV[AST.Term](a, b)) - -// pax_type_list := a.GetTyArgs() -// pax_type_list.Append(x.GetTy()) -// pax = AST.MakerPred(p_id, pax_type_list, Lib.MkListV[AST.Term](a, x)) - -// not_pcd_type_list := c.GetTyArgs() -// not_pcd_type_list.Append(d.GetTyArgs().GetSlice()...) -// not_pcd = AST.MakerNot(AST.MakerPred(p_id, not_pcd_type_list, Lib.MkListV[AST.Term](c, d))) -// } - -// func initCodeTreesTests(lf Lib.List[AST.Form]) (Unif.DataStructure, Unif.DataStructure) { -// tp = Unif.NewNode() -// tn = Unif.NewNode() -// tp = tp.MakeDataStruct(lf, true) -// tn = tn.MakeDataStruct(lf, false) -// return tp, tn -// } - -// func initDebuggers() { -// AST.InitDebugger() -// InitDebugger() -// Typing.InitDebugger() -// Unif.InitDebugger() -// } - -// func TestMain(m *testing.M) { -// Glob.SetStart(time.Now()) -// initDebuggers() -// AST.Init() -// Typing.Init() -// initTestVariable() -// Glob.EnableDebug() -// code := m.Run() -// os.Exit(code) -// } - -// /* Test apply substitution */ - -// func TestAS(t *testing.T) { -// /** -// * Problème : <[X = Y], X, Y> -// * Substitution : (Y, a) -// **/ - -// // Original problem -// lf := Lib.MkListV[AST.Form](eq_x_y) -// tp, tn = initCodeTreesTests(lf) -// eq := retrieveEqualities(tp.Copy()) -// ep := makeEqualityProblem(eq, x, y, makeEmptyConstraintStruct()) - -// // Expected problem -// lf2 := Lib.MkListV[AST.Form](eq_x_a) -// tp, tn = initCodeTreesTests(lf2) -// eq2 := retrieveEqualities(tp.Copy()) -// expected_ep := makeEqualityProblem(eq2, x, a, makeEmptyConstraintStruct()) - -// s := Unif.MakeEmptySubstitution() -// s.Set(y, a) -// new_ep := ep.applySubstitution(s) - -// debug( -// Lib.MkLazy(func() string { return fmt.Sprintf("Current EP : %v", new_ep.ToString()) }), -// ) - -// debug( -// Lib.MkLazy(func() string { return fmt.Sprintf("Expected : %v", expected_ep.ToString()) }), -// ) -// } - -// /*** Test constraints ***/ -// func TestConstraints1(t *testing.T) { -// /* Not consistent */ -// tp_ffx_x := eqStruct.MakeTermPair(ffx, x) -// constraint_ffx_x := MakeConstraint(PREC, tp_ffx_x) -// cs := makeEmptyConstraintStruct() -// append := cs.appendIfConsistent(constraint_ffx_x) - -// if append || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected not consistent and empty PREC list", append, cs.getPrec().toString()) -// } -// } - -// func TestConstraints2(t *testing.T) { -// /* Consistent but useless */ -// tp_x_ffx := eqStruct.MakeTermPair(x, ffx) -// constraint_x_ffx := MakeConstraint(PREC, tp_x_ffx) -// cs := makeEmptyConstraintStruct() -// append := cs.appendIfConsistent(constraint_x_ffx) - -// if !append || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) -// } -// } - -// func TestConstraints3(t *testing.T) { -// /* Consistent and relevant */ - -// tp_fx_a := eqStruct.MakeTermPair(fx, a) -// constraint_fx_a := MakeConstraint(PREC, tp_fx_a) -// cs := makeEmptyConstraintStruct() - -// append := cs.appendIfConsistent(constraint_fx_a) -// if !append || len(cs.getPrec()) != 1 || !cs.getPrec()[0].equals(constraint_fx_a) { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and %v", append, cs.getPrec().toString(), constraint_fx_a.toString()) -// } -// } - -// func TestConstraints4(t *testing.T) { -// /* First constraint is consistent, second is not consistent with the first one */ -// /* -// * On accepte les cas comme f(f(x)) < a et a < f(x) -// */ - -// tp_fx_a := eqStruct.MakeTermPair(fx, a) -// constraint_fx_a := MakeConstraint(PREC, tp_fx_a) -// cs := makeEmptyConstraintStruct() - -// res_constraint_1 := cs.appendIfConsistent(constraint_fx_a) -// if !res_constraint_1 || len(cs.getPrec()) != 1 || !cs.getPrec()[0].equals(constraint_fx_a) { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and %v", res_constraint_1, cs.getPrec().toString(), constraint_fx_a.toString()) -// } - -// tp_a_fx := eqStruct.MakeTermPair(a, fx) -// constraint_a_fx := MakeConstraint(PREC, tp_a_fx) -// res_constraint_2 := cs.appendIfConsistent(constraint_a_fx) -// if res_constraint_2 || len(cs.getPrec()) != 1 || !cs.getPrec()[0].equals(constraint_fx_a) { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected not consistent and %v", res_constraint_2, cs.getPrec().toString(), constraint_fx_a.toString()) -// } - -// } - -// func TestConstraints5(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// /* Not consistent */ -// tp_ffabc_fafbc := eqStruct.MakeTermPair(f_fab_c, f_a_fbc) -// constraint_ffabc_fafbc := MakeConstraint(PREC, tp_ffabc_fafbc) -// res_constraint_1 := cs.appendIfConsistent(constraint_ffabc_fafbc) -// if res_constraint_1 || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected not consistent and empty PREC list", res_constraint_1, cs.getPrec().toString()) -// } - -// /* Consistent but not relevant */ -// tp_fafbc_ffabc := eqStruct.MakeTermPair(f_a_fbc, f_fab_c) -// constraint_fafbc_ffabc := MakeConstraint(PREC, tp_fafbc_ffabc) -// res_constraint_2 := cs.appendIfConsistent(constraint_fafbc_ffabc) -// if !res_constraint_2 || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", res_constraint_1, cs.getPrec().toString()) -// } -// } - -// func TestConstaintes6(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// /* consistent but not relevant */ -// tp_fxfyz_ffxyz := eqStruct.MakeTermPair(f_x_fyz, f_fxy_z) -// constraint_fafbc_ffabc := MakeConstraint(PREC, tp_fxfyz_ffxyz) -// append := cs.appendIfConsistent(constraint_fafbc_ffabc) -// if !append || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) -// } -// } - -// func TestConstaintes7(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// /* consistent, should return X,a and Y, b */ -// tp_fxy_fab := eqStruct.MakeTermPair(fxy, fab) -// constraint_fxy_fab := MakeConstraint(EQ, tp_fxy_fab) -// // append := -// cs.appendIfConsistent(constraint_fxy_fab) -// /* -// if !append || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) -// } -// */ -// } - -// func TestConstaintes8(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// /* consistent, should return X,a and Y, b */ -// tp_fxa_fay := eqStruct.MakeTermPair(fxa, fay) -// constraint_fxa_fay := MakeConstraint(EQ, tp_fxa_fay) -// // append := -// cs.appendIfConsistent(constraint_fxa_fay) -// /* -// if !append || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) -// } -// */ -// } - -// func TestConstaintes9(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// /* consistent, should return X,a and Y, b */ -// tp_gga_ggx := eqStruct.MakeTermPair(gga, ggx) -// constraint_gga_ggx := MakeConstraint(PREC, tp_gga_ggx) -// // append := -// cs.appendIfConsistent(constraint_gga_ggx) -// /* -// if !append || len(cs.getPrec()) > 0 { -// t.Fatalf("Error: %v and %v is not the expected PREC list. Expected consistent and empty PREC list", append, cs.getPrec().toString()) -// } -// */ -// } - -// // --------------------------------------------------------------------------- -// // LPO / PREC edge cases -// // --------------------------------------------------------------------------- - -// // Two identical deferred constraints: the second must be accepted (idempotent). -// // f(X) ≺ a added twice → still only one entry in prec list. -// func TestConstraints_Idempotent(t *testing.T) { -// tp_fx_a := eqStruct.MakeTermPair(fx, a) -// c := MakeConstraint(PREC, tp_fx_a) -// cs := makeEmptyConstraintStruct() - -// res1 := cs.appendIfConsistent(c) -// res2 := cs.appendIfConsistent(c) // duplicate - -// if !res1 || !res2 { -// t.Fatalf("Both insertions should return true for a duplicate, got %v %v", res1, res2) -// } -// if len(cs.getPrec()) != 1 { -// t.Fatalf("Duplicate constraint should not grow the prec list; got %v", cs.getPrec().toString()) -// } -// } - -// // Two distinct deferred constraints that are compatible: both must be accepted. -// // f(X) ≺ a and g(Y) ≺ b — different metas, no conflict. -// func TestConstraints_TwoCompatibleDeferred(t *testing.T) { -// c1 := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) -// c2 := MakeConstraint(PREC, eqStruct.MakeTermPair(fy, b)) -// cs := makeEmptyConstraintStruct() - -// if !cs.appendIfConsistent(c1) { -// t.Fatalf("c1 should be consistent") -// } -// if !cs.appendIfConsistent(c2) { -// t.Fatalf("c2 should be consistent with c1") -// } -// if len(cs.getPrec()) != 2 { -// t.Fatalf("Expected 2 deferred constraints, got %v", cs.getPrec().toString()) -// } -// } - -// // g(g(g(X))) ≺ X is an occur-check violation in LPO (X appears inside gggx). -// // Must be rejected. -// func TestConstraints_OccurCheckPREC(t *testing.T) { -// tp := eqStruct.MakeTermPair(gggx, x) -// c := MakeConstraint(PREC, tp) -// cs := makeEmptyConstraintStruct() - -// if cs.appendIfConsistent(c) { -// t.Fatalf("ggg(X) ≺ X should be rejected (occur-check)") -// } -// } - -// // Ground PREC that is trivially satisfied and does not interact with any -// // deferred constraint: a ≺ f(a). Pure ground, f > a, no metas. -// // Expected: consistent, not added to prec list (ground/comparable). -// func TestConstraints_GroundSatisfied(t *testing.T) { -// tp := eqStruct.MakeTermPair(a, fa) -// c := MakeConstraint(PREC, tp) -// cs := makeEmptyConstraintStruct() - -// if !cs.appendIfConsistent(c) { -// t.Fatalf("a ≺ f(a) should be consistent (ground, f>a)") -// } -// if len(cs.getPrec()) != 0 { -// t.Fatalf("Ground comparable constraint should not be deferred; prec=%v", cs.getPrec().toString()) -// } -// } - -// // Ground PREC that is violated: f(a) ≺ a. f > a, so f(a) > a in LPO. -// // Expected: rejected. -// func TestConstraints_GroundViolated(t *testing.T) { -// tp := eqStruct.MakeTermPair(fa, a) -// c := MakeConstraint(PREC, tp) -// cs := makeEmptyConstraintStruct() - -// if cs.appendIfConsistent(c) { -// t.Fatalf("f(a) ≺ a should be rejected (ground, f>a so f(a)>a)") -// } -// } - -// // Three-way cycle: X ≺ f(X) is fine, but then adding f(X) ≺ X must fail. -// func TestConstraints_Cycle(t *testing.T) { -// c_x_fx := MakeConstraint(PREC, eqStruct.MakeTermPair(x, fx)) -// c_fx_x := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, x)) -// cs := makeEmptyConstraintStruct() - -// // X ≺ f(X): X occurs inside f(X), so this is detected as comparable and -// // satisfied (occur-check direction), prec list stays empty. -// if !cs.appendIfConsistent(c_x_fx) { -// t.Fatalf("X ≺ f(X) should be consistent") -// } -// // f(X) ≺ X: occur-check in reverse → must be rejected. -// if cs.appendIfConsistent(c_fx_x) { -// t.Fatalf("f(X) ≺ X should be rejected after X ≺ f(X)") -// } -// } - -// // --------------------------------------------------------------------------- -// // EQ edge cases -// // --------------------------------------------------------------------------- - -// // EQ constraint with already-equal ground terms: a ≃ a → trivially consistent. -// func TestConstraintsEQ_SameTerm(t *testing.T) { -// c := MakeConstraint(EQ, eqStruct.MakeTermPair(a, a)) -// cs := makeEmptyConstraintStruct() - -// if !cs.appendIfConsistent(c) { -// t.Fatalf("a ≃ a should be consistent") -// } -// } - -// // EQ constraint between two distinct ground constants: a ≃ b → not unifiable. -// func TestConstraintsEQ_GroundConflict(t *testing.T) { -// c := MakeConstraint(EQ, eqStruct.MakeTermPair(a, b)) -// cs := makeEmptyConstraintStruct() - -// if cs.appendIfConsistent(c) { -// t.Fatalf("a ≃ b should be rejected (a ≠ b ground)") -// } -// } - -// // EQ constraint X ≃ a followed by a PREC constraint f(X) ≺ a. -// // After substituting X→a, f(X) becomes f(a), and f(a) ≺ a is ground-violated. -// // Expected: the PREC is rejected. -// func TestConstraints_EQThenPREC_Conflict(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) -// if !cs.appendIfConsistent(cEQ) { -// t.Fatalf("X ≃ a should be accepted") -// } - -// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) -// if cs.appendIfConsistent(cPREC) { -// t.Fatalf("f(X) ≺ a with X→a means f(a) ≺ a, which is violated — should be rejected") -// } -// } - -// // EQ constraint X ≃ a followed by a PREC constraint a ≺ f(X). -// // After substituting X→a, a ≺ f(a) is ground-satisfied. -// // Expected: the PREC is accepted. -// func TestConstraints_EQThenPREC_Satisfied(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) -// if !cs.appendIfConsistent(cEQ) { -// t.Fatalf("X ≃ a should be accepted") -// } - -// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(a, fx)) -// if !cs.appendIfConsistent(cPREC) { -// t.Fatalf("a ≺ f(X) with X→a means a ≺ f(a), which is satisfied — should be accepted") -// } -// } - -// // Deferred PREC f(X) ≺ a, then EQ X ≃ a. -// // Applying X→a to the deferred constraint gives f(a) ≺ a — violated. -// // The EQ must be rejected because it breaks the stored PREC constraint. -// func TestConstraints_PRECThenEQ_Conflict(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) -// if !cs.appendIfConsistent(cPREC) { -// t.Fatalf("f(X) ≺ a should be deferred") -// } - -// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) -// if cs.appendIfConsistent(cEQ) { -// t.Fatalf("X ≃ a should be rejected: it instantiates f(X) ≺ a to f(a) ≺ a which is violated") -// } -// } - -// // Two conflicting EQ constraints: X ≃ a then X ≃ b. -// // Second should be rejected because the substitution already maps X to a. -// func TestConstraintsEQ_ConflictingSubst(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// c1 := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) -// c2 := MakeConstraint(EQ, eqStruct.MakeTermPair(x, b)) - -// if !cs.appendIfConsistent(c1) { -// t.Fatalf("X ≃ a should be accepted") -// } -// if cs.appendIfConsistent(c2) { -// t.Fatalf("X ≃ b should be rejected: X is already bound to a") -// } -// } - -// // Two compatible EQ constraints on different metas: X ≃ a then Y ≃ b. -// // Both should be accepted and the substitution should contain both bindings. -// func TestConstraintsEQ_CompatibleSubst(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// c1 := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) -// c2 := MakeConstraint(EQ, eqStruct.MakeTermPair(y, b)) - -// if !cs.appendIfConsistent(c1) { -// t.Fatalf("X ≃ a should be accepted") -// } -// if !cs.appendIfConsistent(c2) { -// t.Fatalf("Y ≃ b should be accepted alongside X ≃ a") -// } - -// s := cs.getSubst() -// xBound := false -// yBound := false -// for _, pair := range s { -// m, t := pair.Get() -// if m.Equals(x) && t.Equals(a) { -// xBound = true -// } -// if m.Equals(y) && t.Equals(b) { -// yBound = true -// } -// } -// if !xBound || !yBound { -// t.Fatalf("Expected substitution {X→a, Y→b}, got %v", s.ToString()) -// } -// } - -// // Substitution applied to a PREC that remains comparable after instantiation, -// // but in the satisfying direction: deferred f(X) ≺ g(a), then X ≃ a. -// // After X→a: f(a) ≺ g(a). f < g so f(a) < g(a) in LPO — satisfied. -// // Expected: EQ accepted, prec list cleared (constraint resolved). -// func TestConstraints_PRECResolvedByEQ(t *testing.T) { -// cs := makeEmptyConstraintStruct() - -// // f(X) ≺ g(a): f < g, but X is free → deferred -// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, ga)) -// if !cs.appendIfConsistent(cPREC) { -// t.Fatalf("f(X) ≺ g(a) should be deferred as consistent") -// } -// if len(cs.getPrec()) != 1 { -// t.Fatalf("f(X) ≺ g(a) should be in the prec list, got %v", cs.getPrec().toString()) -// } - -// // X ≃ a: should be accepted; after applying, the deferred PREC is satisfied. -// cEQ := MakeConstraint(EQ, eqStruct.MakeTermPair(x, a)) -// if !cs.appendIfConsistent(cEQ) { -// t.Fatalf("X ≃ a should be accepted; it resolves f(X) ≺ g(a) to f(a) ≺ g(a) which holds") -// } -// } - -// // Empty constraint struct — isEmpty must hold. -// func TestConstraintStruct_Empty(t *testing.T) { -// cs := makeEmptyConstraintStruct() -// if !cs.isEmpty() { -// t.Fatalf("Fresh constraint struct should be empty") -// } -// } - -// // After a successful PREC insertion the struct is no longer empty. -// func TestConstraintStruct_NotEmptyAfterInsert(t *testing.T) { -// cs := makeEmptyConstraintStruct() -// c := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) -// cs.appendIfConsistent(c) -// if cs.isEmpty() { -// t.Fatalf("Struct should not be empty after inserting a deferred constraint") -// } -// } - -// // copy() must produce a deep copy: mutating the copy must not affect the original. -// func TestConstraintStruct_Copy(t *testing.T) { -// cs := makeEmptyConstraintStruct() -// c := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) -// cs.appendIfConsistent(c) - -// csCopy := cs.copy() - -// // Add a new constraint only to the copy. -// c2 := MakeConstraint(PREC, eqStruct.MakeTermPair(fy, b)) -// csCopy.appendIfConsistent(c2) - -// if len(cs.getPrec()) != 1 { -// t.Fatalf("Original prec list should still have 1 element after mutating the copy; got %v", cs.getPrec().toString()) -// } -// if len(csCopy.getPrec()) != 2 { -// t.Fatalf("Copy prec list should have 2 elements; got %v", csCopy.getPrec().toString()) -// } -// } - -// // A substitution that maps X to itself (identity) should be treated as empty/trivial. -// func TestConstraintsEQ_IdentitySubst(t *testing.T) { -// cs := makeEmptyConstraintStruct() -// s := Unif.MakeEmptySubstitution() -// s.Set(x, x) -// cs.setSubst(s) - -// // f(X) ≺ a with a substitution that maps X→X: effectively no change. -// cPREC := MakeConstraint(PREC, eqStruct.MakeTermPair(fx, a)) -// if !cs.appendIfConsistent(cPREC) { -// t.Fatalf("f(X) ≺ a should still be deferred as consistent with identity subst") -// } - -// } From ee8194500a624924d2bde6179776e71594e1d81b Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Wed, 6 May 2026 15:02:06 +0200 Subject: [PATCH 11/23] Add concurrency to RetrieveUnifiables --- src/Unif/codetree/code-trees.go | 3 +- src/Unif/discriminationtree/dt_test.go | 78 ++++++++++++++++++++------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/Unif/codetree/code-trees.go b/src/Unif/codetree/code-trees.go index 8723369c..ba68694d 100644 --- a/src/Unif/codetree/code-trees.go +++ b/src/Unif/codetree/code-trees.go @@ -41,7 +41,7 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif/substitution" + subst "github.com/GoelandProver/Goeland/Unif/substitution" ) /*************************/ @@ -101,7 +101,6 @@ func (n Node) Copy() subst.DataStructure { return Node{n.getValue(), n.getChildren(), n.leafFor.Copy(Lib.EitherCpy[AST.Term, AST.Form])} } - /********************/ /* Helper functions */ /********************/ diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 62515b87..cc0ac01a 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -129,6 +129,9 @@ var pb AST.Form var not_pc AST.Form var pab AST.Form +var paa AST.Form +var pbb AST.Form + var pabc AST.Form var pba AST.Form @@ -240,6 +243,9 @@ func initTestVariable() { not_pac = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, c))) not_pc = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c))) pab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) + paa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, a)) + pbb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, b)) + pabc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b, c)) pba = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, a)) pca = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, a)) @@ -453,6 +459,19 @@ func TestPrintHugeTree(t *testing.T) { } +func TestTmp(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + tree = tree.Insert(paa.(AST.Pred)) + _, mix := tree.Unify(pay.(AST.Pred)) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } + + fmt.Println(len(mix)) + +} + func TestParseFormula(t *testing.T) { tmp := parseFormula(pax) @@ -494,8 +513,11 @@ func TestParseTerm(t *testing.T) { var tmp []string var tmp2 []string var tmp3 []string + tmpContext := NewContext() + tmpContext2 := NewContext() + tmpContext3 := NewContext() - seqList := parseTerm(fxy, nil) + seqList := parseTerm(fxy, tmpContext) seq := seqList.GetSlice() if len(seq) != 3 { t.Fatalf("Got %d elements", len(seq)) @@ -505,7 +527,7 @@ func TestParseTerm(t *testing.T) { } fmt.Printf(" Sequence Parsed : % v\n", tmp) - seqList = parseTerm(f_fxy_z, nil) + seqList = parseTerm(f_fxy_z, tmpContext2) seq = seqList.GetSlice() if len(seq) != 5 { t.Fatalf("Got %d elements", len(seq)) @@ -515,7 +537,7 @@ func TestParseTerm(t *testing.T) { } fmt.Printf(" Sequence Parsed : %v\n", tmp2) - seqList = parseTerm(f_x_fyz, nil) + seqList = parseTerm(f_x_fyz, tmpContext3) seq = seqList.GetSlice() if len(seq) != 5 { t.Fatalf("Got %d elements", len(seq)) @@ -547,12 +569,25 @@ func TestRetrieve(t *testing.T) { } fmt.Println() - tree2 := NewNode() - tree2 = tree2.Insert(pax.(AST.Pred)) - results2 := tree2.RetrieveUnifiables(pba) - if len(results2) != 0 { - t.Fatalf(" Not supposed to have Unifiable element") - } + fmt.Println("-----EXPECTED PANIC-----") + func() { + defer func() { + if err := recover(); err != nil { + log.Println("panic occurred:", err) + } else { + fmt.Println("Supposed to throw a Error") + } + }() + + tree2 := NewNode() + tree2 = tree2.Insert(pax.(AST.Pred)) + results2 := tree2.RetrieveUnifiables(pba) + if len(results2) != 0 { + t.Fatalf(" Not supposed to have Unifiable element") + } + + }() + fmt.Println("---END EXPECTED PANIC---") fmt.Println("----- EMPTY -----") fmt.Println("----- EMPTY -----") @@ -596,31 +631,37 @@ func TestEquals(t *testing.T) { func TestGetSubTermLength(t *testing.T) { - seq := parseTerm(ggx, nil).GetSlice() + tmpContext1 := NewContext() + tmpContext2 := NewContext() + tmpContext3 := NewContext() + tmpContext4 := NewContext() + tmpContext5 := NewContext() + + seq := parseTerm(ggx, tmpContext1).GetSlice() var1 := (GetSubTermLength(seq)) if var1 != 3 { t.Fatalf("Error SubTerLength with 2functions & 1Meta ") } - seq2 := parseTerm(fxy, nil).GetSlice() + seq2 := parseTerm(fxy, tmpContext2).GetSlice() var2 := (GetSubTermLength(seq2)) if var2 != 3 { t.Fatalf("Error SubTerLength with 1function & 2Meta") } - seq3 := parseTerm(gx, nil).GetSlice() + seq3 := parseTerm(gx, tmpContext3).GetSlice() var3 := (GetSubTermLength(seq3)) if var3 != 2 { t.Fatalf("Error SubTerLength with 1function & 1Meta") } - seq4 := parseTerm(ga, nil).GetSlice() + seq4 := parseTerm(ga, tmpContext4).GetSlice() var4 := (GetSubTermLength(seq4)) if var4 != 2 { t.Fatalf("Error SubTerLength with 1function & 1cst") } - seq5 := parseTerm(gggx, nil).GetSlice() + seq5 := parseTerm(gggx, tmpContext5).GetSlice() var5 := (GetSubTermLength(seq5)) if var5 != 4 { t.Fatalf("Error SubTerLength with 1function & 3Meta") @@ -1257,8 +1298,13 @@ func TestMakeDataStruct(t *testing.T) { func TestCaMarchePas(t *testing.T) { tree3 := NewNode() - tree3 = tree3.Insert(pfx.(AST.Pred)) - tree3 = tree3.Insert(pfy.(AST.Pred)) + tree3 = tree3.Insert(paa.(AST.Pred)) + tree3 = tree3.Insert(pbb.(AST.Pred)) + _, mix := tree3.Unify(pxx.(AST.Pred)) + for _, elem := range mix { + fmt.Println(elem.ToString()) + } + tree3.Print() } From a55cf19f4d85092954968f8db4dc4e637786edee Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Tue, 12 May 2026 12:00:02 +0200 Subject: [PATCH 12/23] Multi-Thread implemented. Working on Type Unification --- .../discrimination-trees.go | 210 +++++++++++------- src/Unif/discriminationtree/dt_test.go | 52 +++-- 2 files changed, 159 insertions(+), 103 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 37e79c23..043e39ec 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -39,6 +39,7 @@ package discriminationtree import ( "fmt" "strings" + "sync" "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" @@ -46,6 +47,12 @@ import ( subst "github.com/GoelandProver/Goeland/Unif/substitution" ) +var debug Glob.Debugger + +func InitDebugger() { + debug = Glob.CreateDebugger("unif") +} + /*************************/ /* Structures definition */ /*************************/ @@ -183,7 +190,7 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { func parseFormula(formula AST.Form) Lib.List[SymbolType] { res := Lib.NewList[SymbolType]() - ctx := NewContext() // Context gonna start all the transformations ( X == v1, Y == v2, ...) + ctx := NewContext() // Context gonna store all the transformations ( X == v1, Y == v2, ...) switch formula_type := formula.(type) { case AST.Pred: @@ -295,7 +302,7 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm // End of recursion, time to insert if seq.Len() == 0 { Exist := false - for _, pred := range dNode.leafFor.GetSlice() { + for _, pred := range dNode.getLeafFor().GetSlice() { if pred.Equals(originalTerm) { Exist = true break @@ -310,7 +317,7 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm // Create Symbol sym := seq.At(0) foundIndex := -1 - childrenSlice := dNode.children.GetSlice() + childrenSlice := dNode.getChildren().GetSlice() // Looking for already existing child var ok bool @@ -355,7 +362,7 @@ func GetSubTermLength(seq []SymbolType) int { for needed > 0 && index < len(seq) { sym := seq[index] - needed = needed - 1 + sym.arity // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... + needed = needed - 1 + sym.GetArity() // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... index++ } return index @@ -371,7 +378,7 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue return dNode.retrieveRec(remainingQuery, substitutions) } - for _, child := range dNode.children.GetSlice() { + for _, child := range dNode.getChildren().GetSlice() { newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery, substitutions) subs = append(subs, matches...) @@ -380,79 +387,34 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue } -func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { - seq := parseFormula(t).GetSlice() - Env := subst.Substitutions{} - return dNode.retrieveRec(seq, Env) -} - -func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { - - var results []CandidatResult - - if len(seq) == 0 { // End of recursion - for _, p := range dNode.leafFor.GetSlice() { - results = append(results, MakeCandidat(p, currentEnv)) - } - return results - } +func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { + defer wg.Done() symQuery := seq[0] // First Element - for _, child := range dNode.children.GetSlice() { - - isExactMatch := child.symbol.Equals(symQuery) - if isExactMatch { // Exact Match - matches := child.retrieveRec(seq[1:], currentEnv) // Exact Match -> Search next element - results = append(results, matches...) - } - - // Depending of the symbol of the child, there is 2 possilities, either the child is a meta, ether the sym is a meta - symChild := child.getSymbol() - - // Case the child is a AST.Meta - if symChild != nil && symChild.IsMeta() && !isExactMatch { + isExactMatch := child.symbol.Equals(symQuery) + if isExactMatch { // Exact Match + matches := child.retrieveRec(seq[1:], currentEnv) // Exact Match -> Search next element + ch <- matches + } - // We noticed that the term of the dNode is a Meta - // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term - // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 - skip := GetSubTermLength(seq) + symChild := child.getSymbol() // child is meta or cst - if skip <= len(seq) { // Security to prevent segfault + // Case the child is a AST.Meta + if symChild != nil && symChild.IsMeta() && !isExactMatch { - var mergedSub subst.Substitutions - if skip == 1 { - currentSub := subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol()) - tmp3 := subst.Substitutions{currentSub} - // Ok Commat Idoms doesn't works because ?????????????????????????????? - if len(currentEnv) == 0 { - mergedSub = tmp3 - } else { - mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) - } + // We noticed that the term of the dNode is a Meta + // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term + // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 + skip := GetSubTermLength(seq) - } else { - mergedSub = currentEnv - } - - // Verify - if !mergedSub.Equals(subst.Failure()) { - matches := child.retrieveRec(seq[skip:], mergedSub) - results = append(results, matches...) - } + if skip <= len(seq) { // Security to prevent segfault - } - - // First element is a meta - } else if symQuery.getSymbol() != nil && symQuery.getSymbol().IsMeta() && !isExactMatch { - - // Reverse of the situation with the previous if. - // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify - // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode var mergedSub subst.Substitutions - if child.GetArity() == 0 { - currentSub := subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild) // Create a new substitution + if skip == 1 { + currentSub := subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol()) tmp3 := subst.Substitutions{currentSub} + // Ok Commat Idoms doesn't works because ?????????????????????????????? if len(currentEnv) == 0 { mergedSub = tmp3 } else { @@ -462,15 +424,85 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S mergedSub = currentEnv } + // Verify if !mergedSub.Equals(subst.Failure()) { - childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) - results = append(results, childResults...) + matches := child.retrieveRec(seq[skip:], mergedSub) + ch <- matches } + } + + // First element is a meta + } else if symQuery.getSymbol() != nil && symQuery.getSymbol().IsMeta() && !isExactMatch { + // Reverse of the situation with the previous if. + // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify + // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode + + var mergedSub subst.Substitutions + + if child.GetArity() == 0 { + currentSub := subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild) // Create a new substitution + tmp3 := subst.Substitutions{currentSub} + if len(currentEnv) == 0 { + mergedSub = tmp3 + } else { + mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) + } } else { - continue + mergedSub = currentEnv } + + if !mergedSub.Equals(subst.Failure()) { + childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) + ch <- childResults + } + + } else { + // No recursive call or return + } + +} + +func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { + seq := parseFormula(t).GetSlice() + Env := subst.Substitutions{} + return dNode.retrieveRec(seq, Env) +} + +func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { + + ch := make(chan []CandidatResult) + var results []CandidatResult + + var wg sync.WaitGroup + + if len(seq) == 0 { // End of recursion + + for _, p := range dNode.getLeafFor().GetSlice() { + results = append(results, MakeCandidat(p, currentEnv)) + } + return results + } + + // Work goroutine + for _, child := range dNode.children.GetSlice() { + + wg.Add(1) // Create exactly 1 goroutine + go retrieveCase(seq, currentEnv, child, ch, &wg) } + + // Main goroutine waiting until all the goroutine stop + go func() { + wg.Wait() + close(ch) + }() + + for matches := range ch { + + results = append(results, matches...) + + } + return results } @@ -479,7 +511,7 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S /*****************************/ func (dNode DiscriminationNode) Print() { - for _, child := range dNode.children.GetSlice() { + for _, child := range dNode.getChildren().GetSlice() { child.displayRec(2) // Magic Number (Set the indent but bellow 2 the display is horrible and above 2 is bugget for ??? reason) } } @@ -491,15 +523,19 @@ func (dNode DiscriminationNode) displayRec(indent int) { if indent == 2 { prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " } - fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) + debug(Lib.MkLazy(func() string { + return fmt.Sprintf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) + })) - if dNode.leafFor.Len() > 0 { + if dNode.getLeafFor().Len() > 0 { leafPrefix := strings.Repeat(" ", indent) + " [=> " - for _, pred := range dNode.leafFor.GetSlice() { - fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) + for _, pred := range dNode.getLeafFor().GetSlice() { + debug(Lib.MkLazy(func() string { + return fmt.Sprintf("%s%s]\n", leafPrefix, pred.ToString()) + })) } } - for _, child := range dNode.children.GetSlice() { + for _, child := range dNode.getChildren().GetSlice() { child.displayRec(indent + 1) } } @@ -511,13 +547,17 @@ func (dNode DiscriminationNode) IsEmpty() bool { func (dNode DiscriminationNode) Copy() subst.DataStructure { newChildMaster := Lib.NewList[DiscriminationNode]() - for _, child := range dNode.children.GetSlice() { + for _, child := range dNode.getChildren().GetSlice() { newChild := child.Copy().(DiscriminationNode) newChildMaster.Append(newChild) } - newLeafFor := Lib.ListCpy(dNode.leafFor) - return DiscriminationNode{symbol: dNode.symbol, children: newChildMaster, leafFor: newLeafFor} + newLeafFor := Lib.ListCpy(dNode.getLeafFor()) + return DiscriminationNode{ + symbol: dNode.symbol, + children: newChildMaster, + leafFor: newLeafFor, + } } @@ -525,7 +565,6 @@ func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_p form := Lib.NewList[AST.Form]() - // fixme: why are we doing this here? for _, f := range formulas.GetSlice() { switch nf := f.(type) { case AST.Pred: @@ -594,24 +633,25 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe return found, mixed } -func (dNode DiscriminationNode) UnifyTerm(t AST.Term) (bool, []subst.MixedTermSubstitutions) { +func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.MixedTermSubstitutions) { var mixed []subst.MixedTermSubstitutions var found bool + tmpContext := NewContext() - seq := parseTerm(t, nil).GetSlice() + seq := parseTerm(inputTerm, tmpContext).GetSlice() candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) for _, possibleMatch := range candidates { candidateTerm := subst.TransformPred(possibleMatch.getPred()) emptySubst := subst.Substitutions{} - finalSubst := subst.AddUnification(t, candidateTerm, emptySubst) // Call Robinson + finalSubst := subst.AddUnification(inputTerm, candidateTerm, emptySubst) // Call Robinson if !finalSubst.Equals(subst.Failure()) { found = true mixMatch := subst.MixMatchSubstitutions{ - Tof: Lib.MkLeft[AST.Term, AST.Form](t), + Tof: Lib.MkLeft[AST.Term, AST.Form](inputTerm), Subst: finalSubst, } mixed = append(mixed, mixMatch.ToMixedTerm()) diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index cc0ac01a..a13919ac 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -46,9 +46,6 @@ import ( subst "github.com/GoelandProver/Goeland/Unif/substitution" ) -// Code trees -//var tp, tn Unif.DataStructure - // Id var p_id AST.Id var g_id AST.Id @@ -165,6 +162,7 @@ var PRa AST.Form var PRb AST.Form func initTestVariable() { + // Id p_id = AST.MakerId("P") g_id = AST.MakerId("g") @@ -201,7 +199,6 @@ func initTestVariable() { fa = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) fb = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) fc = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c)) - ggx = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gx)) gga = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](ga)) gfy = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fy)) @@ -209,13 +206,11 @@ func initTestVariable() { fxy = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, y)) fyz = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, z)) ffx = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fx)) - gax = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, x)) gxb = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, b)) gyb = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, b)) gab = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) gxc = AST.MakerFun(g_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, c)) - fxa = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, a)) fay = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, y)) fab = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) @@ -233,7 +228,6 @@ func initTestVariable() { f_gxb_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gxb, y)) f_gyb_z = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gyb, z)) f_gab_a = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gab, a)) - f_gxc_b = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gxc, b)) f_z_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](z, y)) f_x_y = AST.MakerFun(f_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, y)) @@ -245,7 +239,6 @@ func initTestVariable() { pab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) paa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, a)) pbb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, b)) - pabc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b, c)) pba = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, a)) pca = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, a)) @@ -261,7 +254,6 @@ func initTestVariable() { pafy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fy)) pafx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fx)) pfac = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fa, c)) - pfgaxc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gax_c)) pfgxby = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gxb_y)) pfgybz = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gyb_z)) @@ -271,7 +263,6 @@ func initTestVariable() { pfxy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_x_y)) pfxx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_x_x)) pfzz = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_z_z)) - not_pcd = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, d))) pa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) pb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) @@ -279,6 +270,31 @@ func initTestVariable() { PRb = AST.MakerPred(PR_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) } +// Typed Part + +var p_typed_id AST.Id +var a_typed AST.Ty +var p_typed_pred_A_Z AST.Pred +var p_typed_pred_int_2 AST.Pred + +func initTestVariable2() { + + p_typed_id = AST.MakerId("p") + + a_typed := AST.MkTyMeta("A", -1) + + p_typed_pred_A_Z = AST.MakerPred(p_typed_id, + Lib.MkListV(a_typed), + Lib.MkListV[AST.Term](AST.MakerMeta("Z", -1, a_typed)), + ) + + p_typed_pred_int_2 = AST.MakerPred(p_typed_id, + Lib.MkListV(AST.MkTyConst("int")), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2"))), + ) + +} + func initDebuggers() { AST.InitDebugger() Typing.InitDebugger() @@ -291,6 +307,7 @@ func TestMain(m *testing.M) { AST.Init() Typing.Init() initTestVariable() + initTestVariable2() Glob.EnableDebug() code := m.Run() os.Exit(code) @@ -1295,16 +1312,15 @@ func TestMakeDataStruct(t *testing.T) { } -func TestCaMarchePas(t *testing.T) { +func TestToutPlaquerPourDevenirCharpentier(t *testing.T) { - tree3 := NewNode() - tree3 = tree3.Insert(paa.(AST.Pred)) - tree3 = tree3.Insert(pbb.(AST.Pred)) - _, mix := tree3.Unify(pxx.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(p_typed_pred_A_Z) + val, mix := tree.Unify(p_typed_pred_int_2) + + fmt.Println("val", val) for _, elem := range mix { - fmt.Println(elem.ToString()) + fmt.Println("elem", elem.ToString()) } - tree3.Print() - } From 05798cfe05576ed02711520e208a7e4fd1ec1cfd Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Tue, 19 May 2026 14:33:21 +0200 Subject: [PATCH 13/23] Implementaiton change from SymbolType and Connexion to the Global Goeland system --- src/Mods/dmt/dmt.go | 14 +- src/Search/destructive.go | 11 +- src/Search/rules.go | 31 ++- src/Search/state.go | 8 +- .../discriminationtree/ContextNormalizer.go | 10 +- .../discrimination-trees.go | 225 +++++++++++++++--- src/Unif/discriminationtree/dt_test.go | 148 +++++++----- src/main.go | 2 + 8 files changed, 344 insertions(+), 105 deletions(-) diff --git a/src/Mods/dmt/dmt.go b/src/Mods/dmt/dmt.go index 1d42559e..3f2b6f77 100644 --- a/src/Mods/dmt/dmt.go +++ b/src/Mods/dmt/dmt.go @@ -43,8 +43,9 @@ import ( "github.com/GoelandProver/Goeland/AST" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - Unif "github.com/GoelandProver/Goeland/Unif/substitution" "github.com/GoelandProver/Goeland/Unif/codetree" + "github.com/GoelandProver/Goeland/Unif/discriminationtree" + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) var positiveRewrite map[string]Lib.List[AST.Form] /* Stores rewrites of atoms with positive occurrences */ @@ -83,9 +84,14 @@ func InitPluginTests(polarized, presko bool) { func initPluginGlobalVariables() { positiveRewrite = make(map[string]Lib.List[AST.Form]) negativeRewrite = make(map[string]Lib.List[AST.Form]) - // TODO - positiveTree = codetree.NewNode() - negativeTree = codetree.NewNode() + + if Glob.GetDt() { + positiveTree = discriminationtree.NewNode() + positiveTree = discriminationtree.NewNode() + } else { + positiveTree = codetree.NewNode() + negativeTree = codetree.NewNode() + } registeredAxioms = Lib.NewList[AST.Form]() } diff --git a/src/Search/destructive.go b/src/Search/destructive.go index b5d76b76..747c6154 100644 --- a/src/Search/destructive.go +++ b/src/Search/destructive.go @@ -45,6 +45,7 @@ import ( "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/dmt" "github.com/GoelandProver/Goeland/Unif/codetree" + "github.com/GoelandProver/Goeland/Unif/discriminationtree" substitution "github.com/GoelandProver/Goeland/Unif/substitution" ) @@ -104,8 +105,8 @@ func (ds *destructiveSearch) doOneStep(limit int, formula AST.Form) (bool, int) if Glob.GetDt() { // TODO : replace by DT - tp = codetree.NewNode() - tn = codetree.NewNode() + tp = discriminationtree.NewNode() + tn = discriminationtree.NewNode() } else { tp = codetree.NewNode() tn = codetree.NewNode() @@ -1070,7 +1071,7 @@ func (ds *destructiveSearch) ManageClosureRule( father_id uint64, st *State, c Communication, - substs Lib.List[Lib.List[substitution.MixedSubstitution]], + given_substs Lib.List[Lib.List[substitution.MixedSubstitution]], f Core.FormAndTerms, node_id int, original_node_id int, @@ -1080,13 +1081,13 @@ func (ds *destructiveSearch) ManageClosureRule( subst := st.GetAppliedSubst().GetSubst() mm = mm.Union(Core.GetMetaFromSubst(subst)) substs_with_mm, substs_with_mm_uncleared, substs_without_mm := - Core.DispatchSubst(substs.Copy(Lib.ListCpy[substitution.MixedSubstitution]), mm) + Core.DispatchSubst(given_substs.Copy(Lib.ListCpy[substitution.MixedSubstitution]), mm) unifier := st.GetGlobUnifier() appliedSubst := st.GetAppliedSubst().GetSubst() switch { - case substs.Empty(): + case given_substs.Empty(): debug( Lib.MkLazy(func() string { return "Branch closed by ¬⊤ or ⊥ or a litteral and its opposite!" }), ) diff --git a/src/Search/rules.go b/src/Search/rules.go index eaff2bec..12d02a7f 100644 --- a/src/Search/rules.go +++ b/src/Search/rules.go @@ -200,11 +200,38 @@ func searchInequalities(form AST.Form) (bool, substitution.Substitutions) { func searchClosureRule(f AST.Form, st State) (bool, []substitution.MixedSubstitutions) { switch nf := f.(type) { case AST.Pred: - return st.GetTreeNeg().Unify(f) + res, subst := st.GetTreeNeg().Unify(f) + if res { + new_list := Lib.NewList[substitution.MixedSubstitutions]() + for _, e := range subst { + subst2, res2 := substitution.MergeMixedSubstitutions(e.GetSubsts(), st.applied_subst.GetSubst()) + if res2 { + new_subst := substitution.MakeMatchingSubstitutions(e.GetForm(), substitution.ToSubstitutions(subst2)) + new_list.Append(new_subst.ToMixed()) + } + } + return !new_list.Empty(), new_list.GetSlice() + } else { + return false, nil + } + case AST.Not: switch nf.GetForm().(type) { case AST.Pred: - return st.GetTreePos().Unify(nf.GetForm()) + res, subst := st.GetTreePos().Unify(nf.GetForm()) + if res { + new_list := Lib.NewList[substitution.MixedSubstitutions]() + for _, e := range subst { + subst2, res2 := substitution.MergeMixedSubstitutions(e.GetSubsts(), st.applied_subst.GetSubst()) + if res2 { + new_subst := substitution.MakeMatchingSubstitutions(e.GetForm(), substitution.ToSubstitutions(subst2)) + new_list.Append(new_subst.ToMixed()) + } + } + return !new_list.Empty(), new_list.GetSlice() + } else { + return false, nil + } default: return false, nil } diff --git a/src/Search/state.go b/src/Search/state.go index 60d4f7c2..b5207940 100644 --- a/src/Search/state.go +++ b/src/Search/state.go @@ -44,7 +44,7 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - "github.com/GoelandProver/Goeland/Unif/substitution" + subst "github.com/GoelandProver/Goeland/Unif/substitution" ) /****************/ @@ -428,10 +428,10 @@ func (st State) Copy() State { // Recréer arbre if Glob.IsLoaded("dmt") { new_state.SetTreePos(st.tree_pos.MakeDataStruct(st.GetAtomic().ExtractForms(), true)) - new_state.SetTreeNeg(st.tree_pos.MakeDataStruct(st.GetAtomic().ExtractForms(), false)) + new_state.SetTreeNeg(st.tree_neg.MakeDataStruct(st.GetAtomic().ExtractForms(), false)) } else { - new_state.SetTreePos(st.GetTreePos()) - new_state.SetTreeNeg(st.GetTreeNeg()) + new_state.SetTreePos(st.GetTreePos().MakeDataStruct(st.GetAtomic().ExtractForms(), true)) + new_state.SetTreeNeg(st.GetTreeNeg().MakeDataStruct(st.GetAtomic().ExtractForms(), false)) } new_state.SetProof([]ProofStruct{}) diff --git a/src/Unif/discriminationtree/ContextNormalizer.go b/src/Unif/discriminationtree/ContextNormalizer.go index 2384918f..556436d3 100644 --- a/src/Unif/discriminationtree/ContextNormalizer.go +++ b/src/Unif/discriminationtree/ContextNormalizer.go @@ -46,6 +46,7 @@ import ( type NormalizerContext struct { counter int // Counter for the naming mapping map[string]AST.Meta // Association a variable name to a normalizedVariable name + ty AST.Ty } // New Instance @@ -53,16 +54,20 @@ func NewContext() *NormalizerContext { return &NormalizerContext{ counter: 0, mapping: make(map[string]AST.Meta), + ty: nil, } } func (ctx *NormalizerContext) GetNormalizedMeta(originalMeta AST.Meta) AST.Meta { originalName := originalMeta.GetName() // Get meeta Name + orignalType := originalMeta.GetTy() // Contains check - if fakeMeta, exists := ctx.mapping[originalName]; exists { - return fakeMeta + if normalizedMeta, exists := ctx.mapping[originalName]; exists { + if normalizedMeta.GetTy().Equals(orignalType) { + return normalizedMeta + } } // Create new name @@ -70,6 +75,7 @@ func (ctx *NormalizerContext) GetNormalizedMeta(originalMeta AST.Meta) AST.Meta newName := fmt.Sprintf("v%d", ctx.counter) // Create the Meta name newMeta := AST.MakeMeta(ctx.counter, 0, newName, 0, originalMeta.GetTy()) ctx.mapping[originalName] = newMeta // Add to the map + ctx.ty = newMeta.GetTy() return newMeta } diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 043e39ec..cebc4b97 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -57,12 +57,158 @@ func InitDebugger() { /* Structures definition */ /*************************/ +type NodeElement interface { + ToString() string + isAllowedNodeElement() + IsMeta() bool + GetArityType() int + Equals(target NodeElement) bool +} + +func (ns NodeString) ToString() string { + return string(ns) +} + +type NodeString string +type TermNode struct{ AST.Term } +type TyNode struct{ AST.Ty } +type PredNode struct{ AST.Pred } + +func (ns NodeString) isAllowedNodeElement() {} +func (tn TermNode) isAllowedNodeElement() {} +func (tn TyNode) isAllowedNodeElement() {} +func (tn PredNode) isAllowedNodeElement() {} + +func (ns NodeString) IsMeta() bool { + return false +} + +func (tn TermNode) IsMeta() bool { + return tn.Term.IsMeta() +} + +func (tn TyNode) IsMeta() bool { + return false +} + +func (tn PredNode) IsMeta() bool { + return false +} + +func (ns NodeString) GetArityType() int { + return 0 +} + +func (tn TermNode) GetArityType() int { + return tn.GetMetaList().Len() +} + +func (tn TyNode) GetArityType() int { + return 0 +} + +func (tn PredNode) GetArityType() int { + return tn.GetSubTerms().Len() +} + +func (sym SymbolType) getTerm() AST.Term { + + if tn, ok := sym.symbol.(TermNode); ok { + return tn.Term + } + Glob.Anomaly("Not a AST.Term", "Not a AST.Term") + return nil + +} + +func (sym SymbolType) GetTy() AST.Ty { + + if tn, ok := sym.symbol.(TyNode); ok { + return tn.Ty + } + Glob.Anomaly("Not a AST.Ty", "Not a AST.Ty") + return nil + +} + +func (sym SymbolType) getPred() AST.Pred { + + if tn, ok := sym.symbol.(PredNode); ok { + return tn.Pred + } + Glob.Anomaly("Not a AST.Term", "Not a AST.Term") + return AST.Pred{} + +} + +func (sym SymbolType) getString() string { + + if ns, ok := sym.symbol.(NodeString); ok { + return ns.ToString() + } + Glob.Anomaly("Not a string", "Not a string") + return "" +} + +func (ns NodeString) Equals(target NodeElement) bool { + + typ, ok := target.(NodeString) + if !ok { + return false + } + return strings.EqualFold(ns.ToString(), typ.ToString()) +} + +func (tn TermNode) Equals(target NodeElement) bool { + + typ, ok := target.(TermNode) + if !ok { + return false + } + return tn.Term.Equals(typ.Term) + +} + +func (tn TyNode) Equals(target NodeElement) bool { + + typ, ok := target.(TyNode) + if !ok { + return false + } + return tn.Ty.Equals(typ.Ty) +} + +func (tn PredNode) Equals(target NodeElement) bool { + + typ, ok := target.(PredNode) + if !ok { + return false + } + return tn.Pred.Equals(typ.Pred) +} + +func createNodeElement(t any) NodeElement { + switch v := t.(type) { + case string: + return NodeString(v) + case AST.Ty: + return TyNode{v} + case AST.Pred: + return PredNode{v} + case AST.Term: + return TermNode{v} + default: + Glob.Anomaly("Unknow Type from createNodeElement(t any) NodeElement ", "Unknow Type from createNodeElement(t any) NodeElement") + return nil + } +} + type SymbolType struct { - symbol AST.Term // Term of the node - arity int // Arity of a node + symbol NodeElement // Term of the node + arity int // Arity of a node } -func (t SymbolType) getSymbol() AST.Term { +func (t SymbolType) getSymbol() NodeElement { return t.symbol } @@ -74,22 +220,17 @@ func (s SymbolType) IsNil() bool { return s.symbol == nil && s.arity == -1 } -func makeSymbolType(t AST.Term, arity int) SymbolType { - return SymbolType{t, arity} +func makeSymbolType(node NodeElement, arity int) SymbolType { + return SymbolType{node, arity} } // Equals between two SymbolType func (s SymbolType) Equals(target SymbolType) bool { - if ok := s.getSymbol().Equals(target.getSymbol()); ok { - if s.GetArity() != target.GetArity() { - fmt.Printf("Symbol Arity : %d, Target Arity : %d", s.GetArity(), target.GetArity()) - Glob.Anomaly("Pred Error", "Same predicat but different arity ") - } else { - return true - } + if s.GetArity() != target.GetArity() { + return false } - return false + return s.symbol.Equals(target.symbol) } /* Each node of a CodeTree is composed of a sequence of instruction and its children. If it's a leaf, it has formulaes corresponding to the sequence of instructions. */ @@ -138,12 +279,16 @@ func (dNode *DiscriminationNode) setSymbol(symbol SymbolType) { dNode.symbol = symbol } +func (dNode DiscriminationNode) getSymbol() SymbolType { + return dNode.symbol +} + func (dNode DiscriminationNode) GetArity() int { return dNode.symbol.GetArity() } -func (dNode DiscriminationNode) getSymbol() AST.Term { - return dNode.symbol.getSymbol() +func (dNode DiscriminationNode) getElement() any { + return dNode.getSymbol().getSymbol() } func (dNode DiscriminationNode) getChildren() Lib.List[DiscriminationNode] { @@ -154,8 +299,14 @@ func (dNode DiscriminationNode) getLeafFor() Lib.List[AST.Pred] { return dNode.leafFor } -func (dNode DiscriminationNode) ToString() string { - return dNode.getSymbol().ToString() +func (dNode DiscriminationNode) toString() string { + + if dNode.getSymbol().getSymbol() == nil { + Glob.Anomaly("Symbol is Nil", "Symbol is nil") + return "" + } + return dNode.getSymbol().getSymbol().ToString() + } // Struct with a Pred and a associated substitution. Used for Robinson @@ -195,7 +346,7 @@ func parseFormula(formula AST.Form) Lib.List[SymbolType] { switch formula_type := formula.(type) { case AST.Pred: // Add First element ( Predicat ) - first_element := makeSymbolType(formula_type.GetID(), formula_type.GetArgs().Len()) + first_element := makeSymbolType(createNodeElement(formula_type.GetID()), formula_type.GetArgs().Len()) res.Append(first_element) // Call the parse on each element of the predicat @@ -216,7 +367,7 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { // if term is a function or cst, add and call his args case AST.Fun: - first_element := makeSymbolType(term.GetID(), term.GetArgs().Len()) + first_element := makeSymbolType(createNodeElement(term.GetID()), term.GetArgs().Len()) res.Append(first_element) for _, arg := range term.GetArgs().GetSlice() { res.Append(parseTerm(arg, ctx).GetSlice()...) @@ -234,8 +385,8 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { ctx.mapping[originalName] = fakeMeta // Update the mapping : X -> v1 or Y -> v2 .... ) } - normalizedMeta := ctx.mapping[originalName] // Return the transformed name association to the originalName before adding - res.Append(makeSymbolType(normalizedMeta, 0)) // Add the new Meta to the return slice + normalizedMeta := ctx.mapping[originalName] // Return the transformed name association to the originalName before adding + res.Append(makeSymbolType(createNodeElement(normalizedMeta), 0)) // Add the new Meta to the return slice } return res @@ -256,12 +407,12 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { func FirstElementToSymbolType(t AST.Term) SymbolType { switch t := t.(type) { case AST.Fun: // Case function - return SymbolType{t.GetID(), t.GetArgs().Len()} + return SymbolType{createNodeElement(t.GetID()), t.GetArgs().Len()} case AST.Meta: // Case metaVariable - return SymbolType{t, 0} + return SymbolType{createNodeElement(t), 0} default: // Not supposed to see something else Glob.Anomaly("TermToST", "Var or Id") - return SymbolType{nil, -1} + return SymbolType{createNodeElement(nil), -1} // Dog Code that will fail but won't be triggered due to Glob.Anomaly + make the compiler happy } } @@ -322,7 +473,7 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm // Looking for already existing child var ok bool for i, child := range childrenSlice { - if ok = child.symbol.Equals(sym); ok { // Set ok to True + if ok = child.getSymbol().Equals(sym); ok { // Set ok to True foundIndex = i break } @@ -401,7 +552,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri symChild := child.getSymbol() // child is meta or cst // Case the child is a AST.Meta - if symChild != nil && symChild.IsMeta() && !isExactMatch { + if !symChild.IsNil() && child.getSymbol().getSymbol().IsMeta() && !isExactMatch { // We noticed that the term of the dNode is a Meta // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term @@ -412,7 +563,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri var mergedSub subst.Substitutions if skip == 1 { - currentSub := subst.MakeSubstitution(symChild.ToMeta(), symQuery.getSymbol()) + currentSub := subst.MakeSubstitution(symChild.getTerm().ToMeta(), symQuery.getTerm()) tmp3 := subst.Substitutions{currentSub} // Ok Commat Idoms doesn't works because ?????????????????????????????? if len(currentEnv) == 0 { @@ -433,7 +584,9 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri } // First element is a meta - } else if symQuery.getSymbol() != nil && symQuery.getSymbol().IsMeta() && !isExactMatch { + + } else if !symQuery.IsNil() && symQuery.getSymbol().IsMeta() && !isExactMatch { + // Reverse of the situation with the previous if. // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode @@ -441,7 +594,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri var mergedSub subst.Substitutions if child.GetArity() == 0 { - currentSub := subst.MakeSubstitution(symQuery.getSymbol().ToMeta(), symChild) // Create a new substitution + currentSub := subst.MakeSubstitution(symQuery.getTerm().ToMeta(), symChild.getTerm()) // Create a new substitution tmp3 := subst.Substitutions{currentSub} if len(currentEnv) == 0 { mergedSub = tmp3 @@ -523,16 +676,13 @@ func (dNode DiscriminationNode) displayRec(indent int) { if indent == 2 { prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " } - debug(Lib.MkLazy(func() string { - return fmt.Sprintf("%s%s arity : %d\n", prefix, dNode.getSymbol().ToString(), dNode.GetArity()) - })) + + fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().symbol.ToString(), dNode.GetArity()) if dNode.getLeafFor().Len() > 0 { leafPrefix := strings.Repeat(" ", indent) + " [=> " for _, pred := range dNode.getLeafFor().GetSlice() { - debug(Lib.MkLazy(func() string { - return fmt.Sprintf("%s%s]\n", leafPrefix, pred.ToString()) - })) + fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) } } for _, child := range dNode.getChildren().GetSlice() { @@ -602,6 +752,11 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { candidates := dNode.RetrieveUnifiables(inputFormula) + + for _, elem := range candidates { + fmt.Println("Pred", elem.getPred().ToString(), "Subs", elem.GetSubs().ToString()) + } + var mixed []subst.MixedSubstitutions var found bool diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index a13919ac..06df6fa3 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -56,10 +56,12 @@ var c_id AST.Id var d_id AST.Id var c1_id AST.Id var c2_id AST.Id -var PR_id AST.Id +var P2_id AST.Id // Meta var x AST.Meta +var v1 AST.Meta +var v2 AST.Meta var y AST.Meta var z AST.Meta var z1 AST.Meta @@ -158,8 +160,8 @@ var pfzz AST.Form var not_pcd AST.Form -var PRa AST.Form -var PRb AST.Form +var P2a AST.Form +var P2b AST.Form func initTestVariable() { @@ -173,10 +175,12 @@ func initTestVariable() { d_id = AST.MakerId("d") c1_id = AST.MakerId("c1") c2_id = AST.MakerId("c2") - PR_id = AST.MakerId("PR") + P2_id = AST.MakerId("P2") // Meta x = AST.MakerMeta("X", -1, AST.TIndividual()) + v1 = AST.MakeMeta(1, 0, "v1", 0, AST.TIndividual()) + v2 = AST.MakeMeta(2, 0, "v2", 0, AST.TIndividual()) y = AST.MakerMeta("Y", -1, AST.TIndividual()) z = AST.MakerMeta("Z", -1, AST.TIndividual()) z1 = AST.MakerMeta("Z1", -1, AST.TIndividual()) @@ -266,33 +270,82 @@ func initTestVariable() { not_pcd = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, d))) pa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) pb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) - PRa = AST.MakerPred(PR_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) - PRb = AST.MakerPred(PR_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) + P2a = AST.MakerPred(P2_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a)) + P2b = AST.MakerPred(P2_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b)) } // Typed Part var p_typed_id AST.Id +var b_typed_id AST.Id +var a_typed_id AST.Id var a_typed AST.Ty -var p_typed_pred_A_Z AST.Pred +var p_typed_pred_Const_A AST.Pred +var p_typed_pred_Const_B AST.Pred var p_typed_pred_int_2 AST.Pred +var p_typed_pred_int_3 AST.Pred +var p_typed_pred_int_int AST.Pred +var p_typed_pred_int_x AST.Pred +var p_typed_pred_reel_x AST.Pred +var p_typed_pred_rational_x AST.Pred + +var p_id_typed AST.Id +var p_typed AST.Pred +var random_type AST.Ty +var banane_id AST.Id +var banane AST.Term +var meta_typee AST.Term func initTestVariable2() { p_typed_id = AST.MakerId("p") + b_typed_id = AST.MakerId("b") + a_typed_id = AST.MakerId("A") - a_typed := AST.MkTyMeta("A", -1) + A := AST.MkTyConst("A") + B := AST.MkTyConst("B") - p_typed_pred_A_Z = AST.MakerPred(p_typed_id, - Lib.MkListV(a_typed), - Lib.MkListV[AST.Term](AST.MakerMeta("Z", -1, a_typed)), + x = AST.MakerMeta("X", -1, AST.TIndividual()) + + p_typed_pred_Const_A = AST.MakerPred(p_typed_id, + Lib.MkListV(A), + Lib.MkListV[AST.Term](AST.MakerConst(a_typed_id)), ) - p_typed_pred_int_2 = AST.MakerPred(p_typed_id, + p_typed_pred_Const_B = AST.MakerPred(p_typed_id, + Lib.MkListV(B), + Lib.MkListV[AST.Term](AST.MakerConst(b_typed_id)), + ) + + p_typed_pred_int_x = AST.MakerPred(p_typed_id, + Lib.MkListV(AST.TInt()), + Lib.MkListV[AST.Term](x), + ) + + p_typed_pred_reel_x = AST.MakerPred(p_typed_id, + Lib.MkListV(AST.TReal()), + Lib.MkListV[AST.Term](x), + ) + + p_typed_pred_rational_x = AST.MakerPred(p_typed_id, + Lib.MkListV(AST.TRat()), + Lib.MkListV[AST.Term](x), + ) + + p_typed_pred_int_3 = AST.MakerPred(p_typed_id, Lib.MkListV(AST.MkTyConst("int")), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("3"))), + ) + + p_typed_pred_int_2 = AST.MakerPred(p_typed_id, + Lib.MkListV(AST.TInt()), Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2"))), ) + random_type = AST.MakerTyBV("random_type") + p_id_typed = AST.MakerId("p_typed") + meta_typee = AST.MakerMeta("Z", -1, random_type) + p_typed = AST.MakerPred(p_id_typed, Lib.MkListV(random_type), Lib.MkListV(meta_typee)) } func initDebuggers() { @@ -323,8 +376,6 @@ func TestFirstElementToSymbolType(t *testing.T) { if tree.GetArity() == -1 { Glob.Anomaly("Arity Error", "Wrong Arity") - } else { - fmt.Println("OK") } tree2 := NewNode() @@ -335,16 +386,12 @@ func TestFirstElementToSymbolType(t *testing.T) { if tree2.GetArity() == -1 { Glob.Anomaly("Arity Error", "Wrong Arity") - } else { - fmt.Println("OK") } argsC := gga.GetArgs() resultC := FirstElementToSymbolType(argsC.At(0)) if resultC.GetArity() == -1 { Glob.Anomaly("Arity Error", "Wrong Arity") - } else { - fmt.Println("OK") } fmt.Println("-----EXPECTED PANIC-----") @@ -359,7 +406,7 @@ func TestFirstElementToSymbolType(t *testing.T) { argsD := c_id resultD := FirstElementToSymbolType(argsD) - println("Not supposed to see this ", resultD.symbol) // Required or Go panic due variable not used. However if you see this print : Bon Courage + println("Not supposed to see this ", resultD.getTerm().ToString()) // Required or Go panic due variable not used. However if you see this print : Bon Courage }() fmt.Println("---END EXPECTED PANIC---") @@ -429,8 +476,8 @@ func TestPrintDiscriminationTree(t *testing.T) { tree := NewNode() tree = tree.Insert(pa.(AST.Pred)) tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(PRa.(AST.Pred)) - tree = tree.Insert(PRb.(AST.Pred)) + tree = tree.Insert(P2a.(AST.Pred)) + tree = tree.Insert(P2b.(AST.Pred)) tree.Print() fmt.Println() @@ -445,8 +492,8 @@ func TestPrintSamePredicatCheck(t *testing.T) { tree := NewNode() tree = tree.Insert(pa.(AST.Pred)) tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(PRa.(AST.Pred)) - tree = tree.Insert(PRb.(AST.Pred)) + tree = tree.Insert(P2a.(AST.Pred)) + tree = tree.Insert(P2b.(AST.Pred)) tree.Print() } @@ -492,37 +539,22 @@ func TestTmp(t *testing.T) { func TestParseFormula(t *testing.T) { tmp := parseFormula(pax) + expectedPred := makeSymbolType(createNodeElement(p_id), 2) + expectedA := makeSymbolType(createNodeElement(a_id), 0) + expectedX := makeSymbolType(createNodeElement(v1), 0) - fmt.Println(tmp.GetSlice()) + for _, elem := range tmp.GetSlice() { - for _, value := range tmp.GetSlice() { - fmt.Println(value.getSymbol().ToString()) - if value.getSymbol().ToString() == "P" { - if value.GetArity() != 2 { - t.Fatalf("Arrity Error") - } - } else if value.getSymbol().ToString() == "a" { - if value.GetArity() != 0 { - t.Fatalf("Arrity Error") - } - } else if value.getSymbol().ToString() == "v1" { - if value.GetArity() != 0 { - t.Fatalf("Arrity Error") - } + if elem.Equals(expectedPred) { + continue + } else if elem.Equals(expectedA) { + continue + } else if elem.Equals(expectedX) { + continue } else { - t.Fatalf("Supposed to have only \"P\", \"a\" or \"v1\" ") + t.Fatalf("Symbole inconnu détecté dans parseFormula : %s", elem.getSymbol().ToString()) } } - - tmp2 := parseFormula(pay) - for _, elem := range tmp2.GetSlice() { - tmp.Append(elem) - } - - for _, elem := range tmp.GetSlice() { - fmt.Println("elem", elem.getSymbol().ToString()) - } - } func TestParseTerm(t *testing.T) { @@ -540,7 +572,7 @@ func TestParseTerm(t *testing.T) { t.Fatalf("Got %d elements", len(seq)) } for _, sym := range seq { - tmp = append(tmp, sym.getSymbol().ToString()) + tmp = append(tmp, sym.getTerm().ToString()) } fmt.Printf(" Sequence Parsed : % v\n", tmp) @@ -550,7 +582,7 @@ func TestParseTerm(t *testing.T) { t.Fatalf("Got %d elements", len(seq)) } for _, sym := range seq { - tmp2 = append(tmp2, sym.getSymbol().ToString()) + tmp2 = append(tmp2, sym.getTerm().ToString()) } fmt.Printf(" Sequence Parsed : %v\n", tmp2) @@ -560,7 +592,7 @@ func TestParseTerm(t *testing.T) { t.Fatalf("Got %d elements", len(seq)) } for _, sym := range seq { - tmp3 = append(tmp3, sym.getSymbol().ToString()) + tmp3 = append(tmp3, sym.getTerm().ToString()) } fmt.Printf(" Sequence Parsed : %v\n", tmp3) @@ -1315,7 +1347,12 @@ func TestMakeDataStruct(t *testing.T) { func TestToutPlaquerPourDevenirCharpentier(t *testing.T) { tree := NewNode() - tree = tree.Insert(p_typed_pred_A_Z) + // tree = tree.Insert(p_typed_pred_int_x) + // tree = tree.Insert(p_typed_pred_reel_x) + tree = tree.Insert(p_typed) + + tree.Print() + val, mix := tree.Unify(p_typed_pred_int_2) fmt.Println("val", val) @@ -1323,4 +1360,9 @@ func TestToutPlaquerPourDevenirCharpentier(t *testing.T) { fmt.Println("elem", elem.ToString()) } + elem := (pba.(AST.Pred)).GetTyArgs() + for _, truc := range elem.GetSlice() { + fmt.Println("hfhfsife", truc.ToString()) + } + } diff --git a/src/main.go b/src/main.go index 44221dd2..9e7bf1fe 100644 --- a/src/main.go +++ b/src/main.go @@ -61,6 +61,7 @@ import ( "github.com/GoelandProver/Goeland/Search/incremental" "github.com/GoelandProver/Goeland/Typing" "github.com/GoelandProver/Goeland/Unif/codetree" + "github.com/GoelandProver/Goeland/Unif/discriminationtree" subst "github.com/GoelandProver/Goeland/Unif/substitution" ) @@ -221,6 +222,7 @@ func initDebuggers() { Search.InitDebugger() Typing.InitDebugger() codetree.InitDebugger() + discriminationtree.InitDebugger() subst.InitDebugger() Engine.InitDebugger() gs3.InitDebugger() From c959aeacd45cc5916973572453fc3db8692ed986 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Fri, 22 May 2026 11:36:08 +0200 Subject: [PATCH 14/23] Changes to SymbolType and creation of news functions to prun the tree --- .../discrimination-trees.go | 598 ++++++++++++++---- 1 file changed, 470 insertions(+), 128 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index cebc4b97..dfc1be24 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -72,12 +72,10 @@ func (ns NodeString) ToString() string { type NodeString string type TermNode struct{ AST.Term } type TyNode struct{ AST.Ty } -type PredNode struct{ AST.Pred } func (ns NodeString) isAllowedNodeElement() {} func (tn TermNode) isAllowedNodeElement() {} func (tn TyNode) isAllowedNodeElement() {} -func (tn PredNode) isAllowedNodeElement() {} func (ns NodeString) IsMeta() bool { return false @@ -91,10 +89,6 @@ func (tn TyNode) IsMeta() bool { return false } -func (tn PredNode) IsMeta() bool { - return false -} - func (ns NodeString) GetArityType() int { return 0 } @@ -107,10 +101,6 @@ func (tn TyNode) GetArityType() int { return 0 } -func (tn PredNode) GetArityType() int { - return tn.GetSubTerms().Len() -} - func (sym SymbolType) getTerm() AST.Term { if tn, ok := sym.symbol.(TermNode); ok { @@ -131,16 +121,6 @@ func (sym SymbolType) GetTy() AST.Ty { } -func (sym SymbolType) getPred() AST.Pred { - - if tn, ok := sym.symbol.(PredNode); ok { - return tn.Pred - } - Glob.Anomaly("Not a AST.Term", "Not a AST.Term") - return AST.Pred{} - -} - func (sym SymbolType) getString() string { if ns, ok := sym.symbol.(NodeString); ok { @@ -178,15 +158,6 @@ func (tn TyNode) Equals(target NodeElement) bool { return tn.Ty.Equals(typ.Ty) } -func (tn PredNode) Equals(target NodeElement) bool { - - typ, ok := target.(PredNode) - if !ok { - return false - } - return tn.Pred.Equals(typ.Pred) -} - func createNodeElement(t any) NodeElement { switch v := t.(type) { case string: @@ -194,7 +165,7 @@ func createNodeElement(t any) NodeElement { case AST.Ty: return TyNode{v} case AST.Pred: - return PredNode{v} + return TermNode{subst.TransformPred(v)} case AST.Term: return TermNode{v} default: @@ -250,7 +221,7 @@ func NewNode() DiscriminationNode { } // Basic Node with SymbolType and no empty list for children and leafFor -func MakeNodeWithSym(sym SymbolType) DiscriminationNode { +func MakeDiscriminationNodeWithSym(sym SymbolType) DiscriminationNode { return DiscriminationNode{ symbol: sym, children: Lib.NewList[DiscriminationNode](), @@ -258,16 +229,8 @@ func MakeNodeWithSym(sym SymbolType) DiscriminationNode { } } -func MakeNodeWithSymAndleaf(sym SymbolType, leaf Lib.List[AST.Pred]) DiscriminationNode { - return DiscriminationNode{ - symbol: sym, - children: Lib.NewList[DiscriminationNode](), - leafFor: leaf, - } -} - // Basic Node with SymbolType, children and empty List for leafFor -func MakeNodeWithSymAndChildren(sym SymbolType, children Lib.List[DiscriminationNode]) DiscriminationNode { +func MakeDiscriminationNodeWithSymAndChildren(sym SymbolType, children Lib.List[DiscriminationNode]) DiscriminationNode { return DiscriminationNode{ symbol: sym, children: children, @@ -275,10 +238,6 @@ func MakeNodeWithSymAndChildren(sym SymbolType, children Lib.List[Discrimination } } -func (dNode *DiscriminationNode) setSymbol(symbol SymbolType) { - dNode.symbol = symbol -} - func (dNode DiscriminationNode) getSymbol() SymbolType { return dNode.symbol } @@ -324,6 +283,18 @@ func (Candidat CandidatResult) GetSubs() subst.Substitutions { return Candidat.Subs } +func (Candidat CandidatResult) toString() string { + return fmt.Sprintf("Pred : %s Subs : %s\n", Candidat.getPred().ToString(), Candidat.GetSubs().ToString()) +} + +// Works only if Len(CandidatResult) is 1. Had Enough to for-each all the time for single element +func ToSingleElement(candidats []CandidatResult) CandidatResult { + if len(candidats) == 1 { + return candidats[0] + } + return CandidatResult{} +} + func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { return CandidatResult{ Pred: p, @@ -339,27 +310,6 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { /*********** Parse ***********/ /*****************************/ -func parseFormula(formula AST.Form) Lib.List[SymbolType] { - res := Lib.NewList[SymbolType]() - ctx := NewContext() // Context gonna store all the transformations ( X == v1, Y == v2, ...) - - switch formula_type := formula.(type) { - case AST.Pred: - // Add First element ( Predicat ) - first_element := makeSymbolType(createNodeElement(formula_type.GetID()), formula_type.GetArgs().Len()) - res.Append(first_element) - - // Call the parse on each element of the predicat - for _, arg := range formula_type.GetArgs().GetSlice() { - arg_list := parseTerm(arg, ctx) - res.Append(arg_list.GetSlice()...) - } - return res - default: - return Lib.NewList[SymbolType]() - } -} - func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { res := Lib.NewList[SymbolType]() @@ -367,7 +317,9 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { // if term is a function or cst, add and call his args case AST.Fun: - first_element := makeSymbolType(createNodeElement(term.GetID()), term.GetArgs().Len()) + + funSansArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) + first_element := makeSymbolType(createNodeElement(funSansArgs), term.GetArgs().Len()) res.Append(first_element) for _, arg := range term.GetArgs().GetSlice() { res.Append(parseTerm(arg, ctx).GetSlice()...) @@ -388,7 +340,17 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { normalizedMeta := ctx.mapping[originalName] // Return the transformed name association to the originalName before adding res.Append(makeSymbolType(createNodeElement(normalizedMeta), 0)) // Add the new Meta to the return slice + case AST.Id: + fmt.Println("Parse AST.id", term.GetName()) + funSansArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + first_element := makeSymbolType(createNodeElement(funSansArgs), 0) + res.Append(first_element) + + default: + fmt.Println("Error in ParseTerm") + } + return res } @@ -407,7 +369,11 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { func FirstElementToSymbolType(t AST.Term) SymbolType { switch t := t.(type) { case AST.Fun: // Case function - return SymbolType{createNodeElement(t.GetID()), t.GetArgs().Len()} + funSansArgs := AST.MakerFun(t.GetID(), t.GetTyArgs(), Lib.NewList[AST.Term]()) + return SymbolType{createNodeElement(funSansArgs), t.GetArgs().Len()} + case AST.Id: + funSansArgs := AST.MakerFun(t, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + return SymbolType{createNodeElement(funSansArgs), 0} case AST.Meta: // Case metaVariable return SymbolType{createNodeElement(t), 0} default: // Not supposed to see something else @@ -423,9 +389,11 @@ func TermToNode(t AST.Term) DiscriminationNode { for _, c := range t.GetArgs().GetSlice() { children.Append(TermToNode(c)) } - return MakeNodeWithSymAndChildren(FirstElementToSymbolType(t), children) + return MakeDiscriminationNodeWithSymAndChildren(FirstElementToSymbolType(t), children) + case AST.Id: + return MakeDiscriminationNodeWithSym(FirstElementToSymbolType(t)) case AST.Meta: // Node with T as SymbolType and empty children / leafFor - return MakeNodeWithSym(FirstElementToSymbolType(t)) + return MakeDiscriminationNodeWithSym(FirstElementToSymbolType(t)) default: Glob.Anomaly("TermToST", "Var or Id") return NewNode() @@ -443,7 +411,8 @@ func TermToNode(t AST.Term) DiscriminationNode { // Insert a AST.Pred in the tree. If using a AST.term, it have to be cast when inserting ( tree = tree.Insert(px.(AST.pred)) ) // Call the parser then the auxiliary function func (dNode DiscriminationNode) Insert(p AST.Pred) DiscriminationNode { - sym_list := parseFormula(p) + termP := subst.TransformPred(p) + sym_list := parseTerm(termP, NewContext()) return dNode.insertRec(sym_list, p) } @@ -469,13 +438,19 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm sym := seq.At(0) foundIndex := -1 childrenSlice := dNode.getChildren().GetSlice() + var ok = false // Looking for already existing child - var ok bool for i, child := range childrenSlice { - if ok = child.getSymbol().Equals(sym); ok { // Set ok to True - foundIndex = i - break + if child.getSymbol().getSymbol().Equals(sym.getSymbol()) { + if child.GetArity() == sym.GetArity() { + foundIndex = i + ok = true + break + } else { + Glob.Anomaly("Arity Missmatch", "Same Symbol but different Arity") + } + } } @@ -487,7 +462,7 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm } else { // if Child doesn't exist - newChild := MakeNodeWithSym(sym) // Create a new Node with the new SymbolType and his leafFor + newChild := MakeDiscriminationNodeWithSym(sym) // Create a new Node with the new SymbolType and his leafFor updatedChild := newChild.insertRec(seq.RemoveAt(0), originalTerm) // Insert the rest of the sequence after the new child dNode.children.Append(updatedChild) // Update the children of the args node } @@ -538,6 +513,52 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue } +func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { + predFormula, _ := t.(AST.Pred) + termQuery := subst.TransformPred(predFormula) + + seq := parseTerm(termQuery, NewContext()).GetSlice() + Env := subst.Substitutions{} + return dNode.retrieveRec(seq, Env) +} + +func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { + + ch := make(chan []CandidatResult) + var results []CandidatResult + + var wg sync.WaitGroup + + if len(seq) == 0 { // End of recursion + + for _, p := range dNode.getLeafFor().GetSlice() { + results = append(results, MakeCandidat(p, currentEnv)) + } + return results + } + + // Work goroutine + for _, child := range dNode.children.GetSlice() { + + wg.Add(1) // Create exactly 1 goroutine + go retrieveCase(seq, currentEnv, child, ch, &wg) + } + + // Main goroutine waiting until all the goroutine stop + go func() { + wg.Wait() + close(ch) + }() + + for matches := range ch { + + results = append(results, matches...) + + } + + return results +} + func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { defer wg.Done() @@ -549,10 +570,10 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri ch <- matches } - symChild := child.getSymbol() // child is meta or cst + childSym := child.getSymbol() // child is meta or cst // Case the child is a AST.Meta - if !symChild.IsNil() && child.getSymbol().getSymbol().IsMeta() && !isExactMatch { + if child.getSymbol().getSymbol().IsMeta() && !isExactMatch { // We noticed that the term of the dNode is a Meta // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term @@ -563,7 +584,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri var mergedSub subst.Substitutions if skip == 1 { - currentSub := subst.MakeSubstitution(symChild.getTerm().ToMeta(), symQuery.getTerm()) + currentSub := subst.MakeSubstitution(childSym.getTerm().ToMeta(), symQuery.getTerm()) tmp3 := subst.Substitutions{currentSub} // Ok Commat Idoms doesn't works because ?????????????????????????????? if len(currentEnv) == 0 { @@ -585,7 +606,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri // First element is a meta - } else if !symQuery.IsNil() && symQuery.getSymbol().IsMeta() && !isExactMatch { + } else if symQuery.getSymbol().IsMeta() && !isExactMatch { // Reverse of the situation with the previous if. // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify @@ -594,7 +615,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri var mergedSub subst.Substitutions if child.GetArity() == 0 { - currentSub := subst.MakeSubstitution(symQuery.getTerm().ToMeta(), symChild.getTerm()) // Create a new substitution + currentSub := subst.MakeSubstitution(symQuery.getTerm().ToMeta(), childSym.getTerm()) // Create a new substitution tmp3 := subst.Substitutions{currentSub} if len(currentEnv) == 0 { mergedSub = tmp3 @@ -616,49 +637,6 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri } -func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { - seq := parseFormula(t).GetSlice() - Env := subst.Substitutions{} - return dNode.retrieveRec(seq, Env) -} - -func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { - - ch := make(chan []CandidatResult) - var results []CandidatResult - - var wg sync.WaitGroup - - if len(seq) == 0 { // End of recursion - - for _, p := range dNode.getLeafFor().GetSlice() { - results = append(results, MakeCandidat(p, currentEnv)) - } - return results - } - - // Work goroutine - for _, child := range dNode.children.GetSlice() { - - wg.Add(1) // Create exactly 1 goroutine - go retrieveCase(seq, currentEnv, child, ch, &wg) - } - - // Main goroutine waiting until all the goroutine stop - go func() { - wg.Wait() - close(ch) - }() - - for matches := range ch { - - results = append(results, matches...) - - } - - return results -} - /*****************************/ /* DataStruct implementation */ /*****************************/ @@ -752,11 +730,6 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { candidates := dNode.RetrieveUnifiables(inputFormula) - - for _, elem := range candidates { - fmt.Println("Pred", elem.getPred().ToString(), "Subs", elem.GetSubs().ToString()) - } - var mixed []subst.MixedSubstitutions var found bool @@ -818,3 +791,372 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix } return found, mixed } + +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// +/////////////////////////////////////////////// + +func parseTerm2(t AST.Term) Lib.List[SymbolType] { + res := Lib.NewList[SymbolType]() + + switch term := t.(type) { + + // if term is a function or cst, add and call his args + case AST.Fun: + + funSansArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) + first_element := makeSymbolType(createNodeElement(funSansArgs), term.GetArgs().Len()) + fmt.Println("ParserTerm2 case Fun", term.GetName()) + res.Append(first_element) + for _, arg := range term.GetArgs().GetSlice() { + res.Append(parseTerm2(arg).GetSlice()...) + } + + // Case meta, we have to transform it + case AST.Meta: + fmt.Println("ParserTerm2 case Meta", term.GetName()) + + res.Append(makeSymbolType(createNodeElement(term), 0)) // Add the new Meta to the return slice + + case AST.Id: + fmt.Println("ParserTerm2 case Id", term.GetName()) + + funSansArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + first_element := makeSymbolType(createNodeElement(funSansArgs), 0) + res.Append(first_element) + + default: + fmt.Println("Default parseTerm2 name", term.GetName()) + fmt.Println("Default parseTerm2 index ", term.GetIndex()) + } + return res + +} + +func (dNode DiscriminationNode) SkipTreeTermAndContinue2(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []CandidatResult { + + var subs []CandidatResult + // End of recursion + if needed == 0 { + return dNode.retrieveRec2(remainingQuery, substitutions) + } + for _, child := range dNode.getChildren().GetSlice() { + newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term + matches := child.SkipTreeTermAndContinue2(newNeeded, remainingQuery, substitutions) + subs = append(subs, matches...) + } + return subs + +} + +func (dNode DiscriminationNode) Unify2(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { + + fmt.Println("Unify2 AST.Form", inputFormula.ToString()) + + candidates := dNode.RetrieveUnifiables2(inputFormula) + var mixed []subst.MixedSubstitutions + var found bool + + fmt.Println("Unify2 candidates taille ", len(candidates)) + + predFormula, isPred := inputFormula.(AST.Pred) + if !isPred { + fmt.Println("Bug InputFormula n'est pas castable en PRED") + return false, nil + } + + fmt.Println("Unify2 AST.Pred", predFormula.ToString()) + + queryTerm := subst.TransformPred(predFormula) + + for _, possibleMatch := range candidates { + + fmt.Println("Unify2 candidates taille N°2 ", len(candidates)) + + currentSubst := possibleMatch.GetSubs() + + fmt.Println("Candidat Term", possibleMatch.getPred().ToString()) + fmt.Println("Candidat Subs", possibleMatch.GetSubs().ToString()) + + possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) + + fmt.Println("PossibleMatchterm avant Robinson : ", possibleMatchTerm.ToString()) + fmt.Println("QueryTerm avant Robinson : ", queryTerm.ToString()) + + finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, currentSubst) + + if !finalSubst.Equals(subst.Failure()) { + + fmt.Println("Unification reussit avec Term : ", possibleMatchTerm.ToString()) + + found = true + matching := subst.MakeMatchingSubstitutions(inputFormula, finalSubst) + mixed = append(mixed, matching.ToMixed()) + } else { + + fmt.Println("Unification echoue avec Term : ", possibleMatchTerm.ToString()) + + } + } + return found, mixed +} + +func (dNode DiscriminationNode) UnifyTerm2(inputTerm AST.Term) (bool, []subst.MixedTermSubstitutions) { + var mixed []subst.MixedTermSubstitutions + var found bool + + seq := parseTerm2(inputTerm).GetSlice() + + for _, elem := range seq { + fmt.Println("seq Parser Symbol", elem.getSymbol().ToString()) + fmt.Println("seq Parser arite", elem.GetArity()) + } + + candidates := dNode.retrieveRec2(seq, subst.MakeEmptySubstitution()) + + fmt.Println("Unify2 len Candidat", len(candidates)) + + for _, possibleMatch := range candidates { + currentSubst := possibleMatch.GetSubs() + candidateTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term + + finalSubst := subst.AddUnification(inputTerm, candidateTerm, currentSubst) + + if !finalSubst.Equals(subst.Failure()) { + found = true + mixMatch := subst.MixMatchSubstitutions{ + Tof: Lib.MkLeft[AST.Term, AST.Form](inputTerm), + Subst: finalSubst, + } + mixed = append(mixed, mixMatch.ToMixedTerm()) // All the subs returned + } + } + return found, mixed +} + +// Take a Sequence of SymbolType and return the first AST.Term + the remaining sequence +func ReconstructTerm(seq []SymbolType) (AST.Term, []SymbolType) { + if len(seq) == 0 { + return nil, seq + } + + head := seq[0] + arite := head.GetArity() + term := head.getTerm() + + fmt.Println("ReconstructTerm head Terme", term.ToString()) + fmt.Println("ReconstructTerm head Arite", arite) + + switch t := term.(type) { + + case AST.Fun: + + fmt.Println("reconstructTerm Cas Fun") + currentSeq := seq[1:] + args := Lib.NewList[AST.Term]() + for i := 0; i < arite; i++ { + var arg AST.Term + arg, currentSeq = ReconstructTerm(currentSeq) + args.Append(arg) + } + return AST.MakerFun(t.GetID(), t.GetTyArgs(), args), currentSeq // Create Fun + + case AST.Meta: + fmt.Println("reconstructTerm Cas Meta") + return t, seq[1:] // Go next + + default: + + fmt.Println("reconstructTerm Cas Default") + fmt.Println("t index", t.GetIndex()) + fmt.Println("t name", t.GetName()) + fmt.Println("t isFun", t.IsFun()) + fmt.Println("t isMeta", t.IsMeta()) + + return nil, seq[1:] // Error type + } +} + +func (dNode DiscriminationNode) RetrieveUnifiables2(t AST.Form) []CandidatResult { + predFormula, _ := t.(AST.Pred) + termQuery := subst.TransformPred(predFormula) + + seq := parseTerm2(termQuery).GetSlice() + Env := subst.Substitutions{} + return dNode.retrieveRec2(seq, Env) +} + +func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { + + ch := make(chan []CandidatResult) + var results []CandidatResult + + var wg sync.WaitGroup + + if len(seq) == 0 { // End of recursion + + for _, p := range dNode.getLeafFor().GetSlice() { + results = append(results, MakeCandidat(p, currentEnv)) + } + return results + } + + // Work goroutine + for _, child := range dNode.children.GetSlice() { + + wg.Add(1) // Create exactly 1 goroutine + go retrieveCase2(seq, currentEnv, child, ch, &wg) + } + + // Main goroutine waiting until all the goroutine stop + go func() { + wg.Wait() + close(ch) + }() + + for matches := range ch { + + results = append(results, matches...) + + } + + return results +} + +func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { + + defer wg.Done() + symQuery := seq[0] // First Element + + fmt.Println("SymQuery Debut symbol", symQuery.getSymbol().ToString()) + fmt.Println("SymQuery Debut arite ", symQuery.GetArity()) + + isExactMatch := child.symbol.Equals(symQuery) + if isExactMatch { // Exact Match + matches := child.retrieveRec2(seq[1:], currentEnv) // Exact Match -> Search next element + ch <- matches + } + + childSym := child.getSymbol() // child is meta or cst + + // Case the child is a AST.Meta + if childSym.getSymbol().IsMeta() && !isExactMatch { + + fmt.Println("SymQuery Child symbol", symQuery.getSymbol().ToString()) + fmt.Println("SymQuery Child arite ", symQuery.GetArity()) + + // We noticed that the term of the dNode is a Meta + // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term + // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 + + fmt.Println("Cas Enfant Meta : Symbol", child.getSymbol().getSymbol().ToString()) + + queryTerm, restSeq := ReconstructTerm(seq) + + for _, elem := range restSeq { + fmt.Println("Cas Enfant Meta : resteSequence", elem.getSymbol().ToString()) + } + + if queryTerm != nil { + + fmt.Println("Cas Enfant Meta : childSym", childSym.getTerm().ToString()) + fmt.Println("Cas Enfant Meta : queryTerm", queryTerm.ToString()) + fmt.Println("Cas Enfant Meta : currentEnv", currentEnv.ToString()) + + fmt.Println("childSym Fun", childSym.getTerm().IsFun()) + fmt.Println("childSym Fun", queryTerm.IsFun()) + fmt.Println("childSym Meta", childSym.getTerm().IsMeta()) + fmt.Println("childSym Meta", queryTerm.IsMeta()) + + mergedSub := subst.AddUnification(childSym.getTerm(), queryTerm, currentEnv) // Robinson Call + + fmt.Println("Cas Enfant Meta : mergeSub", mergedSub.ToString()) + + if !mergedSub.Equals(subst.Failure()) { + + fmt.Println("Cas Enfant Meta mergeSub : Bool True") + + matches := child.retrieveRec2(restSeq, mergedSub) + ch <- matches + } else { + fmt.Println("Cas Enfant Meta mergeSub : Bool False") + } + } + + } else if symQuery.getSymbol().IsMeta() && !isExactMatch { + + fmt.Println("SymQuery Meta symbol", symQuery.getSymbol().ToString()) + fmt.Println("SymQuery Meta arite ", symQuery.GetArity()) + + var mergedSub subst.Substitutions + + if child.GetArity() == 0 { + + childTerm := childSym.getTerm() + var properTerm AST.Term = childTerm + + // If for ??? reason it's a AST.id, we transform it to AST.Fun + if id, ok := childTerm.(AST.Id); ok { + properTerm = AST.MakerFun(id, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + } + + currentSub := subst.MakeSubstitution(symQuery.getTerm().ToMeta(), properTerm) + tmp3 := subst.Substitutions{currentSub} + if len(currentEnv) == 0 { + mergedSub = tmp3 + } else { + mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) + } + } else { + // Si l'arbre contient une fonction (arité > 0), ses arguments sont plus bas dans l'arbre. + // On reporte l'unification de CETTE variable pour ne pas crasher Robinson. + mergedSub = currentEnv + } + + if !mergedSub.Equals(subst.Failure()) { + // On saute le nombre de nœuds correspondants à l'arité dans l'arbre + childResults := child.SkipTreeTermAndContinue2(child.GetArity(), seq[1:], mergedSub) + ch <- childResults + } + + } else { + + fmt.Println("SymQuery Mort symbol", symQuery.getSymbol().ToString()) + fmt.Println("SymQuery Mort arite ", symQuery.GetArity()) + + // No recursive call or return + } +} From dd3fe3154ae574ccd19fa9dc2ca5b49b7dd8e59d Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Fri, 22 May 2026 15:07:52 +0200 Subject: [PATCH 15/23] reowrk and Improvement of DT-test --- .../discriminationtree/ContextNormalizer.go | 7 + src/Unif/discriminationtree/dt_test.go | 2339 +++++++++++------ 2 files changed, 1550 insertions(+), 796 deletions(-) diff --git a/src/Unif/discriminationtree/ContextNormalizer.go b/src/Unif/discriminationtree/ContextNormalizer.go index 556436d3..4536ea30 100644 --- a/src/Unif/discriminationtree/ContextNormalizer.go +++ b/src/Unif/discriminationtree/ContextNormalizer.go @@ -79,3 +79,10 @@ func (ctx *NormalizerContext) GetNormalizedMeta(originalMeta AST.Meta) AST.Meta return newMeta } + +func (ctx *NormalizerContext) Reset() { + + ctx.counter = 0 + ctx.mapping = make(map[string]AST.Meta) + +} diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 06df6fa3..fe3f516f 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -34,7 +34,6 @@ package discriminationtree import ( "fmt" - "log" "os" "testing" "time" @@ -94,6 +93,7 @@ var ggx AST.Fun var gga AST.Fun var gfy AST.Fun var gfa AST.Fun +var gahc AST.Fun var fxy AST.Fun var fyz AST.Fun var ffx AST.Fun @@ -123,6 +123,7 @@ var f_x_y AST.Fun // Form var pggab AST.Form var not_pac AST.Form +var not_pba AST.Form var pa AST.Form var pb AST.Form @@ -239,6 +240,7 @@ func initTestVariable() { // Predicates pggab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gga, b)) not_pac = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, c))) + not_pba = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](b, a))) not_pc = AST.MakerNot(AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c))) pab = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, b)) paa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, a)) @@ -368,1001 +370,1746 @@ func TestMain(m *testing.M) { func TestFirstElementToSymbolType(t *testing.T) { - tree := NewNode() // NewNode create a symbolType with arity == -1. Arity will be 0 if create normaly -> lead to false negative - argsA := pa.GetSubTerms() - termA := argsA.At(0) - resultA := FirstElementToSymbolType(termA) - tree.setSymbol(resultA) + t.Run("Fun_Multiple_Args", func(t *testing.T) { + st := FirstElementToSymbolType(fxy) + if st.GetArity() != 2 { + t.Errorf("Expected arity 2 for f(x, y), got %d", st.GetArity()) + } + }) - if tree.GetArity() == -1 { - Glob.Anomaly("Arity Error", "Wrong Arity") - } + t.Run("Fun_Constant", func(t *testing.T) { + st := FirstElementToSymbolType(a) + if st.GetArity() != 0 { + t.Errorf("Expected arity 0 for constant a, got %d", st.GetArity()) + } + }) - tree2 := NewNode() - argsB := pb.GetSubTerms() - termB := argsB.At(0) - resultB := FirstElementToSymbolType(termB) - tree2.setSymbol(resultB) + t.Run("Meta_Variable", func(t *testing.T) { + st := FirstElementToSymbolType(x) + if st.GetArity() != 0 { + t.Errorf("Expected arity 0 for Meta variable x, got %d", st.GetArity()) + } + if !st.getSymbol().IsMeta() { + t.Errorf("Expected symbol to be recognized as Meta") + } + }) - if tree2.GetArity() == -1 { - Glob.Anomaly("Arity Error", "Wrong Arity") - } + t.Run("Id_Type", func(t *testing.T) { + st := FirstElementToSymbolType(f_id) + if st.GetArity() != 0 { + t.Errorf("Expected arity 0 for ID type, got %d", st.GetArity()) + } + }) - argsC := gga.GetArgs() - resultC := FirstElementToSymbolType(argsC.At(0)) - if resultC.GetArity() == -1 { - Glob.Anomaly("Arity Error", "Wrong Arity") - } + t.Run("Complex_Fun", func(t *testing.T) { + // f_gax_c represents f(g(a, x), c), which has 2 direct top-level arguments + st := FirstElementToSymbolType(f_gax_c) + if st.GetArity() != 2 { + t.Errorf("Expected arity 2 for complex function f_gax_c, got %d", st.GetArity()) + } + }) - fmt.Println("-----EXPECTED PANIC-----") - func() { + t.Run("Exception_On_Nil_Term", func(t *testing.T) { defer func() { - if err := recover(); err != nil { - log.Println("panic occurred:", err) - } else { - fmt.Println("Supposed to throw a Error") + if r := recover(); r == nil { + t.Error("Expected a panic/exception when passing nil to FirstElementToSymbolType, but it completed without panicking") } }() + // Passing nil will trigger the default case inside the switch block or cause a controlled crash + FirstElementToSymbolType(nil) + }) +} +func TestTermToNode(t *testing.T) { + + t.Run("Nominal_Fun_Single_Arg", func(t *testing.T) { + + node := TermToNode(ggx) + if node.GetArity() != 1 { + t.Errorf("Expected arity 1 for ggx, got %d", node.GetArity()) + } + if node.getChildren().Len() != 1 { + t.Errorf("Expected 1 child node for ggx, got %d", node.getChildren().Len()) + } + }) - argsD := c_id - resultD := FirstElementToSymbolType(argsD) - println("Not supposed to see this ", resultD.getTerm().ToString()) // Required or Go panic due variable not used. However if you see this print : Bon Courage + t.Run("Nominal_Fun_Multiple_Args", func(t *testing.T) { - }() - fmt.Println("---END EXPECTED PANIC---") + node := TermToNode(fbc) + if node.GetArity() != 2 { + t.Errorf("Expected arity 2 for fbc, got %d", node.GetArity()) + } + if node.getChildren().Len() != 2 { + t.Errorf("Expected 2 child nodes for fbc, got %d", node.getChildren().Len()) + } + }) + + t.Run("Nominal_Constant", func(t *testing.T) { + + node := TermToNode(a) + if node.GetArity() != 0 { + t.Errorf("Expected arity 0 for constant 'a', got %d", node.GetArity()) + } + if node.getChildren().Len() != 0 { + t.Errorf("Expected 0 child nodes for constant 'a', got %d", node.getChildren().Len()) + } + }) + t.Run("Nominal_Meta", func(t *testing.T) { + + node := TermToNode(x) + if node.GetArity() != 0 { + t.Errorf("Expected arity 0 for Meta variable 'x', got %d", node.GetArity()) + } + if node.getChildren().Len() != 0 { + t.Errorf("Expected 0 child nodes for Meta variable, got %d", node.getChildren().Len()) + } + }) + + t.Run("Nominal_Id", func(t *testing.T) { + + node := TermToNode(f_id) + if node.GetArity() != 0 { + t.Errorf("Expected arity 0 for ID type 'f_id', got %d", node.GetArity()) + } + }) + + t.Run("Exception_On_Nil_Term", func(t *testing.T) { + + defer func() { + if r := recover(); r == nil { + t.Error("Expected a panic/exception when passing nil to TermToNode, but it completed without panicking") + } + }() + TermToNode(nil) + }) } -func TestTermToNode(t *testing.T) { +func TestCreateNodeElement(t *testing.T) { + // --- NOMINAL TESTS --- - tree := NewNode() - tree = TermToNode(ggx) - tmp := tree.GetArity() + // 1. Testing the "string" case + t.Run("Nominal_String", func(t *testing.T) { + strInput := "test_identifier" + node := createNodeElement(strInput) - if tmp != 1 { - Glob.Anomaly("Element number", "Wrong number of element") - } + // Verify it returns a NodeString + if ns, ok := node.(NodeString); !ok { + t.Errorf("Expected return type NodeString, got %T", node) + } else if ns.ToString() != strInput { + t.Errorf("Expected NodeString value to be '%s', got '%s'", strInput, ns.ToString()) + } + }) - tree2 := NewNode() - tree2 = TermToNode(fbc) - tmp2 := tree2.GetArity() - if tmp2 != 2 { - Glob.Anomaly("Element number", "Wrong number of element") - } + // 2. Testing the "AST.Ty" case + t.Run("Nominal_AST_Ty", func(t *testing.T) { + // random_type is defined in dt_test.go (e.g., AST.MakerTyBV("random_type")) + node := createNodeElement(random_type) + + // Verify it returns a TyNode + if _, ok := node.(TyNode); !ok { + t.Errorf("Expected return type TyNode for AST.Ty input, got %T", node) + } + }) + + // 3. Testing the "AST.Pred" case + t.Run("Nominal_AST_Pred", func(t *testing.T) { + // 'pa' is a predicate P(a) defined in dt_test.go + node := createNodeElement(pa.(AST.Pred)) + + // Verify it returns a TermNode (because predicates are transformed into terms) + termNode, ok := node.(TermNode) + if !ok { + t.Errorf("Expected return type TermNode for AST.Pred input, got %T", node) + } + + // Verify the underlying transformation occurred (P(a) should now be treated as a Fun) + if !termNode.Term.IsFun() { + t.Errorf("Expected the transformed AST.Pred to be wrapped as an AST.Fun inside the TermNode") + } + }) + + // 4. Testing the "AST.Term" case + t.Run("Nominal_AST_Term", func(t *testing.T) { + // 'fxy' is an AST.Fun (which implements AST.Term) defined in dt_test.go + node := createNodeElement(fxy) + + // Verify it returns a TermNode directly without issues + if _, ok := node.(TermNode); !ok { + t.Errorf("Expected return type TermNode for AST.Term input, got %T", node) + } + }) + + // --- FAILING TESTS (EXPECTING EXCEPTIONS) --- + + // 5. Testing an unhandled data type (e.g., an integer) + t.Run("Exception_On_Unhandled_Type", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected a panic/exception when passing an integer, but it completed without panicking") + } + }() + + // Passing an int will trigger the 'default' case and cause Glob.Anomaly to panic + createNodeElement(42) + }) + + // 6. Testing a nil input + t.Run("Exception_On_Nil", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("Expected a panic/exception when passing nil, but it completed without panicking") + } + }() + + // Passing nil will trigger the 'default' case + createNodeElement(nil) + }) } func TestInsert(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pba.(AST.Pred)) - tree = tree.Insert(pab.(AST.Pred)) - tree = tree.Insert(pafx.(AST.Pred)) - tree = tree.Insert(pafy.(AST.Pred)) + t.Run("Nominal_Multiple_Inserts", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + tree = tree.Insert(pafx.(AST.Pred)) + tree = tree.Insert(pafy.(AST.Pred)) + + children := tree.getChildren().GetSlice() + if len(children) != 1 { + t.Errorf("Expected root node to have exactly 1 child (the 'P' predicate node), got %d", len(children)) + } + pNode := children[0] + if pNode.GetArity() != 2 { + t.Errorf("Expected the 'P' node to have arity 2, got %d", pNode.GetArity()) + } + }) - fmt.Println("-------------PANIC EXPECTED------------- ") - func() { + t.Run("Nominal_Single_Nested_Insert", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pfx.(AST.Pred)) + children := tree.getChildren().GetSlice() + if len(children) != 1 { + t.Fatalf("Expected root node to have exactly 1 child, got %d", len(children)) + } + pNode := children[0] + if pNode.GetArity() != 1 { + t.Errorf("Expected the 'P' node to have arity 1, got %d", pNode.GetArity()) + } + }) + + t.Run("Nominal_Insert_Variable_Overlap", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pfx.(AST.Pred)) + tree = tree.Insert(pfy.(AST.Pred)) + + children := tree.getChildren().GetSlice() + if len(children) != 1 { + t.Fatalf("Expected root node to have exactly 1 child, got %d", len(children)) + } + + }) + + t.Run("Exception_Arity_Collision", func(t *testing.T) { defer func() { - if err := recover(); err != nil { - log.Println("panic occurred:", err) + if r := recover(); r == nil { + t.Error("Expected a panic when inserting a predicate with a conflicting arity on an existing path") } }() - tree = tree.Insert(pabc.(AST.Pred)) // pabc supposed to throw a error - }() - fmt.Println("-----------END PANIC EXPECTED----------- ") - tree.Print() + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + tree = tree.Insert(pabc.(AST.Pred)) // Arity Error + tree.Print() + }) +} + +func TestPrintDoublonCheck(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree.Print() + + if tree.getChildren().Len() != 1 { + t.Fatalf("Error Duplicate Branch") + } + +} + +func TestPrintHugeTree(t *testing.T) { + + t.Run("Nominal_Huge_Tree_Structure", func(t *testing.T) { + tree := NewNode() + + tree = tree.Insert(pfgaxc.(AST.Pred)) + tree = tree.Insert(pfgxby.(AST.Pred)) + tree = tree.Insert(pfgybz.(AST.Pred)) + tree = tree.Insert(pfgaba.(AST.Pred)) + tree = tree.Insert(pfgxcb.(AST.Pred)) + tree = tree.Insert(pfzy.(AST.Pred)) + tree = tree.Insert(pfxy.(AST.Pred)) + tree = tree.Insert(pfxx.(AST.Pred)) + tree = tree.Insert(pfzz.(AST.Pred)) + tree.Print() + + rootChildren := tree.getChildren().GetSlice() + if len(rootChildren) != 1 { // P + t.Fatalf("Expected the root node to have exactly 1 child (the 'P' predicate), got %d", len(rootChildren)) + } + + pNode := rootChildren[0] + if pNode.GetArity() != 1 { // P + t.Errorf("Expected 'P' node to have arity 1, got %d", pNode.GetArity()) + } + + pChildren := pNode.getChildren().GetSlice() + if len(pChildren) != 1 { // f() + t.Fatalf("Expected 'P' node to have exactly 1 child (the 'f' function), got %d", len(pChildren)) + } + + fNode := pChildren[0] + if fNode.GetArity() != 2 { // g() & v1 + t.Errorf("Expected 'f' node to have arity 2, got %d", fNode.GetArity()) + } + + fChildren := fNode.getChildren().GetSlice() + if len(fChildren) != 2 { // g() & v1 + t.Fatalf("Expected 'f' node to branch into exactly 2 paths ('g' and a Meta variable), got %d", len(fChildren)) + } + + // Determine who is g and who is v1 + var gNode, metaNode *DiscriminationNode + for i := range fChildren { + if fChildren[i].getSymbol().getSymbol().IsMeta() { + metaNode = &fChildren[i] + } else { + gNode = &fChildren[i] + } + } + + if gNode == nil || metaNode == nil { + t.Fatalf("Expected to find one 'g' node and one Meta node as children of 'f'") + } + + if metaNode.GetArity() != 0 { + t.Errorf("Expected Meta node to have arity 0, got %d", metaNode.GetArity()) + } + + if metaNode.getChildren().Len() != 2 { + t.Fatalf("v1 must have 2 children, v1 and v2") + } + + meta1Meta2Node := metaNode.getChildren().At(0) + meta1Meta1Node := metaNode.getChildren().At(1) + + if meta1Meta2Node.getChildren().Len() != 0 { + t.Fatalf("Must have 0 children") + } + if meta1Meta2Node.getLeafFor().Len() != 2 { + t.Fatalf("Must have 2 Leaf") + } + + if meta1Meta1Node.getChildren().Len() != 0 { + t.Fatalf("Must have 0 children") + } + if meta1Meta1Node.getLeafFor().Len() != 2 { + t.Fatalf("Must have 2 Leaf") + } + + if gNode.GetArity() != 2 { // g(arg1, arg2) + t.Errorf("Expected 'g' node to have arity 2, got %d", gNode.GetArity()) + } + + gChildren := gNode.getChildren().GetSlice() + if len(gChildren) != 2 { + t.Fatalf("Expected 'g' node to branch into exactly 2 paths ('a' and a Meta variable), got %d", len(gChildren)) + } + + aNode := gChildren[0] + gMetaNode := gChildren[1] + + if aNode.getSymbol().getSymbol().ToString() != "a" { + t.Errorf("Expected first child of 'g' to be 'a', got %s", aNode.getSymbol().getSymbol().ToString()) + } + + aChildren := aNode.getChildren().GetSlice() + if len(aChildren) != 2 { + t.Fatalf("Expected 'a' node to have exactly 2 children (Meta 'v1' and constant 'b'), got %d", len(aChildren)) + } + + aMetaNode := aChildren[0] + abNode := aChildren[1] + + if !aMetaNode.getSymbol().getSymbol().IsMeta() { + t.Errorf("Expected first child of 'a' to be a Meta variable") + } + if aMetaNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'v1' under 'a' to have 1 child ('c'), got %d", aMetaNode.getChildren().Len()) + } + acNode := aMetaNode.getChildren().At(0) + if acNode.getSymbol().getSymbol().ToString() != "c" { + t.Errorf("Expected node to be 'c', got %s", acNode.getSymbol().getSymbol().ToString()) + } + if acNode.getLeafFor().Len() != 1 { + t.Errorf("Expected 'c' node to have exactly 1 leaf formula (pfgaxc), got %d", acNode.getLeafFor().Len()) + } + + if abNode.getSymbol().getSymbol().ToString() != "b" { + t.Errorf("Expected second child of 'a' to be 'b', got %s", abNode.getSymbol().getSymbol().ToString()) + } + if abNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'b' under 'a' to have 1 child ('a'), got %d", abNode.getChildren().Len()) + } + abaNode := abNode.getChildren().At(0) + if abaNode.getSymbol().getSymbol().ToString() != "a" { + t.Errorf("Expected leaf node to be 'a', got %s", abaNode.getSymbol().getSymbol().ToString()) + } + if abaNode.getLeafFor().Len() != 1 { + t.Errorf("Expected leaf 'a' node to have exactly 1 leaf formula (pfgaba), got %d", abaNode.getLeafFor().Len()) + } + + if !gMetaNode.getSymbol().getSymbol().IsMeta() { + t.Errorf("Expected second child of 'g' to be a Meta variable") + } + + gMetaChildren := gMetaNode.getChildren().GetSlice() + if len(gMetaChildren) != 2 { + t.Fatalf("Expected 'v1' under 'g' to have 2 children ('b' and 'c'), got %d", len(gMetaChildren)) + } + + gbNode := gMetaChildren[0] + gcNode := gMetaChildren[1] + + if gbNode.getSymbol().getSymbol().ToString() != "b" { + t.Errorf("Expected first child of 'v1' under 'g' to be 'b', got %s", gbNode.getSymbol().getSymbol().ToString()) + } + if gbNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'b' under 'v1' to have 1 child (Meta 'v2'), got %d", gbNode.getChildren().Len()) + } + gbMetaNode := gbNode.getChildren().At(0) + if !gbMetaNode.getSymbol().getSymbol().IsMeta() { + t.Errorf("Expected child of 'b' to be a Meta variable 'v2'") + } + if gbMetaNode.getLeafFor().Len() != 2 { + t.Errorf("Expected 'v2' node to contain exactly 2 leaf formulas (pfgxby and pfgybz), got %d", gbMetaNode.getLeafFor().Len()) + } + + if gcNode.getSymbol().getSymbol().ToString() != "c" { + t.Errorf("Expected second child of 'v1' under 'g' to be 'c', got %s", gcNode.getSymbol().getSymbol().ToString()) + } + if gcNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'c' under 'v1' to have 1 child ('b'), got %d", gcNode.getChildren().Len()) + } + gcbNode := gcNode.getChildren().At(0) + if gcbNode.getSymbol().getSymbol().ToString() != "b" { + t.Errorf("Expected leaf node to be 'b', got %s", gcbNode.getSymbol().getSymbol().ToString()) + } + if gcbNode.getLeafFor().Len() != 1 { + t.Errorf("Expected leaf 'b' node to have exactly 1 leaf formula (pfgxcb), got %d", gcbNode.getLeafFor().Len()) + } + }) +} + +func TestParseTerm(t *testing.T) { + + t.Run("Parse_Simple_fxy", func(t *testing.T) { + tmpContext := NewContext() + seqList := parseTerm(fxy, tmpContext) + seq := seqList.GetSlice() + + if len(seq) != 3 { + t.Fatalf("Expected 3 elements for f(x,y), got %d", len(seq)) + } + + if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { + t.Fatal("Element 0 must be function 'f' with arity 2") + } + + if seq[1].GetArity() != 0 || !seq[1].getSymbol().IsMeta() { + t.Fatal("Element 1 must be meta variable 'v1' with arity 0") + } + + if seq[2].GetArity() != 0 || !seq[2].getSymbol().IsMeta() { + t.Fatal("Element 2 must be meta variable 'v2' with arity 0") + } + }) + t.Run("Parse_Nested_Left_f_fxy_z", func(t *testing.T) { + tmpContext2 := NewContext() + seqList := parseTerm(f_fxy_z, tmpContext2) + seq := seqList.GetSlice() + + if len(seq) != 5 { + t.Fatalf("Expected 5 elements for f(f(x,y), z), got %d", len(seq)) + } + + if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { + t.Fatal("Element 0 must be the outer function 'f' with arity 2") + } + + if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { + t.Fatal("Element 1 must be the inner function 'f' with arity 2") + } + + if seq[2].GetArity() != 0 || !seq[2].getSymbol().IsMeta() { + t.Fatal("Element 2 must be meta variable 'v1' with arity 0") + } + + if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { + t.Fatal("Element 3 must be meta variable 'v2' with arity 0") + } + + if seq[4].GetArity() != 0 || !seq[4].getSymbol().IsMeta() { + t.Fatal("Element 4 must be meta variable 'v3' with arity 0") + } + }) + + t.Run("Parse_Nested_Right_f_x_fyz", func(t *testing.T) { + tmpContext4 := NewContext() + seqList := parseTerm(f_x_fyz, tmpContext4) + seq := seqList.GetSlice() + + if len(seq) != 5 { + t.Fatalf("Expected 5 elements for f(x, f(y,z)), got %d", len(seq)) + } + + if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { + t.Fatal("Element 0 must be outer function 'f' with arity 2") + } + + if seq[1].GetArity() != 0 || !seq[1].getSymbol().IsMeta() { + t.Fatal("Element 1 must be meta variable 'v1' with arity 0") + } + + if seq[2].GetArity() != 2 || seq[2].getSymbol().ToString() != "f" { + t.Fatal("Element 2 must be inner function 'f' with arity 2") + } + + if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { + t.Fatal("Element 3 must be meta variable 'v2' with arity 0") + } + + if seq[4].GetArity() != 0 || !seq[4].getSymbol().IsMeta() { + t.Fatal("Element 4 must be meta variable 'v3' with arity 0") + } + }) +} + +func TestRetrieve(t *testing.T) { + + t.Run("RetrieveUnifiable_pax_pay_and_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + tree = tree.Insert(pay.(AST.Pred)) + results := tree.RetrieveUnifiables(pab) + if len(results) != 2 { + t.Fatalf("Must return 2 element") + } else { + fmt.Printf("returned %d element", len(results)) + } + + }) + + t.Run("RetrieveUnifiable_pax_pxy_and_pab", func(t *testing.T) { + + fmt.Println() + tree2 := NewNode() + tree2 = tree2.Insert(pxy.(AST.Pred)) + results2 := tree2.RetrieveUnifiables(pab) + if len(results2) != 1 { + t.Fatalf("Supposed to have 1 CandidatResults") + } + resultat3 := ToSingleElement(results2) + if len(resultat3.GetSubs()) != 2 { + t.Fatalf("Supposed to have 2 unifiables") + } + + }) + +} +func TestEquals(t *testing.T) { + + t.Run("x Equals x", func(t *testing.T) { + + ok := x.Equals(x) + if !ok { + t.Fatalf("Equals Test failure on x Equals x ") + } + + }) + + t.Run("pxx Equals pxx", func(t *testing.T) { + + ok2 := pxx.Equals(pxx) + if !ok2 { + t.Fatalf("Equals Test failure on pxx Equals pxx ") + } + + }) + + t.Run("fab Equals fab", func(t *testing.T) { + + ok3 := fab.Equals(fab) + if !ok3 { + t.Fatalf("Equals Test failure on pab Equals pab ") + } + + }) + + t.Run("fab Equals fay", func(t *testing.T) { + + ok4 := fab.Equals(fay) + if ok4 { + t.Fatalf("Equals Test Succes on fab Equals fay") + } + + }) + + t.Run("fx Equals fy", func(t *testing.T) { + + ok5 := fx.Equals(fy) + if ok5 { + t.Fatalf("Equals Test Succes on fab Equals fay") + } + + }) + +} + +func TestGetSubTermLength(t *testing.T) { + + Context := NewContext() + + t.Run("SubTerm_ggx", func(t *testing.T) { + + seq := parseTerm(ggx, Context).GetSlice() + var1 := (GetSubTermLength(seq)) + if var1 != 3 { + t.Fatalf("Error SubTerLength with 2functions & 1Meta ") + } + }) + Context.Reset() + + t.Run("SubTerm_fxy", func(t *testing.T) { + seq2 := parseTerm(fxy, Context).GetSlice() + var2 := (GetSubTermLength(seq2)) + if var2 != 3 { + t.Fatalf("Error SubTerLength with 1function & 2Meta") + } + Context.Reset() + }) + + t.Run("SubTerm_gx", func(t *testing.T) { + + seq3 := parseTerm(gx, Context).GetSlice() + var3 := (GetSubTermLength(seq3)) + if var3 != 2 { + t.Fatalf("Error SubTerLength with 1function & 1Meta") + } + Context.Reset() + }) + + t.Run("SubTerm_ga", func(t *testing.T) { + + seq4 := parseTerm(ga, Context).GetSlice() + var4 := (GetSubTermLength(seq4)) + if var4 != 2 { + t.Fatalf("Error SubTerLength with 1function & 1cst") + } + Context.Reset() + }) + + t.Run("SubTerm_gggx", func(t *testing.T) { + + seq5 := parseTerm(gggx, Context).GetSlice() + var5 := (GetSubTermLength(seq5)) + if var5 != 4 { + t.Fatalf("Error SubTerLength with 1function & 3Meta") + } + Context.Reset() + }) + + t.Run("SubTerm_f_y_y", func(t *testing.T) { + + seq6 := parseTerm(f_y_y, Context).GetSlice() + var6 := (GetSubTermLength(seq6)) + if var6 != 3 { + t.Fatalf("Error SubTerLength with 1function & 2Meta") + } + }) + +} + +func TestSkipTreeTermAndContinue(t *testing.T) { + + t.Run("SkipTreeTermAndContinue_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + needed := 1 + emptyQuery := []SymbolType{} + emptyEnv := subst.Substitutions{} + results := tree.SkipTreeTermAndContinue(needed, emptyQuery, emptyEnv) + if len(results) != 1 { + t.Fatalf(" Expected 1 Element, got %d", len(results)) + } + }) + + t.Run("SkipTreeTermAndContinue_px", func(t *testing.T) { + + tree2 := NewNode() + tree2 = tree2.Insert(px.(AST.Pred)) + needed2 := 1 + emptyQuery2 := []SymbolType{} + emptyEnv2 := subst.Substitutions{} + results2 := tree2.SkipTreeTermAndContinue(needed2, emptyQuery2, emptyEnv2) + if len(results2) != 1 { + t.Fatalf(" Expected 1 Element, got %d", len(results2)) + } + }) + + t.Run("SkipTreeTermAndContinue_pab_pba", func(t *testing.T) { + + tree3 := NewNode() + tree3 = tree3.Insert(pba.(AST.Pred)) + tree3 = tree3.Insert(pab.(AST.Pred)) + needed3 := 1 + emptyQuery3 := []SymbolType{} + emptyEnv3 := subst.Substitutions{} + results3 := tree3.SkipTreeTermAndContinue(needed3, emptyQuery3, emptyEnv3) + if len(results3) != 2 { + t.Fatalf(" Expected 2 Element, got %d", len(results3)) + } + }) + + t.Run("SkipTreeTermAndContinue_pab_pab_pca", func(t *testing.T) { + + tree4 := NewNode() + tree4 = tree4.Insert(pba.(AST.Pred)) + tree4 = tree4.Insert(pab.(AST.Pred)) + tree4 = tree4.Insert(pca.(AST.Pred)) + needed4 := 1 + emptyQuery4 := []SymbolType{} + emptyEnv4 := subst.Substitutions{} + results4 := tree4.SkipTreeTermAndContinue(needed4, emptyQuery4, emptyEnv4) + if len(results4) != 3 { + t.Fatalf(" Expected 3 Element, got %d", len(results4)) + } + }) + +} + +func TestRetrieveUnifiables(t *testing.T) { + + tree := NewNode() + candidat := []CandidatResult{} + + t.Run("TestRetrieveUnifiables_pax_pba", func(t *testing.T) { + + tree = tree.Insert(pax.(AST.Pred)) + tree = tree.Insert(pba.(AST.Pred)) + candidat = tree.RetrieveUnifiables(pay) + if len(candidat) != 1 { + t.Fatalf("Should be only 1") + } + + }) + + t.Run("TestRetrieveUnifiables_pafx", func(t *testing.T) { + + tree = tree.Insert(pafx.(AST.Pred)) + candidat = tree.RetrieveUnifiables(pay) + if len(candidat) != 2 { + t.Fatalf("Should be only 2") + } + + }) + + t.Run("TestRetrieveUnifiables_pafy", func(t *testing.T) { + + tree = tree.Insert(pafy.(AST.Pred)) + candidat = tree.RetrieveUnifiables(pay) + if len(candidat) != 3 { + t.Fatalf("Should be only 3") + } + + }) + + t.Run("TestRetrieveUnifiables_pa", func(t *testing.T) { + + tree2 := NewNode() + tree2 = tree2.Insert(pa.(AST.Pred)) + candidat2 := tree2.RetrieveUnifiables(pb) + if len(candidat2) != 0 { + t.Fatalf("Should be 0 because pa and pb can't be unified") + } + + }) + +} + +func TestCopy(t *testing.T) { + + t.Run("TestCopy_pax", func(t *testing.T) { + + tree1 := NewNode() + tree2 := tree1.Copy() + tree1 = tree1.Insert(pax.(AST.Pred)) + res2 := tree2.IsEmpty() + if !res2 { + t.Fatalf("Tree2 is not a copy, it s only a pointer to tree1") + } + + }) + + t.Run("TestCopy_pafx", func(t *testing.T) { + + tree3 := NewNode() + tree4 := tree3.Copy() + tree3 = tree3.Insert(pafx.(AST.Pred)) + res3 := tree4.IsEmpty() + if !res3 { + t.Fatalf("Tree4 is not a copy, it s only a pointer to tree3") + } + + }) + +} + +func TestUnify(t *testing.T) { + + t.Run("Unify_pax_with_pay", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + + found, mix := tree.Unify(pay) + + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a, Y)" { + t.Errorf("Expected unified form to be 'P(a, Y)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_pax_with_pab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + + found, mix := tree.Unify(pab) + + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a, b)" { + t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_pa_with_pa", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + + found, mix := tree.Unify(pa) + + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a)" { + t.Errorf("Expected unified form to be 'P(a)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_pax_with_pafy", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + + found, mix := tree.Unify(pafy) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(a, f(Y))" { + t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_pafx_with_pafy", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + + found, mix := tree.Unify(pafy) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(a, f(Y))" { + t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_px_with_py", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + + found, mix := tree.Unify(py) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(Y)" { + t.Errorf("Expected unified form to be 'P(Y)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_pxy_with_pab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + + found, mix := tree.Unify(pab) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(a, b)" { + t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify_Multiple_Inserts_with_py", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pfx.(AST.Pred)) + + // P(Y) can unify with P(b), P(a), and P(f(x)) + found, mix := tree.Unify(py) + + if !found || len(mix) != 3 { + t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) + } + + // Unification wraps the input formula + for i, elem := range mix { + if elem.GetForm().ToString() != "P(Y)" { + t.Errorf("Expected unified form %d to be 'P(Y)', got '%s'", i, elem.GetForm().ToString()) + } + } + }) - println() - println() - println() + t.Run("Unify_pggab_with_pxy", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) - tree2 := NewNode() - tree2 = tree2.Insert(pfx.(AST.Pred)) - tree2.Print() + found, mix := tree.Unify(pxy) - println() - println() - println() + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(X, Y)" { + t.Errorf("Expected unified form to be 'P(X, Y)', got '%s'", mix[0].GetForm().ToString()) + } + }) - tree3 := NewNode() - tree3 = tree3.Insert(pfx.(AST.Pred)) - tree3 = tree3.Insert(pfy.(AST.Pred)) - tree3.Print() + t.Run("Exception_Unify_pa_with_pb", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) -} + // Constants 'a' and 'b' cannot unify + found, mix := tree.Unify(pb) -func TestPrintDiscriminationTree(t *testing.T) { + if found { + t.Errorf("Unification should have failed for P(a) and P(b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list, got %d elements", len(mix)) + } + }) - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(P2a.(AST.Pred)) - tree = tree.Insert(P2b.(AST.Pred)) - tree.Print() + t.Run("Exception_Unify_pxx_with_pab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) // P(x, x) requires both arguments to be identical - fmt.Println() - fmt.Println() + found, mix := tree.Unify(pab) // P(a, b) has different arguments - tree = tree.Insert(pa.(AST.Pred)) - tree.Print() -} + if found { + t.Errorf("Unification should have failed: P(x, x) cannot unify with P(a, b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list") + } + }) -func TestPrintSamePredicatCheck(t *testing.T) { + t.Run("Exception_Unify_pba_pab_with_pxx", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(P2a.(AST.Pred)) - tree = tree.Insert(P2b.(AST.Pred)) - tree.Print() + // P(X, X) requires identical arguments, neither P(b, a) nor P(a, b) fits + found, mix := tree.Unify(pxx) -} + if found { + t.Errorf("Unification should have failed: P(X, X) cannot unify with P(b, a) or P(a, b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list") + } + }) -func TestPrintDoublonCheck(t *testing.T) { + t.Run("Exception_Unify_pab_with_pxx", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - tree = tree.Insert(pa.(AST.Pred)) - tree.Print() + found, mix := tree.Unify(pxx) + if found || len(mix) != 0 { + t.Errorf("Unification should have failed: P(a, b) cannot unify with P(X, X)") + } + }) } +func TestUnifyTerm(t *testing.T) { -func TestPrintHugeTree(t *testing.T) { + t.Run("UnifyTerm_pax_with_pay", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) - tree := NewNode() - tree = tree.Insert(pfgaxc.(AST.Pred)) - tree = tree.Insert(pfgxby.(AST.Pred)) - tree = tree.Insert(pfgybz.(AST.Pred)) - tree = tree.Insert(pfgaba.(AST.Pred)) - tree = tree.Insert(pfgxcb.(AST.Pred)) - tree = tree.Insert(pfzy.(AST.Pred)) - tree = tree.Insert(pfxy.(AST.Pred)) - tree = tree.Insert(pfxx.(AST.Pred)) - tree = tree.Insert(pfzz.(AST.Pred)) - tree.Print() + val, mix := tree.UnifyTerm(queryTerm) -} + if !val { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + }) -func TestTmp(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - tree = tree.Insert(paa.(AST.Pred)) - _, mix := tree.Unify(pay.(AST.Pred)) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } + t.Run("UnifyTerm_pax_with_pab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) - fmt.Println(len(mix)) + val, mix := tree.UnifyTerm(queryTerm) -} + if !val { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + } + }) -func TestParseFormula(t *testing.T) { + t.Run("UnifyTerm_pa_with_pa", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + queryTerm := subst.TransformPred(pa.(AST.Pred)) - tmp := parseFormula(pax) - expectedPred := makeSymbolType(createNodeElement(p_id), 2) - expectedA := makeSymbolType(createNodeElement(a_id), 0) - expectedX := makeSymbolType(createNodeElement(v1), 0) + val, mix := tree.UnifyTerm(queryTerm) - for _, elem := range tmp.GetSlice() { + if !val { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + } + if mix[0].ToString() != "P(a) {}" { + t.Errorf("Expected result 'P(a) {}', got '%s'", mix[0].ToString()) + } + }) - if elem.Equals(expectedPred) { - continue - } else if elem.Equals(expectedA) { - continue - } else if elem.Equals(expectedX) { - continue - } else { - t.Fatalf("Symbole inconnu détecté dans parseFormula : %s", elem.getSymbol().ToString()) + t.Run("UnifyTerm_pab_with_pay", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) + + val, mix := tree.UnifyTerm(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) } - } -} + if mix[0].Term().ToString() != "P(a, Y)" { + t.Errorf("Expected term to be 'P(a, Y)', got '%s'", mix[0].Term().ToString()) + } + }) -func TestParseTerm(t *testing.T) { + t.Run("UnifyTerm_pafx_with_pafy", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + queryTerm := subst.TransformPred(pafy.(AST.Pred)) - var tmp []string - var tmp2 []string - var tmp3 []string - tmpContext := NewContext() - tmpContext2 := NewContext() - tmpContext3 := NewContext() - - seqList := parseTerm(fxy, tmpContext) - seq := seqList.GetSlice() - if len(seq) != 3 { - t.Fatalf("Got %d elements", len(seq)) - } - for _, sym := range seq { - tmp = append(tmp, sym.getTerm().ToString()) - } - fmt.Printf(" Sequence Parsed : % v\n", tmp) + val, mix := tree.UnifyTerm(queryTerm) - seqList = parseTerm(f_fxy_z, tmpContext2) - seq = seqList.GetSlice() - if len(seq) != 5 { - t.Fatalf("Got %d elements", len(seq)) - } - for _, sym := range seq { - tmp2 = append(tmp2, sym.getTerm().ToString()) - } - fmt.Printf(" Sequence Parsed : %v\n", tmp2) + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(a, f(Y))" { + t.Errorf("Expected term to be 'P(a, f(Y))', got '%s'", mix[0].Term().ToString()) + } + }) - seqList = parseTerm(f_x_fyz, tmpContext3) - seq = seqList.GetSlice() - if len(seq) != 5 { - t.Fatalf("Got %d elements", len(seq)) - } - for _, sym := range seq { - tmp3 = append(tmp3, sym.getTerm().ToString()) - } - fmt.Printf(" Sequence Parsed : %v\n", tmp3) + t.Run("UnifyTerm_px_with_py", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + queryTerm := subst.TransformPred(py.(AST.Pred)) -} + val, mix := tree.UnifyTerm(queryTerm) -func TestRetrieve(t *testing.T) { + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(Y)" { + t.Errorf("Expected term to be 'P(Y)', got '%s'", mix[0].Term().ToString()) + } + }) - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - tree = tree.Insert(pay.(AST.Pred)) - results := tree.RetrieveUnifiables(pab) - if len(results) == 0 { - t.Fatalf("Returned 0 element") - } else { - fmt.Printf("returned %d element", len(results)) - } + t.Run("UnifyTerm_pxy_with_pab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) - for _, result := range results { - fmt.Println("Pred : ", result.getPred().ToString()) - for _, element := range result.GetSubs() { - fmt.Println("Subs : ", element.ToString()) + val, mix := tree.UnifyTerm(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) } - } - fmt.Println() + if mix[0].Term().ToString() != "P(a, b)" { + t.Errorf("Expected term to be 'P(a, b)', got '%s'", mix[0].Term().ToString()) + } + }) - fmt.Println("-----EXPECTED PANIC-----") - func() { - defer func() { - if err := recover(); err != nil { - log.Println("panic occurred:", err) - } else { - fmt.Println("Supposed to throw a Error") + t.Run("UnifyTerm_Multiple_Inserts_with_py", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pfx.(AST.Pred)) + queryTerm := subst.TransformPred(py.(AST.Pred)) + + val, mix := tree.UnifyTerm(queryTerm) + + if !val { + t.Fatalf("Unification failed, expected success with matches") + } + if len(mix) != 3 { + t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) + } + for i, elem := range mix { + if elem.Term().ToString() != "P(Y)" { + t.Errorf("Expected unified term %d to be 'P(Y)', got '%s'", i, elem.Term().ToString()) } - }() + } + }) - tree2 := NewNode() - tree2 = tree2.Insert(pax.(AST.Pred)) - results2 := tree2.RetrieveUnifiables(pba) - if len(results2) != 0 { - t.Fatalf(" Not supposed to have Unifiable element") + t.Run("UnifyTerm_pggab_with_pxy", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) + queryTerm := subst.TransformPred(pxy.(AST.Pred)) + + val, mix := tree.UnifyTerm(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) } + if mix[0].Term().ToString() != "P(X, Y)" { + t.Errorf("Expected term to be 'P(X, Y)', got '%s'", mix[0].Term().ToString()) + } + }) + + t.Run("Exception_UnifyTerm_pa_with_pb", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + queryTerm := subst.TransformPred(pb.(AST.Pred)) - }() - fmt.Println("---END EXPECTED PANIC---") + val, mix := tree.UnifyTerm(queryTerm) - fmt.Println("----- EMPTY -----") - fmt.Println("----- EMPTY -----") + if val { + t.Fatalf("Unification should have failed for P(a) and P(b)") + } + if len(mix) != 0 { + t.Fatalf("Expected 0 elements, got %d", len(mix)) + } + }) - fmt.Println() + t.Run("Exception_UnifyTerm_pxx_with_pab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) - tree3 := NewNode() - tree3 = tree3.Insert(pxy.(AST.Pred)) - results3 := tree3.RetrieveUnifiables(pab) - if len(results3) != 1 { - t.Fatalf("Supposed to have 2 unifiables") - } + val, mix := tree.UnifyTerm(queryTerm) - for _, result := range results3 { - fmt.Println("Pred : ", result.getPred().ToString()) - for _, element := range result.GetSubs() { - fmt.Println("Subs : ", element.ToString()) + if val || len(mix) != 0 { + t.Fatalf("Unification should have failed: P(x,x) cannot unify with P(a,b)") } - } + }) -} + t.Run("Exception_UnifyTerm_pba_pab_with_pxx", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) -func TestEquals(t *testing.T) { + val, mix := tree.UnifyTerm(queryTerm) - ok := x.Equals(x) - if !ok { - t.Fatalf("Equals Test with failled") - } + if val { + t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + } + }) - ok2 := pxx.Equals(pxx) - if !ok2 { - t.Fatalf("Equals Test with failled") - } + t.Run("Exception_UnifyTerm_pab_with_pxx", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) - ok3 := fab.Equals(fab) - if !ok3 { - t.Fatalf("Equals Test with failled") - } + val, mix := tree.UnifyTerm(queryTerm) + if val { + t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + } + if len(mix) != 0 { + t.Fatalf("Expected result array to be empty, got %d elements", len(mix)) + } + }) } -func TestGetSubTermLength(t *testing.T) { +func TestMakeDataStruct(t *testing.T) { - tmpContext1 := NewContext() - tmpContext2 := NewContext() - tmpContext3 := NewContext() - tmpContext4 := NewContext() - tmpContext5 := NewContext() + t.Run("MakeDataStruct_Positive_Tree", func(t *testing.T) { + tree1 := NewNode() + formulas1 := Lib.NewList[AST.Form]() + formulas1.Append(pab) // + => Inserted + formulas1.Append(not_pac) // - => Ignored + formulas1.Append(not_pba) // - => Ignored + formulas1.Append(pba) // + => Inserted + + actualTree1, ok := tree1.MakeDataStruct(formulas1, true).(*DiscriminationNode) + if !ok { + if directTree, okDirect := tree1.MakeDataStruct(formulas1, true).(DiscriminationNode); okDirect { + actualTree1 = &directTree + } else { + t.Fatalf("MakeDataStruct didn't return a valid DiscriminationNode type") + } + } - seq := parseTerm(ggx, tmpContext1).GetSlice() - var1 := (GetSubTermLength(seq)) - if var1 != 3 { - t.Fatalf("Error SubTerLength with 2functions & 1Meta ") - } + actualTree1.Print() - seq2 := parseTerm(fxy, tmpContext2).GetSlice() - var2 := (GetSubTermLength(seq2)) - if var2 != 3 { - t.Fatalf("Error SubTerLength with 1function & 2Meta") - } + rootChildren := actualTree1.getChildren().GetSlice() + if len(rootChildren) != 1 { + t.Fatalf("Expected exactly 1 predicate root node ('P'), got %d", len(rootChildren)) + } - seq3 := parseTerm(gx, tmpContext3).GetSlice() - var3 := (GetSubTermLength(seq3)) - if var3 != 2 { - t.Fatalf("Error SubTerLength with 1function & 1Meta") - } + pNode := rootChildren[0] + if pNode.getSymbol().getSymbol().ToString() != "P" { + t.Errorf("Expected root node symbol to be 'P', got '%s'", pNode.getSymbol().getSymbol().ToString()) + } - seq4 := parseTerm(ga, tmpContext4).GetSlice() - var4 := (GetSubTermLength(seq4)) - if var4 != 2 { - t.Fatalf("Error SubTerLength with 1function & 1cst") - } + pChildren := pNode.getChildren().GetSlice() + if len(pChildren) != 2 { + t.Fatalf("Expected 'P' to have exactly 2 children ('a' and 'b') from positive formulas, got %d", len(pChildren)) + } - seq5 := parseTerm(gggx, tmpContext5).GetSlice() - var5 := (GetSubTermLength(seq5)) - if var5 != 4 { - t.Fatalf("Error SubTerLength with 1function & 3Meta") - } + for _, child := range pChildren { + symStr := child.getSymbol().getSymbol().ToString() + if symStr == "c" { + t.Errorf("Negative formula 'not_pac' was incorrectly inserted into the positive tree") + } + } + }) -} + t.Run("MakeDataStruct_Negative_Tree", func(t *testing.T) { -func TestSkipTreeTermAndContinue(t *testing.T) { + tree2 := NewNode() + formulas2 := Lib.NewList[AST.Form]() + formulas2.Append(pab) // + => Ignored + formulas2.Append(not_pac) // - => Inserted + formulas2.Append(not_pba) // - => Inserted + formulas2.Append(pba) // + => Ignored + + actualTree2, ok := tree2.MakeDataStruct(formulas2, false).(*DiscriminationNode) + if !ok { + if directTree, okDirect := tree2.MakeDataStruct(formulas2, false).(DiscriminationNode); okDirect { + actualTree2 = &directTree + } else { + t.Fatalf("MakeDataStruct didn't return a valid DiscriminationNode type") + } + } - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - needed := 1 - emptyQuery := []SymbolType{} - emptyEnv := subst.Substitutions{} - results := tree.SkipTreeTermAndContinue(needed, emptyQuery, emptyEnv) - if len(results) != 1 { - t.Fatalf(" Expected 1 Element, got %d", len(results)) - } + actualTree2.Print() - tree2 := NewNode() - tree2 = tree2.Insert(px.(AST.Pred)) - needed2 := 1 - emptyQuery2 := []SymbolType{} - emptyEnv2 := subst.Substitutions{} - results2 := tree2.SkipTreeTermAndContinue(needed2, emptyQuery2, emptyEnv2) - if len(results2) != 1 { - t.Fatalf(" Expected 1 Element, got %d", len(results2)) - } + rootChildren := actualTree2.getChildren().GetSlice() + if len(rootChildren) != 1 { + t.Fatalf("Expected exactly 1 predicate root node ('P'), got %d", len(rootChildren)) + } - tree3 := NewNode() - tree3 = tree3.Insert(pba.(AST.Pred)) - tree3 = tree3.Insert(pab.(AST.Pred)) - needed3 := 1 - emptyQuery3 := []SymbolType{} - emptyEnv3 := subst.Substitutions{} - results3 := tree3.SkipTreeTermAndContinue(needed3, emptyQuery3, emptyEnv3) - if len(results3) != 2 { - t.Fatalf(" Expected 2 Element, got %d", len(results3)) - } + pNode := rootChildren[0] + pChildren := pNode.getChildren().GetSlice() + if len(pChildren) == 0 { + t.Fatalf("Negative tree is empty, expected nodes from negative formulas") + } + }) +} +func TestUnify2(t *testing.T) { - tree4 := NewNode() - tree4 = tree4.Insert(pba.(AST.Pred)) - tree4 = tree4.Insert(pab.(AST.Pred)) - tree4 = tree4.Insert(pca.(AST.Pred)) - needed4 := 1 - emptyQuery4 := []SymbolType{} - emptyEnv4 := subst.Substitutions{} - results4 := tree4.SkipTreeTermAndContinue(needed4, emptyQuery4, emptyEnv4) - if len(results4) != 3 { - t.Fatalf(" Expected 3 Element, got %d", len(results4)) - } + t.Run("Unify2_pax_with_pay", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) -} + found, mix := tree.Unify2(pay) -func TestRetrieveUnifiables(t *testing.T) { + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a, Y)" { + t.Errorf("Expected unified form to be 'P(a, Y)', got '%s'", mix[0].GetForm().ToString()) + } + }) - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - tree = tree.Insert(pba.(AST.Pred)) - candidat := tree.RetrieveUnifiables(pay) - if len(candidat) != 1 { - t.Fatalf("Should be only 1") - } + t.Run("Unify2_pax_with_pab", func(t *testing.T) { - tree = tree.Insert(pafx.(AST.Pred)) - candidat = tree.RetrieveUnifiables(pay) - if len(candidat) != 2 { - t.Fatalf("Should be only 2") - } + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + found, mix := tree.Unify2(pab) - tree = tree.Insert(pafy.(AST.Pred)) - candidat = tree.RetrieveUnifiables(pay) - if len(candidat) != 3 { - t.Fatalf("Should be only 3") - } + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a, b)" { + t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) + } + }) - tree2 := NewNode() - tree2 = tree2.Insert(pa.(AST.Pred)) - candidat2 := tree2.RetrieveUnifiables(pb) - if len(candidat2) != 0 { - t.Fatalf("Should be 0 because pa and pb can't be unified") - } + t.Run("Unify2_pa_with_pa", func(t *testing.T) { -} + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + found, mix := tree.Unify2(pa) -func TestCopy(t *testing.T) { + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a)" { + t.Errorf("Expected unified form to be 'P(a)', got '%s'", mix[0].GetForm().ToString()) + } + }) - tree1 := NewNode() - tree2 := tree1.Copy() - tree1 = tree1.Insert(pax.(AST.Pred)) - res2 := tree2.IsEmpty() - if !res2 { - t.Fatalf("Tree2 is not a copy, it s only a pointer to tree1") - } -} + t.Run("Unify2_pax_with_pafy", func(t *testing.T) { -func TestUnify(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + found, mix := tree.Unify2(pafy) - fmt.Println("-----TEST 01 -----") - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - var mix []subst.MixedSubstitutions - _, mix = tree.Unify(pay) - for _, elem := range mix { - fmt.Println(elem.ToString()) - } - if len(mix) != 1 { - t.Fatalf("Should have a found 1 unification") - } - for _, elem := range mix { - if elem.GetForm().ToString() != "P(a, Y)" { - t.Fatalf("Fatal Failure, shouhd have P(a, Y)") + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") } - } + if mix[0].GetForm().ToString() != "P(a, f(Y))" { + t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("Unify2_pafx_with_pafy", func(t *testing.T) { - fmt.Println("-----TEST 02 -----") - tree1 := NewNode() - tree1 = tree1.Insert(pax.(AST.Pred)) - var mix1 []subst.MixedSubstitutions - _, mix1 = tree1.Unify(pab) - for _, elem := range mix1 { - fmt.Println(elem.ToString()) - } - if len(mix1) != 1 { - t.Fatalf("Should have a found 1 unification") - } - for _, elem := range mix1 { - if elem.GetForm().ToString() != "P(a, b)" { - t.Fatalf("Form must be P(a, b)") - } - } + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + found, mix := tree.Unify2(pafy) - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 03 -----") - fmt.Println("----- EMPTY -----") - tree2 := NewNode() - tree2 = tree2.Insert(pa.(AST.Pred)) - var mix2 []subst.MixedSubstitutions - _, mix2 = tree2.Unify(pb) - if len(mix2) != 0 { - t.Fatalf("can't Unify Predicat(Cst) and Predicat(Cst)") - } - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 04 -----") - tree3 := NewNode() - tree3 = tree3.Insert(pa.(AST.Pred)) - var mix3 []subst.MixedSubstitutions - _, mix3 = tree3.Unify(pa) - for _, elem := range mix3 { - fmt.Println(elem.ToString()) - } - if len(mix3) != 1 { - t.Fatalf("Should return empty list") - } - for _, elem := range mix3 { - if elem.GetForm().ToString() != "P(a)" { - t.Fatalf("Must be P(a)") - } - } - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 05 -----") - tree4 := NewNode() - tree4 = tree4.Insert(pax.(AST.Pred)) - var mix4 []subst.MixedSubstitutions - _, mix4 = tree4.Unify(pafy) - for _, elem := range mix4 { - fmt.Println(elem.ToString()) - } - if len(mix4) != 1 { - t.Fatalf("Should have a found 1 unification") - } - for _, elem := range mix4 { - if elem.GetForm().ToString() != "P(a, f(Y))" { - t.Fatalf("Return must be P(a, f(Y))") + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") } - } - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 06 -----") - tree5 := NewNode() - tree5 = tree5.Insert(pafx.(AST.Pred)) - var mix5 []subst.MixedSubstitutions - _, mix5 = tree5.Unify(pafy) - for _, elem5 := range mix5 { - fmt.Println(elem5.ToString()) - } - if len(mix5) != 1 { - t.Fatalf("Should have a found 1 unification") - } - for _, elem := range mix5 { - if elem.GetForm().ToString() != "P(a, f(Y))" { - t.Fatalf("Must return P(a, f(Y))") + if mix[0].GetForm().ToString() != "P(a, f(Y))" { + t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) } - } - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 07 -----") - tree6 := NewNode() - tree6 = tree6.Insert(px.(AST.Pred)) - var mix6 []subst.MixedSubstitutions - _, mix6 = tree6.Unify(py) - for _, elem := range mix6 { - fmt.Println(elem.ToString()) - } - if len(mix6) != 1 { - t.Fatalf("Should have a found 1 unification") - } - for _, elem := range mix6 { - fmt.Println(elem.GetForm().ToString()) - if elem.GetForm().ToString() != "P(Y)" { - t.Fatalf("Must return P(Y)") + }) + + t.Run("Unify2_px_with_py", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + found, mix := tree.Unify2(py) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") } - } - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 08 -----") - tree7 := NewNode() - tree7 = tree7.Insert(pxy.(AST.Pred)) - var mix7 []subst.MixedSubstitutions - _, mix7 = tree7.Unify(pab) - for _, elem := range mix7 { - fmt.Println(elem.ToString()) - } - if len(mix7) != 1 { - t.Fatalf("Should have a found 1 unification") - } - for _, elem := range mix7 { - if elem.GetForm().ToString() != "P(a, b)" { - t.Fatalf("Must return P(a, b)") + if mix[0].GetForm().ToString() != "P(Y)" { + t.Errorf("Expected unified form to be 'P(Y)', got '%s'", mix[0].GetForm().ToString()) } - } - fmt.Println("-----END TEST-----") - fmt.Println() + }) - fmt.Println("-----TEST 09 -----") - fmt.Println("-----EXPECTED FAILURE -----") + t.Run("Unify2_pxy_with_pab", func(t *testing.T) { - tree8 := NewNode() - tree8 = tree8.Insert(pxx.(AST.Pred)) - val8, mix8 := tree8.Unify(pab) - fmt.Println("-----EXPECTED FAILURE -----") + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + found, mix := tree.Unify2(pab) - if len(mix8) != 0 { - fmt.Println("return must Be empty ") - } - if val8 { - t.Fatalf("This test must fail") - } + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(a, b)" { + t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("Unify2_Multiple_Inserts_with_py", func(t *testing.T) { - fmt.Println("-----TEST 10 -----") - fmt.Println("-----EXPECTED FAILURE -----") + tree := NewNode() + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pfx.(AST.Pred)) + found, mix := tree.Unify2(py) - tree9 := NewNode() - tree9 = tree9.Insert(pba.(AST.Pred)) - tree9 = tree9.Insert(pab.(AST.Pred)) - val9, mix9 := tree9.Unify(pxx) - if val9 { - t.Fatalf(" This test must fail ") - } - if len(mix9) != 0 { - t.Fatal("Return must be empty") - } - fmt.Println("-----EXPECTED FAILURE -----") - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 11 -----") - tree10 := NewNode() - tree10 = tree10.Insert(pb.(AST.Pred)) - tree10 = tree10.Insert(pa.(AST.Pred)) - tree10 = tree10.Insert(pfx.(AST.Pred)) - var mix10 []subst.MixedSubstitutions - _, mix10 = tree10.Unify(py) - - if len(mix10) != 3 { - t.Fatalf("Size must be 3") - } - for _, elem := range mix10 { - if elem.GetForm().ToString() != "P(Y)" { - t.Fatalf("Return must be P(Y)") + if !found || len(mix) != 3 { + t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) } - } - fmt.Println("-----END TEST-----") - fmt.Println() - fmt.Println("-----TEST 12 -----") - fmt.Println("-----EXPECTED FAILURE -----") + for i, elem := range mix { + if elem.GetForm().ToString() != "P(Y)" { + t.Errorf("Expected unified form %d to be 'P(Y)', got '%s'", i, elem.GetForm().ToString()) + } + } + }) - tree11 := NewNode() - tree11 = tree11.Insert(pab.(AST.Pred)) - val11, mix11 := tree11.Unify(pxx) - if val11 { - t.Fatalf("This test must fail ") - } - if len(mix11) != 0 { - t.Fatalf("Size of mix11 must be empty") - } + t.Run("Unify2_pggab_with_pxy", func(t *testing.T) { - fmt.Println("-----EXPECTED FAILURE -----") - fmt.Println("-----END TEST-----") - fmt.Println() + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) + found, mix := tree.Unify2(pxy) - fmt.Println("-----TEST 13 -----") - tree12 := NewNode() - tree12 = tree12.Insert(pggab.(AST.Pred)) - var mix12 []subst.MixedSubstitutions - _, mix12 = tree12.Unify(pxy) - for _, elem := range mix12 { - if elem.GetForm().ToString() != "P(X, Y)" { - t.Fatalf("Return must be P(X, Y)") + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") } - } - if len(mix12) != 1 { - t.Fatalf("Should have a found 1 unification") - } - fmt.Println("-----END TEST-----") - fmt.Println() + if mix[0].GetForm().ToString() != "P(X, Y)" { + t.Errorf("Expected unified form to be 'P(X, Y)', got '%s'", mix[0].GetForm().ToString()) + } + }) -} + t.Run("Exception_Unify2_pa_with_pb", func(t *testing.T) { -func TestUnifyTerm(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + found, mix := tree.Unify2(pb) - fmt.Println("-----TEST 01 -----") - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - queryTerm := subst.TransformPred(pay.(AST.Pred)) + if found { + t.Errorf("Unification should have failed for P(a) and P(b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list, got %d elements", len(mix)) + } + }) + + t.Run("Exception_Unify2_pxx_with_pab", func(t *testing.T) { - var val bool - var mix []subst.MixedTermSubstitutions - val, mix = tree.UnifyTerm(queryTerm) + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + found, mix := tree.Unify2(pab) - if val { - for _, elem := range mix { - fmt.Println(" ->", elem.ToString()) + if found { + t.Errorf("Unification should have failed: P(x, x) cannot unify with P(a, b)") } - } else { - t.Fatalf("Unify Failure") - } - fmt.Println("-----END TEST-----") - fmt.Println() + if len(mix) != 0 { + t.Errorf("Expected empty result list") + } + }) - fmt.Println("-----TEST 02 -----") - tree2 := NewNode() - tree2 = tree2.Insert(pax.(AST.Pred)) - queryTerm2 := subst.TransformPred(pab.(AST.Pred)) + t.Run("Exception_Unify2_pba_pab_with_pxx", func(t *testing.T) { - var mix2 []subst.MixedTermSubstitutions - _, mix2 = tree2.UnifyTerm(queryTerm2) + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + found, mix := tree.Unify2(pxx) - if len(mix2) != 1 { - t.Fatalf("Unify Failure") - } - for _, elem := range mix2 { - fmt.Println(elem.ToString()) - } + if found { + t.Errorf("Unification should have failed: P(X, X) cannot unify with P(b, a) or P(a, b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list") + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("Exception_Unify2_pab_with_pxx", func(t *testing.T) { - fmt.Println("-----TEST 03 -----") - tree3 := NewNode() - tree3 = tree3.Insert(pa.(AST.Pred)) - queryTerm3 := subst.TransformPred(pb.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + found, mix := tree.Unify2(pxx) + + if found || len(mix) != 0 { + t.Errorf("Unification should have failed: P(a, b) cannot unify with P(X, X)") + } + }) +} - var val3 bool - var mix3 []subst.MixedTermSubstitutions - val3, mix3 = tree3.UnifyTerm(queryTerm3) +func TestUnifyTerm2(t *testing.T) { - if len(mix3) != 0 { - t.Fatalf("Must return null because it can't be unified") - } - if val3 { - t.Fatalf("Unify Failure") - } - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("UnifyTerm2_pax_with_pay", func(t *testing.T) { - fmt.Println("-----TEST 04 -----") - tree4 := NewNode() - tree4 = tree4.Insert(pa.(AST.Pred)) - queryTerm4 := subst.TransformPred(pa.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) - var val4 bool - var mix4 []subst.MixedTermSubstitutions - val4, mix4 = tree4.UnifyTerm(queryTerm4) + val, mix := tree.UnifyTerm2(queryTerm) - if !val4 { - t.Fatalf("Unify Failure") - } - for _, elem := range mix4 { - if elem.ToString() != "P(a) {}" { - t.Fatalf("Must be P(a) {}") + if !val { + t.Fatalf("Unification failed, expected success") } - } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("UnifyTerm2_pax_with_pab", func(t *testing.T) { - fmt.Println("-----TEST 05 -----") - tree5 := NewNode() - tree5 = tree5.Insert(pab.(AST.Pred)) - queryTerm5 := subst.TransformPred(pay.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) - var mix5 []subst.MixedTermSubstitutions - _, mix5 = tree5.UnifyTerm(queryTerm5) + val, mix := tree.UnifyTerm2(queryTerm) - for _, elem := range mix5 { - if elem.Term().ToString() != "P(a, Y)" { - t.Fatalf("Return must be P(a, Y) ") + if !val { + t.Fatalf("Unification failed, expected success") } - } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("UnifyTerm2_pa_with_pa", func(t *testing.T) { - fmt.Println("-----TEST 06 -----") - tree6 := NewNode() - tree6 = tree6.Insert(pafx.(AST.Pred)) - queryTerm6 := subst.TransformPred(pafy.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + queryTerm := subst.TransformPred(pa.(AST.Pred)) - var mix6 []subst.MixedTermSubstitutions - _, mix6 = tree6.UnifyTerm(queryTerm6) + val, mix := tree.UnifyTerm2(queryTerm) - for _, elem := range mix6 { - if elem.Term().ToString() != "P(a, f(Y))" { - t.Fatalf("Must return P(a, f(Y))") + if !val { + t.Fatalf("Unification failed, expected success") } - } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + } + if mix[0].ToString() != "P(a) {}" { + t.Errorf("Expected result 'P(a) {}', got '%s'", mix[0].ToString()) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("UnifyTerm2_pab_with_pay", func(t *testing.T) { - fmt.Println("-----TEST 07 -----") - tree7 := NewNode() - tree7 = tree7.Insert(px.(AST.Pred)) - queryTerm7 := subst.TransformPred(py.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) - var mix7 []subst.MixedTermSubstitutions - _, mix7 = tree7.UnifyTerm(queryTerm7) + val, mix := tree.UnifyTerm2(queryTerm) - for _, elem := range mix7 { - if elem.Term().ToString() != "P(Y)" { - t.Fatalf("Must return P(Y)") + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) } - } + if mix[0].Term().ToString() != "P(a, Y)" { + t.Errorf("Expected term to be 'P(a, Y)', got '%s'", mix[0].Term().ToString()) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("UnifyTerm2_pafx_with_pafy", func(t *testing.T) { - fmt.Println("-----TEST 08 -----") - tree8 := NewNode() - tree8 = tree8.Insert(pxy.(AST.Pred)) - queryTerm8 := subst.TransformPred(pab.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + queryTerm := subst.TransformPred(pafy.(AST.Pred)) - var mix8 []subst.MixedTermSubstitutions - _, mix8 = tree8.UnifyTerm(queryTerm8) + val, mix := tree.UnifyTerm2(queryTerm) - for _, elem := range mix8 { - if elem.Term().ToString() != "P(a, b)" { - t.Fatalf("Must return P(a, b)") + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) } - } - fmt.Println("-----END TEST-----") - fmt.Println() - - fmt.Println("-----TEST 09 -----") - fmt.Println("-----EXPECTED FAILURE -----") + if mix[0].Term().ToString() != "P(a, f(Y))" { + t.Errorf("Expected term to be 'P(a, f(Y))', got '%s'", mix[0].Term().ToString()) + } + }) - tree9 := NewNode() - tree9 = tree9.Insert(pxx.(AST.Pred)) - queryTerm9 := subst.TransformPred(pab.(AST.Pred)) + t.Run("UnifyTerm2_px_with_py", func(t *testing.T) { - var val9 bool - val9, _ = tree9.UnifyTerm(queryTerm9) + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + queryTerm := subst.TransformPred(py.(AST.Pred)) - if val9 { - t.Fatalf("Unify Failure") - } - fmt.Println("-----EXPECTED FAILURE -----") + val, mix := tree.UnifyTerm2(queryTerm) - fmt.Println("-----END TEST-----") - fmt.Println() + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(Y)" { + t.Errorf("Expected term to be 'P(Y)', got '%s'", mix[0].Term().ToString()) + } + }) - fmt.Println("-----TEST 10 -----") - fmt.Println("-----EXPECTED FAILURE -----") + t.Run("UnifyTerm2_pxy_with_pab", func(t *testing.T) { - tree10 := NewNode() - tree10 = tree10.Insert(pba.(AST.Pred)) - tree10 = tree10.Insert(pab.(AST.Pred)) - queryTerm10 := subst.TransformPred(pxx.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) - var val10 bool - var mix10 []subst.MixedTermSubstitutions - val10, mix10 = tree10.UnifyTerm(queryTerm10) + val, mix := tree.UnifyTerm2(queryTerm) - if val10 { - t.Fatalf("Got %d elements instead of 0", len(mix10)) - } else { - fmt.Println("Unify Failure (Expected)") - } - fmt.Println("-----EXPECTED FAILURE -----") + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(a, b)" { + t.Errorf("Expected term to be 'P(a, b)', got '%s'", mix[0].Term().ToString()) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("UnifyTerm2_Multiple_Inserts_with_py", func(t *testing.T) { - fmt.Println("-----TEST 11 -----") - tree11 := NewNode() - tree11 = tree11.Insert(pb.(AST.Pred)) - tree11 = tree11.Insert(pa.(AST.Pred)) - tree11 = tree11.Insert(pfx.(AST.Pred)) - queryTerm11 := subst.TransformPred(py.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pfx.(AST.Pred)) + queryTerm := subst.TransformPred(py.(AST.Pred)) - var mix11 []subst.MixedTermSubstitutions - _, mix11 = tree11.UnifyTerm(queryTerm11) + val, mix := tree.UnifyTerm2(queryTerm) - for _, elem := range mix11 { - if elem.Term().ToString() != "P(Y)" { - t.Fatalf("Unify Failure") + if !val { + t.Fatalf("Unification failed, expected success with matches") } - } - - fmt.Println("-----END TEST-----") - fmt.Println() + if len(mix) != 3 { + t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) + } + for i, elem := range mix { + if elem.Term().ToString() != "P(Y)" { + t.Errorf("Expected unified term %d to be 'P(Y)', got '%s'", i, elem.Term().ToString()) + } + } + }) - fmt.Println("-----TEST 12 -----") - fmt.Println("-----EXPECTED FAILURE -----") + t.Run("UnifyTerm2_pggab_with_pxy", func(t *testing.T) { - tree12 := NewNode() - tree12 = tree12.Insert(pab.(AST.Pred)) - queryTerm12 := subst.TransformPred(pxx.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) + queryTerm := subst.TransformPred(pxy.(AST.Pred)) - var val12 bool - var mix12 []subst.MixedTermSubstitutions - val12, mix12 = tree12.UnifyTerm(queryTerm12) + val, mix := tree.UnifyTerm2(queryTerm) - if val12 { - t.Fatalf("Got %d elements instead of 0", len(mix12)) - } else { - fmt.Println("Unify Failure (Expected)") - } - if len(mix12) != 0 { - t.Fatalf("Got %d elements instead of 0", len(mix12)) + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(X, Y)" { + t.Errorf("Expected term to be 'P(X, Y)', got '%s'", mix[0].Term().ToString()) + } + }) - } - fmt.Println("-----EXPECTED FAILURE -----") - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("Exception_UnifyTerm2_pa_with_pb", func(t *testing.T) { - fmt.Println("-----TEST 13 -----") - tree13 := NewNode() - tree13 = tree13.Insert(pggab.(AST.Pred)) - queryTerm13 := subst.TransformPred(pxy.(AST.Pred)) + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + queryTerm := subst.TransformPred(pb.(AST.Pred)) - var mix13 []subst.MixedTermSubstitutions - _, mix13 = tree13.UnifyTerm(queryTerm13) + val, mix := tree.UnifyTerm2(queryTerm) - for _, elem := range mix13 { - if elem.Term().ToString() != "P(X, Y)" { - t.Fatalf("Must return P(X, Y)") + if val { + t.Fatalf("Unification should have failed for P(a) and P(b)") } - } + if len(mix) != 0 { + t.Fatalf("Expected 0 elements, got %d", len(mix)) + } + }) - fmt.Println("-----END TEST-----") - fmt.Println() + t.Run("Exception_UnifyTerm2_pxx_with_pab", func(t *testing.T) { -} + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) -func TestMakeDataStruct(t *testing.T) { + val, mix := tree.UnifyTerm2(queryTerm) - pac_form := not_pac.(AST.Not).GetForm().(AST.Pred) + if val || len(mix) != 0 { + t.Fatalf("Unification should have failed: P(x,x) cannot unify with P(a,b)") + } + }) - fmt.Println("Test positive tree") + t.Run("Exception_UnifyTerm2_pba_pab_with_pxx", func(t *testing.T) { - tree1 := NewNode() - formulas1 := Lib.NewList[AST.Form]() - formulas1.Append(pab) // + => Insert - formulas1.Append(not_pac) // - => Ignore - formulas1.Append(pba) // + => Insert + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) - resultTree1 := tree1.MakeDataStruct(formulas1, true).(DiscriminationNode) + val, mix := tree.UnifyTerm2(queryTerm) - // Vérifications - if len(resultTree1.RetrieveUnifiables(pab)) == 0 { - t.Fatalf(" Tree must contain pab") - } - if len(resultTree1.RetrieveUnifiables(pba)) == 0 { - t.Fatalf(" Tree must contain pba ") - } - if len(resultTree1.RetrieveUnifiables(pac_form)) != 0 { - t.Fatalf(" Tree mustn't contain pac because it's negative in the positive tree") - } + if val { + t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + } + }) - fmt.Println("Test negative tree") + t.Run("Exception_UnifyTerm2_pab_with_pxx", func(t *testing.T) { - tree2 := NewNode() - formulas2 := Lib.NewList[AST.Form]() - formulas2.Append(pab) // + => Ignored - formulas2.Append(not_pac) // - => Insert - formulas2.Append(pba) // + => Ignored + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) - resultTree2 := tree2.MakeDataStruct(formulas2, false).(DiscriminationNode) + val, mix := tree.UnifyTerm2(queryTerm) - // Vérifications - if len(resultTree2.RetrieveUnifiables(pac_form)) == 0 { - t.Fatalf(" Tree must contain not_pac ") - } - if len(resultTree2.RetrieveUnifiables(pab)) != 0 { - t.Fatalf(" Tree musn't contain pab because it's positive in the negative tree") - } - if len(resultTree2.RetrieveUnifiables(pba)) != 0 { - t.Fatalf(" Tree mustn't contain pba because it's positive in the negative tree") - } + if val { + t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + } + if len(mix) != 0 { + t.Fatalf("Expected result array to be empty, got %d elements", len(mix)) + } + }) +} - resultTree1.Print() - resultTree2.Print() +func TestContextStange(t *testing.T) { -} + t.Run("Exception_Occur_Check_Cyclic", func(t *testing.T) { + tree := NewNode() + tree.Insert(pfx.(AST.Pred)) + val, mix := tree.Unify(px) -func TestToutPlaquerPourDevenirCharpentier(t *testing.T) { + if val { + t.Fatalf("Occur Check Faillure") + } + if len(mix) != 0 { + t.Fatalf("Occur Check Faillure") + } + }) - tree := NewNode() - // tree = tree.Insert(p_typed_pred_int_x) - // tree = tree.Insert(p_typed_pred_reel_x) - tree = tree.Insert(p_typed) + t.Run("UnifyTerm_Complex_SkipTerm", func(t *testing.T) { + tree := NewNode() + tree.Insert(pfgaba.(AST.Pred)) + val, mix := tree.Unify(px) - tree.Print() + if val { + t.Fatalf("Occur Check Faillure") + } + if len(mix) != 0 { + t.Fatalf("Occur Check Faillure") + } + }) - val, mix := tree.Unify(p_typed_pred_int_2) + t.Run("Exception_Shared_Query_Variables", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) - fmt.Println("val", val) - for _, elem := range mix { - fmt.Println("elem", elem.ToString()) - } + queryTerm := subst.TransformPred(pxx.(AST.Pred)) + val, _ := tree.UnifyTerm(queryTerm) - elem := (pba.(AST.Pred)).GetTyArgs() - for _, truc := range elem.GetSlice() { - fmt.Println("hfhfsife", truc.ToString()) - } + if val { + t.Fatalf("Unification should fail because X cannot be 'a' and 'b' simultaneously") + } + }) } From ba5f60979d4613b136d1ce69cbce1ba9221cc179 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Tue, 2 Jun 2026 13:42:35 +0200 Subject: [PATCH 16/23] Rework of discriminationTree. Add Ty to the tree and rework of tests --- .../discrimination-trees.go | 409 ++++++++----- src/Unif/discriminationtree/dt_test.go | 549 +++++++++++------- 2 files changed, 606 insertions(+), 352 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index dfc1be24..9d276c69 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -62,6 +62,7 @@ type NodeElement interface { isAllowedNodeElement() IsMeta() bool GetArityType() int + GetTy() AST.Ty Equals(target NodeElement) bool } @@ -106,7 +107,6 @@ func (sym SymbolType) getTerm() AST.Term { if tn, ok := sym.symbol.(TermNode); ok { return tn.Term } - Glob.Anomaly("Not a AST.Term", "Not a AST.Term") return nil } @@ -116,7 +116,6 @@ func (sym SymbolType) GetTy() AST.Ty { if tn, ok := sym.symbol.(TyNode); ok { return tn.Ty } - Glob.Anomaly("Not a AST.Ty", "Not a AST.Ty") return nil } @@ -126,7 +125,6 @@ func (sym SymbolType) getString() string { if ns, ok := sym.symbol.(NodeString); ok { return ns.ToString() } - Glob.Anomaly("Not a string", "Not a string") return "" } @@ -140,15 +138,21 @@ func (ns NodeString) Equals(target NodeElement) bool { } func (tn TermNode) Equals(target NodeElement) bool { - typ, ok := target.(TermNode) if !ok { return false } - return tn.Term.Equals(typ.Term) -} + res := tn.Term.Equals(typ.Term) + if !res { + fmt.Println("--- EQUALS FAILED ---") + fmt.Printf("%s | Type : %T\n", tn.Term.ToString(), tn.Term) + fmt.Printf("%s | Type: %T\n", typ.Term.ToString(), typ.Term) + fmt.Println("---------------------") + } + return res +} func (tn TyNode) Equals(target NodeElement) bool { typ, ok := target.(TyNode) @@ -158,7 +162,26 @@ func (tn TyNode) Equals(target NodeElement) bool { return tn.Ty.Equals(typ.Ty) } +func (ns NodeString) GetTy() AST.Ty { + + return AST.TIndividual() // Temporary + +} + +func (tn TermNode) GetTy() AST.Ty { + + return tn.ToMeta().GetTy() + +} + +func (tn TyNode) GetTy() AST.Ty { + + return tn.Ty + +} + func createNodeElement(t any) NodeElement { + switch v := t.(type) { case string: return NodeString(v) @@ -179,6 +202,10 @@ type SymbolType struct { arity int // Arity of a node } +func (t SymbolType) ToString() string { + return fmt.Sprintf("Symbol : %s Arity : %d\n", t.symbol.ToString(), t.GetArity()) +} + func (t SymbolType) getSymbol() NodeElement { return t.symbol } @@ -191,7 +218,12 @@ func (s SymbolType) IsNil() bool { return s.symbol == nil && s.arity == -1 } +func makeSymbolTypeTy(node NodeElement) SymbolType { + return SymbolType{node, 0} +} + func makeSymbolType(node NodeElement, arity int) SymbolType { + return SymbolType{node, arity} } @@ -201,6 +233,7 @@ func (s SymbolType) Equals(target SymbolType) bool { if s.GetArity() != target.GetArity() { return false } + return s.symbol.Equals(target.symbol) } @@ -272,7 +305,6 @@ func (dNode DiscriminationNode) toString() string { type CandidatResult struct { Pred AST.Pred // Predicat Subs subst.Substitutions // The associated substitution - } func (Candidat CandidatResult) getPred() AST.Pred { @@ -311,8 +343,12 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { /*****************************/ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { + res := Lib.NewList[SymbolType]() + termTy := t.ToMeta().GetTy() + res.Append(makeSymbolTypeTy(createNodeElement(termTy))) + switch term := t.(type) { // if term is a function or cst, add and call his args @@ -341,14 +377,12 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { res.Append(makeSymbolType(createNodeElement(normalizedMeta), 0)) // Add the new Meta to the return slice case AST.Id: - fmt.Println("Parse AST.id", term.GetName()) funSansArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) first_element := makeSymbolType(createNodeElement(funSansArgs), 0) res.Append(first_element) default: - fmt.Println("Error in ParseTerm") - + Glob.Anomaly("Error with %s in ParseTerm", term.GetName()) } return res @@ -364,9 +398,11 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { /*****************************/ // Case t is a Function => SymbolType{t.ID, t.getArgs} +// Case t is a cst => SymbolType{MakerFun(t.ID), 0} // Case t is a Meta => SymbolType{t.ID, 0} // Else Glob.Anomaly func FirstElementToSymbolType(t AST.Term) SymbolType { + switch t := t.(type) { case AST.Fun: // Case function funSansArgs := AST.MakerFun(t.GetID(), t.GetTyArgs(), Lib.NewList[AST.Term]()) @@ -377,11 +413,15 @@ func FirstElementToSymbolType(t AST.Term) SymbolType { case AST.Meta: // Case metaVariable return SymbolType{createNodeElement(t), 0} default: // Not supposed to see something else - Glob.Anomaly("TermToST", "Var or Id") - return SymbolType{createNodeElement(nil), -1} // Dog Code that will fail but won't be triggered due to Glob.Anomaly + make the compiler happy + Glob.Anomaly("FirstElementToSymbolType Error", "Unknow type in FirstElementToSymbolType") + return SymbolType{createNodeElement(nil), -1} // Dog Code that will fail but won't be triggered due to Glob.Anomaly. Only here to please the compiler. } } +// Depend of the Type, create the follow DiscriminationNode +// case AST.Fun => Create a List and fill it with TermToNode(GetArgs) then Maker DiscrmiminationNode +// Case AST.Id => MakeDiscriminationNode(FirstElementToSymbol) => Create Fun +// Case AST.Meta => MakeDiscriminationNode(FirstElementToSymbol) => Create Meta func TermToNode(t AST.Term) DiscriminationNode { switch t := t.(type) { case AST.Fun: // Accumulate all the term of the AST.Term then create a Node with all the children @@ -408,11 +448,28 @@ func TermToNode(t AST.Term) DiscriminationNode { /*********** Insrt ***********/ /*****************************/ -// Insert a AST.Pred in the tree. If using a AST.term, it have to be cast when inserting ( tree = tree.Insert(px.(AST.pred)) ) +// Predicat Parser. Skip the Predicat Type and Transform it into a Function. +// Then Call parseTerm on the args of the predicat. +func parsePred(p AST.Pred, ctx *NormalizerContext) Lib.List[SymbolType] { + + res := Lib.NewList[SymbolType]() + + // Required Overwise the SymbolType of the predicat will be AST.ID and will be compared with a AST.Fun -> Automatic faillure + tmpFun := AST.MakerFun(p.GetID(), Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + res.Append(makeSymbolType(createNodeElement(tmpFun), p.GetArgs().Len())) + + for _, arg := range p.GetArgs().GetSlice() { + res.Append(parseTerm(arg, ctx).GetSlice()...) + } + + return res +} + +// Insert a AST.Pred in the tree. If using a AST.Form or Something Else, it have to be cast when inserting ( tree = tree.Insert(px.(AST.pred)) ) // Call the parser then the auxiliary function func (dNode DiscriminationNode) Insert(p AST.Pred) DiscriminationNode { - termP := subst.TransformPred(p) - sym_list := parseTerm(termP, NewContext()) + + sym_list := parsePred(p, NewContext()) return dNode.insertRec(sym_list, p) } @@ -478,21 +535,25 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm /*****************************/ func GetSubTermLength(seq []SymbolType) int { - if len(seq) == 0 { return 0 } - needed := 1 + needed := 1 // Type + Term index := 0 for needed > 0 && index < len(seq) { sym := seq[index] - needed = needed - 1 + sym.GetArity() // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... + arite := sym.GetArity() + + if arite > 0 { + arite = arite * 2 + } + + needed = needed - 1 + arite // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... index++ } return index - } func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []CandidatResult { @@ -505,7 +566,13 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue } for _, child := range dNode.getChildren().GetSlice() { - newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term + + arite := child.GetArity() + if arite > 0 { + arite = arite * 2 // Each Type + Term + } + + newNeeded := needed - 1 + arite // 0 if Meta, Else Arity of the Term matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery, substitutions) subs = append(subs, matches...) } @@ -515,9 +582,7 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { predFormula, _ := t.(AST.Pred) - termQuery := subst.TransformPred(predFormula) - - seq := parseTerm(termQuery, NewContext()).GetSlice() + seq := parsePred(predFormula, NewContext()).GetSlice() Env := subst.Substitutions{} return dNode.retrieveRec(seq, Env) } @@ -562,7 +627,9 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { defer wg.Done() - symQuery := seq[0] // First Element + + symQuery := seq[0] // First Element + childSym := child.getSymbol() // child is meta or cst isExactMatch := child.symbol.Equals(symQuery) if isExactMatch { // Exact Match @@ -570,13 +637,11 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri ch <- matches } - childSym := child.getSymbol() // child is meta or cst - // Case the child is a AST.Meta if child.getSymbol().getSymbol().IsMeta() && !isExactMatch { // We noticed that the term of the dNode is a Meta - // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term + // Meaning that we can skip the current term of the seq ( paramater of this function ) because it will be unify with the current term // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 skip := GetSubTermLength(seq) @@ -627,7 +692,13 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri } if !mergedSub.Equals(subst.Failure()) { - childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) + + tokensToSkip := child.GetArity() + if tokensToSkip > 0 { + tokensToSkip = tokensToSkip * 2 + } + + childResults := child.SkipTreeTermAndContinue(tokensToSkip, seq[1:], mergedSub) ch <- childResults } @@ -642,34 +713,47 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri /*****************************/ func (dNode DiscriminationNode) Print() { + if dNode.IsEmpty() { + fmt.Println("Empty Tree") + return + } + fmt.Println("[ROOT]") for _, child := range dNode.getChildren().GetSlice() { - child.displayRec(2) // Magic Number (Set the indent but bellow 2 the display is horrible and above 2 is bugget for ??? reason) + child.displayRec(1) } } -func (dNode DiscriminationNode) displayRec(indent int) { +func (dNode DiscriminationNode) displayRec(depth int) { + + indent := strings.Repeat(" ", depth) - prefix := strings.Repeat(" ", indent-1) + " |-- " + var nodeTag string - if indent == 2 { - prefix = strings.Repeat("[ROOT]", indent-1) + " |-- " + switch dNode.getSymbol().getSymbol().(type) { + case TyNode: + nodeTag = "[Ty]" + case TermNode: + nodeTag = "[Term]" + case NodeString: + nodeTag = "[String]" } - fmt.Printf("%s%s arity : %d\n", prefix, dNode.getSymbol().symbol.ToString(), dNode.GetArity()) + fmt.Printf("%s|-- %s %s (arity: %d)\n", indent, nodeTag, dNode.toString(), dNode.GetArity()) if dNode.getLeafFor().Len() > 0 { - leafPrefix := strings.Repeat(" ", indent) + " [=> " + leafIndent := indent + " " for _, pred := range dNode.getLeafFor().GetSlice() { - fmt.Printf("%s%s]\n", leafPrefix, pred.ToString()) + fmt.Printf("%s[=> %s]\n", leafIndent, pred.ToString()) } } + for _, child := range dNode.getChildren().GetSlice() { - child.displayRec(indent + 1) + child.displayRec(depth + 1) } } func (dNode DiscriminationNode) IsEmpty() bool { - return dNode.symbol.IsNil() + return dNode.getChildren().Empty() } func (dNode DiscriminationNode) Copy() subst.DataStructure { @@ -708,7 +792,6 @@ func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_p } } } - return dNode.InsertFormulaListToDataStructure(form) } @@ -739,11 +822,9 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe Glob.Anomaly("DiscriminationTree Unify", "Expected a predicate") return false, nil } - queryTerm := subst.TransformPred(predFormula) // For Robinson for _, possibleMatch := range candidates { - initialSubst := subst.Substitutions{} possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term for Robinson finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson @@ -754,8 +835,8 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe fmt.Println("-------------------------") } else { found = true - matching := subst.MakeMatchingSubstitutions(inputFormula, finalSubst) // constructor - mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return + matching := subst.MakeMatchingSubstitutions(possibleMatch.getPred(), finalSubst) // constructor + mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return } } return found, mixed @@ -768,8 +849,16 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix tmpContext := NewContext() seq := parseTerm(inputTerm, tmpContext).GetSlice() - candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) + // Remove the Initial $i + var seq2 []SymbolType + for i, elem := range seq { + if i > 0 { + seq2 = append(seq2, elem) + } + } + + candidates := dNode.retrieveRec(seq2, subst.MakeEmptySubstitution()) for _, possibleMatch := range candidates { candidateTerm := subst.TransformPred(possibleMatch.getPred()) @@ -829,9 +918,30 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix /////////////////////////////////////////////// /////////////////////////////////////////////// +func parsePred2(p AST.Pred) Lib.List[SymbolType] { + + res := Lib.NewList[SymbolType]() + + // Required Overwise the SymbolType of the predicat will be AST.ID and will be compared with a AST.Fun -> Automatic faillure + tmpFun := AST.MakerFun(p.GetID(), Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + res.Append(makeSymbolType(createNodeElement(tmpFun), p.GetArgs().Len())) + + fmt.Println("fun tmp (paramtre)", tmpFun.IsFun()) + + for _, arg := range p.GetArgs().GetSlice() { + res.Append(parseTerm2(arg).GetSlice()...) + } + + return res +} + func parseTerm2(t AST.Term) Lib.List[SymbolType] { + res := Lib.NewList[SymbolType]() + termTy := t.ToMeta().GetTy() + res.Append(makeSymbolTypeTy(createNodeElement(termTy))) + switch term := t.(type) { // if term is a function or cst, add and call his args @@ -839,7 +949,6 @@ func parseTerm2(t AST.Term) Lib.List[SymbolType] { funSansArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) first_element := makeSymbolType(createNodeElement(funSansArgs), term.GetArgs().Len()) - fmt.Println("ParserTerm2 case Fun", term.GetName()) res.Append(first_element) for _, arg := range term.GetArgs().GetSlice() { res.Append(parseTerm2(arg).GetSlice()...) @@ -847,90 +956,93 @@ func parseTerm2(t AST.Term) Lib.List[SymbolType] { // Case meta, we have to transform it case AST.Meta: - fmt.Println("ParserTerm2 case Meta", term.GetName()) res.Append(makeSymbolType(createNodeElement(term), 0)) // Add the new Meta to the return slice case AST.Id: - fmt.Println("ParserTerm2 case Id", term.GetName()) funSansArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) first_element := makeSymbolType(createNodeElement(funSansArgs), 0) res.Append(first_element) default: - fmt.Println("Default parseTerm2 name", term.GetName()) - fmt.Println("Default parseTerm2 index ", term.GetIndex()) + Glob.Anomaly("Error with %s in ParseTerm2", term.GetName()) } return res } func (dNode DiscriminationNode) SkipTreeTermAndContinue2(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []CandidatResult { - var subs []CandidatResult + // End of recursion if needed == 0 { return dNode.retrieveRec2(remainingQuery, substitutions) } + for _, child := range dNode.getChildren().GetSlice() { - newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term + arite := child.GetArity() + if arite > 0 { + arite = arite * 2 + } + + newNeeded := needed - 1 + arite matches := child.SkipTreeTermAndContinue2(newNeeded, remainingQuery, substitutions) subs = append(subs, matches...) } return subs - } func (dNode DiscriminationNode) Unify2(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { - fmt.Println("Unify2 AST.Form", inputFormula.ToString()) + dNode.Print() + fmt.Println("inputFOrmula", inputFormula.ToString()) candidates := dNode.RetrieveUnifiables2(inputFormula) var mixed []subst.MixedSubstitutions var found bool - fmt.Println("Unify2 candidates taille ", len(candidates)) + fmt.Println("Len Candidates", len(candidates)) - predFormula, isPred := inputFormula.(AST.Pred) - if !isPred { - fmt.Println("Bug InputFormula n'est pas castable en PRED") - return false, nil + queryPred, isQueryPred := inputFormula.(AST.Pred) + if !isQueryPred { + return false, mixed } - fmt.Println("Unify2 AST.Pred", predFormula.ToString()) - - queryTerm := subst.TransformPred(predFormula) - for _, possibleMatch := range candidates { - fmt.Println("Unify2 candidates taille N°2 ", len(candidates)) - - currentSubst := possibleMatch.GetSubs() + fmt.Println("Execution Candidates") - fmt.Println("Candidat Term", possibleMatch.getPred().ToString()) - fmt.Println("Candidat Subs", possibleMatch.GetSubs().ToString()) + candPred := possibleMatch.getPred() - possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) - - fmt.Println("PossibleMatchterm avant Robinson : ", possibleMatchTerm.ToString()) - fmt.Println("QueryTerm avant Robinson : ", queryTerm.ToString()) + if !queryPred.GetID().Equals(candPred.GetID()) { + continue + } - finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, currentSubst) + argsQuery := queryPred.GetArgs().GetSlice() + argsCand := candPred.GetArgs().GetSlice() + if len(argsQuery) != len(argsCand) { + continue + } - if !finalSubst.Equals(subst.Failure()) { + currentEnv := subst.Substitutions{} + isUnifiable := true - fmt.Println("Unification reussit avec Term : ", possibleMatchTerm.ToString()) + for i := 0; i < len(argsQuery); i++ { + currentEnv = subst.AddUnification(argsQuery[i], argsCand[i], currentEnv) + if currentEnv.Equals(subst.Failure()) { + isUnifiable = false + break + } + } + if isUnifiable { found = true - matching := subst.MakeMatchingSubstitutions(inputFormula, finalSubst) + matching := subst.MakeMatchingSubstitutions(inputFormula, currentEnv) mixed = append(mixed, matching.ToMixed()) - } else { - - fmt.Println("Unification echoue avec Term : ", possibleMatchTerm.ToString()) - } } + return found, mixed } @@ -940,16 +1052,23 @@ func (dNode DiscriminationNode) UnifyTerm2(inputTerm AST.Term) (bool, []subst.Mi seq := parseTerm2(inputTerm).GetSlice() + if len(seq) > 0 { + if _, isTy := seq[0].getSymbol().(TyNode); isTy { + seq = seq[1:] + } + } + for _, elem := range seq { - fmt.Println("seq Parser Symbol", elem.getSymbol().ToString()) - fmt.Println("seq Parser arite", elem.GetArity()) + fmt.Println("elem : ", elem.ToString()) + fmt.Println("Type : ", elem.getSymbol().GetTy().ToString()) } candidates := dNode.retrieveRec2(seq, subst.MakeEmptySubstitution()) - fmt.Println("Unify2 len Candidat", len(candidates)) - for _, possibleMatch := range candidates { + + fmt.Println("Candidates", possibleMatch.toString()) + currentSubst := possibleMatch.GetSubs() candidateTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term @@ -961,7 +1080,7 @@ func (dNode DiscriminationNode) UnifyTerm2(inputTerm AST.Term) (bool, []subst.Mi Tof: Lib.MkLeft[AST.Term, AST.Form](inputTerm), Subst: finalSubst, } - mixed = append(mixed, mixMatch.ToMixedTerm()) // All the subs returned + mixed = append(mixed, mixMatch.ToMixedTerm()) } } return found, mixed @@ -969,23 +1088,29 @@ func (dNode DiscriminationNode) UnifyTerm2(inputTerm AST.Term) (bool, []subst.Mi // Take a Sequence of SymbolType and return the first AST.Term + the remaining sequence func ReconstructTerm(seq []SymbolType) (AST.Term, []SymbolType) { + if len(seq) == 0 { return nil, seq } + index := 0 - head := seq[0] + if _, ok := seq[index].getSymbol().(TyNode); ok { + index++ + } + if index >= len(seq) { + return nil, seq[index:] + } + + head := seq[index] arite := head.GetArity() term := head.getTerm() - - fmt.Println("ReconstructTerm head Terme", term.ToString()) - fmt.Println("ReconstructTerm head Arite", arite) + index++ switch t := term.(type) { case AST.Fun: - fmt.Println("reconstructTerm Cas Fun") - currentSeq := seq[1:] + currentSeq := seq[index:] args := Lib.NewList[AST.Term]() for i := 0; i < arite; i++ { var arg AST.Term @@ -995,27 +1120,24 @@ func ReconstructTerm(seq []SymbolType) (AST.Term, []SymbolType) { return AST.MakerFun(t.GetID(), t.GetTyArgs(), args), currentSeq // Create Fun case AST.Meta: - fmt.Println("reconstructTerm Cas Meta") - return t, seq[1:] // Go next - + return t, seq[index:] // Go next + case AST.Id: + return AST.MakerFun(t, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()), seq[index:] default: - - fmt.Println("reconstructTerm Cas Default") - fmt.Println("t index", t.GetIndex()) - fmt.Println("t name", t.GetName()) - fmt.Println("t isFun", t.IsFun()) - fmt.Println("t isMeta", t.IsMeta()) - - return nil, seq[1:] // Error type + return nil, seq[index:] // Error type } } func (dNode DiscriminationNode) RetrieveUnifiables2(t AST.Form) []CandidatResult { predFormula, _ := t.(AST.Pred) - termQuery := subst.TransformPred(predFormula) - seq := parseTerm2(termQuery).GetSlice() + seq := parsePred2(predFormula).GetSlice() Env := subst.Substitutions{} + + for _, elem := range seq { + fmt.Println("ParsePred2", elem.ToString()) + } + return dNode.retrieveRec2(seq, Env) } @@ -1023,7 +1145,6 @@ func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst. ch := make(chan []CandidatResult) var results []CandidatResult - var wg sync.WaitGroup if len(seq) == 0 { // End of recursion @@ -1036,7 +1157,6 @@ func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst. // Work goroutine for _, child := range dNode.children.GetSlice() { - wg.Add(1) // Create exactly 1 goroutine go retrieveCase2(seq, currentEnv, child, ch, &wg) } @@ -1048,9 +1168,7 @@ func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst. }() for matches := range ch { - results = append(results, matches...) - } return results @@ -1060,75 +1178,52 @@ func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child Discr defer wg.Done() symQuery := seq[0] // First Element - - fmt.Println("SymQuery Debut symbol", symQuery.getSymbol().ToString()) - fmt.Println("SymQuery Debut arite ", symQuery.GetArity()) - isExactMatch := child.symbol.Equals(symQuery) if isExactMatch { // Exact Match matches := child.retrieveRec2(seq[1:], currentEnv) // Exact Match -> Search next element ch <- matches } - childSym := child.getSymbol() // child is meta or cst - // Case the child is a AST.Meta + // We noticed that the term of the dNode is a Meta + // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term + // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 if childSym.getSymbol().IsMeta() && !isExactMatch { - fmt.Println("SymQuery Child symbol", symQuery.getSymbol().ToString()) - fmt.Println("SymQuery Child arite ", symQuery.GetArity()) - - // We noticed that the term of the dNode is a Meta - // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term - // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 - - fmt.Println("Cas Enfant Meta : Symbol", child.getSymbol().getSymbol().ToString()) + fmt.Println("ChildSym Symbol", childSym.getSymbol().ToString()) queryTerm, restSeq := ReconstructTerm(seq) - - for _, elem := range restSeq { - fmt.Println("Cas Enfant Meta : resteSequence", elem.getSymbol().ToString()) - } - if queryTerm != nil { - - fmt.Println("Cas Enfant Meta : childSym", childSym.getTerm().ToString()) - fmt.Println("Cas Enfant Meta : queryTerm", queryTerm.ToString()) - fmt.Println("Cas Enfant Meta : currentEnv", currentEnv.ToString()) - - fmt.Println("childSym Fun", childSym.getTerm().IsFun()) - fmt.Println("childSym Fun", queryTerm.IsFun()) - fmt.Println("childSym Meta", childSym.getTerm().IsMeta()) - fmt.Println("childSym Meta", queryTerm.IsMeta()) - + fmt.Println("QueryTerm not null") mergedSub := subst.AddUnification(childSym.getTerm(), queryTerm, currentEnv) // Robinson Call - - fmt.Println("Cas Enfant Meta : mergeSub", mergedSub.ToString()) - if !mergedSub.Equals(subst.Failure()) { + fmt.Println("MergeSub reussit") + matches := child.retrieveRec2(restSeq, mergedSub) - fmt.Println("Cas Enfant Meta mergeSub : Bool True") + for _, elem := range matches { + fmt.Println("matches", elem.getPred().ToString()) + } - matches := child.retrieveRec2(restSeq, mergedSub) ch <- matches } else { - fmt.Println("Cas Enfant Meta mergeSub : Bool False") + fmt.Println("MergeSub Failure") } } } else if symQuery.getSymbol().IsMeta() && !isExactMatch { - fmt.Println("SymQuery Meta symbol", symQuery.getSymbol().ToString()) - fmt.Println("SymQuery Meta arite ", symQuery.GetArity()) + fmt.Println("symQuery Symbol", symQuery.getSymbol().ToString()) var mergedSub subst.Substitutions - if child.GetArity() == 0 { + // Case 1: The tree contains a constant (arity 0). + // We have the full term right here, so we can immediately bind the Query's Meta variable + // to this constant and update our substitution environment. childTerm := childSym.getTerm() var properTerm AST.Term = childTerm - // If for ??? reason it's a AST.id, we transform it to AST.Fun + // If for ??? reason it's a AST.id, we transform it to AST.Fun ( Tmp? ) if id, ok := childTerm.(AST.Id); ok { properTerm = AST.MakerFun(id, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) } @@ -1141,22 +1236,22 @@ func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child Discr mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) } } else { - // Si l'arbre contient une fonction (arité > 0), ses arguments sont plus bas dans l'arbre. - // On reporte l'unification de CETTE variable pour ne pas crasher Robinson. + // Case 2: The tree contains a function with arity > 0. + // At this node, we only see the function symbol, not its arguments (which live deeper in the tree). + // To avoid sending an incomplete term to Robinson, we DEFER the unification of this Meta variable. + // We pass the current environment as-is, allowing the recursion to consume all the function's + // arguments further down the branch before finally binding the complete structural term. mergedSub = currentEnv } if !mergedSub.Equals(subst.Failure()) { - // On saute le nombre de nœuds correspondants à l'arité dans l'arbre - childResults := child.SkipTreeTermAndContinue2(child.GetArity(), seq[1:], mergedSub) + tokensToSkip := child.GetArity() + if tokensToSkip > 0 { + tokensToSkip = tokensToSkip * 2 // Type + Term + } + childResults := child.SkipTreeTermAndContinue2(tokensToSkip, seq[1:], mergedSub) ch <- childResults } - } else { - - fmt.Println("SymQuery Mort symbol", symQuery.getSymbol().ToString()) - fmt.Println("SymQuery Mort arite ", symQuery.GetArity()) - - // No recursive call or return - } + } // No recursive call or return } diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index fe3f516f..b89b38f9 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -138,6 +138,8 @@ var pba AST.Form var pca AST.Form var pax AST.Form var pay AST.Form +var pxa AST.Form +var pya AST.Form var pxy AST.Form var pxx AST.Form var px AST.Form @@ -148,6 +150,8 @@ var pfy AST.Form var pafx AST.Form var pafy AST.Form var pfac AST.Form +var pgxb AST.Form +var pfxyb AST.Form var pfgaxc AST.Form var pfgxby AST.Form @@ -250,6 +254,8 @@ func initTestVariable() { pca = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](c, a)) pax = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, x)) pay = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, y)) + pxa = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, a)) + pya = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](y, a)) pxy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, y)) pxx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x, x)) px = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](x)) @@ -260,6 +266,8 @@ func initTestVariable() { pafy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fy)) pafx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fx)) pfac = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fa, c)) + pgxb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gx, b)) + pfxyb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_x_y, b)) pfgaxc = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gax_c)) pfgxby = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gxb_y)) pfgybz = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_gyb_z)) @@ -310,37 +318,37 @@ func initTestVariable2() { x = AST.MakerMeta("X", -1, AST.TIndividual()) p_typed_pred_Const_A = AST.MakerPred(p_typed_id, - Lib.MkListV(A), + Lib.MkListV[AST.Ty](A), Lib.MkListV[AST.Term](AST.MakerConst(a_typed_id)), ) p_typed_pred_Const_B = AST.MakerPred(p_typed_id, - Lib.MkListV(B), + Lib.MkListV[AST.Ty](B), Lib.MkListV[AST.Term](AST.MakerConst(b_typed_id)), ) p_typed_pred_int_x = AST.MakerPred(p_typed_id, - Lib.MkListV(AST.TInt()), + Lib.MkListV[AST.Ty](AST.TInt()), Lib.MkListV[AST.Term](x), ) p_typed_pred_reel_x = AST.MakerPred(p_typed_id, - Lib.MkListV(AST.TReal()), + Lib.MkListV[AST.Ty](AST.TReal()), Lib.MkListV[AST.Term](x), ) p_typed_pred_rational_x = AST.MakerPred(p_typed_id, - Lib.MkListV(AST.TRat()), + Lib.MkListV[AST.Ty](AST.TRat()), Lib.MkListV[AST.Term](x), ) p_typed_pred_int_3 = AST.MakerPred(p_typed_id, - Lib.MkListV(AST.MkTyConst("int")), + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("3"))), ) p_typed_pred_int_2 = AST.MakerPred(p_typed_id, - Lib.MkListV(AST.TInt()), + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2"))), ) @@ -485,14 +493,11 @@ func TestTermToNode(t *testing.T) { } func TestCreateNodeElement(t *testing.T) { - // --- NOMINAL TESTS --- - // 1. Testing the "string" case t.Run("Nominal_String", func(t *testing.T) { strInput := "test_identifier" node := createNodeElement(strInput) - // Verify it returns a NodeString if ns, ok := node.(NodeString); !ok { t.Errorf("Expected return type NodeString, got %T", node) } else if ns.ToString() != strInput { @@ -500,48 +505,35 @@ func TestCreateNodeElement(t *testing.T) { } }) - // 2. Testing the "AST.Ty" case t.Run("Nominal_AST_Ty", func(t *testing.T) { - // random_type is defined in dt_test.go (e.g., AST.MakerTyBV("random_type")) node := createNodeElement(random_type) - // Verify it returns a TyNode if _, ok := node.(TyNode); !ok { t.Errorf("Expected return type TyNode for AST.Ty input, got %T", node) } }) - // 3. Testing the "AST.Pred" case t.Run("Nominal_AST_Pred", func(t *testing.T) { - // 'pa' is a predicate P(a) defined in dt_test.go node := createNodeElement(pa.(AST.Pred)) - // Verify it returns a TermNode (because predicates are transformed into terms) termNode, ok := node.(TermNode) if !ok { t.Errorf("Expected return type TermNode for AST.Pred input, got %T", node) } - // Verify the underlying transformation occurred (P(a) should now be treated as a Fun) if !termNode.Term.IsFun() { t.Errorf("Expected the transformed AST.Pred to be wrapped as an AST.Fun inside the TermNode") } }) - // 4. Testing the "AST.Term" case t.Run("Nominal_AST_Term", func(t *testing.T) { - // 'fxy' is an AST.Fun (which implements AST.Term) defined in dt_test.go node := createNodeElement(fxy) - // Verify it returns a TermNode directly without issues if _, ok := node.(TermNode); !ok { t.Errorf("Expected return type TermNode for AST.Term input, got %T", node) } }) - // --- FAILING TESTS (EXPECTING EXCEPTIONS) --- - - // 5. Testing an unhandled data type (e.g., an integer) t.Run("Exception_On_Unhandled_Type", func(t *testing.T) { defer func() { if r := recover(); r == nil { @@ -549,11 +541,9 @@ func TestCreateNodeElement(t *testing.T) { } }() - // Passing an int will trigger the 'default' case and cause Glob.Anomaly to panic createNodeElement(42) }) - // 6. Testing a nil input t.Run("Exception_On_Nil", func(t *testing.T) { defer func() { if r := recover(); r == nil { @@ -561,7 +551,6 @@ func TestCreateNodeElement(t *testing.T) { } }() - // Passing nil will trigger the 'default' case createNodeElement(nil) }) } @@ -577,6 +566,7 @@ func TestInsert(t *testing.T) { tree = tree.Insert(pafy.(AST.Pred)) children := tree.getChildren().GetSlice() + if len(children) != 1 { t.Errorf("Expected root node to have exactly 1 child (the 'P' predicate node), got %d", len(children)) } @@ -584,6 +574,7 @@ func TestInsert(t *testing.T) { if pNode.GetArity() != 2 { t.Errorf("Expected the 'P' node to have arity 2, got %d", pNode.GetArity()) } + }) t.Run("Nominal_Single_Nested_Insert", func(t *testing.T) { @@ -662,32 +653,43 @@ func TestPrintHugeTree(t *testing.T) { } pNode := rootChildren[0] - if pNode.GetArity() != 1 { // P + if pNode.GetArity() != 1 { t.Errorf("Expected 'P' node to have arity 1, got %d", pNode.GetArity()) } pChildren := pNode.getChildren().GetSlice() - if len(pChildren) != 1 { // f() - t.Fatalf("Expected 'P' node to have exactly 1 child (the 'f' function), got %d", len(pChildren)) + if len(pChildren) != 1 { + t.Fatalf("Expected 'P' node to have exactly 1 child (the Type node of 'f'), got %d", len(pChildren)) + } + tyFNode := pChildren[0] // [Ty] + + tyFChildren := tyFNode.getChildren().GetSlice() + if len(tyFChildren) != 1 { + t.Fatalf("Expected Type node of 'f' to have exactly 1 child (the 'f' function node), got %d", len(tyFChildren)) } + fNode := tyFChildren[0] // [Term] f - fNode := pChildren[0] - if fNode.GetArity() != 2 { // g() & v1 + if fNode.GetArity() != 2 { // f(arg1, arg2) t.Errorf("Expected 'f' node to have arity 2, got %d", fNode.GetArity()) } fChildren := fNode.getChildren().GetSlice() - if len(fChildren) != 2 { // g() & v1 - t.Fatalf("Expected 'f' node to branch into exactly 2 paths ('g' and a Meta variable), got %d", len(fChildren)) + if len(fChildren) != 1 { + t.Fatalf("Expected 'f' node to have exactly 1 child (the Type node of its first argument), got %d", len(fChildren)) + } + tyArg1FNode := fChildren[0] // [Ty] + + fTermChildren := tyArg1FNode.getChildren().GetSlice() + if len(fTermChildren) != 2 { // g() & v1 + t.Fatalf("Expected Type node under 'f' to branch into exactly 2 paths ('g' and a Meta variable), got %d", len(fTermChildren)) } - // Determine who is g and who is v1 var gNode, metaNode *DiscriminationNode - for i := range fChildren { - if fChildren[i].getSymbol().getSymbol().IsMeta() { - metaNode = &fChildren[i] + for i := range fTermChildren { + if fTermChildren[i].getSymbol().getSymbol().IsMeta() { + metaNode = &fTermChildren[i] } else { - gNode = &fChildren[i] + gNode = &fTermChildren[i] } } @@ -699,12 +701,17 @@ func TestPrintHugeTree(t *testing.T) { t.Errorf("Expected Meta node to have arity 0, got %d", metaNode.GetArity()) } - if metaNode.getChildren().Len() != 2 { - t.Fatalf("v1 must have 2 children, v1 and v2") + if metaNode.getChildren().Len() != 1 { + t.Fatalf("Expected Meta node v1 to have exactly 1 child (the Type node of f's second argument), got %d", metaNode.getChildren().Len()) + } + tyMetaSecArg := metaNode.getChildren().At(0) // [Ty] + + if tyMetaSecArg.getChildren().Len() != 2 { + t.Fatalf("Expected Type node to branch into 2 children (v2 and v1), got %d", tyMetaSecArg.getChildren().Len()) } - meta1Meta2Node := metaNode.getChildren().At(0) - meta1Meta1Node := metaNode.getChildren().At(1) + meta1Meta2Node := tyMetaSecArg.getChildren().At(0) // [Term] v2 + meta1Meta1Node := tyMetaSecArg.getChildren().At(1) // [Term] v1 if meta1Meta2Node.getChildren().Len() != 0 { t.Fatalf("Must have 0 children") @@ -724,33 +731,49 @@ func TestPrintHugeTree(t *testing.T) { t.Errorf("Expected 'g' node to have arity 2, got %d", gNode.GetArity()) } - gChildren := gNode.getChildren().GetSlice() - if len(gChildren) != 2 { - t.Fatalf("Expected 'g' node to branch into exactly 2 paths ('a' and a Meta variable), got %d", len(gChildren)) + if gNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'g' node to have exactly 1 child (the Type node of its first argument), got %d", gNode.getChildren().Len()) } + tyArg1GNode := gNode.getChildren().At(0) // [Ty] - aNode := gChildren[0] - gMetaNode := gChildren[1] + gTermChildren := tyArg1GNode.getChildren().GetSlice() + if len(gTermChildren) != 2 { + t.Fatalf("Expected Type node under 'g' to branch into exactly 2 paths ('a' and a Meta variable), got %d", len(gTermChildren)) + } + + aNode := gTermChildren[0] // [Term] a + gMetaNode := gTermChildren[1] // [Term] v1 if aNode.getSymbol().getSymbol().ToString() != "a" { t.Errorf("Expected first child of 'g' to be 'a', got %s", aNode.getSymbol().getSymbol().ToString()) } - aChildren := aNode.getChildren().GetSlice() - if len(aChildren) != 2 { - t.Fatalf("Expected 'a' node to have exactly 2 children (Meta 'v1' and constant 'b'), got %d", len(aChildren)) + if aNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'a' node to have exactly 1 child (the Type node of g's second argument), got %d", aNode.getChildren().Len()) } + tySecArgGAfterA := aNode.getChildren().At(0) // [Ty] - aMetaNode := aChildren[0] - abNode := aChildren[1] + aTermChildren := tySecArgGAfterA.getChildren().GetSlice() + if len(aTermChildren) != 2 { + t.Fatalf("Expected Type node to have exactly 2 children (Meta 'v1' and constant 'b'), got %d", len(aTermChildren)) + } + + aMetaNode := aTermChildren[0] // [Term] v1 + abNode := aTermChildren[1] // [Term] b if !aMetaNode.getSymbol().getSymbol().IsMeta() { t.Errorf("Expected first child of 'a' to be a Meta variable") } if aMetaNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'v1' under 'a' to have 1 child ('c'), got %d", aMetaNode.getChildren().Len()) + t.Fatalf("Expected 'v1' under 'a' to have 1 child (the Type node of f's second argument), got %d", aMetaNode.getChildren().Len()) } - acNode := aMetaNode.getChildren().At(0) + tySecArgFAfterAMeta := aMetaNode.getChildren().At(0) // [Ty] + + if tySecArgFAfterAMeta.getChildren().Len() != 1 { + t.Fatalf("Expected Type node to have exactly 1 child ('c'), got %d", tySecArgFAfterAMeta.getChildren().Len()) + } + acNode := tySecArgFAfterAMeta.getChildren().At(0) // [Term] c + if acNode.getSymbol().getSymbol().ToString() != "c" { t.Errorf("Expected node to be 'c', got %s", acNode.getSymbol().getSymbol().ToString()) } @@ -762,9 +785,15 @@ func TestPrintHugeTree(t *testing.T) { t.Errorf("Expected second child of 'a' to be 'b', got %s", abNode.getSymbol().getSymbol().ToString()) } if abNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'b' under 'a' to have 1 child ('a'), got %d", abNode.getChildren().Len()) + t.Fatalf("Expected 'b' under 'a' to have 1 child (the Type node of f's second argument), got %d", abNode.getChildren().Len()) + } + tySecArgFAfterAb := abNode.getChildren().At(0) // [Ty] + + if tySecArgFAfterAb.getChildren().Len() != 1 { + t.Fatalf("Expected Type node to have 1 child ('a'), got %d", tySecArgFAfterAb.getChildren().Len()) } - abaNode := abNode.getChildren().At(0) + abaNode := tySecArgFAfterAb.getChildren().At(0) // [Term] a + if abaNode.getSymbol().getSymbol().ToString() != "a" { t.Errorf("Expected leaf node to be 'a', got %s", abaNode.getSymbol().getSymbol().ToString()) } @@ -776,21 +805,32 @@ func TestPrintHugeTree(t *testing.T) { t.Errorf("Expected second child of 'g' to be a Meta variable") } - gMetaChildren := gMetaNode.getChildren().GetSlice() + if gMetaNode.getChildren().Len() != 1 { + t.Fatalf("Expected 'v1' under 'g' to have exactly 1 child (the Type node of g's second argument), got %d", gMetaNode.getChildren().Len()) + } + tySecArgGAfterGMeta := gMetaNode.getChildren().At(0) // [Ty] + + gMetaChildren := tySecArgGAfterGMeta.getChildren().GetSlice() if len(gMetaChildren) != 2 { - t.Fatalf("Expected 'v1' under 'g' to have 2 children ('b' and 'c'), got %d", len(gMetaChildren)) + t.Fatalf("Expected Type node under 'v1' to branch into exactly 2 paths ('b' and 'c'), got %d", len(gMetaChildren)) } - gbNode := gMetaChildren[0] - gcNode := gMetaChildren[1] + gbNode := gMetaChildren[0] // [Term] b + gcNode := gMetaChildren[1] // [Term] c if gbNode.getSymbol().getSymbol().ToString() != "b" { t.Errorf("Expected first child of 'v1' under 'g' to be 'b', got %s", gbNode.getSymbol().getSymbol().ToString()) } if gbNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'b' under 'v1' to have 1 child (Meta 'v2'), got %d", gbNode.getChildren().Len()) + t.Fatalf("Expected 'b' under 'v1' to have 1 child (the Type node of f's second argument), got %d", gbNode.getChildren().Len()) + } + tySecArgFAfterGb := gbNode.getChildren().At(0) // Nœud [Ty] + + if tySecArgFAfterGb.getChildren().Len() != 1 { + t.Fatalf("Expected Type node to have 1 child (Meta 'v2'), got %d", tySecArgFAfterGb.getChildren().Len()) } - gbMetaNode := gbNode.getChildren().At(0) + gbMetaNode := tySecArgFAfterGb.getChildren().At(0) // [Term] v2 + if !gbMetaNode.getSymbol().getSymbol().IsMeta() { t.Errorf("Expected child of 'b' to be a Meta variable 'v2'") } @@ -802,9 +842,15 @@ func TestPrintHugeTree(t *testing.T) { t.Errorf("Expected second child of 'v1' under 'g' to be 'c', got %s", gcNode.getSymbol().getSymbol().ToString()) } if gcNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'c' under 'v1' to have 1 child ('b'), got %d", gcNode.getChildren().Len()) + t.Fatalf("Expected 'c' under 'v1' to have 1 child (the Type node of f's second argument), got %d", gcNode.getChildren().Len()) + } + tySecArgFAfterGc := gcNode.getChildren().At(0) // [Ty] + + if tySecArgFAfterGc.getChildren().Len() != 1 { + t.Fatalf("Expected Type node to have 1 child ('b'), got %d", tySecArgFAfterGc.getChildren().Len()) } - gcbNode := gcNode.getChildren().At(0) + gcbNode := tySecArgFAfterGc.getChildren().At(0) // [Term] b + if gcbNode.getSymbol().getSymbol().ToString() != "b" { t.Errorf("Expected leaf node to be 'b', got %s", gcbNode.getSymbol().getSymbol().ToString()) } @@ -817,24 +863,29 @@ func TestPrintHugeTree(t *testing.T) { func TestParseTerm(t *testing.T) { t.Run("Parse_Simple_fxy", func(t *testing.T) { + tmpContext := NewContext() seqList := parseTerm(fxy, tmpContext) seq := seqList.GetSlice() - if len(seq) != 3 { - t.Fatalf("Expected 3 elements for f(x,y), got %d", len(seq)) + for _, elem := range seq { + fmt.Println(elem.ToString()) } - if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { - t.Fatal("Element 0 must be function 'f' with arity 2") + if len(seq) != 6 { + t.Fatalf("Expected 6 elements for f(x,y), got %d", len(seq)) } - if seq[1].GetArity() != 0 || !seq[1].getSymbol().IsMeta() { - t.Fatal("Element 1 must be meta variable 'v1' with arity 0") + if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { + t.Fatal("Element 1 must be function 'f' with arity 2") } - if seq[2].GetArity() != 0 || !seq[2].getSymbol().IsMeta() { - t.Fatal("Element 2 must be meta variable 'v2' with arity 0") + if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { + t.Fatal("Element 3 must be meta variable 'v1' with arity 0") + } + + if seq[5].GetArity() != 0 || !seq[5].getSymbol().IsMeta() { + t.Fatal("Element 5 must be meta variable 'v2' with arity 0") } }) t.Run("Parse_Nested_Left_f_fxy_z", func(t *testing.T) { @@ -842,28 +893,28 @@ func TestParseTerm(t *testing.T) { seqList := parseTerm(f_fxy_z, tmpContext2) seq := seqList.GetSlice() - if len(seq) != 5 { - t.Fatalf("Expected 5 elements for f(f(x,y), z), got %d", len(seq)) + if len(seq) != 10 { + t.Fatalf("Expected 10 elements for f(f(x,y), z), got %d", len(seq)) } - if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { - t.Fatal("Element 0 must be the outer function 'f' with arity 2") + if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { + t.Fatal("Element 1 must be the outer function 'f' with arity 2") } - if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { - t.Fatal("Element 1 must be the inner function 'f' with arity 2") + if seq[3].GetArity() != 2 || seq[3].getSymbol().ToString() != "f" { + t.Fatal("Element 3 must be the inner function 'f' with arity 2") } - if seq[2].GetArity() != 0 || !seq[2].getSymbol().IsMeta() { - t.Fatal("Element 2 must be meta variable 'v1' with arity 0") + if seq[5].GetArity() != 0 || !seq[5].getSymbol().IsMeta() { + t.Fatal("Element 5 must be meta variable 'v1' with arity 0") } - if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { - t.Fatal("Element 3 must be meta variable 'v2' with arity 0") + if seq[7].GetArity() != 0 || !seq[7].getSymbol().IsMeta() { + t.Fatal("Element 7 must be meta variable 'v2' with arity 0") } - if seq[4].GetArity() != 0 || !seq[4].getSymbol().IsMeta() { - t.Fatal("Element 4 must be meta variable 'v3' with arity 0") + if seq[9].GetArity() != 0 || !seq[9].getSymbol().IsMeta() { + t.Fatal("Element 9 must be meta variable 'v3' with arity 0") } }) @@ -872,28 +923,28 @@ func TestParseTerm(t *testing.T) { seqList := parseTerm(f_x_fyz, tmpContext4) seq := seqList.GetSlice() - if len(seq) != 5 { - t.Fatalf("Expected 5 elements for f(x, f(y,z)), got %d", len(seq)) + if len(seq) != 10 { + t.Fatalf("Expected 10 elements for f(x, f(y,z)), got %d", len(seq)) } - if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { - t.Fatal("Element 0 must be outer function 'f' with arity 2") + if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { + t.Fatal("Element 1 must be outer function 'f' with arity 2") } - if seq[1].GetArity() != 0 || !seq[1].getSymbol().IsMeta() { - t.Fatal("Element 1 must be meta variable 'v1' with arity 0") + if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { + t.Fatal("Element 3 must be meta variable 'v1' with arity 0") } - if seq[2].GetArity() != 2 || seq[2].getSymbol().ToString() != "f" { - t.Fatal("Element 2 must be inner function 'f' with arity 2") + if seq[5].GetArity() != 2 || seq[5].getSymbol().ToString() != "f" { + t.Fatal("Element 5 must be inner function 'f' with arity 2") } - if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { - t.Fatal("Element 3 must be meta variable 'v2' with arity 0") + if seq[7].GetArity() != 0 || !seq[7].getSymbol().IsMeta() { + t.Fatal("Element 7 must be meta variable 'v2' with arity 0") } - if seq[4].GetArity() != 0 || !seq[4].getSymbol().IsMeta() { - t.Fatal("Element 4 must be meta variable 'v3' with arity 0") + if seq[9].GetArity() != 0 || !seq[9].getSymbol().IsMeta() { + t.Fatal("Element 9 must be meta variable 'v3' with arity 0") } }) } @@ -906,6 +957,7 @@ func TestRetrieve(t *testing.T) { tree = tree.Insert(pax.(AST.Pred)) tree = tree.Insert(pay.(AST.Pred)) results := tree.RetrieveUnifiables(pab) + if len(results) != 2 { t.Fatalf("Must return 2 element") } else { @@ -918,15 +970,12 @@ func TestRetrieve(t *testing.T) { fmt.Println() tree2 := NewNode() - tree2 = tree2.Insert(pxy.(AST.Pred)) - results2 := tree2.RetrieveUnifiables(pab) + tree2 = tree2.Insert(pax.(AST.Pred)) + results2 := tree2.RetrieveUnifiables(pay) + if len(results2) != 1 { t.Fatalf("Supposed to have 1 CandidatResults") } - resultat3 := ToSingleElement(results2) - if len(resultat3.GetSubs()) != 2 { - t.Fatalf("Supposed to have 2 unifiables") - } }) @@ -981,126 +1030,150 @@ func TestEquals(t *testing.T) { } func TestGetSubTermLength(t *testing.T) { - Context := NewContext() - t.Run("SubTerm_ggx", func(t *testing.T) { + t.Run("SubTerm_Constant_Or_Meta", func(t *testing.T) { + seq := parseTerm(gx, Context).GetSlice() + // seq == [Ty_gx, g, Ty_x, x] -> seq[3:] == [x] + metaSeq := seq[3:] - seq := parseTerm(ggx, Context).GetSlice() - var1 := (GetSubTermLength(seq)) - if var1 != 3 { - t.Fatalf("Error SubTerLength with 2functions & 1Meta ") - } - }) - Context.Reset() - - t.Run("SubTerm_fxy", func(t *testing.T) { - seq2 := parseTerm(fxy, Context).GetSlice() - var2 := (GetSubTermLength(seq2)) - if var2 != 3 { - t.Fatalf("Error SubTerLength with 1function & 2Meta") + length := GetSubTermLength(metaSeq) + if length != 1 { + t.Fatalf("Expected length 1 for a single Meta/Constant, got %d", length) } Context.Reset() }) t.Run("SubTerm_gx", func(t *testing.T) { + // g(x) -> [g, Ty_x, x] + seq := parseTerm(gx, Context).GetSlice() + termSeq := seq[1:] // Remove [Ty_gx] - seq3 := parseTerm(gx, Context).GetSlice() - var3 := (GetSubTermLength(seq3)) - if var3 != 2 { - t.Fatalf("Error SubTerLength with 1function & 1Meta") + length := GetSubTermLength(termSeq) + if length != 3 { + t.Fatalf("Error SubTermLength with 1 function & 1 Meta. Expected 3, got %d", length) } Context.Reset() }) - t.Run("SubTerm_ga", func(t *testing.T) { + t.Run("SubTerm_ggx", func(t *testing.T) { + // g(g(x)) -> [g, Ty_gx, g, Ty_x, x] + seq := parseTerm(ggx, Context).GetSlice() + termSeq := seq[1:] - seq4 := parseTerm(ga, Context).GetSlice() - var4 := (GetSubTermLength(seq4)) - if var4 != 2 { - t.Fatalf("Error SubTerLength with 1function & 1cst") + length := GetSubTermLength(termSeq) + if length != 5 { + t.Fatalf("Error SubTermLength with 2 nested functions. Expected 5, got %d", length) } Context.Reset() }) t.Run("SubTerm_gggx", func(t *testing.T) { + // g(g(g(x))) -> [g, Ty_ggx, g, Ty_gx, g, Ty_x, x] + seq := parseTerm(gggx, Context).GetSlice() + termSeq := seq[1:] - seq5 := parseTerm(gggx, Context).GetSlice() - var5 := (GetSubTermLength(seq5)) - if var5 != 4 { - t.Fatalf("Error SubTerLength with 1function & 3Meta") + length := GetSubTermLength(termSeq) + if length != 7 { + t.Fatalf("Error SubTermLength with 3 nested functions. Expected 7, got %d", length) } Context.Reset() }) - t.Run("SubTerm_f_y_y", func(t *testing.T) { + t.Run("SubTerm_fxy", func(t *testing.T) { + // f(x, y) -> [f, Ty_x, x, Ty_y, y] + seq := parseTerm(fxy, Context).GetSlice() + termSeq := seq[1:] - seq6 := parseTerm(f_y_y, Context).GetSlice() - var6 := (GetSubTermLength(seq6)) - if var6 != 3 { - t.Fatalf("Error SubTerLength with 1function & 2Meta") + length := GetSubTermLength(termSeq) + if length != 5 { + t.Fatalf("Error SubTermLength with 1 function & 2 Metas. Expected 5, got %d", length) } + Context.Reset() }) + t.Run("SubTerm_With_Trailing_Tokens", func(t *testing.T) { + // P(g(x), a) after consumming P and g(x). + // [g, Ty_x, x, Ty_a, a] + // GetSubTermLength stop after g(x) + seqGx := parseTerm(gx, Context).GetSlice()[1:] // [g, Ty_x, x] + seqB := parseTerm(ga, Context).GetSlice() // [Ty_a, a] + + mixedSeq := append(seqGx, seqB...) // [g, Ty_x, x, Ty_a, a] + + length := GetSubTermLength(mixedSeq) + if length != 3 { + t.Fatalf("GetSubTermLength failed to isolate the first subterm when trailing tokens are present. Expected 3, got %d", length) + } + }) } func TestSkipTreeTermAndContinue(t *testing.T) { - t.Run("SkipTreeTermAndContinue_pab", func(t *testing.T) { + ctx := NewContext() + t.Run("SkipTreeTermAndContinue_pfx", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - needed := 1 - emptyQuery := []SymbolType{} + tree = tree.Insert(pfx.(AST.Pred)) + + fullSeq := parsePred(pfx.(AST.Pred), ctx).GetSlice() + + tree = tree.children.At(0) + tree = tree.children.At(0) + tree = tree.children.At(0) + + needed := 2 + + remainingQuery := fullSeq[5:] emptyEnv := subst.Substitutions{} - results := tree.SkipTreeTermAndContinue(needed, emptyQuery, emptyEnv) + + results := tree.SkipTreeTermAndContinue(needed, remainingQuery, emptyEnv) if len(results) != 1 { - t.Fatalf(" Expected 1 Element, got %d", len(results)) + t.Fatalf("Expected 1 Element, got %d", len(results)) } }) - t.Run("SkipTreeTermAndContinue_px", func(t *testing.T) { + t.Run("SkipTreeTermAndContinue_pfxy", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pfxy.(AST.Pred)) - tree2 := NewNode() - tree2 = tree2.Insert(px.(AST.Pred)) - needed2 := 1 - emptyQuery2 := []SymbolType{} - emptyEnv2 := subst.Substitutions{} - results2 := tree2.SkipTreeTermAndContinue(needed2, emptyQuery2, emptyEnv2) - if len(results2) != 1 { - t.Fatalf(" Expected 1 Element, got %d", len(results2)) - } - }) + fullSeq := parsePred(pfxy.(AST.Pred), ctx).GetSlice() - t.Run("SkipTreeTermAndContinue_pab_pba", func(t *testing.T) { + tree = tree.children.At(0) + tree = tree.children.At(0) + tree = tree.children.At(0) - tree3 := NewNode() - tree3 = tree3.Insert(pba.(AST.Pred)) - tree3 = tree3.Insert(pab.(AST.Pred)) - needed3 := 1 - emptyQuery3 := []SymbolType{} - emptyEnv3 := subst.Substitutions{} - results3 := tree3.SkipTreeTermAndContinue(needed3, emptyQuery3, emptyEnv3) - if len(results3) != 2 { - t.Fatalf(" Expected 2 Element, got %d", len(results3)) + needed := 4 + + remainingQuery := fullSeq[7:] + emptyEnv := subst.Substitutions{} + + results := tree.SkipTreeTermAndContinue(needed, remainingQuery, emptyEnv) + if len(results) != 1 { + t.Fatalf("Expected 1 Element, got %d", len(results)) } }) - t.Run("SkipTreeTermAndContinue_pab_pab_pca", func(t *testing.T) { + t.Run("SkipTreeTermAndContinue_pggab", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) + + fullSeq := parsePred(pggab.(AST.Pred), ctx).GetSlice() + + tree = tree.children.At(0) + tree = tree.children.At(0) + tree = tree.children.At(0) + + needed := 2 - tree4 := NewNode() - tree4 = tree4.Insert(pba.(AST.Pred)) - tree4 = tree4.Insert(pab.(AST.Pred)) - tree4 = tree4.Insert(pca.(AST.Pred)) - needed4 := 1 - emptyQuery4 := []SymbolType{} - emptyEnv4 := subst.Substitutions{} - results4 := tree4.SkipTreeTermAndContinue(needed4, emptyQuery4, emptyEnv4) - if len(results4) != 3 { - t.Fatalf(" Expected 3 Element, got %d", len(results4)) + remainingQuery := fullSeq[7:] + emptyEnv := subst.Substitutions{} + + results := tree.SkipTreeTermAndContinue(needed, remainingQuery, emptyEnv) + if len(results) != 1 { + t.Fatalf("Test imbriqué pggab a échoué. Attendu 1 élément, obtenu %d", len(results)) } }) - } func TestRetrieveUnifiables(t *testing.T) { @@ -1150,6 +1223,18 @@ func TestRetrieveUnifiables(t *testing.T) { }) + t.Run("TestRetrieveUnifiables_pxy", func(t *testing.T) { + + tree2 := NewNode() + tree2 = tree2.Insert(pxy.(AST.Pred)) + candidat2 := tree2.RetrieveUnifiables(pab) + if len(candidat2) == 0 { + t.Fatalf("pxy and pab are unifiables") + } + + fmt.Println(len(candidat2)) + + }) } func TestCopy(t *testing.T) { @@ -1194,8 +1279,8 @@ func TestUnify(t *testing.T) { if len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) } - if mix[0].GetForm().ToString() != "P(a, Y)" { - t.Errorf("Expected unified form to be 'P(a, Y)', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(a, X)" { + t.Errorf("Expected unified form to be 'P(a, X)', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1211,8 +1296,8 @@ func TestUnify(t *testing.T) { if len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) } - if mix[0].GetForm().ToString() != "P(a, b)" { - t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(a, X)" { + t.Errorf("Expected unified form to be 'P(a, X)', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1242,8 +1327,8 @@ func TestUnify(t *testing.T) { if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } - if mix[0].GetForm().ToString() != "P(a, f(Y))" { - t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(a, X)" { + t.Errorf("Expected unified form to be 'P(a, X)', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1256,8 +1341,8 @@ func TestUnify(t *testing.T) { if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } - if mix[0].GetForm().ToString() != "P(a, f(Y))" { - t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(a, f(X))" { + t.Errorf("Expected unified form to be 'P(a, f(X))', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1270,8 +1355,8 @@ func TestUnify(t *testing.T) { if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } - if mix[0].GetForm().ToString() != "P(Y)" { - t.Errorf("Expected unified form to be 'P(Y)', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(X)" { + t.Errorf("Expected unified form to be 'P(X)', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1281,11 +1366,13 @@ func TestUnify(t *testing.T) { found, mix := tree.Unify(pab) + fmt.Println(len(mix)) + if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } - if mix[0].GetForm().ToString() != "P(a, b)" { - t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(X, Y)" { + t.Errorf("Expected unified form to be 'P(X, Y)', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1304,8 +1391,14 @@ func TestUnify(t *testing.T) { // Unification wraps the input formula for i, elem := range mix { - if elem.GetForm().ToString() != "P(Y)" { - t.Errorf("Expected unified form %d to be 'P(Y)', got '%s'", i, elem.GetForm().ToString()) + if elem.GetForm().ToString() == "P(b)" { + continue + } else if elem.GetForm().ToString() == "P(a)" { + continue + } else if elem.GetForm().ToString() == "P(f(X))" { + continue + } else { + t.Errorf("Expected unified form %d to be 'P(b) or P(a) or P(f(X))', got '%s'", i, elem.GetForm().ToString()) } } }) @@ -1314,13 +1407,14 @@ func TestUnify(t *testing.T) { tree := NewNode() tree = tree.Insert(pggab.(AST.Pred)) + tree.Print() found, mix := tree.Unify(pxy) if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } - if mix[0].GetForm().ToString() != "P(X, Y)" { - t.Errorf("Expected unified form to be 'P(X, Y)', got '%s'", mix[0].GetForm().ToString()) + if mix[0].GetForm().ToString() != "P(g(g(a)), b)" { + t.Errorf("Expected unified form to be 'P(g(g(a)), b)', got '%s'", mix[0].GetForm().ToString()) } }) @@ -1387,8 +1481,20 @@ func TestUnifyTerm(t *testing.T) { tree = tree.Insert(pax.(AST.Pred)) queryTerm := subst.TransformPred(pay.(AST.Pred)) + fmt.Println("Test1") + for _, elem := range queryTerm.GetSubTerms().GetSlice() { + fmt.Println("QueryTerm Element", elem.ToString()) + } + fmt.Println("Test2") + val, mix := tree.UnifyTerm(queryTerm) + fmt.Println(val) + + for _, elem := range mix { + fmt.Println("elem", elem.ToString()) + } + if !val { t.Fatalf("Unification failed, expected success") } @@ -1586,6 +1692,7 @@ func TestUnifyTerm(t *testing.T) { func TestMakeDataStruct(t *testing.T) { t.Run("MakeDataStruct_Positive_Tree", func(t *testing.T) { + tree1 := NewNode() formulas1 := Lib.NewList[AST.Form]() formulas1.Append(pab) // + => Inserted @@ -1602,8 +1709,6 @@ func TestMakeDataStruct(t *testing.T) { } } - actualTree1.Print() - rootChildren := actualTree1.getChildren().GetSlice() if len(rootChildren) != 1 { t.Fatalf("Expected exactly 1 predicate root node ('P'), got %d", len(rootChildren)) @@ -1615,11 +1720,14 @@ func TestMakeDataStruct(t *testing.T) { } pChildren := pNode.getChildren().GetSlice() - if len(pChildren) != 2 { + pChildren2 := pChildren[0] + pChildren3 := pChildren2.getChildren().GetSlice() + + if len(pChildren3) != 2 { t.Fatalf("Expected 'P' to have exactly 2 children ('a' and 'b') from positive formulas, got %d", len(pChildren)) } - for _, child := range pChildren { + for _, child := range pChildren3 { symStr := child.getSymbol().getSymbol().ToString() if symStr == "c" { t.Errorf("Negative formula 'not_pac' was incorrectly inserted into the positive tree") @@ -1659,6 +1767,7 @@ func TestMakeDataStruct(t *testing.T) { } }) } + func TestUnify2(t *testing.T) { t.Run("Unify2_pax_with_pay", func(t *testing.T) { @@ -1667,6 +1776,10 @@ func TestUnify2(t *testing.T) { found, mix := tree.Unify2(pay) + for _, elem := range mix { + fmt.Println("elem de mix", elem.ToString()) + } + if !found { t.Fatalf("Unification failed, expected success") } @@ -1718,6 +1831,11 @@ func TestUnify2(t *testing.T) { tree = tree.Insert(pax.(AST.Pred)) found, mix := tree.Unify2(pafy) + for _, elem := range mix { + fmt.Println("elem", elem.ToString()) + } + fmt.Println("len(elem)", len(mix)) + if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } @@ -1732,6 +1850,9 @@ func TestUnify2(t *testing.T) { tree = tree.Insert(pafx.(AST.Pred)) found, mix := tree.Unify2(pafy) + tree.Print() + fmt.Println("pafy", pafy.ToString()) + if !found || len(mix) != 1 { t.Fatalf("Expected exactly 1 unification result") } @@ -1864,6 +1985,9 @@ func TestUnifyTerm2(t *testing.T) { tree = tree.Insert(pax.(AST.Pred)) queryTerm := subst.TransformPred(pay.(AST.Pred)) + tree.Print() + fmt.Println(queryTerm.ToString()) + val, mix := tree.UnifyTerm2(queryTerm) if !val { @@ -2072,7 +2196,7 @@ func TestUnifyTerm2(t *testing.T) { }) } -func TestContextStange(t *testing.T) { +func TestTrickyProblem(t *testing.T) { t.Run("Exception_Occur_Check_Cyclic", func(t *testing.T) { tree := NewNode() @@ -2113,3 +2237,38 @@ func TestContextStange(t *testing.T) { }) } + +func TestInsertCustomType(t *testing.T) { + + tree := NewNode() + + La := p_typed_pred_int_3.GetTyArgs() + for _, elem := range La.GetSlice() { + fmt.Println("Tyargs : ", elem.ToString()) + } + + LT := p_typed_pred_int_3.GetArgs() + for _, elem := range LT.GetSlice() { + fmt.Println("Args : ", elem.ToString()) + } + + tree = tree.Insert(p_typed_pred_int_3) + + a := p_typed_pred_int_3.GetTyArgs() + for _, elem := range a.GetSlice() { + fmt.Println("elem", elem.ToString()) + } + + tree.Print() + +} + +func TestCustom(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(px.(AST.Pred)) + tree.Print() + +} From cfa9ba093b395f3c3e4ec37c1834763129353730 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Fri, 12 Jun 2026 04:53:37 +0200 Subject: [PATCH 17/23] Fix Bug in Test. Unify works again. To Do : Find Why UnifyTerm Bug. Fix all the v2. Try the different tptp test --- .../discrimination-trees.go | 548 +++------- src/Unif/discriminationtree/dt_test.go | 965 ++++++------------ 2 files changed, 424 insertions(+), 1089 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 9d276c69..578ccd98 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -138,27 +138,40 @@ func (ns NodeString) Equals(target NodeElement) bool { } func (tn TermNode) Equals(target NodeElement) bool { + typ, ok := target.(TermNode) if !ok { return false } + var res bool - res := tn.Term.Equals(typ.Term) - if !res { - fmt.Println("--- EQUALS FAILED ---") - fmt.Printf("%s | Type : %T\n", tn.Term.ToString(), tn.Term) - fmt.Printf("%s | Type: %T\n", typ.Term.ToString(), typ.Term) - fmt.Println("---------------------") + if tn.Term != nil && typ.Term != nil { + res = tn.Term.Equals(typ.Term) + } + if tn.Term == nil || typ.Term == nil { + res = false } + // FIX ME : tn.Term.ToString() and typ.Term.ToString() provoc segfault with test tfa_syntax_chk.p and few other + // if !res { + // debug(Lib.MkLazy(func() string { return "--- EQUALS TERM FAILED ---" })) + // debug(Lib.MkLazy(func() string { return fmt.Sprintf("%s | Type: %T", tn.Term.ToString(), tn.Term) })) + // debug(Lib.MkLazy(func() string { return fmt.Sprintf("%s | Type: %T", typ.Term.ToString(), typ.Term) })) + // debug(Lib.MkLazy(func() string { return "--------------------------" })) + // } + return res } func (tn TyNode) Equals(target NodeElement) bool { - typ, ok := target.(TyNode) if !ok { return false } + + if tn.Ty == nil || typ.Ty == nil { + return false + } + return tn.Ty.Equals(typ.Ty) } @@ -180,6 +193,18 @@ func (tn TyNode) GetTy() AST.Ty { } +func (tn TermNode) ToString() string { + if fun, ok := tn.Term.(AST.Fun); ok { + return fun.GetID().ToString() + } + + if tn.Term != nil { + return tn.Term.ToString() + } + + return "nil" +} + func createNodeElement(t any) NodeElement { switch v := t.(type) { @@ -292,13 +317,12 @@ func (dNode DiscriminationNode) getLeafFor() Lib.List[AST.Pred] { } func (dNode DiscriminationNode) toString() string { + sym := dNode.getSymbol().getSymbol() - if dNode.getSymbol().getSymbol() == nil { - Glob.Anomaly("Symbol is Nil", "Symbol is nil") + if sym == nil { return "" } - return dNode.getSymbol().getSymbol().ToString() - + return sym.ToString() } // Struct with a Pred and a associated substitution. Used for Robinson @@ -342,21 +366,52 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { /*********** Parse ***********/ /*****************************/ -func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { +// Predicat Parser. Skip the Predicat Type and Transform it into a Function. +// Then Call parseTerm on the args of the predicat. +func parsePred(p AST.Pred, ctx *NormalizerContext) Lib.List[SymbolType] { + + if p.GetTyArgs().Len() != p.GetArgs().Len() && (p.GetTyArgs().Len() > 1) { + Glob.Anomaly("Ambigious number of types", " Ambigious number of types, don't match the number of args or not one unique types, leading to a ambigious typing for args") + } res := Lib.NewList[SymbolType]() + // Required Overwise the SymbolType of the predicat will be AST.ID and will be compared with a AST.Fun -> Automatic faillure + tmpFun := AST.MakerFun(p.GetID(), Lib.MkListV[AST.Ty](), Lib.MkListV[AST.Term]()) + res.Append(makeSymbolType(createNodeElement(tmpFun), p.GetArgs().Len())) + + // fmt.Println("Len TyArgs", p.GetTyArgs().Len()) + // fmt.Println("Len Args", p.GetArgs().Len()) + + // Add the Type. + for _, elem := range p.GetTyArgs().GetSlice() { + fmt.Println("Add Type", elem.ToString()) + res.Append(makeSymbolTypeTy(createNodeElement(elem))) + } + // Add the element + for _, arg := range p.GetArgs().GetSlice() { + argSeq := parseTerm(arg, ctx).GetSlice() + res.Append(argSeq...) + } - termTy := t.ToMeta().GetTy() - res.Append(makeSymbolTypeTy(createNodeElement(termTy))) + return res +} +func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { + + res := Lib.NewList[SymbolType]() switch term := t.(type) { - // if term is a function or cst, add and call his args + // if term is a function or cst, add it and call his args case AST.Fun: funSansArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) first_element := makeSymbolType(createNodeElement(funSansArgs), term.GetArgs().Len()) + // first_element := makeSymbolType(createNodeElement(term.GetID()), term.GetArgs().Len()) -> passer par ID ? res.Append(first_element) + + // for _, ty := range term.GetTyArgs().GetSlice() { + // res.Append(parseTerm(ty, ctx).GetSlice()...) + // } for _, arg := range term.GetArgs().GetSlice() { res.Append(parseTerm(arg, ctx).GetSlice()...) } @@ -448,23 +503,6 @@ func TermToNode(t AST.Term) DiscriminationNode { /*********** Insrt ***********/ /*****************************/ -// Predicat Parser. Skip the Predicat Type and Transform it into a Function. -// Then Call parseTerm on the args of the predicat. -func parsePred(p AST.Pred, ctx *NormalizerContext) Lib.List[SymbolType] { - - res := Lib.NewList[SymbolType]() - - // Required Overwise the SymbolType of the predicat will be AST.ID and will be compared with a AST.Fun -> Automatic faillure - tmpFun := AST.MakerFun(p.GetID(), Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) - res.Append(makeSymbolType(createNodeElement(tmpFun), p.GetArgs().Len())) - - for _, arg := range p.GetArgs().GetSlice() { - res.Append(parseTerm(arg, ctx).GetSlice()...) - } - - return res -} - // Insert a AST.Pred in the tree. If using a AST.Form or Something Else, it have to be cast when inserting ( tree = tree.Insert(px.(AST.pred)) ) // Call the parser then the auxiliary function func (dNode DiscriminationNode) Insert(p AST.Pred) DiscriminationNode { @@ -546,10 +584,6 @@ func GetSubTermLength(seq []SymbolType) int { sym := seq[index] arite := sym.GetArity() - if arite > 0 { - arite = arite * 2 - } - needed = needed - 1 + arite // If Arity == 0 ( Meta ) end this loop, else add the arity of the form/func/... index++ } @@ -566,13 +600,7 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue } for _, child := range dNode.getChildren().GetSlice() { - - arite := child.GetArity() - if arite > 0 { - arite = arite * 2 // Each Type + Term - } - - newNeeded := needed - 1 + arite // 0 if Meta, Else Arity of the Term + newNeeded := needed - 1 + child.GetArity() // 0 if Meta, Else Arity of the Term matches := child.SkipTreeTermAndContinue(newNeeded, remainingQuery, substitutions) subs = append(subs, matches...) } @@ -632,9 +660,30 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri childSym := child.getSymbol() // child is meta or cst isExactMatch := child.symbol.Equals(symQuery) + + fmt.Println("symQuery", symQuery.ToString()) + fmt.Println("Symbol", child.symbol.ToString()) + fmt.Println("isExactMatch", isExactMatch) + if isExactMatch { // Exact Match - matches := child.retrieveRec(seq[1:], currentEnv) // Exact Match -> Search next element - ch <- matches + var mergedSub = currentEnv + + // Si c'est un match exact mais que c'est une Meta (ex: v1 == v1), + // on DOIT enregistrer la substitution pour ne pas la perdre. + if symQuery.getSymbol().IsMeta() { + currentSub := subst.MakeSubstitution(childSym.getTerm().ToMeta(), symQuery.getTerm()) + if len(currentEnv) == 0 { + mergedSub = subst.Substitutions{currentSub} + } else { + mergedSub, _ = subst.MergeSubstitutions(currentEnv, subst.Substitutions{currentSub}) + } + } + + // On continue avec l'environnement potentiellement mis à jour + if !mergedSub.Equals(subst.Failure()) { + matches := child.retrieveRec(seq[1:], mergedSub) + ch <- matches + } } // Case the child is a AST.Meta @@ -651,7 +700,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri if skip == 1 { currentSub := subst.MakeSubstitution(childSym.getTerm().ToMeta(), symQuery.getTerm()) tmp3 := subst.Substitutions{currentSub} - // Ok Commat Idoms doesn't works because ?????????????????????????????? + // Ok Commat Idoms doesn't works because ?? if len(currentEnv) == 0 { mergedSub = tmp3 } else { @@ -692,13 +741,7 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri } if !mergedSub.Equals(subst.Failure()) { - - tokensToSkip := child.GetArity() - if tokensToSkip > 0 { - tokensToSkip = tokensToSkip * 2 - } - - childResults := child.SkipTreeTermAndContinue(tokensToSkip, seq[1:], mergedSub) + childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) ch <- childResults } @@ -824,11 +867,29 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe } queryTerm := subst.TransformPred(predFormula) // For Robinson + fmt.Println("Len Candidates", len(candidates)) + for _, possibleMatch := range candidates { initialSubst := subst.Substitutions{} possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term for Robinson finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson + //fmt.Printf("DEBUG Tree Node Structure: %s, Args: %d\n", possibleMatchTerm.ToString(), possibleMatchTerm.GetSubTerms().Len()) + // fmt.Printf("DEBUG Tree Node Structure: %s, Args: %d\n", queryTerm.ToString(), queryTerm.GetSubTerms().Len()) + + for _, elem := range possibleMatchTerm.GetSubTerms().GetSlice() { + fmt.Println("Element t1 : ", elem.ToString()) + } + + // for _, elem := range queryTerm.GetSubTerms().GetSlice() { + // fmt.Println("Element t2 : ", elem.ToString()) + // } + fmt.Println("possibleMatchTerm", possibleMatchTerm.ToString()) + fmt.Println("possibleMatchTerm LEN", possibleMatchTerm.GetSubTerms().Len()) + fmt.Println("queryTerm", queryTerm.ToString()) + fmt.Println("queryTerm LEN ", queryTerm.GetSubTerms().Len()) + fmt.Println("InitialSubst", initialSubst.ToString()) + if finalSubst.Equals(subst.Failure()) { fmt.Println("-------------------------") fmt.Println("Substitution FAILURE") @@ -839,6 +900,7 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return } } + return found, mixed } @@ -850,7 +912,6 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix seq := parseTerm(inputTerm, tmpContext).GetSlice() - // Remove the Initial $i var seq2 []SymbolType for i, elem := range seq { if i > 0 { @@ -859,6 +920,9 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix } candidates := dNode.retrieveRec(seq2, subst.MakeEmptySubstitution()) + + fmt.Println("Len Candidates", len(candidates)) + for _, possibleMatch := range candidates { candidateTerm := subst.TransformPred(possibleMatch.getPred()) @@ -873,6 +937,7 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix } mixed = append(mixed, mixMatch.ToMixedTerm()) } else { + fmt.Println("-------------------------") fmt.Println("Substitution FAILURE") fmt.Println("-------------------------") @@ -880,378 +945,3 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix } return found, mixed } - -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// -/////////////////////////////////////////////// - -func parsePred2(p AST.Pred) Lib.List[SymbolType] { - - res := Lib.NewList[SymbolType]() - - // Required Overwise the SymbolType of the predicat will be AST.ID and will be compared with a AST.Fun -> Automatic faillure - tmpFun := AST.MakerFun(p.GetID(), Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) - res.Append(makeSymbolType(createNodeElement(tmpFun), p.GetArgs().Len())) - - fmt.Println("fun tmp (paramtre)", tmpFun.IsFun()) - - for _, arg := range p.GetArgs().GetSlice() { - res.Append(parseTerm2(arg).GetSlice()...) - } - - return res -} - -func parseTerm2(t AST.Term) Lib.List[SymbolType] { - - res := Lib.NewList[SymbolType]() - - termTy := t.ToMeta().GetTy() - res.Append(makeSymbolTypeTy(createNodeElement(termTy))) - - switch term := t.(type) { - - // if term is a function or cst, add and call his args - case AST.Fun: - - funSansArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) - first_element := makeSymbolType(createNodeElement(funSansArgs), term.GetArgs().Len()) - res.Append(first_element) - for _, arg := range term.GetArgs().GetSlice() { - res.Append(parseTerm2(arg).GetSlice()...) - } - - // Case meta, we have to transform it - case AST.Meta: - - res.Append(makeSymbolType(createNodeElement(term), 0)) // Add the new Meta to the return slice - - case AST.Id: - - funSansArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) - first_element := makeSymbolType(createNodeElement(funSansArgs), 0) - res.Append(first_element) - - default: - Glob.Anomaly("Error with %s in ParseTerm2", term.GetName()) - } - return res - -} - -func (dNode DiscriminationNode) SkipTreeTermAndContinue2(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []CandidatResult { - var subs []CandidatResult - - // End of recursion - if needed == 0 { - return dNode.retrieveRec2(remainingQuery, substitutions) - } - - for _, child := range dNode.getChildren().GetSlice() { - arite := child.GetArity() - if arite > 0 { - arite = arite * 2 - } - - newNeeded := needed - 1 + arite - matches := child.SkipTreeTermAndContinue2(newNeeded, remainingQuery, substitutions) - subs = append(subs, matches...) - } - return subs -} - -func (dNode DiscriminationNode) Unify2(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { - - dNode.Print() - fmt.Println("inputFOrmula", inputFormula.ToString()) - - candidates := dNode.RetrieveUnifiables2(inputFormula) - var mixed []subst.MixedSubstitutions - var found bool - - fmt.Println("Len Candidates", len(candidates)) - - queryPred, isQueryPred := inputFormula.(AST.Pred) - if !isQueryPred { - return false, mixed - } - - for _, possibleMatch := range candidates { - - fmt.Println("Execution Candidates") - - candPred := possibleMatch.getPred() - - if !queryPred.GetID().Equals(candPred.GetID()) { - continue - } - - argsQuery := queryPred.GetArgs().GetSlice() - argsCand := candPred.GetArgs().GetSlice() - if len(argsQuery) != len(argsCand) { - continue - } - - currentEnv := subst.Substitutions{} - isUnifiable := true - - for i := 0; i < len(argsQuery); i++ { - currentEnv = subst.AddUnification(argsQuery[i], argsCand[i], currentEnv) - if currentEnv.Equals(subst.Failure()) { - isUnifiable = false - break - } - } - - if isUnifiable { - found = true - matching := subst.MakeMatchingSubstitutions(inputFormula, currentEnv) - mixed = append(mixed, matching.ToMixed()) - } - } - - return found, mixed -} - -func (dNode DiscriminationNode) UnifyTerm2(inputTerm AST.Term) (bool, []subst.MixedTermSubstitutions) { - var mixed []subst.MixedTermSubstitutions - var found bool - - seq := parseTerm2(inputTerm).GetSlice() - - if len(seq) > 0 { - if _, isTy := seq[0].getSymbol().(TyNode); isTy { - seq = seq[1:] - } - } - - for _, elem := range seq { - fmt.Println("elem : ", elem.ToString()) - fmt.Println("Type : ", elem.getSymbol().GetTy().ToString()) - } - - candidates := dNode.retrieveRec2(seq, subst.MakeEmptySubstitution()) - - for _, possibleMatch := range candidates { - - fmt.Println("Candidates", possibleMatch.toString()) - - currentSubst := possibleMatch.GetSubs() - candidateTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term - - finalSubst := subst.AddUnification(inputTerm, candidateTerm, currentSubst) - - if !finalSubst.Equals(subst.Failure()) { - found = true - mixMatch := subst.MixMatchSubstitutions{ - Tof: Lib.MkLeft[AST.Term, AST.Form](inputTerm), - Subst: finalSubst, - } - mixed = append(mixed, mixMatch.ToMixedTerm()) - } - } - return found, mixed -} - -// Take a Sequence of SymbolType and return the first AST.Term + the remaining sequence -func ReconstructTerm(seq []SymbolType) (AST.Term, []SymbolType) { - - if len(seq) == 0 { - return nil, seq - } - index := 0 - - if _, ok := seq[index].getSymbol().(TyNode); ok { - index++ - } - if index >= len(seq) { - return nil, seq[index:] - } - - head := seq[index] - arite := head.GetArity() - term := head.getTerm() - index++ - - switch t := term.(type) { - - case AST.Fun: - - currentSeq := seq[index:] - args := Lib.NewList[AST.Term]() - for i := 0; i < arite; i++ { - var arg AST.Term - arg, currentSeq = ReconstructTerm(currentSeq) - args.Append(arg) - } - return AST.MakerFun(t.GetID(), t.GetTyArgs(), args), currentSeq // Create Fun - - case AST.Meta: - return t, seq[index:] // Go next - case AST.Id: - return AST.MakerFun(t, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()), seq[index:] - default: - return nil, seq[index:] // Error type - } -} - -func (dNode DiscriminationNode) RetrieveUnifiables2(t AST.Form) []CandidatResult { - predFormula, _ := t.(AST.Pred) - - seq := parsePred2(predFormula).GetSlice() - Env := subst.Substitutions{} - - for _, elem := range seq { - fmt.Println("ParsePred2", elem.ToString()) - } - - return dNode.retrieveRec2(seq, Env) -} - -func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { - - ch := make(chan []CandidatResult) - var results []CandidatResult - var wg sync.WaitGroup - - if len(seq) == 0 { // End of recursion - - for _, p := range dNode.getLeafFor().GetSlice() { - results = append(results, MakeCandidat(p, currentEnv)) - } - return results - } - - // Work goroutine - for _, child := range dNode.children.GetSlice() { - wg.Add(1) // Create exactly 1 goroutine - go retrieveCase2(seq, currentEnv, child, ch, &wg) - } - - // Main goroutine waiting until all the goroutine stop - go func() { - wg.Wait() - close(ch) - }() - - for matches := range ch { - results = append(results, matches...) - } - - return results -} - -func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { - - defer wg.Done() - symQuery := seq[0] // First Element - isExactMatch := child.symbol.Equals(symQuery) - if isExactMatch { // Exact Match - matches := child.retrieveRec2(seq[1:], currentEnv) // Exact Match -> Search next element - ch <- matches - } - childSym := child.getSymbol() // child is meta or cst - - // We noticed that the term of the dNode is a Meta - // Meaning that we can skip the current term of the seq ( paramater of this function ) bc it will be unify with the current term - // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 - if childSym.getSymbol().IsMeta() && !isExactMatch { - - fmt.Println("ChildSym Symbol", childSym.getSymbol().ToString()) - - queryTerm, restSeq := ReconstructTerm(seq) - if queryTerm != nil { - fmt.Println("QueryTerm not null") - mergedSub := subst.AddUnification(childSym.getTerm(), queryTerm, currentEnv) // Robinson Call - if !mergedSub.Equals(subst.Failure()) { - fmt.Println("MergeSub reussit") - matches := child.retrieveRec2(restSeq, mergedSub) - - for _, elem := range matches { - fmt.Println("matches", elem.getPred().ToString()) - } - - ch <- matches - } else { - fmt.Println("MergeSub Failure") - } - } - - } else if symQuery.getSymbol().IsMeta() && !isExactMatch { - - fmt.Println("symQuery Symbol", symQuery.getSymbol().ToString()) - - var mergedSub subst.Substitutions - if child.GetArity() == 0 { - - // Case 1: The tree contains a constant (arity 0). - // We have the full term right here, so we can immediately bind the Query's Meta variable - // to this constant and update our substitution environment. - childTerm := childSym.getTerm() - var properTerm AST.Term = childTerm - - // If for ??? reason it's a AST.id, we transform it to AST.Fun ( Tmp? ) - if id, ok := childTerm.(AST.Id); ok { - properTerm = AST.MakerFun(id, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) - } - - currentSub := subst.MakeSubstitution(symQuery.getTerm().ToMeta(), properTerm) - tmp3 := subst.Substitutions{currentSub} - if len(currentEnv) == 0 { - mergedSub = tmp3 - } else { - mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) - } - } else { - // Case 2: The tree contains a function with arity > 0. - // At this node, we only see the function symbol, not its arguments (which live deeper in the tree). - // To avoid sending an incomplete term to Robinson, we DEFER the unification of this Meta variable. - // We pass the current environment as-is, allowing the recursion to consume all the function's - // arguments further down the branch before finally binding the complete structural term. - mergedSub = currentEnv - } - - if !mergedSub.Equals(subst.Failure()) { - tokensToSkip := child.GetArity() - if tokensToSkip > 0 { - tokensToSkip = tokensToSkip * 2 // Type + Term - } - childResults := child.SkipTreeTermAndContinue2(tokensToSkip, seq[1:], mergedSub) - ch <- childResults - } - - } // No recursive call or return -} diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index b89b38f9..224a1abe 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -76,6 +76,7 @@ var c1 AST.Fun var c2 AST.Fun // Fun +var f3 AST.Fun var gx AST.Fun var ga AST.Fun var fx AST.Fun @@ -148,6 +149,7 @@ var pxc AST.Form var pfx AST.Form var pfy AST.Form var pafx AST.Form +var pafb AST.Form var pafy AST.Form var pfac AST.Form var pgxb AST.Form @@ -265,6 +267,7 @@ func initTestVariable() { pfy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fy)) pafy = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fy)) pafx = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fx)) + pafb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](a, fb)) pfac = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](fa, c)) pgxb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](gx, b)) pfxyb = AST.MakerPred(p_id, Lib.NewList[AST.Ty](), Lib.MkListV[AST.Term](f_x_y, b)) @@ -294,9 +297,21 @@ var p_typed_pred_Const_A AST.Pred var p_typed_pred_Const_B AST.Pred var p_typed_pred_int_2 AST.Pred var p_typed_pred_int_3 AST.Pred +var p_typed_pred_int_2_int_3 AST.Pred +var p_typed_pred_int_2_double_4 AST.Pred +var p_typed_pred_int_2_int_3_a AST.Pred +var p_typed_pred_int_2_int_3_b AST.Pred +var p_typed_pred_int_2_double_4_a AST.Pred +var p_typed_pred_int_2_double_4_b AST.Pred +var p_typed_pred_int_2_double_4_x AST.Pred +var p_typed_pred_int_2_f_3 AST.Pred + var p_typed_pred_int_int AST.Pred var p_typed_pred_int_x AST.Pred +var p_typed_pred_int_x_y_z AST.Pred var p_typed_pred_reel_x AST.Pred +var p_typed_pred_reel_x_y_z AST.Pred + var p_typed_pred_rational_x AST.Pred var p_id_typed AST.Id @@ -332,11 +347,21 @@ func initTestVariable2() { Lib.MkListV[AST.Term](x), ) + p_typed_pred_int_x_y_z = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.TInt()), + Lib.MkListV[AST.Term](x, y, z), + ) + p_typed_pred_reel_x = AST.MakerPred(p_typed_id, Lib.MkListV[AST.Ty](AST.TReal()), Lib.MkListV[AST.Term](x), ) + p_typed_pred_reel_x_y_z = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.TReal()), + Lib.MkListV[AST.Term](x, y, z), + ) + p_typed_pred_rational_x = AST.MakerPred(p_typed_id, Lib.MkListV[AST.Ty](AST.TRat()), Lib.MkListV[AST.Term](x), @@ -347,6 +372,41 @@ func initTestVariable2() { Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("3"))), ) + p_typed_pred_int_2_int_3 = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("3"))), + ) + + p_typed_pred_int_2_double_4 = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int"), (AST.MkTyConst("double"))), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("4"))), + ) + + p_typed_pred_int_2_int_3_a = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("3")), a), + ) + + p_typed_pred_int_2_int_3_b = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("3")), b), + ) + + p_typed_pred_int_2_double_4_a = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int"), (AST.MkTyConst("double")), AST.TIndividual()), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("4")), a), + ) + + p_typed_pred_int_2_double_4_b = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int"), (AST.MkTyConst("double")), AST.TIndividual()), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("4")), b), + ) + + p_typed_pred_int_2_double_4_x = AST.MakerPred(p_typed_id, + Lib.MkListV[AST.Ty](AST.MkTyConst("int"), (AST.MkTyConst("double")), AST.TIndividual()), + Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2")), AST.MakerConst(AST.MakerId("4")), x), + ) + p_typed_pred_int_2 = AST.MakerPred(p_typed_id, Lib.MkListV[AST.Ty](AST.MkTyConst("int")), Lib.MkListV[AST.Term](AST.MakerConst(AST.MakerId("2"))), @@ -564,9 +624,9 @@ func TestInsert(t *testing.T) { tree = tree.Insert(pab.(AST.Pred)) tree = tree.Insert(pafx.(AST.Pred)) tree = tree.Insert(pafy.(AST.Pred)) + tree.Print() children := tree.getChildren().GetSlice() - if len(children) != 1 { t.Errorf("Expected root node to have exactly 1 child (the 'P' predicate node), got %d", len(children)) } @@ -659,37 +719,25 @@ func TestPrintHugeTree(t *testing.T) { pChildren := pNode.getChildren().GetSlice() if len(pChildren) != 1 { - t.Fatalf("Expected 'P' node to have exactly 1 child (the Type node of 'f'), got %d", len(pChildren)) + t.Fatalf("Expected 'P' node to have exactly 1 child ('f'), got %d", len(pChildren)) } - tyFNode := pChildren[0] // [Ty] - - tyFChildren := tyFNode.getChildren().GetSlice() - if len(tyFChildren) != 1 { - t.Fatalf("Expected Type node of 'f' to have exactly 1 child (the 'f' function node), got %d", len(tyFChildren)) - } - fNode := tyFChildren[0] // [Term] f + fNode := pChildren[0] // [Term] f if fNode.GetArity() != 2 { // f(arg1, arg2) t.Errorf("Expected 'f' node to have arity 2, got %d", fNode.GetArity()) } fChildren := fNode.getChildren().GetSlice() - if len(fChildren) != 1 { - t.Fatalf("Expected 'f' node to have exactly 1 child (the Type node of its first argument), got %d", len(fChildren)) - } - tyArg1FNode := fChildren[0] // [Ty] - - fTermChildren := tyArg1FNode.getChildren().GetSlice() - if len(fTermChildren) != 2 { // g() & v1 - t.Fatalf("Expected Type node under 'f' to branch into exactly 2 paths ('g' and a Meta variable), got %d", len(fTermChildren)) + if len(fChildren) != 2 { // g & v1 + t.Fatalf("Expected 'f' node to branch into exactly 2 paths ('g' and a Meta variable v1), got %d", len(fChildren)) } var gNode, metaNode *DiscriminationNode - for i := range fTermChildren { - if fTermChildren[i].getSymbol().getSymbol().IsMeta() { - metaNode = &fTermChildren[i] + for i := range fChildren { + if fChildren[i].getSymbol().getSymbol().IsMeta() { + metaNode = &fChildren[i] } else { - gNode = &fTermChildren[i] + gNode = &fChildren[i] } } @@ -701,161 +749,126 @@ func TestPrintHugeTree(t *testing.T) { t.Errorf("Expected Meta node to have arity 0, got %d", metaNode.GetArity()) } - if metaNode.getChildren().Len() != 1 { - t.Fatalf("Expected Meta node v1 to have exactly 1 child (the Type node of f's second argument), got %d", metaNode.getChildren().Len()) + metaChildren := metaNode.getChildren().GetSlice() + if len(metaChildren) != 2 { + t.Fatalf("Expected Meta variable v1 under 'f' to branch into 2 children (v2 and v1), got %d", len(metaChildren)) } - tyMetaSecArg := metaNode.getChildren().At(0) // [Ty] - if tyMetaSecArg.getChildren().Len() != 2 { - t.Fatalf("Expected Type node to branch into 2 children (v2 and v1), got %d", tyMetaSecArg.getChildren().Len()) + var meta1Meta1Node, meta1Meta2Node *DiscriminationNode + for i := range metaChildren { + if metaChildren[i].getSymbol().getSymbol().ToString() == "v1" { // ou selon l'index de ta variable + meta1Meta1Node = &metaChildren[i] + } else { + meta1Meta2Node = &metaChildren[i] + } } - meta1Meta2Node := tyMetaSecArg.getChildren().At(0) // [Term] v2 - meta1Meta1Node := tyMetaSecArg.getChildren().At(1) // [Term] v1 - if meta1Meta2Node.getChildren().Len() != 0 { - t.Fatalf("Must have 0 children") + t.Fatalf("Expected v2 node to be a leaf (0 children), got %d", meta1Meta2Node.getChildren().Len()) } if meta1Meta2Node.getLeafFor().Len() != 2 { - t.Fatalf("Must have 2 Leaf") + t.Errorf("Expected v2 node to contain exactly 2 leaf formulas, got %d", meta1Meta2Node.getLeafFor().Len()) } if meta1Meta1Node.getChildren().Len() != 0 { - t.Fatalf("Must have 0 children") + t.Fatalf("Expected v1 node to be a leaf (0 children), got %d", meta1Meta1Node.getChildren().Len()) } if meta1Meta1Node.getLeafFor().Len() != 2 { - t.Fatalf("Must have 2 Leaf") + t.Errorf("Expected v1 node to contain exactly 2 leaf formulas, got %d", meta1Meta1Node.getLeafFor().Len()) } if gNode.GetArity() != 2 { // g(arg1, arg2) t.Errorf("Expected 'g' node to have arity 2, got %d", gNode.GetArity()) } - if gNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'g' node to have exactly 1 child (the Type node of its first argument), got %d", gNode.getChildren().Len()) + gChildren := gNode.getChildren().GetSlice() + if len(gChildren) != 2 { // a & v1 + t.Fatalf("Expected 'g' node to branch into exactly 2 paths ('a' and a Meta variable v1), got %d", len(gChildren)) } - tyArg1GNode := gNode.getChildren().At(0) // [Ty] - gTermChildren := tyArg1GNode.getChildren().GetSlice() - if len(gTermChildren) != 2 { - t.Fatalf("Expected Type node under 'g' to branch into exactly 2 paths ('a' and a Meta variable), got %d", len(gTermChildren)) + var aNode, gMetaNode *DiscriminationNode + for i := range gChildren { + if gChildren[i].getSymbol().getSymbol().IsMeta() { + gMetaNode = &gChildren[i] + } else if gChildren[i].getSymbol().getSymbol().ToString() == "a" { + aNode = &gChildren[i] + } } - aNode := gTermChildren[0] // [Term] a - gMetaNode := gTermChildren[1] // [Term] v1 - - if aNode.getSymbol().getSymbol().ToString() != "a" { - t.Errorf("Expected first child of 'g' to be 'a', got %s", aNode.getSymbol().getSymbol().ToString()) + if aNode == nil || gMetaNode == nil { + t.Fatalf("Expected to find one 'a' node and one Meta node 'v1' under 'g'") } - if aNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'a' node to have exactly 1 child (the Type node of g's second argument), got %d", aNode.getChildren().Len()) + aChildren := aNode.getChildren().GetSlice() + if len(aChildren) != 2 { // v1 & b + t.Fatalf("Expected 'a' node to branch into exactly 2 paths (Meta 'v1' and constant 'b'), got %d", len(aChildren)) } - tySecArgGAfterA := aNode.getChildren().At(0) // [Ty] - aTermChildren := tySecArgGAfterA.getChildren().GetSlice() - if len(aTermChildren) != 2 { - t.Fatalf("Expected Type node to have exactly 2 children (Meta 'v1' and constant 'b'), got %d", len(aTermChildren)) + var aMetaNode, abNode *DiscriminationNode + for i := range aChildren { + if aChildren[i].getSymbol().getSymbol().IsMeta() { + aMetaNode = &aChildren[i] + } else if aChildren[i].getSymbol().getSymbol().ToString() == "b" { + abNode = &aChildren[i] + } } - aMetaNode := aTermChildren[0] // [Term] v1 - abNode := aTermChildren[1] // [Term] b - - if !aMetaNode.getSymbol().getSymbol().IsMeta() { - t.Errorf("Expected first child of 'a' to be a Meta variable") - } if aMetaNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'v1' under 'a' to have 1 child (the Type node of f's second argument), got %d", aMetaNode.getChildren().Len()) + t.Fatalf("Expected 'v1' under 'a' to have exactly 1 child ('c'), got %d", aMetaNode.getChildren().Len()) } - tySecArgFAfterAMeta := aMetaNode.getChildren().At(0) // [Ty] - - if tySecArgFAfterAMeta.getChildren().Len() != 1 { - t.Fatalf("Expected Type node to have exactly 1 child ('c'), got %d", tySecArgFAfterAMeta.getChildren().Len()) - } - acNode := tySecArgFAfterAMeta.getChildren().At(0) // [Term] c - + acNode := aMetaNode.getChildren().At(0) if acNode.getSymbol().getSymbol().ToString() != "c" { t.Errorf("Expected node to be 'c', got %s", acNode.getSymbol().getSymbol().ToString()) } if acNode.getLeafFor().Len() != 1 { - t.Errorf("Expected 'c' node to have exactly 1 leaf formula (pfgaxc), got %d", acNode.getLeafFor().Len()) + t.Errorf("Expected 'c' node to have exactly 1 leaf formula, got %d", acNode.getLeafFor().Len()) } - if abNode.getSymbol().getSymbol().ToString() != "b" { - t.Errorf("Expected second child of 'a' to be 'b', got %s", abNode.getSymbol().getSymbol().ToString()) - } if abNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'b' under 'a' to have 1 child (the Type node of f's second argument), got %d", abNode.getChildren().Len()) + t.Fatalf("Expected 'b' under 'a' to have exactly 1 child ('a'), got %d", abNode.getChildren().Len()) } - tySecArgFAfterAb := abNode.getChildren().At(0) // [Ty] - - if tySecArgFAfterAb.getChildren().Len() != 1 { - t.Fatalf("Expected Type node to have 1 child ('a'), got %d", tySecArgFAfterAb.getChildren().Len()) - } - abaNode := tySecArgFAfterAb.getChildren().At(0) // [Term] a - + abaNode := abNode.getChildren().At(0) if abaNode.getSymbol().getSymbol().ToString() != "a" { t.Errorf("Expected leaf node to be 'a', got %s", abaNode.getSymbol().getSymbol().ToString()) } if abaNode.getLeafFor().Len() != 1 { - t.Errorf("Expected leaf 'a' node to have exactly 1 leaf formula (pfgaba), got %d", abaNode.getLeafFor().Len()) + t.Errorf("Expected leaf 'a' node to have exactly 1 leaf formula, got %d", abaNode.getLeafFor().Len()) } - if !gMetaNode.getSymbol().getSymbol().IsMeta() { - t.Errorf("Expected second child of 'g' to be a Meta variable") + gMetaChildren := gMetaNode.getChildren().GetSlice() + if len(gMetaChildren) != 2 { // b & c + t.Fatalf("Expected Meta node 'v1' under 'g' to branch into exactly 2 paths ('b' and 'c'), got %d", len(gMetaChildren)) } - if gMetaNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'v1' under 'g' to have exactly 1 child (the Type node of g's second argument), got %d", gMetaNode.getChildren().Len()) - } - tySecArgGAfterGMeta := gMetaNode.getChildren().At(0) // [Ty] - - gMetaChildren := tySecArgGAfterGMeta.getChildren().GetSlice() - if len(gMetaChildren) != 2 { - t.Fatalf("Expected Type node under 'v1' to branch into exactly 2 paths ('b' and 'c'), got %d", len(gMetaChildren)) + var gbNode, gcNode *DiscriminationNode + for i := range gMetaChildren { + if gMetaChildren[i].getSymbol().getSymbol().ToString() == "b" { + gbNode = &gMetaChildren[i] + } else if gMetaChildren[i].getSymbol().getSymbol().ToString() == "c" { + gcNode = &gMetaChildren[i] + } } - gbNode := gMetaChildren[0] // [Term] b - gcNode := gMetaChildren[1] // [Term] c - - if gbNode.getSymbol().getSymbol().ToString() != "b" { - t.Errorf("Expected first child of 'v1' under 'g' to be 'b', got %s", gbNode.getSymbol().getSymbol().ToString()) - } if gbNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'b' under 'v1' to have 1 child (the Type node of f's second argument), got %d", gbNode.getChildren().Len()) + t.Fatalf("Expected 'b' under 'v1' to have exactly 1 child (Meta 'v2'), got %d", gbNode.getChildren().Len()) } - tySecArgFAfterGb := gbNode.getChildren().At(0) // Nœud [Ty] - - if tySecArgFAfterGb.getChildren().Len() != 1 { - t.Fatalf("Expected Type node to have 1 child (Meta 'v2'), got %d", tySecArgFAfterGb.getChildren().Len()) - } - gbMetaNode := tySecArgFAfterGb.getChildren().At(0) // [Term] v2 - + gbMetaNode := gbNode.getChildren().At(0) if !gbMetaNode.getSymbol().getSymbol().IsMeta() { - t.Errorf("Expected child of 'b' to be a Meta variable 'v2'") + t.Errorf("Expected child of 'b' to be a Meta variable") } if gbMetaNode.getLeafFor().Len() != 2 { - t.Errorf("Expected 'v2' node to contain exactly 2 leaf formulas (pfgxby and pfgybz), got %d", gbMetaNode.getLeafFor().Len()) + t.Errorf("Expected Meta leaf node to contain exactly 2 leaf formulas, got %d", gbMetaNode.getLeafFor().Len()) } - if gcNode.getSymbol().getSymbol().ToString() != "c" { - t.Errorf("Expected second child of 'v1' under 'g' to be 'c', got %s", gcNode.getSymbol().getSymbol().ToString()) - } if gcNode.getChildren().Len() != 1 { - t.Fatalf("Expected 'c' under 'v1' to have 1 child (the Type node of f's second argument), got %d", gcNode.getChildren().Len()) + t.Fatalf("Expected 'c' under 'v1' to have exactly 1 child ('b'), got %d", gcNode.getChildren().Len()) } - tySecArgFAfterGc := gcNode.getChildren().At(0) // [Ty] - - if tySecArgFAfterGc.getChildren().Len() != 1 { - t.Fatalf("Expected Type node to have 1 child ('b'), got %d", tySecArgFAfterGc.getChildren().Len()) - } - gcbNode := tySecArgFAfterGc.getChildren().At(0) // [Term] b - + gcbNode := gcNode.getChildren().At(0) if gcbNode.getSymbol().getSymbol().ToString() != "b" { t.Errorf("Expected leaf node to be 'b', got %s", gcbNode.getSymbol().getSymbol().ToString()) } if gcbNode.getLeafFor().Len() != 1 { - t.Errorf("Expected leaf 'b' node to have exactly 1 leaf formula (pfgxcb), got %d", gcbNode.getLeafFor().Len()) + t.Errorf("Expected leaf 'b' node to have exactly 1 leaf formula, got %d", gcbNode.getLeafFor().Len()) } }) } @@ -872,20 +885,20 @@ func TestParseTerm(t *testing.T) { fmt.Println(elem.ToString()) } - if len(seq) != 6 { - t.Fatalf("Expected 6 elements for f(x,y), got %d", len(seq)) + if len(seq) != 3 { + t.Fatalf("Expected 3 elements for f(x,y), got %d", len(seq)) } - if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { - t.Fatal("Element 1 must be function 'f' with arity 2") + if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { + t.Fatal("Element 0 must be function 'f' with arity 2") } - if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { - t.Fatal("Element 3 must be meta variable 'v1' with arity 0") + if seq[1].GetArity() != 0 || !seq[1].getSymbol().IsMeta() { + t.Fatal("Element 1 must be meta variable 'v1' with arity 0") } - if seq[5].GetArity() != 0 || !seq[5].getSymbol().IsMeta() { - t.Fatal("Element 5 must be meta variable 'v2' with arity 0") + if seq[2].GetArity() != 0 || !seq[2].getSymbol().IsMeta() { + t.Fatal("Element 2 must be meta variable 'v2' with arity 0") } }) t.Run("Parse_Nested_Left_f_fxy_z", func(t *testing.T) { @@ -893,28 +906,28 @@ func TestParseTerm(t *testing.T) { seqList := parseTerm(f_fxy_z, tmpContext2) seq := seqList.GetSlice() - if len(seq) != 10 { - t.Fatalf("Expected 10 elements for f(f(x,y), z), got %d", len(seq)) + if len(seq) != 5 { + t.Fatalf("Expected 5 elements for f(f(x,y), z), got %d", len(seq)) } - if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { - t.Fatal("Element 1 must be the outer function 'f' with arity 2") + if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { + t.Fatal("Element 0 must be the outer function 'f' with arity 2") } - if seq[3].GetArity() != 2 || seq[3].getSymbol().ToString() != "f" { - t.Fatal("Element 3 must be the inner function 'f' with arity 2") + if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { + t.Fatal("Element 1 must be the inner function 'f' with arity 2") } - if seq[5].GetArity() != 0 || !seq[5].getSymbol().IsMeta() { - t.Fatal("Element 5 must be meta variable 'v1' with arity 0") + if seq[2].GetArity() != 0 || !seq[2].getSymbol().IsMeta() { + t.Fatal("Element 2 must be meta variable 'v1' with arity 0") } - if seq[7].GetArity() != 0 || !seq[7].getSymbol().IsMeta() { - t.Fatal("Element 7 must be meta variable 'v2' with arity 0") + if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { + t.Fatal("Element 3 must be meta variable 'v2' with arity 0") } - if seq[9].GetArity() != 0 || !seq[9].getSymbol().IsMeta() { - t.Fatal("Element 9 must be meta variable 'v3' with arity 0") + if seq[4].GetArity() != 0 || !seq[4].getSymbol().IsMeta() { + t.Fatal("Element 4 must be meta variable 'v3' with arity 0") } }) @@ -923,28 +936,28 @@ func TestParseTerm(t *testing.T) { seqList := parseTerm(f_x_fyz, tmpContext4) seq := seqList.GetSlice() - if len(seq) != 10 { - t.Fatalf("Expected 10 elements for f(x, f(y,z)), got %d", len(seq)) + if len(seq) != 5 { + t.Fatalf("Expected 5 elements for f(x, f(y,z)), got %d", len(seq)) } - if seq[1].GetArity() != 2 || seq[1].getSymbol().ToString() != "f" { - t.Fatal("Element 1 must be outer function 'f' with arity 2") + if seq[0].GetArity() != 2 || seq[0].getSymbol().ToString() != "f" { + t.Fatal("Element 0 must be outer function 'f' with arity 2") } - if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { - t.Fatal("Element 3 must be meta variable 'v1' with arity 0") + if seq[1].GetArity() != 0 || !seq[1].getSymbol().IsMeta() { + t.Fatal("Element 1 must be meta variable 'v1' with arity 0") } - if seq[5].GetArity() != 2 || seq[5].getSymbol().ToString() != "f" { - t.Fatal("Element 5 must be inner function 'f' with arity 2") + if seq[2].GetArity() != 2 || seq[2].getSymbol().ToString() != "f" { + t.Fatal("Element 2 must be inner function 'f' with arity 2") } - if seq[7].GetArity() != 0 || !seq[7].getSymbol().IsMeta() { - t.Fatal("Element 7 must be meta variable 'v2' with arity 0") + if seq[3].GetArity() != 0 || !seq[3].getSymbol().IsMeta() { + t.Fatal("Element 3 must be meta variable 'v2' with arity 0") } - if seq[9].GetArity() != 0 || !seq[9].getSymbol().IsMeta() { - t.Fatal("Element 9 must be meta variable 'v3' with arity 0") + if seq[4].GetArity() != 0 || !seq[4].getSymbol().IsMeta() { + t.Fatal("Element 4 must be meta variable 'v3' with arity 0") } }) } @@ -1034,60 +1047,58 @@ func TestGetSubTermLength(t *testing.T) { t.Run("SubTerm_Constant_Or_Meta", func(t *testing.T) { seq := parseTerm(gx, Context).GetSlice() - // seq == [Ty_gx, g, Ty_x, x] -> seq[3:] == [x] - metaSeq := seq[3:] + // seq == [g, x] == [x] - length := GetSubTermLength(metaSeq) - if length != 1 { + length := GetSubTermLength(seq) + + for _, elem := range seq { + fmt.Println(elem.ToString()) + } + + if length != 2 { t.Fatalf("Expected length 1 for a single Meta/Constant, got %d", length) } - Context.Reset() }) t.Run("SubTerm_gx", func(t *testing.T) { - // g(x) -> [g, Ty_x, x] + // g(x) -> [g, x] seq := parseTerm(gx, Context).GetSlice() - termSeq := seq[1:] // Remove [Ty_gx] + termSeq := seq[1:] // Remove g() length := GetSubTermLength(termSeq) - if length != 3 { - t.Fatalf("Error SubTermLength with 1 function & 1 Meta. Expected 3, got %d", length) + if length != 1 { + t.Fatalf("Error SubTermLength Expected 1, got %d", length) } Context.Reset() }) t.Run("SubTerm_ggx", func(t *testing.T) { - // g(g(x)) -> [g, Ty_gx, g, Ty_x, x] + // g(g(x)) -> [g, g, x] seq := parseTerm(ggx, Context).GetSlice() - termSeq := seq[1:] - length := GetSubTermLength(termSeq) - if length != 5 { - t.Fatalf("Error SubTermLength with 2 nested functions. Expected 5, got %d", length) + length := GetSubTermLength(seq) + if length != 3 { + t.Fatalf("Error SubTermLength with 2 nested functions. Expected 3, got %d", length) } - Context.Reset() }) t.Run("SubTerm_gggx", func(t *testing.T) { - // g(g(g(x))) -> [g, Ty_ggx, g, Ty_gx, g, Ty_x, x] + // g(g(g(x))) -> [g, g, g, x] seq := parseTerm(gggx, Context).GetSlice() - termSeq := seq[1:] - length := GetSubTermLength(termSeq) - if length != 7 { - t.Fatalf("Error SubTermLength with 3 nested functions. Expected 7, got %d", length) + length := GetSubTermLength(seq) + if length != 4 { + t.Fatalf("Error SubTermLength with 3 nested functions. Expected 4, got %d", length) } - Context.Reset() }) t.Run("SubTerm_fxy", func(t *testing.T) { - // f(x, y) -> [f, Ty_x, x, Ty_y, y] + // f(x, y) -> [f, x, y] seq := parseTerm(fxy, Context).GetSlice() - termSeq := seq[1:] - length := GetSubTermLength(termSeq) - if length != 5 { - t.Fatalf("Error SubTermLength with 1 function & 2 Metas. Expected 5, got %d", length) + length := GetSubTermLength(seq) + if length != 3 { + t.Fatalf("Error SubTermLength with 1 function & 2 Metas. Expected 3, got %d", length) } Context.Reset() }) @@ -1096,13 +1107,10 @@ func TestGetSubTermLength(t *testing.T) { // P(g(x), a) after consumming P and g(x). // [g, Ty_x, x, Ty_a, a] // GetSubTermLength stop after g(x) - seqGx := parseTerm(gx, Context).GetSlice()[1:] // [g, Ty_x, x] - seqB := parseTerm(ga, Context).GetSlice() // [Ty_a, a] + seq := parseTerm(x, Context).GetSlice() // [g, x] - mixedSeq := append(seqGx, seqB...) // [g, Ty_x, x, Ty_a, a] - - length := GetSubTermLength(mixedSeq) - if length != 3 { + length := GetSubTermLength(seq) + if length != 1 { t.Fatalf("GetSubTermLength failed to isolate the first subterm when trailing tokens are present. Expected 3, got %d", length) } }) @@ -1115,22 +1123,21 @@ func TestSkipTreeTermAndContinue(t *testing.T) { t.Run("SkipTreeTermAndContinue_pfx", func(t *testing.T) { tree := NewNode() tree = tree.Insert(pfx.(AST.Pred)) - fullSeq := parsePred(pfx.(AST.Pred), ctx).GetSlice() + tree = tree.children.At(0) // 'P' + needed := 1 - tree = tree.children.At(0) - tree = tree.children.At(0) - tree = tree.children.At(0) - - needed := 2 - - remainingQuery := fullSeq[5:] + remainingQuery := fullSeq[len(fullSeq):] emptyEnv := subst.Substitutions{} results := tree.SkipTreeTermAndContinue(needed, remainingQuery, emptyEnv) if len(results) != 1 { t.Fatalf("Expected 1 Element, got %d", len(results)) } + + for _, elem := range results { + fmt.Println("elem", elem.toString()) + } }) t.Run("SkipTreeTermAndContinue_pfxy", func(t *testing.T) { @@ -1139,19 +1146,20 @@ func TestSkipTreeTermAndContinue(t *testing.T) { fullSeq := parsePred(pfxy.(AST.Pred), ctx).GetSlice() - tree = tree.children.At(0) - tree = tree.children.At(0) - tree = tree.children.At(0) + tree = tree.children.At(0) // 'P' - needed := 4 + needed := 1 - remainingQuery := fullSeq[7:] + remainingQuery := fullSeq[len(fullSeq):] emptyEnv := subst.Substitutions{} results := tree.SkipTreeTermAndContinue(needed, remainingQuery, emptyEnv) if len(results) != 1 { t.Fatalf("Expected 1 Element, got %d", len(results)) } + for _, elem := range results { + fmt.Println("elem", elem.toString()) + } }) t.Run("SkipTreeTermAndContinue_pggab", func(t *testing.T) { @@ -1160,29 +1168,30 @@ func TestSkipTreeTermAndContinue(t *testing.T) { fullSeq := parsePred(pggab.(AST.Pred), ctx).GetSlice() - tree = tree.children.At(0) - tree = tree.children.At(0) - tree = tree.children.At(0) - - needed := 2 + tree = tree.children.At(0) // 'P' + needed := tree.GetArity() - remainingQuery := fullSeq[7:] + remainingQuery := fullSeq[len(fullSeq):] emptyEnv := subst.Substitutions{} results := tree.SkipTreeTermAndContinue(needed, remainingQuery, emptyEnv) if len(results) != 1 { - t.Fatalf("Test imbriqué pggab a échoué. Attendu 1 élément, obtenu %d", len(results)) + t.Fatalf("Got %d, Expected 1", len(results)) + } + + for _, elem := range results { + fmt.Println("elem", elem.toString()) } }) } func TestRetrieveUnifiables(t *testing.T) { - tree := NewNode() - candidat := []CandidatResult{} - t.Run("TestRetrieveUnifiables_pax_pba", func(t *testing.T) { + tree := NewNode() + candidat := []CandidatResult{} + tree = tree.Insert(pax.(AST.Pred)) tree = tree.Insert(pba.(AST.Pred)) candidat = tree.RetrieveUnifiables(pay) @@ -1190,34 +1199,42 @@ func TestRetrieveUnifiables(t *testing.T) { t.Fatalf("Should be only 1") } - }) - - t.Run("TestRetrieveUnifiables_pafx", func(t *testing.T) { - - tree = tree.Insert(pafx.(AST.Pred)) - candidat = tree.RetrieveUnifiables(pay) - if len(candidat) != 2 { - t.Fatalf("Should be only 2") + for _, elem := range candidat { + fmt.Println("elem Pred", elem.getPred().ToString()) + fmt.Println("elem Subs", elem.GetSubs().ToString()) } }) - t.Run("TestRetrieveUnifiables_pafy", func(t *testing.T) { + t.Run("TestRetrieveUnifiables_pafb", func(t *testing.T) { + + tree := NewNode() + candidat := []CandidatResult{} + + tree = tree.Insert(pafb.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + tree.Print() - tree = tree.Insert(pafy.(AST.Pred)) candidat = tree.RetrieveUnifiables(pay) - if len(candidat) != 3 { - t.Fatalf("Should be only 3") + + for _, elem := range candidat { + fmt.Println("elem", elem.toString()) + } + + if len(candidat) != 2 { + t.Fatalf("Should be only 2 got %d", len(candidat)) } }) t.Run("TestRetrieveUnifiables_pa", func(t *testing.T) { - tree2 := NewNode() - tree2 = tree2.Insert(pa.(AST.Pred)) - candidat2 := tree2.RetrieveUnifiables(pb) - if len(candidat2) != 0 { + tree := NewNode() + candidat := []CandidatResult{} + + tree = tree.Insert(pa.(AST.Pred)) + candidat = tree.RetrieveUnifiables(pb) + if len(candidat) != 0 { t.Fatalf("Should be 0 because pa and pb can't be unified") } @@ -1225,14 +1242,14 @@ func TestRetrieveUnifiables(t *testing.T) { t.Run("TestRetrieveUnifiables_pxy", func(t *testing.T) { - tree2 := NewNode() - tree2 = tree2.Insert(pxy.(AST.Pred)) - candidat2 := tree2.RetrieveUnifiables(pab) - if len(candidat2) == 0 { + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + candidat := tree.RetrieveUnifiables(pab) + if len(candidat) == 0 { t.Fatalf("pxy and pab are unifiables") } - fmt.Println(len(candidat2)) + fmt.Println(len(candidat)) }) } @@ -1723,8 +1740,8 @@ func TestMakeDataStruct(t *testing.T) { pChildren2 := pChildren[0] pChildren3 := pChildren2.getChildren().GetSlice() - if len(pChildren3) != 2 { - t.Fatalf("Expected 'P' to have exactly 2 children ('a' and 'b') from positive formulas, got %d", len(pChildren)) + if len(pChildren3) != 1 { + t.Fatalf("Expected 'P' to have exactly 1 children ('b') from positive formulas, got %d", len(pChildren)) } for _, child := range pChildren3 { @@ -1768,507 +1785,135 @@ func TestMakeDataStruct(t *testing.T) { }) } -func TestUnify2(t *testing.T) { - - t.Run("Unify2_pax_with_pay", func(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - - found, mix := tree.Unify2(pay) - - for _, elem := range mix { - fmt.Println("elem de mix", elem.ToString()) - } - - if !found { - t.Fatalf("Unification failed, expected success") - } - if len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) - } - if mix[0].GetForm().ToString() != "P(a, Y)" { - t.Errorf("Expected unified form to be 'P(a, Y)', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_pax_with_pab", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - found, mix := tree.Unify2(pab) - - if !found { - t.Fatalf("Unification failed, expected success") - } - if len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) - } - if mix[0].GetForm().ToString() != "P(a, b)" { - t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_pa_with_pa", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - found, mix := tree.Unify2(pa) - - if !found { - t.Fatalf("Unification failed, expected success") - } - if len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) - } - if mix[0].GetForm().ToString() != "P(a)" { - t.Errorf("Expected unified form to be 'P(a)', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_pax_with_pafy", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - found, mix := tree.Unify2(pafy) - - for _, elem := range mix { - fmt.Println("elem", elem.ToString()) - } - fmt.Println("len(elem)", len(mix)) - - if !found || len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result") - } - if mix[0].GetForm().ToString() != "P(a, f(Y))" { - t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_pafx_with_pafy", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pafx.(AST.Pred)) - found, mix := tree.Unify2(pafy) - - tree.Print() - fmt.Println("pafy", pafy.ToString()) - - if !found || len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result") - } - if mix[0].GetForm().ToString() != "P(a, f(Y))" { - t.Errorf("Expected unified form to be 'P(a, f(Y))', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_px_with_py", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(px.(AST.Pred)) - found, mix := tree.Unify2(py) - - if !found || len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result") - } - if mix[0].GetForm().ToString() != "P(Y)" { - t.Errorf("Expected unified form to be 'P(Y)', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_pxy_with_pab", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pxy.(AST.Pred)) - found, mix := tree.Unify2(pab) - - if !found || len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result") - } - if mix[0].GetForm().ToString() != "P(a, b)" { - t.Errorf("Expected unified form to be 'P(a, b)', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Unify2_Multiple_Inserts_with_py", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(pa.(AST.Pred)) - tree = tree.Insert(pfx.(AST.Pred)) - found, mix := tree.Unify2(py) - - if !found || len(mix) != 3 { - t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) - } - - for i, elem := range mix { - if elem.GetForm().ToString() != "P(Y)" { - t.Errorf("Expected unified form %d to be 'P(Y)', got '%s'", i, elem.GetForm().ToString()) - } - } - }) - - t.Run("Unify2_pggab_with_pxy", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pggab.(AST.Pred)) - found, mix := tree.Unify2(pxy) - - if !found || len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result") - } - if mix[0].GetForm().ToString() != "P(X, Y)" { - t.Errorf("Expected unified form to be 'P(X, Y)', got '%s'", mix[0].GetForm().ToString()) - } - }) - - t.Run("Exception_Unify2_pa_with_pb", func(t *testing.T) { +func TestTrickyProblem(t *testing.T) { + t.Run("Exception_Occur_Check_Cyclic", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - found, mix := tree.Unify2(pb) + tree.Insert(pfx.(AST.Pred)) + val, mix := tree.Unify(px) - if found { - t.Errorf("Unification should have failed for P(a) and P(b)") + if val { + t.Fatalf("Occur Check Faillure") } if len(mix) != 0 { - t.Errorf("Expected empty result list, got %d elements", len(mix)) + t.Fatalf("Occur Check Faillure") } }) - t.Run("Exception_Unify2_pxx_with_pab", func(t *testing.T) { - + t.Run("UnifyTerm_Complex_SkipTerm", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pxx.(AST.Pred)) - found, mix := tree.Unify2(pab) + tree.Insert(pfgaba.(AST.Pred)) + val, mix := tree.Unify(px) - if found { - t.Errorf("Unification should have failed: P(x, x) cannot unify with P(a, b)") + if val { + t.Fatalf("Occur Check Faillure") } if len(mix) != 0 { - t.Errorf("Expected empty result list") + t.Fatalf("Occur Check Faillure") } }) - t.Run("Exception_Unify2_pba_pab_with_pxx", func(t *testing.T) { - + t.Run("Exception_Shared_Query_Variables", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pba.(AST.Pred)) tree = tree.Insert(pab.(AST.Pred)) - found, mix := tree.Unify2(pxx) - if found { - t.Errorf("Unification should have failed: P(X, X) cannot unify with P(b, a) or P(a, b)") - } - if len(mix) != 0 { - t.Errorf("Expected empty result list") - } - }) - - t.Run("Exception_Unify2_pab_with_pxx", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - found, mix := tree.Unify2(pxx) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) + val, _ := tree.UnifyTerm(queryTerm) - if found || len(mix) != 0 { - t.Errorf("Unification should have failed: P(a, b) cannot unify with P(X, X)") + if val { + t.Fatalf("Unification should fail because X cannot be 'a' and 'b' simultaneously") } }) + } -func TestUnifyTerm2(t *testing.T) { +func TestInsertCustomType(t *testing.T) { - t.Run("UnifyTerm2_pax_with_pay", func(t *testing.T) { + t.Run("Insert_typed_var", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - queryTerm := subst.TransformPred(pay.(AST.Pred)) - + tree = tree.Insert(p_typed_pred_int_3) tree.Print() - fmt.Println(queryTerm.ToString()) - - val, mix := tree.UnifyTerm2(queryTerm) - if !val { - t.Fatalf("Unification failed, expected success") + t1 := tree.getChildren() + if t1.Len() != 1 { + t.Fatalf("Supposed to be P") } - if len(mix) != 1 { - t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + if t1.At(0).toString() != "p" { + t.Fatalf("Supposed to be p (predicat)") } - }) - - t.Run("UnifyTerm2_pax_with_pab", func(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pax.(AST.Pred)) - queryTerm := subst.TransformPred(pab.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - - if !val { - t.Fatalf("Unification failed, expected success") - } - if len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + t2 := t1.At(0) + t3 := t2.getChildren() + if t3.Len() != 1 { + t.Fatalf("Supposed to be int") } - }) - - t.Run("UnifyTerm2_pa_with_pa", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - queryTerm := subst.TransformPred(pa.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - if !val { - t.Fatalf("Unification failed, expected success") - } - if len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, got %d", len(mix)) - } - if mix[0].ToString() != "P(a) {}" { - t.Errorf("Expected result 'P(a) {}', got '%s'", mix[0].ToString()) + if t3.At(0).toString() != "int" { + t.Fatalf("Supposed to be type \"int \" ") } - }) - - t.Run("UnifyTerm2_pab_with_pay", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - queryTerm := subst.TransformPred(pay.(AST.Pred)) - val, mix := tree.UnifyTerm2(queryTerm) - - if !val || len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) - } - if mix[0].Term().ToString() != "P(a, Y)" { - t.Errorf("Expected term to be 'P(a, Y)', got '%s'", mix[0].Term().ToString()) + t4 := t3.At(0) + t5 := t4.getChildren() + if t5.Len() != 1 { + t.Fatalf("Supposed to be int") } - }) - - t.Run("UnifyTerm2_pafx_with_pafy", func(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pafx.(AST.Pred)) - queryTerm := subst.TransformPred(pafy.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - - if !val || len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) - } - if mix[0].Term().ToString() != "P(a, f(Y))" { - t.Errorf("Expected term to be 'P(a, f(Y))', got '%s'", mix[0].Term().ToString()) + if t5.At(0).toString() != "3" { + t.Fatalf("Supposed to be 3") } - }) - - t.Run("UnifyTerm2_px_with_py", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(px.(AST.Pred)) - queryTerm := subst.TransformPred(py.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - if !val || len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) - } - if mix[0].Term().ToString() != "P(Y)" { - t.Errorf("Expected term to be 'P(Y)', got '%s'", mix[0].Term().ToString()) - } }) - t.Run("UnifyTerm2_pxy_with_pab", func(t *testing.T) { + t.Run("Insert_different_typed_var", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pxy.(AST.Pred)) - queryTerm := subst.TransformPred(pab.(AST.Pred)) + tree = tree.Insert(p_typed_pred_int_2_int_3_a) + tree = tree.Insert(p_typed_pred_int_2_int_3_b) + tree = tree.Insert(p_typed_pred_int_x_y_z) + tree = tree.Insert(p_typed_pred_reel_x_y_z) - val, mix := tree.UnifyTerm2(queryTerm) - - if !val || len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) - } - if mix[0].Term().ToString() != "P(a, b)" { - t.Errorf("Expected term to be 'P(a, b)', got '%s'", mix[0].Term().ToString()) - } }) - t.Run("UnifyTerm2_Multiple_Inserts_with_py", func(t *testing.T) { + t.Run("Insert_different_typed_var", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(pa.(AST.Pred)) - tree = tree.Insert(pfx.(AST.Pred)) - queryTerm := subst.TransformPred(py.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - - if !val { - t.Fatalf("Unification failed, expected success with matches") - } - if len(mix) != 3 { - t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) - } - for i, elem := range mix { - if elem.Term().ToString() != "P(Y)" { - t.Errorf("Expected unified term %d to be 'P(Y)', got '%s'", i, elem.Term().ToString()) - } - } - }) - - t.Run("UnifyTerm2_pggab_with_pxy", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pggab.(AST.Pred)) - queryTerm := subst.TransformPred(pxy.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) + tree = tree.Insert(p_typed_pred_int_2_double_4_a) + tree.Print() - if !val || len(mix) != 1 { - t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) - } - if mix[0].Term().ToString() != "P(X, Y)" { - t.Errorf("Expected term to be 'P(X, Y)', got '%s'", mix[0].Term().ToString()) - } }) +} - t.Run("Exception_UnifyTerm2_pa_with_pb", func(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - queryTerm := subst.TransformPred(pb.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - - if val { - t.Fatalf("Unification should have failed for P(a) and P(b)") - } - if len(mix) != 0 { - t.Fatalf("Expected 0 elements, got %d", len(mix)) - } - }) +func TestRetrieveCustomType(t *testing.T) { - t.Run("Exception_UnifyTerm2_pxx_with_pab", func(t *testing.T) { + t.Run("Retrieve_same_typed_var", func(t *testing.T) { tree := NewNode() - tree = tree.Insert(pxx.(AST.Pred)) - queryTerm := subst.TransformPred(pab.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) + tree = tree.Insert(p_typed_pred_int_2_double_4_a) + tree.Print() + a := tree.RetrieveUnifiables(p_typed_pred_int_2_double_4_x) - if val || len(mix) != 0 { - t.Fatalf("Unification should have failed: P(x,x) cannot unify with P(a,b)") + if len(a) != 1 { + t.Fatalf("Should be able to Unify") } - }) - - t.Run("Exception_UnifyTerm2_pba_pab_with_pxx", func(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pba.(AST.Pred)) - tree = tree.Insert(pab.(AST.Pred)) - queryTerm := subst.TransformPred(pxx.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - - if val { - t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + for _, elem := range a { + fmt.Println("elem", elem.toString()) } - }) - - t.Run("Exception_UnifyTerm2_pab_with_pxx", func(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - queryTerm := subst.TransformPred(pxx.(AST.Pred)) - - val, mix := tree.UnifyTerm2(queryTerm) - - if val { - t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) - } - if len(mix) != 0 { - t.Fatalf("Expected result array to be empty, got %d elements", len(mix)) - } }) -} -func TestTrickyProblem(t *testing.T) { + t.Run("Retrieve_different_typed_var", func(t *testing.T) { - t.Run("Exception_Occur_Check_Cyclic", func(t *testing.T) { tree := NewNode() - tree.Insert(pfx.(AST.Pred)) - val, mix := tree.Unify(px) + tree = tree.Insert(p_typed_pred_int_2_double_4_a) + tree.Print() + a := tree.RetrieveUnifiables(p_typed_pred_int_2_double_4_b) - if val { - t.Fatalf("Occur Check Faillure") + if len(a) != 0 { + t.Fatalf("Shouldn't be able to Unify") } - if len(mix) != 0 { - t.Fatalf("Occur Check Faillure") - } - }) - t.Run("UnifyTerm_Complex_SkipTerm", func(t *testing.T) { - tree := NewNode() - tree.Insert(pfgaba.(AST.Pred)) - val, mix := tree.Unify(px) - - if val { - t.Fatalf("Occur Check Faillure") - } - if len(mix) != 0 { - t.Fatalf("Occur Check Faillure") - } }) - t.Run("Exception_Shared_Query_Variables", func(t *testing.T) { - tree := NewNode() - tree = tree.Insert(pab.(AST.Pred)) - - queryTerm := subst.TransformPred(pxx.(AST.Pred)) - val, _ := tree.UnifyTerm(queryTerm) - - if val { - t.Fatalf("Unification should fail because X cannot be 'a' and 'b' simultaneously") - } - }) - -} - -func TestInsertCustomType(t *testing.T) { - - tree := NewNode() - - La := p_typed_pred_int_3.GetTyArgs() - for _, elem := range La.GetSlice() { - fmt.Println("Tyargs : ", elem.ToString()) - } - - LT := p_typed_pred_int_3.GetArgs() - for _, elem := range LT.GetSlice() { - fmt.Println("Args : ", elem.ToString()) - } - - tree = tree.Insert(p_typed_pred_int_3) - - a := p_typed_pred_int_3.GetTyArgs() - for _, elem := range a.GetSlice() { - fmt.Println("elem", elem.ToString()) - } - - tree.Print() - -} - -func TestCustom(t *testing.T) { - - tree := NewNode() - tree = tree.Insert(pa.(AST.Pred)) - tree = tree.Insert(pb.(AST.Pred)) - tree = tree.Insert(px.(AST.Pred)) - tree.Print() - } From d8689716fd669b3af6a47b0525d6f23cd3c8e37c Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Mon, 15 Jun 2026 01:28:08 +0200 Subject: [PATCH 18/23] Test Green for UnifyTerm --- src/Unif/discriminationtree/discrimination-trees.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 578ccd98..9aea263c 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -912,14 +912,7 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix seq := parseTerm(inputTerm, tmpContext).GetSlice() - var seq2 []SymbolType - for i, elem := range seq { - if i > 0 { - seq2 = append(seq2, elem) - } - } - - candidates := dNode.retrieveRec(seq2, subst.MakeEmptySubstitution()) + candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) fmt.Println("Len Candidates", len(candidates)) From bcd0856415286a57df50ee900b69cd5600fb65cb Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Sat, 20 Jun 2026 01:43:46 +0200 Subject: [PATCH 19/23] Final Commit. Unify and UnifyTerm works pretty good. Unify2 and UnifyTerm2 Workish, fail on some TPTP and the code is grabage. It's probably better to delete all V2 fonction and start from scratch. --- devtools/run-test-suite.py | 4 +- devtools/run_theorem_test.py | 72 +- src/Search/child_management.go | 7 +- src/Search/rules.go | 41 +- .../discriminationtree/ContextNormalizer.go | 7 +- .../discrimination-trees.go | 482 ++++++++----- src/Unif/discriminationtree/dt_test.go | 647 +++++++++++++++++- src/Unif/substitution/data_structure.go | 2 + 8 files changed, 1055 insertions(+), 207 deletions(-) mode change 100755 => 100644 devtools/run_theorem_test.py diff --git a/devtools/run-test-suite.py b/devtools/run-test-suite.py index 771f2919..03b558eb 100644 --- a/devtools/run-test-suite.py +++ b/devtools/run-test-suite.py @@ -13,7 +13,7 @@ class Parser: RES = "% result: " ENV = "% env: " EXIT_CODE = "% exit: " - no_rocq_check = False + no_rocq_check = True def __init__(self, filename): self.filename = filename @@ -52,7 +52,7 @@ def getCommandLine(self): arguments = self.arguments if not self.no_rocq_check: arguments += " -context -orocq" - return self.env + " ../src/_build/goeland " + arguments + " " + self.filename + return self.env + " ../src/_build/goeland -dt " + arguments + " " + self.filename def getArgsForPrinting(self): rocq_chk_str = "" diff --git a/devtools/run_theorem_test.py b/devtools/run_theorem_test.py old mode 100755 new mode 100644 index 75fd7c72..c20b51a2 --- a/devtools/run_theorem_test.py +++ b/devtools/run_theorem_test.py @@ -1,40 +1,72 @@ import os import sys import re +import time +import subprocess +from pathlib import Path from subprocess import PIPE, run def Out(command): result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True, encoding='utf-8') return result.stdout -def LaunchTest(prover_name, command_line, succes, memory_limit=None, failure=None): - output = Out(command_line).encode('utf-8', errors='ignore').decode(errors='ignore') - res = False +def LaunchTest(prover_name, command_line, succes, f, memory_limit=None, failure=None): + output = Out(command_line).encode('utf-8', errors='ignore').decode(errors='ignore') + res = False + if re.search(succes, output): + f.write(f"Found proof. Good job, {prover_name} !\n") + res = True + else: + f.write("Proof not found\n") + return res - if re.search(succes, output): - print(f"Found proof. Good job, {prover_name} !") - res = True - else: - print("Proof not found") - - return res +def set_cpu_freq(freq_khz): + # Define CPU Speed + cmd = f"sudo cpupower frequency-set -g performance -u {freq_khz} -d {freq_khz}" + result = subprocess.run(cmd, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True) + if result.returncode != 0: + print("Error. sudo ?.") + print(result.stderr) + else: + print(f"CPU Speed is {int(freq_khz)/1000} MHz.") -if len(sys.argv) < 3: +if len(sys.argv) < 3: print(f"python3 {sys.argv[0]} problem_folder timeout goeland_options") else: + NB_CORES = 4 + CORES = ",".join(str(i) for i in range(NB_CORES)) + FREQ_KHZ = 2000000 # 2.0 GHz + + set_cpu_freq(FREQ_KHZ) + print(f"{NB_CORES} cores are working") + folder = sys.argv[1] folder_split = folder.split("/") folder += "/" - entries = os.listdir(folder) timeout = sys.argv[2] + total = len(entries) - cpt = 0 - total = len(entries) # TODO - - for index, file in enumerate(entries): - print(f"Problem {index+1}/{len(entries)} : {folder+file}") - if LaunchTest("Goéland", "timeout "+timeout+" src/_build/goeland " + " ".join(sys.argv[4:]) + " " +folder+file, "% RES : VALID", None, "% RES : NOT VALID"): - cpt+=1 + filename = "" + for i in range (0,sys.maxsize) : + a = Path("resultV1_" + str(i) + ".txt") + if not a.exists(): + filename = a + break - print(f"Number of problems solved : {cpt}/{total}") + with open(filename, "w") as f: + milli_sec_deb = int(round(time.time() * 1000)) + cpt = 0 + for index, file in enumerate(entries): + f.write(f"Problem {index+1}/{len(entries)} : {folder+file}\n") + command = ( + f"taskset -c {CORES} env GOMAXPROCS={NB_CORES} " + f"timeout {timeout} ../src/_build/goeland -dt " + + " ".join(sys.argv[4:]) + " " + folder + file + ) + if LaunchTest("Goéland", command, "% RES : VALID", f, None, "% RES : NOT VALID"): + cpt += 1 + milli_sec_fin = int(round(time.time() * 1000)) + timer = milli_sec_fin - milli_sec_deb + f.write(f"Number of problems solved : {cpt}/{total}\n") + f.write(f"Execution Time : {timer}ms\n") diff --git a/src/Search/child_management.go b/src/Search/child_management.go index 76a8086f..d2be1902 100644 --- a/src/Search/child_management.go +++ b/src/Search/child_management.go @@ -32,13 +32,12 @@ package Search import ( - "errors" "fmt" "github.com/GoelandProver/Goeland/Core" "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" - "github.com/GoelandProver/Goeland/Unif/substitution" + subst "github.com/GoelandProver/Goeland/Unif/substitution" ) /* Arguments for waitChildren function & utilitary subfunctions */ @@ -137,10 +136,6 @@ func (ds *destructiveSearch) childrenClosedByThemselves(args wcdArgs, proofChild // No need to append the current substitution, because the children returns it anyway (if it exists) // So here, the current substitution should be empty. Otherwise, there's a big bug somewhere else. - if !args.currentSubst.IsEmpty() { - return errors.New("current substitution is not empty but children close by themselves which shouldn't happen") - } - // Updates the proof using the proofs of the children of the node. args.st = updateProof(args, proofChildren) diff --git a/src/Search/rules.go b/src/Search/rules.go index 12d02a7f..f487824a 100644 --- a/src/Search/rules.go +++ b/src/Search/rules.go @@ -202,36 +202,49 @@ func searchClosureRule(f AST.Form, st State) (bool, []substitution.MixedSubstitu case AST.Pred: res, subst := st.GetTreeNeg().Unify(f) if res { - new_list := Lib.NewList[substitution.MixedSubstitutions]() + returnList := Lib.NewList[substitution.MixedSubstitutions]() + + running_subst := st.applied_subst.GetSubst() + for _, e := range subst { - subst2, res2 := substitution.MergeMixedSubstitutions(e.GetSubsts(), st.applied_subst.GetSubst()) - if res2 { + subst2, isCompatible := substitution.MergeMixedSubstitutions(e.GetSubsts(), running_subst) + + if isCompatible { + running_subst = subst2 + new_subst := substitution.MakeMatchingSubstitutions(e.GetForm(), substitution.ToSubstitutions(subst2)) - new_list.Append(new_subst.ToMixed()) + returnList.Append(new_subst.ToMixed()) } } - return !new_list.Empty(), new_list.GetSlice() - } else { - return false, nil + + return !returnList.Empty(), returnList.GetSlice() } + return false, nil case AST.Not: switch nf.GetForm().(type) { case AST.Pred: res, subst := st.GetTreePos().Unify(nf.GetForm()) if res { - new_list := Lib.NewList[substitution.MixedSubstitutions]() + returnList := Lib.NewList[substitution.MixedSubstitutions]() + + running_subst := st.applied_subst.GetSubst() + for _, e := range subst { - subst2, res2 := substitution.MergeMixedSubstitutions(e.GetSubsts(), st.applied_subst.GetSubst()) - if res2 { + subst2, isCompatible := substitution.MergeMixedSubstitutions(e.GetSubsts(), running_subst) + + if isCompatible { + running_subst = subst2 + new_subst := substitution.MakeMatchingSubstitutions(e.GetForm(), substitution.ToSubstitutions(subst2)) - new_list.Append(new_subst.ToMixed()) + returnList.Append(new_subst.ToMixed()) } } - return !new_list.Empty(), new_list.GetSlice() - } else { - return false, nil + + return !returnList.Empty(), returnList.GetSlice() } + return false, nil + default: return false, nil } diff --git a/src/Unif/discriminationtree/ContextNormalizer.go b/src/Unif/discriminationtree/ContextNormalizer.go index 4536ea30..457b0d92 100644 --- a/src/Unif/discriminationtree/ContextNormalizer.go +++ b/src/Unif/discriminationtree/ContextNormalizer.go @@ -58,10 +58,13 @@ func NewContext() *NormalizerContext { } } +// Transform the discriminationTree to a perfect disriminationTree. +// Using a Map, when visiting a branch, each Meta is stored and transform into a NormalizedMeta. This allow memory gain. +// Branch 1 : f(x), Branch 2 : f(y) => Branch 1 : f(v1), Branch 2 : f(v1) => We notice it's the same one => Possible to fuse them to save memory func (ctx *NormalizerContext) GetNormalizedMeta(originalMeta AST.Meta) AST.Meta { - originalName := originalMeta.GetName() // Get meeta Name - orignalType := originalMeta.GetTy() + originalName := originalMeta.GetName() // Get meta Name + orignalType := originalMeta.GetTy() // Get Meta Ty // Contains check if normalizedMeta, exists := ctx.mapping[originalName]; exists { diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 9aea263c..4faaea3d 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -86,7 +86,12 @@ func (tn TermNode) IsMeta() bool { return tn.Term.IsMeta() } +// Required to make transitivity.p valid func (tn TyNode) IsMeta() bool { + if tn.Ty != nil { + _, isTypeVariable := tn.Ty.(AST.TyMeta) + return isTypeVariable + } return false } @@ -102,6 +107,7 @@ func (tn TyNode) GetArityType() int { return 0 } +// If SymbolType is AST.Term, return It, else nil func (sym SymbolType) getTerm() AST.Term { if tn, ok := sym.symbol.(TermNode); ok { @@ -111,6 +117,7 @@ func (sym SymbolType) getTerm() AST.Term { } +// If SymbolType is AST.Ty, return It, else nil func (sym SymbolType) GetTy() AST.Ty { if tn, ok := sym.symbol.(TyNode); ok { @@ -120,6 +127,7 @@ func (sym SymbolType) GetTy() AST.Ty { } +// If SymbolType is string, return It, else nil func (sym SymbolType) getString() string { if ns, ok := sym.symbol.(NodeString); ok { @@ -128,6 +136,7 @@ func (sym SymbolType) getString() string { return "" } +// Equals made between NodeString and one NodeElement func (ns NodeString) Equals(target NodeElement) bool { typ, ok := target.(NodeString) @@ -137,31 +146,24 @@ func (ns NodeString) Equals(target NodeElement) bool { return strings.EqualFold(ns.ToString(), typ.ToString()) } +// Equals made between TermNode and one NodeElement func (tn TermNode) Equals(target NodeElement) bool { typ, ok := target.(TermNode) if !ok { return false } - var res bool if tn.Term != nil && typ.Term != nil { - res = tn.Term.Equals(typ.Term) + return tn.Term.Equals(typ.Term) } if tn.Term == nil || typ.Term == nil { - res = false + return false } - - // FIX ME : tn.Term.ToString() and typ.Term.ToString() provoc segfault with test tfa_syntax_chk.p and few other - // if !res { - // debug(Lib.MkLazy(func() string { return "--- EQUALS TERM FAILED ---" })) - // debug(Lib.MkLazy(func() string { return fmt.Sprintf("%s | Type: %T", tn.Term.ToString(), tn.Term) })) - // debug(Lib.MkLazy(func() string { return fmt.Sprintf("%s | Type: %T", typ.Term.ToString(), typ.Term) })) - // debug(Lib.MkLazy(func() string { return "--------------------------" })) - // } - - return res + return false } + +// Equals made between TyNode and one NodeElement func (tn TyNode) Equals(target NodeElement) bool { typ, ok := target.(TyNode) if !ok { @@ -175,24 +177,29 @@ func (tn TyNode) Equals(target NodeElement) bool { return tn.Ty.Equals(typ.Ty) } +// Return The Type of a NodeString. +// Return Temporary a AST.Tindividual() func (ns NodeString) GetTy() AST.Ty { return AST.TIndividual() // Temporary } +// Convert The termNode to Meta then getTy() func (tn TermNode) GetTy() AST.Ty { return tn.ToMeta().GetTy() } +// Return the Ty of the TyNode func (tn TyNode) GetTy() AST.Ty { return tn.Ty } +// If the is AST.Fun return ID.ToString else term.toString else nil func (tn TermNode) ToString() string { if fun, ok := tn.Term.(AST.Fun); ok { return fun.GetID().ToString() @@ -205,6 +212,12 @@ func (tn TermNode) ToString() string { return "nil" } +// Take Any Parameter. Depending of the Parameter : +// String => NodeString +// AST.Ty => TyNode +// AST.Pred => TermNode(TransformPred(Param)) +// AST.Term => TermNode +// Else Anomaly func createNodeElement(t any) NodeElement { switch v := t.(type) { @@ -239,14 +252,17 @@ func (t SymbolType) GetArity() int { return t.arity } +// Nil is Symbol is nil and arity == -1 ( default Arity for a SymbolType) func (s SymbolType) IsNil() bool { return s.symbol == nil && s.arity == -1 } +// Maker a SymbolType with a Arity of 0 func makeSymbolTypeTy(node NodeElement) SymbolType { return SymbolType{node, 0} } +// Maker SymbolType func makeSymbolType(node NodeElement, arity int) SymbolType { return SymbolType{node, arity} @@ -304,10 +320,6 @@ func (dNode DiscriminationNode) GetArity() int { return dNode.symbol.GetArity() } -func (dNode DiscriminationNode) getElement() any { - return dNode.getSymbol().getSymbol() -} - func (dNode DiscriminationNode) getChildren() Lib.List[DiscriminationNode] { return dNode.children } @@ -316,6 +328,7 @@ func (dNode DiscriminationNode) getLeafFor() Lib.List[AST.Pred] { return dNode.leafFor } +// If NodeElement of the dNode is nil return "" else toString func (dNode DiscriminationNode) toString() string { sym := dNode.getSymbol().getSymbol() @@ -339,6 +352,7 @@ func (Candidat CandidatResult) GetSubs() subst.Substitutions { return Candidat.Subs } +// Return Pred and Subs func (Candidat CandidatResult) toString() string { return fmt.Sprintf("Pred : %s Subs : %s\n", Candidat.getPred().ToString(), Candidat.GetSubs().ToString()) } @@ -351,6 +365,7 @@ func ToSingleElement(candidats []CandidatResult) CandidatResult { return CandidatResult{} } +// Maker CandidatResult func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { return CandidatResult{ Pred: p, @@ -370,24 +385,21 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { // Then Call parseTerm on the args of the predicat. func parsePred(p AST.Pred, ctx *NormalizerContext) Lib.List[SymbolType] { - if p.GetTyArgs().Len() != p.GetArgs().Len() && (p.GetTyArgs().Len() > 1) { - Glob.Anomaly("Ambigious number of types", " Ambigious number of types, don't match the number of args or not one unique types, leading to a ambigious typing for args") - } + // if p.GetTyArgs().Len() != p.GetArgs().Len() && (p.GetTyArgs().Len() > 1) { + // Glob.Anomaly("Ambigious number of types", " Ambigious number of types, don't match the number of args or not one unique types, leading to a ambigious typing for args") + // } res := Lib.NewList[SymbolType]() // Required Overwise the SymbolType of the predicat will be AST.ID and will be compared with a AST.Fun -> Automatic faillure tmpFun := AST.MakerFun(p.GetID(), Lib.MkListV[AST.Ty](), Lib.MkListV[AST.Term]()) res.Append(makeSymbolType(createNodeElement(tmpFun), p.GetArgs().Len())) - // fmt.Println("Len TyArgs", p.GetTyArgs().Len()) - // fmt.Println("Len Args", p.GetArgs().Len()) - - // Add the Type. + // Add the Type(s). for _, elem := range p.GetTyArgs().GetSlice() { - fmt.Println("Add Type", elem.ToString()) res.Append(makeSymbolTypeTy(createNodeElement(elem))) } - // Add the element + + // Add the element(s) for _, arg := range p.GetArgs().GetSlice() { argSeq := parseTerm(arg, ctx).GetSlice() res.Append(argSeq...) @@ -404,14 +416,15 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { // if term is a function or cst, add it and call his args case AST.Fun: - funSansArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) - first_element := makeSymbolType(createNodeElement(funSansArgs), term.GetArgs().Len()) - // first_element := makeSymbolType(createNodeElement(term.GetID()), term.GetArgs().Len()) -> passer par ID ? + funNoArgs := AST.MakerFun(term.GetID(), term.GetTyArgs(), Lib.NewList[AST.Term]()) + first_element := makeSymbolType(createNodeElement(funNoArgs), term.GetArgs().Len()) + //first_element := makeSymbolType(createNodeElement(term.GetID()), term.GetArgs().Len()) -> passer par ID ? res.Append(first_element) // for _, ty := range term.GetTyArgs().GetSlice() { // res.Append(parseTerm(ty, ctx).GetSlice()...) // } + for _, arg := range term.GetArgs().GetSlice() { res.Append(parseTerm(arg, ctx).GetSlice()...) } @@ -432,8 +445,8 @@ func parseTerm(t AST.Term, ctx *NormalizerContext) Lib.List[SymbolType] { res.Append(makeSymbolType(createNodeElement(normalizedMeta), 0)) // Add the new Meta to the return slice case AST.Id: - funSansArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) - first_element := makeSymbolType(createNodeElement(funSansArgs), 0) + funNoArgs := AST.MakerFun(term, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + first_element := makeSymbolType(createNodeElement(funNoArgs), 0) res.Append(first_element) default: @@ -460,11 +473,11 @@ func FirstElementToSymbolType(t AST.Term) SymbolType { switch t := t.(type) { case AST.Fun: // Case function - funSansArgs := AST.MakerFun(t.GetID(), t.GetTyArgs(), Lib.NewList[AST.Term]()) - return SymbolType{createNodeElement(funSansArgs), t.GetArgs().Len()} - case AST.Id: - funSansArgs := AST.MakerFun(t, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) - return SymbolType{createNodeElement(funSansArgs), 0} + funNoArgs := AST.MakerFun(t.GetID(), t.GetTyArgs(), Lib.NewList[AST.Term]()) + return SymbolType{createNodeElement(funNoArgs), t.GetArgs().Len()} + case AST.Id: // Case Constant + funNoArgs := AST.MakerFun(t, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + return SymbolType{createNodeElement(funNoArgs), 0} case AST.Meta: // Case metaVariable return SymbolType{createNodeElement(t), 0} default: // Not supposed to see something else @@ -529,11 +542,11 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm return dNode } - // Create Symbol - sym := seq.At(0) - foundIndex := -1 + sym := seq.At(0) // Current symbol childrenSlice := dNode.getChildren().GetSlice() - var ok = false + + foundIndex := -1 // Index of the symbol if found + var ok = false // Boolean if a match is found // Looking for already existing child for i, child := range childrenSlice { @@ -609,15 +622,15 @@ func (dNode DiscriminationNode) SkipTreeTermAndContinue(needed int, remainingQue } func (dNode DiscriminationNode) RetrieveUnifiables(t AST.Form) []CandidatResult { - predFormula, _ := t.(AST.Pred) - seq := parsePred(predFormula, NewContext()).GetSlice() + predFormula, _ := t.(AST.Pred) // Cast + seq := parsePred(predFormula, NewContext()).GetSlice() // Parser Env := subst.Substitutions{} return dNode.retrieveRec(seq, Env) } func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { - ch := make(chan []CandidatResult) + ch := make(chan []CandidatResult) // Channel var results []CandidatResult var wg sync.WaitGroup @@ -644,9 +657,7 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S }() for matches := range ch { - results = append(results, matches...) - } return results @@ -654,101 +665,31 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { - defer wg.Done() - - symQuery := seq[0] // First Element - childSym := child.getSymbol() // child is meta or cst + defer wg.Done() // Stop the Goroutine in case of failure to prevent crash or unknow behavior + symQuery := seq[0] // Term of the Query + // Case 1. Exact Match, we go to the next element isExactMatch := child.symbol.Equals(symQuery) - - fmt.Println("symQuery", symQuery.ToString()) - fmt.Println("Symbol", child.symbol.ToString()) - fmt.Println("isExactMatch", isExactMatch) - - if isExactMatch { // Exact Match - var mergedSub = currentEnv - - // Si c'est un match exact mais que c'est une Meta (ex: v1 == v1), - // on DOIT enregistrer la substitution pour ne pas la perdre. - if symQuery.getSymbol().IsMeta() { - currentSub := subst.MakeSubstitution(childSym.getTerm().ToMeta(), symQuery.getTerm()) - if len(currentEnv) == 0 { - mergedSub = subst.Substitutions{currentSub} - } else { - mergedSub, _ = subst.MergeSubstitutions(currentEnv, subst.Substitutions{currentSub}) - } - } - - // On continue avec l'environnement potentiellement mis à jour - if !mergedSub.Equals(subst.Failure()) { - matches := child.retrieveRec(seq[1:], mergedSub) - ch <- matches - } + if isExactMatch { + matches := child.retrieveRec(seq[1:], currentEnv) + ch <- matches } - // Case the child is a AST.Meta + // Case 2. The Symbol from the discriminationTree is a Meta + // We need to look the len of the actual term from the sequence. f(x) == 2, y == 1 and we skip the entire term if child.getSymbol().getSymbol().IsMeta() && !isExactMatch { - - // We noticed that the term of the dNode is a Meta - // Meaning that we can skip the current term of the seq ( paramater of this function ) because it will be unify with the current term - // e.g dNode = x, seq = [f,a] so [f,a] |-> x and we skip 2 because GetSbTermLength of [f,a] is 2 skip := GetSubTermLength(seq) - - if skip <= len(seq) { // Security to prevent segfault - - var mergedSub subst.Substitutions - if skip == 1 { - currentSub := subst.MakeSubstitution(childSym.getTerm().ToMeta(), symQuery.getTerm()) - tmp3 := subst.Substitutions{currentSub} - // Ok Commat Idoms doesn't works because ?? - if len(currentEnv) == 0 { - mergedSub = tmp3 - } else { - mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) - } - } else { - mergedSub = currentEnv - } - - // Verify - if !mergedSub.Equals(subst.Failure()) { - matches := child.retrieveRec(seq[skip:], mergedSub) - ch <- matches - } - + if skip <= len(seq) { + matches := child.retrieveRec(seq[skip:], currentEnv) + ch <- matches } - // First element is a meta - + // Case 3. Reverse of the Case 2. + // The term from the Sequence is a Meta, so we look the len of the term from the DTree and we got skip it. } else if symQuery.getSymbol().IsMeta() && !isExactMatch { - - // Reverse of the situation with the previous if. - // The symbol from seq ( parameter of this function ) is a Meta, meaning we skip the current term of dNode because it will be unify - // e.g dNode = a, seq = [x] so a |-> x and we got to the next term of the dNode - - var mergedSub subst.Substitutions - - if child.GetArity() == 0 { - currentSub := subst.MakeSubstitution(symQuery.getTerm().ToMeta(), childSym.getTerm()) // Create a new substitution - tmp3 := subst.Substitutions{currentSub} - if len(currentEnv) == 0 { - mergedSub = tmp3 - } else { - mergedSub, _ = subst.MergeSubstitutions(currentEnv, tmp3) - } - } else { - mergedSub = currentEnv - } - - if !mergedSub.Equals(subst.Failure()) { - childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], mergedSub) - ch <- childResults - } - - } else { - // No recursive call or return + childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], currentEnv) + ch <- childResults } - } /*****************************/ @@ -800,7 +741,6 @@ func (dNode DiscriminationNode) IsEmpty() bool { } func (dNode DiscriminationNode) Copy() subst.DataStructure { - newChildMaster := Lib.NewList[DiscriminationNode]() for _, child := range dNode.getChildren().GetSlice() { newChild := child.Copy().(DiscriminationNode) @@ -813,7 +753,6 @@ func (dNode DiscriminationNode) Copy() subst.DataStructure { children: newChildMaster, leafFor: newLeafFor, } - } func (dNode DiscriminationNode) MakeDataStruct(formulas Lib.List[AST.Form], is_pos bool) subst.DataStructure { @@ -867,37 +806,19 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe } queryTerm := subst.TransformPred(predFormula) // For Robinson - fmt.Println("Len Candidates", len(candidates)) - for _, possibleMatch := range candidates { initialSubst := subst.Substitutions{} possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term for Robinson finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson - //fmt.Printf("DEBUG Tree Node Structure: %s, Args: %d\n", possibleMatchTerm.ToString(), possibleMatchTerm.GetSubTerms().Len()) - // fmt.Printf("DEBUG Tree Node Structure: %s, Args: %d\n", queryTerm.ToString(), queryTerm.GetSubTerms().Len()) - - for _, elem := range possibleMatchTerm.GetSubTerms().GetSlice() { - fmt.Println("Element t1 : ", elem.ToString()) - } - - // for _, elem := range queryTerm.GetSubTerms().GetSlice() { - // fmt.Println("Element t2 : ", elem.ToString()) - // } - fmt.Println("possibleMatchTerm", possibleMatchTerm.ToString()) - fmt.Println("possibleMatchTerm LEN", possibleMatchTerm.GetSubTerms().Len()) - fmt.Println("queryTerm", queryTerm.ToString()) - fmt.Println("queryTerm LEN ", queryTerm.GetSubTerms().Len()) - fmt.Println("InitialSubst", initialSubst.ToString()) - if finalSubst.Equals(subst.Failure()) { fmt.Println("-------------------------") fmt.Println("Substitution FAILURE") fmt.Println("-------------------------") } else { found = true - matching := subst.MakeMatchingSubstitutions(possibleMatch.getPred(), finalSubst) // constructor - mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return + matching := subst.MakeMatchingSubstitutions(possibleMatch.getPred(), finalSubst) + mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return } } @@ -905,22 +826,125 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe } func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.MixedTermSubstitutions) { + return dNode.UnifyTermWithSubst(inputTerm, subst.MakeEmptySubstitution()) +} +func (dNode DiscriminationNode) UnifyTermWithSubst(inputTerm AST.Term, globalSubst subst.Substitutions) (bool, []subst.MixedTermSubstitutions) { var mixed []subst.MixedTermSubstitutions var found bool tmpContext := NewContext() seq := parseTerm(inputTerm, tmpContext).GetSlice() + candidates := dNode.retrieveRec(seq, globalSubst) + + for _, possibleMatch := range candidates { + candidateTerm := subst.TransformPred(possibleMatch.getPred()) + finalSubst := subst.AddUnification(inputTerm, candidateTerm, globalSubst) + + if !finalSubst.Equals(subst.Failure()) { + found = true + mixMatch := subst.MixMatchSubstitutions{ + Tof: Lib.MkLeft[AST.Term, AST.Form](inputTerm), + Subst: finalSubst, + } + mixed = append(mixed, mixMatch.ToMixedTerm()) + } + } + + return found, mixed +} + +/////////////////////////////////////////////// +/////// EARLY-PRUNING PART OF THE DTREE /////// +/////////////////////////////////////////////// + +// ReconstructTerm reads a flattened sequence of symbols generated by parseTerm and reconstructs the full AST.Term structure +// This allows the discrimination tree to extract a specific sub-query/sub-term and give it Robinson +func ReconstructTerm(seq []SymbolType) (AST.Term, []SymbolType) { + if len(seq) == 0 { + return nil, seq + } + + head := seq[0] + + // Ignore AST.Type if needed + if _, ok := head.getSymbol().(TyNode); ok { + return ReconstructTerm(seq[1:]) + } + + arite := head.GetArity() + term := head.getTerm() + currentSeq := seq[1:] + + switch t := term.(type) { + case AST.Fun: + // Reconstruct function arguments by recursively parsing subsequent elements + args := Lib.NewList[AST.Term]() + for i := 0; i < arite; i++ { + var arg AST.Term + arg, currentSeq = ReconstructTerm(currentSeq) + if arg != nil { + args.Append(arg) + } + } + return AST.MakerFun(t.GetID(), t.GetTyArgs(), args), currentSeq + case AST.Meta: + return t, currentSeq + case AST.Id: + return AST.MakerFun(t, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()), currentSeq + default: + return nil, currentSeq + } +} - candidates := dNode.retrieveRec(seq, subst.MakeEmptySubstitution()) +// Unify2 call Robinson at the end, but relies on early pruning during the retrieval phase +func (dNode DiscriminationNode) Unify2(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { + candidates := dNode.RetrieveUnifiables2(inputFormula, subst.MakeEmptySubstitution()) + var mixed []subst.MixedSubstitutions + var found bool - fmt.Println("Len Candidates", len(candidates)) + queryPred, isQueryPred := inputFormula.(AST.Pred) // Convert for Robinson + if !isQueryPred { + return false, mixed + } + + // Hide type arguments from Robinson to prevent crash during equality. + emptyTyArgs := Lib.NewList[AST.Ty]() + queryTermForRobinson := AST.MakerFun(queryPred.GetID(), emptyTyArgs, queryPred.GetArgs()) for _, possibleMatch := range candidates { + candPred := possibleMatch.getPred() + + candTermForRobinson := AST.MakerFun(candPred.GetID(), emptyTyArgs, candPred.GetArgs()) + currentEnv := possibleMatch.GetSubs().Copy() + + // Final strict unification step + finalSubst := subst.AddUnification(candTermForRobinson, queryTermForRobinson, currentEnv) + + if !finalSubst.Equals(subst.Failure()) { + found = true + matching := subst.MakeMatchingSubstitutions(candPred, finalSubst) + mixed = append(mixed, matching.ToMixed()) + } + } + + return found, mixed +} + +// UnifyTerm performs unification directly on an AST.Term instead of a full AST.Form. +func (dNode DiscriminationNode) UnifyTerm2(inputTerm AST.Term) (bool, []subst.MixedTermSubstitutions) { + var mixed []subst.MixedTermSubstitutions + var found bool + + seq := parseTerm(inputTerm, NewContext()).GetSlice() + + candidates := dNode.retrieveRec2(seq, subst.MakeEmptySubstitution()) + for _, possibleMatch := range candidates { + currentSubst := possibleMatch.GetSubs().Copy() candidateTerm := subst.TransformPred(possibleMatch.getPred()) - emptySubst := subst.Substitutions{} - finalSubst := subst.AddUnification(inputTerm, candidateTerm, emptySubst) // Call Robinson + + finalSubst := subst.AddUnification(inputTerm, candidateTerm, currentSubst) // Robinson call if !finalSubst.Equals(subst.Failure()) { found = true @@ -929,12 +953,148 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix Subst: finalSubst, } mixed = append(mixed, mixMatch.ToMixedTerm()) - } else { - - fmt.Println("-------------------------") - fmt.Println("Substitution FAILURE") - fmt.Println("-------------------------") } } return found, mixed } + +// RetrieveUnifiables2 parses the predicate formula and initiates the recursive, +// concurrent unifiable search starting from the current node. +func (dNode DiscriminationNode) RetrieveUnifiables2(t AST.Form, globalEnv subst.Substitutions) []CandidatResult { + predFormula, _ := t.(AST.Pred) + seq := parsePred(predFormula, NewContext()).GetSlice() + return dNode.retrieveRec2(seq, globalEnv) +} + +// Call a goroutine per branch in the discrimination tree. +func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst.Substitutions) []CandidatResult { + ch := make(chan []CandidatResult) + var results []CandidatResult + var wg sync.WaitGroup + + // End of the sequence reached, collect all predicates stored at this leaf + if len(seq) == 0 { + for _, p := range dNode.getLeafFor().GetSlice() { + results = append(results, MakeCandidat(p, currentEnv.Copy())) + } + return results + } + + // Concurrently evaluate all branch + for _, child := range dNode.children.GetSlice() { + wg.Add(1) + go retrieveCase2(seq, currentEnv.Copy(), child, ch, &wg) + } + + go func() { + wg.Wait() + close(ch) + }() + + for matches := range ch { + results = append(results, matches...) + } + + return results +} + +// retrieveCase2 executes backtracking logic on a specific child node. It performs pruning , unification when encountering variables. +// retrieveCase2 executes backtracking logic. It performs SAFE early pruning. +func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { + defer wg.Done() + symQuery := seq[0] + childSym := child.getSymbol() + + isExactMatch := child.symbol.Equals(symQuery) + + // Case 1: Perfect structural symbol match + if isExactMatch { + matches := child.retrieveRec2(seq[1:], currentEnv) + ch <- matches + } + + // Case 2: The tree contains a meta-variable branch (Early Pruning Attempt) + if childSym.getSymbol().IsMeta() && !isExactMatch { + queryTerm, restSeq := ReconstructTerm(seq) + + if queryTerm != nil && childSym.getTerm() != nil { + mergedSub := subst.AddUnification(childSym.getTerm(), queryTerm, currentEnv.Copy()) // Pruning + + if !mergedSub.Equals(subst.Failure()) { // If Succes Continue + matches := child.retrieveRec2(restSeq, mergedSub) + ch <- matches + } else { + // SAFETY FALLBACK: Robinson's unification failed. This might be a true failure + // or a false positive due to the Occur-Check on normalized variables (e.g., v1 vs v1). + // Instead of killing the branch, we skip the term and let the final Unify2 step decide. + skip := GetSubTermLength(seq) + if skip <= len(seq) { + matches := child.retrieveRec2(seq[skip:], currentEnv) + ch <- matches + } + } + } else if childSym.getTerm() == nil { + // Fallback if the tree term reference is empty: safely skip the matching sub-term length + skip := GetSubTermLength(seq) + if skip <= len(seq) { + matches := child.retrieveRec2(seq[skip:], currentEnv) + ch <- matches + } + } + + // Case 3: The query sequence contains a meta-variable + } else if symQuery.getSymbol().IsMeta() && !isExactMatch { + mergedSub := currentEnv.Copy() + + if child.GetArity() == 0 { + childTerm := childSym.getTerm() + symTerm := symQuery.getTerm() + + if childTerm != nil && symTerm != nil { + properTerm := childTerm + // Normalize standard IDs into empty functions to align with substitution expectations + if id, ok := childTerm.(AST.Id); ok { + properTerm = AST.MakerFun(id, Lib.NewList[AST.Ty](), Lib.NewList[AST.Term]()) + } + + currentSub := subst.MakeSubstitution(symTerm.ToMeta(), properTerm) + newSubstitutionS := subst.Substitutions{currentSub} + + if len(currentEnv) == 0 { + mergedSub = newSubstitutionS + } else { + mergedSub, _ = subst.MergeSubstitutions(currentEnv, newSubstitutionS) + } + } + } + + if !mergedSub.Equals(subst.Failure()) { + // Substitution successful: skip the tree's sub-structure and continue + childResults := child.SkipTreeTermAndContinue2(child.GetArity(), seq[1:], mergedSub) + ch <- childResults + } else { + // SAFETY FALLBACK: Same as above. Do not kill the branch on substitution failure. + // Skip the sub-structure using the unmodified environment a + childResults := child.SkipTreeTermAndContinue2(child.GetArity(), seq[1:], currentEnv) + ch <- childResults + } + } +} + +// SkipTreeTermAndContinue2 skips a Term and his args if needed +func (dNode DiscriminationNode) SkipTreeTermAndContinue2(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []CandidatResult { + var subs []CandidatResult + + if needed == 0 { + return dNode.retrieveRec2(remainingQuery, substitutions) + } + + for _, child := range dNode.getChildren().GetSlice() { + // (amount - 1) + arity. If meta (amount - 1) + 0 else (amount - 1) + len(args) + arite := child.GetArity() + newNeeded := needed - 1 + arite + matches := child.SkipTreeTermAndContinue2(newNeeded, remainingQuery, substitutions) + subs = append(subs, matches...) + } + return subs +} diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 224a1abe..a14e4787 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -470,7 +470,6 @@ func TestFirstElementToSymbolType(t *testing.T) { }) t.Run("Complex_Fun", func(t *testing.T) { - // f_gax_c represents f(g(a, x), c), which has 2 direct top-level arguments st := FirstElementToSymbolType(f_gax_c) if st.GetArity() != 2 { t.Errorf("Expected arity 2 for complex function f_gax_c, got %d", st.GetArity()) @@ -483,7 +482,6 @@ func TestFirstElementToSymbolType(t *testing.T) { t.Error("Expected a panic/exception when passing nil to FirstElementToSymbolType, but it completed without panicking") } }() - // Passing nil will trigger the default case inside the switch block or cause a controlled crash FirstElementToSymbolType(nil) }) } @@ -1491,6 +1489,7 @@ func TestUnify(t *testing.T) { } }) } + func TestUnifyTerm(t *testing.T) { t.Run("UnifyTerm_pax_with_pay", func(t *testing.T) { @@ -1917,3 +1916,647 @@ func TestRetrieveCustomType(t *testing.T) { }) } + +func TestSymbolTypeGetters(t *testing.T) { + + symWithTerm := makeSymbolType(TermNode{Term: a}, 1) + symWithTy := makeSymbolTypeTy(TyNode{Ty: AST.TInt()}) + symWithString := makeSymbolType(NodeString("test_string"), 0) + + t.Run("Test_getTerm_term", func(t *testing.T) { + + gotTerm := symWithTerm.getTerm() + if gotTerm == nil { + t.Fatalf("getTerm() failed: expected a term, got nil") + } + if gotTerm.ToString() != a.ToString() { + t.Errorf("getTerm() failed: expected %s, got %s", a.ToString(), gotTerm.ToString()) + } + }) + + t.Run("Test_getTerm_ty", func(t *testing.T) { + + gotTermFromTy := symWithTy.getTerm() + if gotTermFromTy != nil { + t.Errorf("getTerm() from TyNode failed: expected nil, got %v", gotTermFromTy) + } + + }) + + t.Run("Test_getTerm_term", func(t *testing.T) { + + gotTermFromString := symWithString.getTerm() + if gotTermFromString != nil { + t.Errorf("getTerm() from NodeString failed: expected nil, got %v", gotTermFromString) + } + }) + + t.Run("Test_GetTy_type", func(t *testing.T) { + + gotTy := symWithTy.GetTy() + if gotTy == nil { + t.Fatalf("GetTy() failed: expected a type, got nil") + } + if gotTy.ToString() != AST.TInt().ToString() { + t.Errorf("GetTy() failed: expected %s, got %s", AST.TInt().ToString(), gotTy.ToString()) + } + + }) + + t.Run("Test_GetTy_term", func(t *testing.T) { + + gotTyFromTerm := symWithTerm.GetTy() + if gotTyFromTerm != nil { + t.Errorf("GetTy() from TermNode failed: expected nil, got %v", gotTyFromTerm) + } + + }) + + t.Run("Test_GetTy_string", func(t *testing.T) { + + gotTyFromString := symWithString.GetTy() + if gotTyFromString != nil { + t.Errorf("GetTy() from NodeString failed: expected nil, got %v", gotTyFromString) + } + }) +} + +func TestNodeStringMethods(t *testing.T) { + ns1 := NodeString("testString") + ns1Upper := NodeString("TESTSTRING") + ns2 := NodeString("otherString") + otherNode := TermNode{Term: a} + + t.Run("IsMeta", func(t *testing.T) { + if ns1.IsMeta() { + t.Fatalf("NodeString should never be a meta variable") + } + }) + + t.Run("GetArityType", func(t *testing.T) { + if ns1.GetArityType() != 0 { + t.Fatalf("NodeString arity type should always be 0") + } + }) + + t.Run("Equals_Same_And_Case_Insensitive", func(t *testing.T) { + if !ns1.Equals(ns1) { + t.Errorf("Should be equal to itself") + } + if !ns1.Equals(ns1Upper) { + t.Errorf("Should be equal due to case insensitivity") + } + }) + + t.Run("Equals_Different_Values_And_Types", func(t *testing.T) { + if ns1.Equals(ns2) { + t.Errorf("Should not be equal to a different string") + } + if ns1.Equals(otherNode) { + t.Errorf("Should not be equal to a different NodeElement type") + } + }) +} + +func TestTermNodeMethods(t *testing.T) { + tnRegular := TermNode{Term: a} + tnMeta := TermNode{Term: x} + tnDifferent := TermNode{Term: b} + tnNil := TermNode{Term: nil} + + t.Run("IsMeta", func(t *testing.T) { + if tnRegular.IsMeta() { + t.Errorf("Regular term shouldn't be meta") + } + if !tnMeta.IsMeta() { + t.Errorf("Meta term should be recognized as meta") + } + }) + + t.Run("GetArityType", func(t *testing.T) { + expectedRegularArity := a.GetMetaList().Len() + if tnRegular.GetArityType() != expectedRegularArity { + t.Errorf("Expected arity %d, got %d", expectedRegularArity, tnRegular.GetArityType()) + } + + expectedMetaArity := x.GetMetaList().Len() + if tnMeta.GetArityType() != expectedMetaArity { + t.Errorf("Expected arity %d, got %d", expectedMetaArity, tnMeta.GetArityType()) + } + }) + + t.Run("Equals", func(t *testing.T) { + if !tnRegular.Equals(tnRegular) { + t.Errorf("Identical TermNodes should be equal") + } + if tnRegular.Equals(tnDifferent) { + t.Errorf("Different TermNodes shouldn't be equal") + } + if tnRegular.Equals(tnNil) || tnNil.Equals(tnRegular) { + t.Errorf("Comparison with a nil term should be false") + } + }) +} + +func TestTyNodeMethods(t *testing.T) { + // Variables declared at the top using global variables 'x' and 'random_type' + tnStandard := TyNode{Ty: AST.TIndividual()} + tnDifferent := TyNode{Ty: random_type} + tnNil := TyNode{Ty: nil} + + t.Run("GetArityType", func(t *testing.T) { + if tnStandard.GetArityType() != 0 { + t.Errorf("TyNode arity type should always be 0") + } + }) + + t.Run("Equals", func(t *testing.T) { + if !tnStandard.Equals(tnStandard) { + t.Errorf("Identical TyNodes should be equal") + } + if tnStandard.Equals(tnDifferent) { + t.Errorf("Different TyNodes shouldn't be equal") + } + if tnStandard.Equals(tnNil) || tnNil.Equals(tnStandard) { + t.Errorf("Comparison with a nil type should be false") + } + }) +} + +func TestUnify2(t *testing.T) { + + t.Run("Unify2_pax_with_pay", func(t *testing.T) { + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + + found, mix := tree.Unify2(pay) + + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a, X)" { + t.Errorf("Expected unified form to be 'P(a, X)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_pax_with_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + found, mix := tree.Unify2(pab) + + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a, X)" { + t.Errorf("Expected unified form to be 'P(a, X)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_pa_with_pa", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + found, mix := tree.Unify2(pa) + + if !found { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + if mix[0].GetForm().ToString() != "P(a)" { + t.Errorf("Expected unified form to be 'P(a)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_pax_with_pafy", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + found, mix := tree.Unify2(pafy) + + for _, elem := range mix { + fmt.Println("elem", elem.ToString()) + } + fmt.Println("len(elem)", len(mix)) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(a, X)" { + t.Errorf("Expected unified form to be 'P(a, X)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_pafx_with_pafy", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + found, mix := tree.Unify2(pafy) + + tree.Print() + fmt.Println("pafy", pafy.ToString()) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(a, f(X))" { + t.Errorf("Expected unified form to be 'P(a, f(X))', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_px_with_py", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + found, mix := tree.Unify2(py) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(X)" { + t.Errorf("Expected unified form to be 'P(X)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_pxy_with_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + found, mix := tree.Unify2(pab) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(X, Y)" { + t.Errorf("Expected unified form to be 'P(X, Y)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Unify2_Multiple_Inserts_with_py", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pfx.(AST.Pred)) + found, mix := tree.Unify2(py) + + if !found || len(mix) != 3 { + t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) + } + + for i, elem := range mix { + str := elem.GetForm().ToString() + if str != "P(a)" && str != "P(b)" && str != "P(f(X))" { + t.Errorf("Expected unified form %d to be 'P(a) or P(b) or P(f(X))', got '%s'", i, elem.GetForm().ToString()) + } + } + }) + + t.Run("Unify2_pggab_with_pxy", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) + found, mix := tree.Unify2(pxy) + + if !found || len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result") + } + if mix[0].GetForm().ToString() != "P(g(g(a)), b)" { + t.Errorf("Expected unified form to be ''P(g(g(a)), b)', got '%s'", mix[0].GetForm().ToString()) + } + }) + + t.Run("Exception_Unify2_pa_with_pb", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + found, mix := tree.Unify2(pb) + + if found { + t.Errorf("Unification should have failed for P(a) and P(b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list, got %d elements", len(mix)) + } + }) + + t.Run("Exception_Unify2_pxx_with_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + found, mix := tree.Unify2(pab) + + if found { + t.Errorf("Unification should have failed: P(x, x) cannot unify with P(a, b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list") + } + }) + + t.Run("Exception_Unify2_pba_pab_with_pxx", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + found, mix := tree.Unify2(pxx) + + if found { + t.Errorf("Unification should have failed: P(X, X) cannot unify with P(b, a) or P(a, b)") + } + if len(mix) != 0 { + t.Errorf("Expected empty result list") + } + }) + + t.Run("Exception_Unify2_pab_with_pxx", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + found, mix := tree.Unify2(pxx) + + if found || len(mix) != 0 { + t.Errorf("Unification should have failed: P(a, b) cannot unify with P(X, X)") + } + }) +} + +func TestUnifyTerm2(t *testing.T) { + + t.Run("UnifyTerm2_pax_with_pay", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) + + tree.Print() + fmt.Println(queryTerm.ToString()) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 unification result, got %d", len(mix)) + } + }) + + t.Run("UnifyTerm2_pax_with_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pax.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + } + }) + + t.Run("UnifyTerm2_pa_with_pa", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + queryTerm := subst.TransformPred(pa.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val { + t.Fatalf("Unification failed, expected success") + } + if len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, got %d", len(mix)) + } + if mix[0].ToString() != "P(a) {}" { + t.Errorf("Expected result 'P(a) {}', got '%s'", mix[0].ToString()) + } + }) + + t.Run("UnifyTerm2_pab_with_pay", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pay.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(a, Y)" { + t.Errorf("Expected term to be 'P(a, Y)', got '%s'", mix[0].Term().ToString()) + } + }) + + t.Run("UnifyTerm2_pafx_with_pafy", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pafx.(AST.Pred)) + queryTerm := subst.TransformPred(pafy.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(a, f(Y))" { + t.Errorf("Expected term to be 'P(a, f(Y))', got '%s'", mix[0].Term().ToString()) + } + }) + + t.Run("UnifyTerm2_px_with_py", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(px.(AST.Pred)) + queryTerm := subst.TransformPred(py.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(Y)" { + t.Errorf("Expected term to be 'P(Y)', got '%s'", mix[0].Term().ToString()) + } + }) + + t.Run("UnifyTerm2_pxy_with_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pxy.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(a, b)" { + t.Errorf("Expected term to be 'P(a, b)', got '%s'", mix[0].Term().ToString()) + } + }) + + t.Run("UnifyTerm2_Multiple_Inserts_with_py", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pb.(AST.Pred)) + tree = tree.Insert(pa.(AST.Pred)) + tree = tree.Insert(pfx.(AST.Pred)) + queryTerm := subst.TransformPred(py.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val { + t.Fatalf("Unification failed, expected success with matches") + } + if len(mix) != 3 { + t.Fatalf("Expected exactly 3 unification results, got %d", len(mix)) + } + for i, elem := range mix { + if elem.Term().ToString() != "P(Y)" { + t.Errorf("Expected unified term %d to be 'P(Y)', got '%s'", i, elem.Term().ToString()) + } + } + }) + + t.Run("UnifyTerm2_pggab_with_pxy", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pggab.(AST.Pred)) + queryTerm := subst.TransformPred(pxy.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if !val || len(mix) != 1 { + t.Fatalf("Expected exactly 1 result, found valid=%t, len=%d", val, len(mix)) + } + if mix[0].Term().ToString() != "P(X, Y)" { + t.Errorf("Expected term to be 'P(X, Y)', got '%s'", mix[0].Term().ToString()) + } + }) + + t.Run("Exception_UnifyTerm2_pa_with_pb", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pa.(AST.Pred)) + queryTerm := subst.TransformPred(pb.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if val { + t.Fatalf("Unification should have failed for P(a) and P(b)") + } + if len(mix) != 0 { + t.Fatalf("Expected 0 elements, got %d", len(mix)) + } + }) + + t.Run("Exception_UnifyTerm2_pxx_with_pab", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pxx.(AST.Pred)) + queryTerm := subst.TransformPred(pab.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if val || len(mix) != 0 { + t.Fatalf("Unification should have failed: P(x,x) cannot unify with P(a,b)") + } + }) + + t.Run("Exception_UnifyTerm2_pba_pab_with_pxx", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pba.(AST.Pred)) + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if val { + t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + } + }) + + t.Run("Exception_UnifyTerm2_pab_with_pxx", func(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pab.(AST.Pred)) + queryTerm := subst.TransformPred(pxx.(AST.Pred)) + + val, mix := tree.UnifyTerm2(queryTerm) + + if val { + t.Fatalf("Unification should have failed: expected 0 elements, got %d", len(mix)) + } + if len(mix) != 0 { + t.Fatalf("Expected result array to be empty, got %d elements", len(mix)) + } + }) +} + +func TestReconstructTerm(t *testing.T) { + ctx := NewContext() + + t.Run("Reconstruct_Constant", func(t *testing.T) { + seq := parseTerm(a, ctx).GetSlice() + term, remaining := ReconstructTerm(seq) + + if term == nil { + t.Fatalf("Expected to reconstruct a term, got nil") + } + if term.ToString() != "a" { + t.Errorf("Expected term 'a', got '%s'", term.ToString()) + } + if len(remaining) != 0 { + t.Errorf("Expected remaining sequence to be empty, got %d elements", len(remaining)) + } + }) + + t.Run("Reconstruct_Function_fxy", func(t *testing.T) { + seq := parseTerm(fxy, ctx).GetSlice() + term, remaining := ReconstructTerm(seq) + + if term == nil { + t.Fatalf("Expected to reconstruct a term, got nil") + } + if term.ToString() != "f(v1, v2)" { // v1 and v2 due to parseTerm mapping + t.Errorf("Expected reconstructed term 'f(v1, v2)', got '%s'", term.ToString()) + } + if len(remaining) != 0 { + t.Errorf("Expected remaining sequence to be empty") + } + }) + + t.Run("Reconstruct_Nested_Subsequence", func(t *testing.T) { + // If we only pass a slice of the sequence, it should reconstruct just that part + // e.g., for f(a, b), seq is [f, a, b]. If we pass seq[1:], it should reconstruct 'a' and leave [b] + seq := parseTerm(fab, ctx).GetSlice() + + subSeq := seq[1:] // [a, b] + term, remaining := ReconstructTerm(subSeq) + + if term == nil || term.ToString() != "a" { + t.Fatalf("Expected to reconstruct term 'a'") + } + if len(remaining) != 1 { + t.Fatalf("Expected exactly 1 remaining element, got %d", len(remaining)) + } + if remaining[0].getSymbol().ToString() != "b" { + t.Errorf("Expected remaining element to be 'b', got '%s'", remaining[0].getSymbol().ToString()) + } + }) +} diff --git a/src/Unif/substitution/data_structure.go b/src/Unif/substitution/data_structure.go index d9eb23d6..78bc1640 100644 --- a/src/Unif/substitution/data_structure.go +++ b/src/Unif/substitution/data_structure.go @@ -168,6 +168,7 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { case AST.Fun: if !t1.GetID().Equals(t2.GetID()) { + fmt.Printf("ID WRONG T1 ID: %v, T2 ID: %v\n", t1.GetID(), t2.GetID()) return Failure() } args1 := t1.GetArgs() @@ -175,6 +176,7 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { fmt.Printf("%v\n", Lib.ListToString(args1)) fmt.Printf("%v\n", Lib.ListToString(args2)) if args1.Len() != args2.Len() { + fmt.Printf("ARITY WRONG T1 Arity: %d, T2 Arity: %d\n", args1.Len(), args2.Len()) return Failure() } for i := range args1.GetSlice() { From 6ad4fa528735f62337a8d9837f1509995b08254b Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Sun, 21 Jun 2026 18:23:56 +0200 Subject: [PATCH 20/23] Add more test --- src/Unif/discriminationtree/dt_test.go | 197 ++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 4 deletions(-) diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index a14e4787..88d075dc 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -343,12 +343,12 @@ func initTestVariable2() { ) p_typed_pred_int_x = AST.MakerPred(p_typed_id, - Lib.MkListV[AST.Ty](AST.TInt()), + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), Lib.MkListV[AST.Term](x), ) p_typed_pred_int_x_y_z = AST.MakerPred(p_typed_id, - Lib.MkListV[AST.Ty](AST.TInt()), + Lib.MkListV[AST.Ty](AST.MkTyConst("int")), Lib.MkListV[AST.Term](x, y, z), ) @@ -1832,7 +1832,6 @@ func TestInsertCustomType(t *testing.T) { tree := NewNode() tree = tree.Insert(p_typed_pred_int_3) - tree.Print() t1 := tree.getChildren() if t1.Len() != 1 { @@ -1871,14 +1870,204 @@ func TestInsertCustomType(t *testing.T) { tree = tree.Insert(p_typed_pred_int_2_int_3_b) tree = tree.Insert(p_typed_pred_int_x_y_z) tree = tree.Insert(p_typed_pred_reel_x_y_z) + tree = tree.Insert(p_typed_pred_int_2_double_4_a) + + // 1. Root Level + rootChildren := tree.getChildren() + if rootChildren.Len() != 1 { + t.Fatalf("Expected root to have exactly 1 child, got %d", rootChildren.Len()) + } + pNode := rootChildren.At(0) + if pNode.toString() != "p" { + t.Fatalf("Expected 'p', got '%s'", pNode.toString()) + } + + // 2. Under 'p' -> 'int' and '$real' + pChildren := pNode.getChildren() + if pChildren.Len() != 2 { + t.Fatalf("Expected 'p' to have 2 children, got %d", pChildren.Len()) + } + + intNode := pChildren.At(0) + if intNode.toString() != "int" { + t.Fatalf("Expected first child of 'p' to be 'int', got '%s'", intNode.toString()) + } + + realNode := pChildren.At(1) + if realNode.toString() != "$real" { + t.Fatalf("Expected second child of 'p' to be '$real', got '%s'", realNode.toString()) + } + + // ----------------------------------------------------- + // 3. Under 'int' -> '2', 'v1', 'double' + // ----------------------------------------------------- + intChildren := intNode.getChildren() + if intChildren.Len() != 3 { + t.Fatalf("Expected 'int' to have 3 children ('2', 'v1', 'double'), got %d", intChildren.Len()) + } + + // Branch: int -> 2 -> 3 -> a/b + node2 := intChildren.At(0) + if node2.toString() != "2" { + t.Fatalf("Expected '2', got '%s'", node2.toString()) + } + node2Children := node2.getChildren() + if node2Children.Len() != 1 { + t.Fatalf("Expected '2' to have 1 child ('3'), got %d", node2Children.Len()) + } + node3 := node2Children.At(0) + if node3.toString() != "3" { + t.Fatalf("Expected '3', got '%s'", node3.toString()) + } + node3Children := node3.getChildren() + if node3Children.Len() != 2 { + t.Fatalf("Expected '3' to have 2 children ('a' and 'b'), got %d", node3Children.Len()) + } + if node3Children.At(0).toString() != "a" || node3Children.At(1).toString() != "b" { + t.Fatalf("Expected children of '3' to be 'a' and 'b'") + } + + // Branch: int -> v1 -> v2 -> v3 + nodeV1 := intChildren.At(1) + if nodeV1.toString() != "v1" { + t.Fatalf("Expected 'v1', got '%s'", nodeV1.toString()) + } + nodeV1Children := nodeV1.getChildren() + if nodeV1Children.Len() != 1 || nodeV1Children.At(0).toString() != "v2" { + t.Fatalf("Expected 'v1' to have child 'v2'") + } + nodeV2Children := nodeV1Children.At(0).getChildren() + if nodeV2Children.Len() != 1 || nodeV2Children.At(0).toString() != "v3" { + t.Fatalf("Expected 'v2' to have child 'v3'") + } + + // Branch: int -> double -> $i -> 2 -> 4 -> a + nodeDouble := intChildren.At(2) + if nodeDouble.toString() != "double" { + t.Fatalf("Expected 'double', got '%s'", nodeDouble.toString()) + } + nodeDoubleChildren := nodeDouble.getChildren() + if nodeDoubleChildren.Len() != 1 || nodeDoubleChildren.At(0).toString() != "$i" { + t.Fatalf("Expected 'double' to have child '$i'") + } + nodeIChildren := nodeDoubleChildren.At(0).getChildren() + if nodeIChildren.Len() != 1 || nodeIChildren.At(0).toString() != "2" { + t.Fatalf("Expected '$i' to have child '2'") + } + node2DoubleChildren := nodeIChildren.At(0).getChildren() + if node2DoubleChildren.Len() != 1 || node2DoubleChildren.At(0).toString() != "4" { + t.Fatalf("Expected '2' to have child '4'") + } + node4Children := node2DoubleChildren.At(0).getChildren() + if node4Children.Len() != 1 || node4Children.At(0).toString() != "a" { + t.Fatalf("Expected '4' to have child 'a'") + } + // ----------------------------------------------------- + // 4. Under '$real' -> v1 -> v2 -> v3 + // ----------------------------------------------------- + realChildren := realNode.getChildren() + if realChildren.Len() != 1 { + t.Fatalf("Expected '$real' to have 1 child ('v1'), got %d", realChildren.Len()) + } + realV1 := realChildren.At(0) + if realV1.toString() != "v1" { + t.Fatalf("Expected 'v1' under '$real', got '%s'", realV1.toString()) + } + realV1Children := realV1.getChildren() + if realV1Children.Len() != 1 || realV1Children.At(0).toString() != "v2" { + t.Fatalf("Expected 'v1' to have child 'v2'") + } + realV2Children := realV1Children.At(0).getChildren() + if realV2Children.Len() != 1 || realV2Children.At(0).toString() != "v3" { + t.Fatalf("Expected 'v2' to have child 'v3'") + } }) t.Run("Insert_different_typed_var", func(t *testing.T) { tree := NewNode() tree = tree.Insert(p_typed_pred_int_2_double_4_a) - tree.Print() + tree = tree.Insert(p_typed_pred_int_2_double_4_b) + tree = tree.Insert(p_typed_pred_int_2_double_4_x) + + // 1. Root Level validation + rootChildren := tree.getChildren() + if rootChildren.Len() != 1 { + t.Fatalf("Expected root to have exactly 1 child, got %d", rootChildren.Len()) + } + pNode := rootChildren.At(0) + if pNode.toString() != "p" { + t.Fatalf("Expected node to be 'p', got '%s'", pNode.toString()) + } + + // 2. Under 'p' -> 'int' + pChildren := pNode.getChildren() + if pChildren.Len() != 1 { + t.Fatalf("Expected 'p' to have exactly 1 child, got %d", pChildren.Len()) + } + intNode := pChildren.At(0) + if intNode.toString() != "int" { + t.Fatalf("Expected node to be 'int', got '%s'", intNode.toString()) + } + + // 3. Under 'int' -> 'double' + intChildren := intNode.getChildren() + if intChildren.Len() != 1 { + t.Fatalf("Expected 'int' to have exactly 1 child, got %d", intChildren.Len()) + } + doubleNode := intChildren.At(0) + if doubleNode.toString() != "double" { + t.Fatalf("Expected node to be 'double', got '%s'", doubleNode.toString()) + } + + // 4. Under 'double' -> '$i' + doubleChildren := doubleNode.getChildren() + if doubleChildren.Len() != 1 { + t.Fatalf("Expected 'double' to have exactly 1 child, got %d", doubleChildren.Len()) + } + iNode := doubleChildren.At(0) + if iNode.toString() != "$i" { + t.Fatalf("Expected node to be '$i', got '%s'", iNode.toString()) + } + + // 5. Under '$i' -> '2' + iChildren := iNode.getChildren() + if iChildren.Len() != 1 { + t.Fatalf("Expected '$i' to have exactly 1 child, got %d", iChildren.Len()) + } + node2 := iChildren.At(0) + if node2.toString() != "2" { + t.Fatalf("Expected node to be '2', got '%s'", node2.toString()) + } + + // 6. Under '2' -> '4' + node2Children := node2.getChildren() + if node2Children.Len() != 1 { + t.Fatalf("Expected '2' to have exactly 1 child, got %d", node2Children.Len()) + } + node4 := node2Children.At(0) + if node4.toString() != "4" { + t.Fatalf("Expected node to be '4', got '%s'", node4.toString()) + } + + // 7. Under '4' -> Forks into 'a', 'b', and 'v1' + node4Children := node4.getChildren() + if node4Children.Len() != 3 { + t.Fatalf("Expected '4' to have exactly 3 children ('a', 'b', 'v1'), got %d", node4Children.Len()) + } + + if node4Children.At(0).toString() != "a" { + t.Fatalf("Expected first child of '4' to be 'a', got '%s'", node4Children.At(0).toString()) + } + + if node4Children.At(1).toString() != "b" { + t.Fatalf("Expected second child of '4' to be 'b', got '%s'", node4Children.At(1).toString()) + } + + if node4Children.At(2).toString() != "v1" { + t.Fatalf("Expected third child of '4' to be 'v1', got '%s'", node4Children.At(2).toString()) + } }) } From 9b75fe31f32b42c69fb876fd48063b5469cfd630 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Mon, 22 Jun 2026 03:32:03 +0200 Subject: [PATCH 21/23] Remove CPU Bound on Benchmark because it's annoying to set it back --- devtools/run_theorem_test.py | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/devtools/run_theorem_test.py b/devtools/run_theorem_test.py index c20b51a2..1375cf14 100644 --- a/devtools/run_theorem_test.py +++ b/devtools/run_theorem_test.py @@ -2,7 +2,6 @@ import sys import re import time -import subprocess from pathlib import Path from subprocess import PIPE, run @@ -20,25 +19,12 @@ def LaunchTest(prover_name, command_line, succes, f, memory_limit=None, failure= f.write("Proof not found\n") return res -def set_cpu_freq(freq_khz): - # Define CPU Speed - cmd = f"sudo cpupower frequency-set -g performance -u {freq_khz} -d {freq_khz}" - result = subprocess.run(cmd, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True) - if result.returncode != 0: - print("Error. sudo ?.") - print(result.stderr) - else: - print(f"CPU Speed is {int(freq_khz)/1000} MHz.") if len(sys.argv) < 3: print(f"python3 {sys.argv[0]} problem_folder timeout goeland_options") else: NB_CORES = 4 CORES = ",".join(str(i) for i in range(NB_CORES)) - FREQ_KHZ = 2000000 # 2.0 GHz - - set_cpu_freq(FREQ_KHZ) - print(f"{NB_CORES} cores are working") folder = sys.argv[1] folder_split = folder.split("/") @@ -48,10 +34,10 @@ def set_cpu_freq(freq_khz): total = len(entries) filename = "" - for i in range (0,sys.maxsize) : - a = Path("resultV1_" + str(i) + ".txt") - if not a.exists(): - filename = a + for i in range(0, sys.maxsize): + a = Path("resultV1_" + str(i) + ".txt") + if not a.exists(): + filename = a break with open(filename, "w") as f: @@ -61,7 +47,7 @@ def set_cpu_freq(freq_khz): f.write(f"Problem {index+1}/{len(entries)} : {folder+file}\n") command = ( f"taskset -c {CORES} env GOMAXPROCS={NB_CORES} " - f"timeout {timeout} ../src/_build/goeland -dt " + f"timeout {timeout} ../src/_build/goeland " + " ".join(sys.argv[4:]) + " " + folder + file ) if LaunchTest("Goéland", command, "% RES : VALID", f, None, "% RES : NOT VALID"): @@ -69,4 +55,4 @@ def set_cpu_freq(freq_khz): milli_sec_fin = int(round(time.time() * 1000)) timer = milli_sec_fin - milli_sec_deb f.write(f"Number of problems solved : {cpt}/{total}\n") - f.write(f"Execution Time : {timer}ms\n") + f.write(f"Execution Time : {timer}ms\n") \ No newline at end of file From e8ff625497df69b94e51d9d77ae645a51eb17d67 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Sat, 18 Jul 2026 21:33:08 +0200 Subject: [PATCH 22/23] Upgrading the discrminationTree and working on why test-suite/proofs/tf1_basic_thm-2.p doesn't works anymore --- devtools/run-test-suite.py | 2 +- src/Core/subst_and_form.go | 30 ++ src/Mods/dmt/dmt.go | 2 +- src/Mods/equality/bse/equality_problem.go | 7 +- src/Search/child_management.go | 23 +- src/Search/destructive.go | 29 +- .../discrimination-trees.go | 306 ++++++++++++++++-- src/Unif/discriminationtree/dt_test.go | 60 ++-- 8 files changed, 398 insertions(+), 61 deletions(-) diff --git a/devtools/run-test-suite.py b/devtools/run-test-suite.py index 03b558eb..9dffbe90 100644 --- a/devtools/run-test-suite.py +++ b/devtools/run-test-suite.py @@ -52,7 +52,7 @@ def getCommandLine(self): arguments = self.arguments if not self.no_rocq_check: arguments += " -context -orocq" - return self.env + " ../src/_build/goeland -dt " + arguments + " " + self.filename + return self.env + " ../src/_build/goeland " + arguments + " " + self.filename def getArgsForPrinting(self): rocq_chk_str = "" diff --git a/src/Core/subst_and_form.go b/src/Core/subst_and_form.go index 2b5f9408..c6bcbd6f 100644 --- a/src/Core/subst_and_form.go +++ b/src/Core/subst_and_form.go @@ -204,6 +204,36 @@ func MergeSubstAndForm(s1, s2 SubstAndForm) (error, SubstAndForm) { return nil, MakeSubstAndForm(new_subst, newFormList) } +// Func made because basic-quant-6 was failing with dTree due to merge error +/* Try to merge two SubstAndForm, without assuming they are compatible. + * + * Unlike MergeSubstAndForm, a merge conflict here (e.g. two branches that + * instantiated a shared meta-variable with two different terms) is treated + * as a normal, recoverable "no" - not as a fatal anomaly. Use this whenever + * the caller can gracefully discard an incompatible candidate instead of + * assuming the two substitutions are "supposed to fit". + */ +func TryMergeSubstAndForm(s1, s2 SubstAndForm) (bool, SubstAndForm) { + if s1.IsEmpty() { + return true, s2 + } + + if s2.IsEmpty() { + return true, s1 + } + + new_subst, succeeded := Unif.MergeMixedSubstitutions(s1.GetSubst(), s2.GetSubst()) + + if !succeeded { + return false, MakeEmptySubstAndForm() + } + + newFormList := s1.GetForm() + newFormList = Lib.ListAdd(newFormList, s2.GetForm().GetSlice()...) + + return true, MakeSubstAndForm(new_subst, newFormList) +} + /* Merge a list of subst with one subst */ func MergeSubstListWithSubst(sl []SubstAndForm, subst SubstAndForm) (error, []SubstAndForm) { sl_res := []SubstAndForm{} diff --git a/src/Mods/dmt/dmt.go b/src/Mods/dmt/dmt.go index 3f2b6f77..a321d47d 100644 --- a/src/Mods/dmt/dmt.go +++ b/src/Mods/dmt/dmt.go @@ -87,7 +87,7 @@ func initPluginGlobalVariables() { if Glob.GetDt() { positiveTree = discriminationtree.NewNode() - positiveTree = discriminationtree.NewNode() + negativeTree = discriminationtree.NewNode() } else { positiveTree = codetree.NewNode() negativeTree = codetree.NewNode() diff --git a/src/Mods/equality/bse/equality_problem.go b/src/Mods/equality/bse/equality_problem.go index cd9130ca..591da352 100644 --- a/src/Mods/equality/bse/equality_problem.go +++ b/src/Mods/equality/bse/equality_problem.go @@ -44,8 +44,10 @@ import ( "github.com/GoelandProver/Goeland/Glob" "github.com/GoelandProver/Goeland/Lib" "github.com/GoelandProver/Goeland/Mods/equality/eqStruct" - Unif "github.com/GoelandProver/Goeland/Unif/substitution" "github.com/GoelandProver/Goeland/Unif/codetree" + "github.com/GoelandProver/Goeland/Unif/discriminationtree" + + Unif "github.com/GoelandProver/Goeland/Unif/substitution" ) type EqualityProblem struct { @@ -142,6 +144,9 @@ func makeDataStructFromEqualities(eq Equalities) Unif.DataStructure { formList.Append(e.GetT1(), e.GetT2()) } + if Glob.GetDt() { + return discriminationtree.MakeTermUnifProblem(Lib.ListCpy(formList)) + } return codetree.MakeTermUnifProblem(Lib.ListCpy(formList)) } diff --git a/src/Search/child_management.go b/src/Search/child_management.go index d2be1902..4242952a 100644 --- a/src/Search/child_management.go +++ b/src/Search/child_management.go @@ -186,11 +186,24 @@ func (ds *destructiveSearch) passSubstToParent(args wcdArgs, proofChildren [][]P ) }), ) - err, merged := Core.MergeSubstAndForm(subst, args.st.GetAppliedSubst()) - - if err != nil { - Glob.Anomaly("WC", "Error when merging the children substitution's with the applied one.") - return err + succeeded, merged := Core.TryMergeSubstAndForm(subst, args.st.GetAppliedSubst()) + + if !succeeded { + // This candidate conflicts with what is already applied at this node + // (typically: two sibling branches instantiated a shared meta-variable + // with different terms - e.g. Y9 -> a here vs. Y9 -> c already applied). + // That is an expected search outcome, not an anomaly, so we discard this + // candidate - same as the "cleaned.Empty()" case below - instead of + // aborting the whole proof search. + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "Substitution %v incompatible with applied subst %v: discarded", + subst.ToString(), + args.st.GetAppliedSubst().ToString()) + }), + ) + continue } cleaned := Core.RemoveElementWithoutMM(merged.GetSubst(), args.st.GetMM()) diff --git a/src/Search/destructive.go b/src/Search/destructive.go index 747c6154..389b93a4 100644 --- a/src/Search/destructive.go +++ b/src/Search/destructive.go @@ -912,10 +912,14 @@ func (ds *destructiveSearch) selectChildren(father Communication, children *[]Co new_result_subst := []Core.SubstAndForm{} for _, s := range result_subst { if !Lib.ListEquals(s.GetSubst(), new_current_subst.GetSubst()) { - err, new_subst := Core.MergeSubstAndForm(s.Copy(), new_current_subst.Copy()) - - if err != nil { - Glob.Anomaly("SLC", "Error when merging substitutions.") + succeeded, new_subst := Core.TryMergeSubstAndForm(s.Copy(), new_current_subst.Copy()) + + if !succeeded { + // Expected outcome (conflicting sibling substitutions), not an anomaly. + debug(Lib.MkLazy(func() string { + return fmt.Sprintf("Substitutions %v and %v incompatible: discarded", s.ToString(), new_current_subst.ToString()) + })) + continue } new_result_subst = append(new_result_subst, new_subst) @@ -1189,10 +1193,21 @@ func (ds *destructiveSearch) ManageClosureRule( ) // Merge with applied subst (if any) - err, subst_and_form_for_father := Core.MergeSubstAndForm(subst_and_form_for_father.Copy(), st.GetAppliedSubst()) + succeeded, subst_and_form_for_father := Core.TryMergeSubstAndForm(subst_and_form_for_father.Copy(), st.GetAppliedSubst()) - if err != nil { - Glob.Anomaly("MCR", "Contradiction found between applied subst and child subst.") + if !succeeded { + // This candidate conflicts with what is already applied at this node + // (e.g. two sibling branches instantiating a shared meta-variable + // differently) - an expected search outcome, not an anomaly, so we + // discard this candidate and keep checking the others. + debug( + Lib.MkLazy(func() string { + return fmt.Sprintf( + "Candidate %v incompatible with applied subst %v: discarded", + subst_and_form_for_father.ToString(), + st.GetAppliedSubst().ToString()) + }), + ) } else { st.SetSubstsFound(Core.AppendIfNotContainsSubstAndForm(st.GetSubstsFound(), subst_and_form_for_father)) diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 4faaea3d..5d3676c0 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -283,32 +283,50 @@ type DiscriminationNode struct { symbol SymbolType // Contain the AST.Term and Arity children Lib.List[DiscriminationNode] // All the children of the node leafFor Lib.List[AST.Pred] // If not empty, contains the where it come from + + // Parallel to leafFor, but for trees populated with raw terms (InsertTerm) + // instead of predicates (Insert) - used by UnifyTerm/UnifyTermWithSubst. + // Kept separate from leafFor rather than merged into a single Either-typed + // field, since nothing in this codebase mixes both kinds of insertion on + // the same tree instance: it keeps this addition purely additive, with + // zero risk to the existing predicate-based Insert/Unify/Unify2 code. + termLeafFor Lib.List[AST.Term] + + // Set once, on the tree returned by InsertTerm/MakeTermUnifProblem, so + // UnifyTermWithSubst knows which single traversal to run instead of + // always running both (which would double the cost of every call for + // no benefit, since a given tree is always exclusively one or the + // other in practice). + termOnly bool } // Basic Node. Create a SymbolType{nil, -1} and empty list for children and leafFor func NewNode() DiscriminationNode { return DiscriminationNode{ - symbol: SymbolType{symbol: nil, arity: -1}, - children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[AST.Pred](), + symbol: SymbolType{symbol: nil, arity: -1}, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[AST.Pred](), + termLeafFor: Lib.NewList[AST.Term](), } } // Basic Node with SymbolType and no empty list for children and leafFor func MakeDiscriminationNodeWithSym(sym SymbolType) DiscriminationNode { return DiscriminationNode{ - symbol: sym, - children: Lib.NewList[DiscriminationNode](), - leafFor: Lib.NewList[AST.Pred](), + symbol: sym, + children: Lib.NewList[DiscriminationNode](), + leafFor: Lib.NewList[AST.Pred](), + termLeafFor: Lib.NewList[AST.Term](), } } // Basic Node with SymbolType, children and empty List for leafFor func MakeDiscriminationNodeWithSymAndChildren(sym SymbolType, children Lib.List[DiscriminationNode]) DiscriminationNode { return DiscriminationNode{ - symbol: sym, - children: children, - leafFor: Lib.NewList[AST.Pred](), + symbol: sym, + children: children, + leafFor: Lib.NewList[AST.Pred](), + termLeafFor: Lib.NewList[AST.Term](), } } @@ -328,6 +346,10 @@ func (dNode DiscriminationNode) getLeafFor() Lib.List[AST.Pred] { return dNode.leafFor } +func (dNode DiscriminationNode) getTermLeafFor() Lib.List[AST.Term] { + return dNode.termLeafFor +} + // If NodeElement of the dNode is nil return "" else toString func (dNode DiscriminationNode) toString() string { sym := dNode.getSymbol().getSymbol() @@ -373,6 +395,27 @@ func MakeCandidat(p AST.Pred, sub subst.Substitutions) CandidatResult { } } +// Mirrors CandidatResult, for trees populated via InsertTerm instead of Insert. +type TermCandidatResult struct { + Term AST.Term // The raw term stored at this leaf + Subs subst.Substitutions // The associated substitution +} + +func (Candidat TermCandidatResult) getTerm() AST.Term { + return Candidat.Term +} + +func (Candidat TermCandidatResult) GetSubs() subst.Substitutions { + return Candidat.Subs +} + +func MakeTermCandidat(t AST.Term, sub subst.Substitutions) TermCandidatResult { + return TermCandidatResult{ + Term: t, + Subs: sub, + } +} + /*****************************/ /* End Structures definition */ /*****************************/ @@ -581,6 +624,75 @@ func (dNode DiscriminationNode) insertRec(seq Lib.List[SymbolType], originalTerm /********* End insrt *********/ /*****************************/ +/* Insert a raw term (as opposed to Insert, which takes a full predicate). + * Used to build a tree purely for term-level unification (UnifyTerm), the + * discriminationtree equivalent of codetree.MakeTermUnifProblem. */ +func (dNode DiscriminationNode) InsertTerm(t AST.Term) DiscriminationNode { + + sym_list := parseTerm(t, NewContext()) + result := dNode.insertTermRec(sym_list, t) + result.termOnly = true + return result +} + +// Auxiliary function for InsertTerm. Mirrors insertRec exactly, but stores +// into termLeafFor (AST.Term) instead of leafFor (AST.Pred). +func (dNode DiscriminationNode) insertTermRec(seq Lib.List[SymbolType], originalTerm AST.Term) DiscriminationNode { + + // End of recursion, time to insert + if seq.Len() == 0 { + Exist := false + for _, t := range dNode.getTermLeafFor().GetSlice() { + if t.Equals(originalTerm) { + Exist = true + break + } + } + if !Exist { + dNode.termLeafFor.Append(originalTerm) + } + return dNode + } + + sym := seq.At(0) // Current symbol + childrenSlice := dNode.getChildren().GetSlice() + + foundIndex := -1 // Index of the symbol if found + var ok = false // Boolean if a match is found + + // Looking for already existing child + for i, child := range childrenSlice { + if child.getSymbol().getSymbol().Equals(sym.getSymbol()) { + if child.GetArity() == sym.GetArity() { + foundIndex = i + ok = true + break + } else { + Glob.Anomaly("Arity Missmatch", "Same Symbol but different Arity") + } + + } + } + + if ok { // Child already exist + + // Insert and update the sequence + updatedChild := childrenSlice[foundIndex].insertTermRec(seq.RemoveAt(0), originalTerm) + dNode.children.Upd(foundIndex, updatedChild) // Update children[foundIntex] = updateChild + + } else { // if Child doesn't exist + + newChild := MakeDiscriminationNodeWithSym(sym) // Create a new Node with the new SymbolType and his leafFor + updatedChild := newChild.insertTermRec(seq.RemoveAt(0), originalTerm) // Insert the rest of the sequence after the new child + dNode.children.Append(updatedChild) // Update the children of the args node + } + return dNode +} + +/*****************************/ +/******* End term insrt ******/ +/*****************************/ + /*****************************/ /********** Retriev **********/ /*****************************/ @@ -643,7 +755,11 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S return results } - // Work goroutine + // Work goroutine: fan out one goroutine per child. Each child's subtree is + // independent of its siblings (no shared mutable state - currentEnv is + // read-only here, and every recursive call gets its own fresh results + // slice/channel), so this is safe, and lets wide/deep subtrees be searched + // in parallel instead of one child at a time. for _, child := range dNode.children.GetSlice() { wg.Add(1) // Create exactly 1 goroutine @@ -663,9 +779,10 @@ func (dNode DiscriminationNode) retrieveRec(seq []SymbolType, currentEnv subst.S return results } +// Retrieve all the Unifiable func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { - defer wg.Done() // Stop the Goroutine in case of failure to prevent crash or unknow behavior + defer wg.Done() // Always signal completion, even if this case matches nothing symQuery := seq[0] // Term of the Query // Case 1. Exact Match, we go to the next element @@ -673,25 +790,112 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri if isExactMatch { matches := child.retrieveRec(seq[1:], currentEnv) ch <- matches + return } // Case 2. The Symbol from the discriminationTree is a Meta // We need to look the len of the actual term from the sequence. f(x) == 2, y == 1 and we skip the entire term - if child.getSymbol().getSymbol().IsMeta() && !isExactMatch { + if child.getSymbol().getSymbol().IsMeta() { skip := GetSubTermLength(seq) if skip <= len(seq) { matches := child.retrieveRec(seq[skip:], currentEnv) ch <- matches } + return // Case 3. Reverse of the Case 2. // The term from the Sequence is a Meta, so we look the len of the term from the DTree and we got skip it. - } else if symQuery.getSymbol().IsMeta() && !isExactMatch { + } else if symQuery.getSymbol().IsMeta() { childResults := child.SkipTreeTermAndContinue(child.GetArity(), seq[1:], currentEnv) ch <- childResults } } +// Term-level mirror of SkipTreeTermAndContinue/RetrieveUnifiables/retrieveRec/ +// retrieveCase above: same exact logic, operating on termLeafFor (AST.Term) +// instead of leafFor (AST.Pred). Deliberately does not build up any +// substitution while walking down (case 3 below just skips structurally, +// like the classic predicate version does) - the caller redoes a full, +// clean Robinson unification at the end in UnifyTermWithSubst, so there is +// nothing here that could leak the tree's internal v1/v2-style normalized +// meta names into a caller's result (see the Unify2 fix elsewhere in this +// file for the bug this pattern avoids). + +func (dNode DiscriminationNode) SkipTreeTermAndContinueTerm(needed int, remainingQuery []SymbolType, substitutions subst.Substitutions) []TermCandidatResult { + + var subs []TermCandidatResult + + if needed == 0 { + return dNode.retrieveTermRec(remainingQuery, substitutions) + } + + for _, child := range dNode.getChildren().GetSlice() { + newNeeded := needed - 1 + child.GetArity() + matches := child.SkipTreeTermAndContinueTerm(newNeeded, remainingQuery, substitutions) + subs = append(subs, matches...) + } + return subs +} + +func (dNode DiscriminationNode) RetrieveUnifiableTerms(t AST.Term) []TermCandidatResult { + seq := parseTerm(t, NewContext()).GetSlice() + Env := subst.Substitutions{} + return dNode.retrieveTermRec(seq, Env) +} + +func (dNode DiscriminationNode) retrieveTermRec(seq []SymbolType, currentEnv subst.Substitutions) []TermCandidatResult { + + ch := make(chan []TermCandidatResult) + var results []TermCandidatResult + var wg sync.WaitGroup + + if len(seq) == 0 { + for _, t := range dNode.getTermLeafFor().GetSlice() { + results = append(results, MakeTermCandidat(t, currentEnv)) + } + return results + } + + for _, child := range dNode.children.GetSlice() { + wg.Add(1) + go retrieveCaseTerm(seq, currentEnv, child, ch, &wg) + } + + go func() { + wg.Wait() + close(ch) + }() + + for matches := range ch { + results = append(results, matches...) + } + + return results +} + +func retrieveCaseTerm(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []TermCandidatResult, wg *sync.WaitGroup) { + defer wg.Done() + + symQuery := seq[0] + + isExactMatch := child.symbol.Equals(symQuery) + if isExactMatch { + ch <- child.retrieveTermRec(seq[1:], currentEnv) + return + } + + if child.getSymbol().getSymbol().IsMeta() { + skip := GetSubTermLength(seq) + if skip <= len(seq) { + ch <- child.retrieveTermRec(seq[skip:], currentEnv) + } + return + + } else if symQuery.getSymbol().IsMeta() { + ch <- child.SkipTreeTermAndContinueTerm(child.GetArity(), seq[1:], currentEnv) + } +} + /*****************************/ /* DataStruct implementation */ /*****************************/ @@ -748,10 +952,13 @@ func (dNode DiscriminationNode) Copy() subst.DataStructure { } newLeafFor := Lib.ListCpy(dNode.getLeafFor()) + newTermLeafFor := Lib.ListCpy(dNode.getTermLeafFor()) return DiscriminationNode{ - symbol: dNode.symbol, - children: newChildMaster, - leafFor: newLeafFor, + symbol: dNode.symbol, + children: newChildMaster, + leafFor: newLeafFor, + termLeafFor: newTermLeafFor, + termOnly: dNode.termOnly, } } @@ -792,6 +999,18 @@ func (dNode DiscriminationNode) InsertFormulaListToDataStructure(lf Lib.List[AST return dNode } +/* Take a list of terms and build the corresponding discrimination tree. + * The discriminationtree equivalent of codetree.MakeTermUnifProblem: builds + * a tree purely for term-level unification (UnifyTerm), as opposed to + * Insert/Unify which index full predicates. */ +func MakeTermUnifProblem(l Lib.List[AST.Term]) subst.DataStructure { + root := NewNode() + for _, t := range l.GetSlice() { + root = root.InsertTerm(t) + } + return root +} + func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { candidates := dNode.RetrieveUnifiables(inputFormula) @@ -832,12 +1051,36 @@ func (dNode DiscriminationNode) UnifyTerm(inputTerm AST.Term) (bool, []subst.Mix func (dNode DiscriminationNode) UnifyTermWithSubst(inputTerm AST.Term, globalSubst subst.Substitutions) (bool, []subst.MixedTermSubstitutions) { var mixed []subst.MixedTermSubstitutions var found bool - tmpContext := NewContext() - seq := parseTerm(inputTerm, tmpContext).GetSlice() - candidates := dNode.retrieveRec(seq, globalSubst) + seq := parseTerm(inputTerm, NewContext()).GetSlice() - for _, possibleMatch := range candidates { + if dNode.termOnly { + // Term-only leaves (termLeafFor, populated via InsertTerm / + // MakeTermUnifProblem) - needed for trees that only ever hold raw terms. + termCandidates := dNode.retrieveTermRec(seq, globalSubst) + for _, possibleMatch := range termCandidates { + candidateTerm := possibleMatch.getTerm() + // Re-unify from the caller's own globalSubst, not from whatever + // possibleMatch itself carries - same reasoning as the Unify2 fix: + // the traversal's own bookkeeping must never leak into the result. + finalSubst := subst.AddUnification(inputTerm, candidateTerm, globalSubst.Copy()) + + if !finalSubst.Equals(subst.Failure()) { + found = true + mixMatch := subst.MixMatchSubstitutions{ + Tof: Lib.MkLeft[AST.Term, AST.Form](inputTerm), + Subst: finalSubst, + } + mixed = append(mixed, mixMatch.ToMixedTerm()) + } + } + return found, mixed + } + + // Predicate leaves (leafFor, populated via Insert), compared as terms via + // TransformPred - the original behaviour, for a tree built the "normal" way. + predCandidates := dNode.retrieveRec(seq, globalSubst) + for _, possibleMatch := range predCandidates { candidateTerm := subst.TransformPred(possibleMatch.getPred()) finalSubst := subst.AddUnification(inputTerm, candidateTerm, globalSubst) @@ -916,7 +1159,27 @@ func (dNode DiscriminationNode) Unify2(inputFormula AST.Form) (bool, []subst.Mix candPred := possibleMatch.getPred() candTermForRobinson := AST.MakerFun(candPred.GetID(), emptyTyArgs, candPred.GetArgs()) - currentEnv := possibleMatch.GetSubs().Copy() + + // NOTE: possibleMatch.GetSubs() holds whatever bindings the early-pruning + // traversal accumulated on its way down the tree - but those are keyed by + // the tree's OWN internally-normalized meta-variables (parsePred/parseTerm + // rename every meta to a fresh v1, v2, ... via NewContext(), purely so the + // tree can compare structure without caring about the caller's actual meta + // identities). Reusing that substitution here leaks those internal v1/v2 + // names into the result returned to the caller, alongside the correct + // bindings for the caller's real query meta-variables (e.g. bse's + // METAEQ1/METAEQ2). A caller that expects the returned substitution to + // only mention its own meta-variables - like + // Mods/equality/bse's orderSubstForRetrieve - then chokes on the + // unexpected v1/v2 keys and raises a fatal "Meta EQ/NEQ not found" + // anomaly. + // + // The traversal's only job was to cheaply prune candidates that can't + // possibly match; it doesn't need to contribute anything to the final + // answer, since the Robinson call below re-unifies the two full terms + // from scratch anyway. So, exactly like the classic Unify (which starts + // the equivalent step from subst.Substitutions{}), start clean here too. + currentEnv := subst.Substitutions{} // Final strict unification step finalSubst := subst.AddUnification(candTermForRobinson, queryTermForRobinson, currentEnv) @@ -999,7 +1262,6 @@ func (dNode DiscriminationNode) retrieveRec2(seq []SymbolType, currentEnv subst. } // retrieveCase2 executes backtracking logic on a specific child node. It performs pruning , unification when encountering variables. -// retrieveCase2 executes backtracking logic. It performs SAFE early pruning. func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child DiscriminationNode, ch chan<- []CandidatResult, wg *sync.WaitGroup) { defer wg.Done() symQuery := seq[0] diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 88d075dc..343d77f7 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -1832,6 +1832,7 @@ func TestInsertCustomType(t *testing.T) { tree := NewNode() tree = tree.Insert(p_typed_pred_int_3) + tree.Print() t1 := tree.getChildren() if t1.Len() != 1 { @@ -2182,12 +2183,6 @@ func TestNodeStringMethods(t *testing.T) { } }) - t.Run("GetArityType", func(t *testing.T) { - if ns1.GetArityType() != 0 { - t.Fatalf("NodeString arity type should always be 0") - } - }) - t.Run("Equals_Same_And_Case_Insensitive", func(t *testing.T) { if !ns1.Equals(ns1) { t.Errorf("Should be equal to itself") @@ -2222,18 +2217,6 @@ func TestTermNodeMethods(t *testing.T) { } }) - t.Run("GetArityType", func(t *testing.T) { - expectedRegularArity := a.GetMetaList().Len() - if tnRegular.GetArityType() != expectedRegularArity { - t.Errorf("Expected arity %d, got %d", expectedRegularArity, tnRegular.GetArityType()) - } - - expectedMetaArity := x.GetMetaList().Len() - if tnMeta.GetArityType() != expectedMetaArity { - t.Errorf("Expected arity %d, got %d", expectedMetaArity, tnMeta.GetArityType()) - } - }) - t.Run("Equals", func(t *testing.T) { if !tnRegular.Equals(tnRegular) { t.Errorf("Identical TermNodes should be equal") @@ -2253,12 +2236,6 @@ func TestTyNodeMethods(t *testing.T) { tnDifferent := TyNode{Ty: random_type} tnNil := TyNode{Ty: nil} - t.Run("GetArityType", func(t *testing.T) { - if tnStandard.GetArityType() != 0 { - t.Errorf("TyNode arity type should always be 0") - } - }) - t.Run("Equals", func(t *testing.T) { if !tnStandard.Equals(tnStandard) { t.Errorf("Identical TyNodes should be equal") @@ -2749,3 +2726,38 @@ func TestReconstructTerm(t *testing.T) { } }) } + +func TestA(t *testing.T) { + + tree := NewNode() + tree = tree.Insert(pfx.(AST.Pred)) + a, b := tree.Unify(px) + if a { + for _, elem := range b { + fmt.Println("a1", elem.ToString()) + } + } + tree.Print() + + tree2 := NewNode() + tree2 = tree2.Insert(pfx.(AST.Pred)) + a2, b2 := tree2.Unify(py) + if a2 { + for _, elem := range b2 { + fmt.Println("a2", elem.ToString()) + } + } + tree2.Print() + + tree3 := NewNode() + tree3 = tree3.Insert(py.(AST.Pred)) + tree3 = tree3.Insert(px.(AST.Pred)) + a3, b3 := tree3.Unify(pfx) + if a3 { + for _, elem := range b3 { + fmt.Println("a3", elem.ToString()) + } + } + tree3.Print() + +} From 4215a8b102e79625774f8d341780ce677f61da78 Mon Sep 17 00:00:00 2001 From: MinaroliCorentin Date: Fri, 24 Jul 2026 21:37:59 +0200 Subject: [PATCH 23/23] Modification to make test-suite works --- devtools/run-test-suite.py | 2 +- src/Glob/helper.go | 12 ++ src/Search/rules.go | 8 +- .../discrimination-trees.go | 200 ++++++++++++++---- src/Unif/discriminationtree/dt_test.go | 1 + src/Unif/substitution/data_structure.go | 4 - .../substitution/matching_substitutions.go | 13 ++ src/options.go | 8 + 8 files changed, 197 insertions(+), 51 deletions(-) diff --git a/devtools/run-test-suite.py b/devtools/run-test-suite.py index 9dffbe90..40a07737 100644 --- a/devtools/run-test-suite.py +++ b/devtools/run-test-suite.py @@ -52,7 +52,7 @@ def getCommandLine(self): arguments = self.arguments if not self.no_rocq_check: arguments += " -context -orocq" - return self.env + " ../src/_build/goeland " + arguments + " " + self.filename + return self.env + " ../src/_build/goeland -dt -ep " + arguments + " " + self.filename def getArgsForPrinting(self): rocq_chk_str = "" diff --git a/src/Glob/helper.go b/src/Glob/helper.go index 301953d4..40c19f9f 100644 --- a/src/Glob/helper.go +++ b/src/Glob/helper.go @@ -83,6 +83,7 @@ var allowFlattening = false var type_check = true var list_dbgs = false var dt = false +var early_pruning = false var IncrEq = false @@ -292,6 +293,13 @@ func GetDt() bool { return dt } +// GetEarlyPruning reports whether the discrimination tree should use its +// early-pruning retrieval strategy instead of the classic one. Only meaningful +// together with GetDt (the discrimination tree is enabled). +func GetEarlyPruning() bool { + return early_pruning +} + /* Setters */ func SetDebug(debug_list string) { if debug_list == "none" { @@ -451,3 +459,7 @@ func SetListDebuggers() { func SetDt() { dt = true } + +func SetEarlyPruning() { + early_pruning = true +} diff --git a/src/Search/rules.go b/src/Search/rules.go index f487824a..1cc35aa3 100644 --- a/src/Search/rules.go +++ b/src/Search/rules.go @@ -212,8 +212,8 @@ func searchClosureRule(f AST.Form, st State) (bool, []substitution.MixedSubstitu if isCompatible { running_subst = subst2 - new_subst := substitution.MakeMatchingSubstitutions(e.GetForm(), substitution.ToSubstitutions(subst2)) - returnList.Append(new_subst.ToMixed()) + new_subst := substitution.MakeMixedSubstitutions(e.GetForm(), subst2) + returnList.Append(new_subst) } } @@ -236,8 +236,8 @@ func searchClosureRule(f AST.Form, st State) (bool, []substitution.MixedSubstitu if isCompatible { running_subst = subst2 - new_subst := substitution.MakeMatchingSubstitutions(e.GetForm(), substitution.ToSubstitutions(subst2)) - returnList.Append(new_subst.ToMixed()) + new_subst := substitution.MakeMixedSubstitutions(e.GetForm(), subst2) + returnList.Append(new_subst) } } diff --git a/src/Unif/discriminationtree/discrimination-trees.go b/src/Unif/discriminationtree/discrimination-trees.go index 5d3676c0..3ce223ad 100644 --- a/src/Unif/discriminationtree/discrimination-trees.go +++ b/src/Unif/discriminationtree/discrimination-trees.go @@ -31,7 +31,7 @@ **/ /** -* This file contains all the definitons necessary to make a Code Tree +* This file contains all the definitons necessary to make a Discrimination Tree **/ package discriminationtree @@ -157,9 +157,6 @@ func (tn TermNode) Equals(target NodeElement) bool { if tn.Term != nil && typ.Term != nil { return tn.Term.Equals(typ.Term) } - if tn.Term == nil || typ.Term == nil { - return false - } return false } @@ -278,7 +275,10 @@ func (s SymbolType) Equals(target SymbolType) bool { return s.symbol.Equals(target.symbol) } -/* Each node of a CodeTree is composed of a sequence of instruction and its children. If it's a leaf, it has formulaes corresponding to the sequence of instructions. */ +/* Each node of a discrimination tree holds a single symbol (a function/predicate, + a type, or a meta, together with its arity) and its children. When it is a leaf, + leafFor / termLeafFor hold the predicates / terms whose flattened sequence ends + at this node. */ type DiscriminationNode struct { symbol SymbolType // Contain the AST.Term and Arity children Lib.List[DiscriminationNode] // All the children of the node @@ -697,6 +697,78 @@ func (dNode DiscriminationNode) insertTermRec(seq Lib.List[SymbolType], original /********** Retriev **********/ /*****************************/ +// typesCanUnify reports whether two type arguments, each kept by the tree as a +// single opaque node, could be unified. A type meta-variable unifies with any +// type; two constructors must share head symbol and argument count and unify +// argument-wise; anything else falls back to structural equality. +// +// The discrimination tree is only a *filter*: the final Robinson unification +// recomputes the real bindings, so this check must never under-approximate +// (a wrong "false" silently drops a genuinely unifiable candidate - the bug +// behind polymorphic goals such as maybe(A) vs maybe(sko)). Over-approximating +// is harmless: a wrong "true" merely yields a candidate that Robinson rejects. +func typesCanUnify(a, b AST.Ty) bool { + if _, ok := a.(AST.TyMeta); ok { + return true + } + if _, ok := b.(AST.TyMeta); ok { + return true + } + + ca, aok := a.(AST.TyConstr) + cb, bok := b.(AST.TyConstr) + if aok && bok { + if ca.Symbol() != cb.Symbol() || ca.Args().Len() != cb.Args().Len() { + return false + } + for i := range ca.Args().GetSlice() { + if !typesCanUnify(ca.Args().At(i), cb.Args().At(i)) { + return false + } + } + return true + } + + return a.Equals(b) +} + +// symbolsUnifiableModuloTypes reports whether a stored tree symbol and a query +// symbol denote the same function/predicate up to type-argument unification: +// same identifier, same term arity, and pairwise-unifiable type arguments. +// +// This is deliberately looser than SymbolType.Equals, which demands +// syntactically identical type arguments. Nested function symbols embed their +// type arguments (e.g. head in a stored polymorphic axiom vs head in a +// ground query), so requiring equality there prunes valid candidates exactly +// like the [Ty]-node case does; the concrete type binding is recovered by the +// final Robinson unification. +func symbolsUnifiableModuloTypes(treeSym, querySym SymbolType) bool { + if treeSym.GetArity() != querySym.GetArity() { + return false + } + + treeFun, tok := treeSym.getTerm().(AST.Fun) + queryFun, qok := querySym.getTerm().(AST.Fun) + if !tok || !qok { + return false + } + if !treeFun.GetID().Equals(queryFun.GetID()) { + return false + } + + treeTys := treeFun.GetTyArgs() + queryTys := queryFun.GetTyArgs() + if treeTys.Len() != queryTys.Len() { + return false + } + for i := range treeTys.GetSlice() { + if !typesCanUnify(treeTys.At(i), queryTys.At(i)) { + return false + } + } + return true +} + func GetSubTermLength(seq []SymbolType) int { if len(seq) == 0 { return 0 @@ -793,6 +865,24 @@ func retrieveCase(seq []SymbolType, currentEnv subst.Substitutions, child Discri return } + // Case 1bis. Type-argument matching up to unification. The tree keeps type + // arguments either as standalone [Ty] nodes (predicate level) or embedded on + // a function symbol (nested terms). A polymorphic stored type such as A / + // maybe(A) is not syntactically equal to a concrete query type such as + // sko / maybe(sko), yet the two unify, so we must descend rather than prune. + // Both kinds of node span exactly one sequence element, so we consume one + // element on each side; the final Robinson pass recomputes the type bindings. + if childTy, queryTy := child.getSymbol().GetTy(), symQuery.GetTy(); childTy != nil && queryTy != nil { + if typesCanUnify(childTy, queryTy) { + ch <- child.retrieveRec(seq[1:], currentEnv) + } + return + } + if symbolsUnifiableModuloTypes(child.getSymbol(), symQuery) { + ch <- child.retrieveRec(seq[1:], currentEnv) + return + } + // Case 2. The Symbol from the discriminationTree is a Meta // We need to look the len of the actual term from the sequence. f(x) == 2, y == 1 and we skip the entire term if child.getSymbol().getSymbol().IsMeta() { @@ -884,6 +974,18 @@ func retrieveCaseTerm(seq []SymbolType, currentEnv subst.Substitutions, child Di return } + // Case 1bis. Type-argument matching up to unification (see retrieveCase). + if childTy, queryTy := child.getSymbol().GetTy(), symQuery.GetTy(); childTy != nil && queryTy != nil { + if typesCanUnify(childTy, queryTy) { + ch <- child.retrieveTermRec(seq[1:], currentEnv) + } + return + } + if symbolsUnifiableModuloTypes(child.getSymbol(), symQuery) { + ch <- child.retrieveTermRec(seq[1:], currentEnv) + return + } + if child.getSymbol().getSymbol().IsMeta() { skip := GetSubTermLength(seq) if skip <= len(seq) { @@ -900,12 +1002,16 @@ func retrieveCaseTerm(seq []SymbolType, currentEnv subst.Substitutions, child Di /* DataStruct implementation */ /*****************************/ +// Print routes the tree dump through the "unif" debugger, exactly like the code +// tree's Print does. This keeps it silent on normal runs (e.g. -proof output) +// and only visible when debugging is enabled, instead of writing to stdout +// unconditionally. func (dNode DiscriminationNode) Print() { if dNode.IsEmpty() { - fmt.Println("Empty Tree") + debug(Lib.MkLazy(func() string { return "Empty Tree" })) return } - fmt.Println("[ROOT]") + debug(Lib.MkLazy(func() string { return "[ROOT]" })) for _, child := range dNode.getChildren().GetSlice() { child.displayRec(1) } @@ -926,12 +1032,14 @@ func (dNode DiscriminationNode) displayRec(depth int) { nodeTag = "[String]" } - fmt.Printf("%s|-- %s %s (arity: %d)\n", indent, nodeTag, dNode.toString(), dNode.GetArity()) + debug(Lib.MkLazy(func() string { + return fmt.Sprintf("%s|-- %s %s (arity: %d)", indent, nodeTag, dNode.toString(), dNode.GetArity()) + })) if dNode.getLeafFor().Len() > 0 { leafIndent := indent + " " for _, pred := range dNode.getLeafFor().GetSlice() { - fmt.Printf("%s[=> %s]\n", leafIndent, pred.ToString()) + debug(Lib.MkLazy(func() string { return fmt.Sprintf("%s[=> %s]", leafIndent, pred.ToString()) })) } } @@ -1013,6 +1121,13 @@ func MakeTermUnifProblem(l Lib.List[AST.Term]) subst.DataStructure { func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.MixedSubstitutions) { + // With -ep, use the early-pruning retrieval. Both paths run the exact same + // final Robinson unification (see Unify2), so they return the same result; + // the flag only lets us compare the two retrieval strategies' speed. + if Glob.GetEarlyPruning() { + return dNode.Unify2(inputFormula) + } + candidates := dNode.RetrieveUnifiables(inputFormula) var mixed []subst.MixedSubstitutions var found bool @@ -1030,11 +1145,7 @@ func (dNode DiscriminationNode) Unify(inputFormula AST.Form) (bool, []subst.Mixe possibleMatchTerm := subst.TransformPred(possibleMatch.getPred()) // Pred -> Term for Robinson finalSubst := subst.AddUnification(possibleMatchTerm, queryTerm, initialSubst) // Call Robinson - if finalSubst.Equals(subst.Failure()) { - fmt.Println("-------------------------") - fmt.Println("Substitution FAILURE") - fmt.Println("-------------------------") - } else { + if !finalSubst.Equals(subst.Failure()) { found = true matching := subst.MakeMatchingSubstitutions(possibleMatch.getPred(), finalSubst) mixed = append(mixed, matching.ToMixed()) // convert To Mixed for return @@ -1151,38 +1262,24 @@ func (dNode DiscriminationNode) Unify2(inputFormula AST.Form) (bool, []subst.Mix return false, mixed } - // Hide type arguments from Robinson to prevent crash during equality. - emptyTyArgs := Lib.NewList[AST.Ty]() - queryTermForRobinson := AST.MakerFun(queryPred.GetID(), emptyTyArgs, queryPred.GetArgs()) + // The final unification is exactly the classic Unify one: transform predicate + // to term (types included, handled uniformly as term arguments) and run + // Robinson. Keeping this identical to Unify means the two modes differ *only* + // in their retrieval strategy (RetrieveUnifiables2's early pruning vs the + // classic RetrieveUnifiables), which is the whole point of the comparison. + queryTerm := subst.TransformPred(queryPred) for _, possibleMatch := range candidates { candPred := possibleMatch.getPred() + candTerm := subst.TransformPred(candPred) - candTermForRobinson := AST.MakerFun(candPred.GetID(), emptyTyArgs, candPred.GetArgs()) - - // NOTE: possibleMatch.GetSubs() holds whatever bindings the early-pruning - // traversal accumulated on its way down the tree - but those are keyed by - // the tree's OWN internally-normalized meta-variables (parsePred/parseTerm - // rename every meta to a fresh v1, v2, ... via NewContext(), purely so the - // tree can compare structure without caring about the caller's actual meta - // identities). Reusing that substitution here leaks those internal v1/v2 - // names into the result returned to the caller, alongside the correct - // bindings for the caller's real query meta-variables (e.g. bse's - // METAEQ1/METAEQ2). A caller that expects the returned substitution to - // only mention its own meta-variables - like - // Mods/equality/bse's orderSubstForRetrieve - then chokes on the - // unexpected v1/v2 keys and raises a fatal "Meta EQ/NEQ not found" - // anomaly. - // - // The traversal's only job was to cheaply prune candidates that can't - // possibly match; it doesn't need to contribute anything to the final - // answer, since the Robinson call below re-unifies the two full terms - // from scratch anyway. So, exactly like the classic Unify (which starts - // the equivalent step from subst.Substitutions{}), start clean here too. - currentEnv := subst.Substitutions{} - - // Final strict unification step - finalSubst := subst.AddUnification(candTermForRobinson, queryTermForRobinson, currentEnv) + // possibleMatch.GetSubs() carries whatever the early-pruning traversal + // accumulated, but those bindings are keyed by the tree's internally + // normalized metas (v1, v2, ... from NewContext()) and would leak into the + // result. The traversal only prunes; Robinson below re-unifies both full + // terms from scratch, so - like classic Unify - we start from a clean + // substitution. + finalSubst := subst.AddUnification(candTerm, queryTerm, subst.Substitutions{}) if !finalSubst.Equals(subst.Failure()) { found = true @@ -1275,6 +1372,25 @@ func retrieveCase2(seq []SymbolType, currentEnv subst.Substitutions, child Discr ch <- matches } + // Case 1bis: type-argument / polymorphic symbol match up to unification + // (mirrors retrieveCase). A polymorphic stored type/symbol such as A / head + // is not syntactically equal to a concrete query one, yet the two unify, so we + // descend instead of pruning; the final Robinson pass recomputes the bindings. + // Not threaded through currentEnv on purpose: Unify2 re-unifies from a clean + // substitution at the end anyway, exactly like the classic path. + if !isExactMatch { + if childTy, queryTy := childSym.GetTy(), symQuery.GetTy(); childTy != nil && queryTy != nil { + if typesCanUnify(childTy, queryTy) { + ch <- child.retrieveRec2(seq[1:], currentEnv) + } + return + } + if symbolsUnifiableModuloTypes(childSym, symQuery) { + ch <- child.retrieveRec2(seq[1:], currentEnv) + return + } + } + // Case 2: The tree contains a meta-variable branch (Early Pruning Attempt) if childSym.getSymbol().IsMeta() && !isExactMatch { queryTerm, restSeq := ReconstructTerm(seq) diff --git a/src/Unif/discriminationtree/dt_test.go b/src/Unif/discriminationtree/dt_test.go index 343d77f7..ed938f72 100644 --- a/src/Unif/discriminationtree/dt_test.go +++ b/src/Unif/discriminationtree/dt_test.go @@ -422,6 +422,7 @@ func initDebuggers() { AST.InitDebugger() Typing.InitDebugger() subst.InitDebugger() + InitDebugger() } func TestMain(m *testing.M) { diff --git a/src/Unif/substitution/data_structure.go b/src/Unif/substitution/data_structure.go index 78bc1640..81f17ef7 100644 --- a/src/Unif/substitution/data_structure.go +++ b/src/Unif/substitution/data_structure.go @@ -168,15 +168,11 @@ func robinsonUnify(term1, term2 AST.Term, s Substitutions) Substitutions { case AST.Fun: if !t1.GetID().Equals(t2.GetID()) { - fmt.Printf("ID WRONG T1 ID: %v, T2 ID: %v\n", t1.GetID(), t2.GetID()) return Failure() } args1 := t1.GetArgs() args2 := t2.GetArgs() - fmt.Printf("%v\n", Lib.ListToString(args1)) - fmt.Printf("%v\n", Lib.ListToString(args2)) if args1.Len() != args2.Len() { - fmt.Printf("ARITY WRONG T1 Arity: %d, T2 Arity: %d\n", args1.Len(), args2.Len()) return Failure() } for i := range args1.GetSlice() { diff --git a/src/Unif/substitution/matching_substitutions.go b/src/Unif/substitution/matching_substitutions.go index 863ae067..6e318969 100644 --- a/src/Unif/substitution/matching_substitutions.go +++ b/src/Unif/substitution/matching_substitutions.go @@ -193,6 +193,19 @@ type MixedSubstitutions struct { substs []MixedSubstitution } +// Build a MixedSubstitutions directly from a form and an already-mixed +// (term + type) substitution list. Use this instead of routing through +// MakeMatchingSubstitutions(form, ToSubstitutions(substs)).ToMixed(): that +// path forces the list through Substitutions (term-only) first, which +// silently drops any TySubstitution entries (ToSubstitutions has no case +// for them, and Substitutions itself has no way to represent one). That +// silent drop is what caused type metavariable bindings (e.g. for a +// polymorphic type parameter resolved via GAMMA + CLOSURE) to vanish +// before ever reaching the final proof-printing substitution. +func MakeMixedSubstitutions(form AST.Form, substs Lib.List[MixedSubstitution]) MixedSubstitutions { + return MixedSubstitutions{form.Copy(), Lib.ListCpy(substs).GetSlice()} +} + func (m MixedSubstitutions) GetForm() AST.Form { return m.form.Copy() } diff --git a/src/options.go b/src/options.go index e9fcb1b5..2759529c 100644 --- a/src/options.go +++ b/src/options.go @@ -433,6 +433,14 @@ func buildOptions() { Glob.SetDt() }, func(bool) {}) + (&option[bool]{}).init( + "ep", + false, + "With -dt: use the discrimination tree's early-pruning retrieval", + func(bool) { + Glob.SetEarlyPruning() + }, + func(bool) {}) } func chronoInit() {