code: package experiment.regularities.axiomsets.gg;
  
  import experiment.ont.*;
  import experiment.parser.*;
  import experiment.util.*;
  import experiment.iso.gg.*;
  import experiment.structure.*;
  import experiment.structure.nodes.*;
  import experiment.parser.*;
  import experiment.regularities.axiomsets.*;
  
  import java.io.*;
  import java.util.*;
  import java.util.stream.*;
  import org.semanticweb.owlapi.util.*;
  import org.semanticweb.owlapi.model.OWLOntologyManager;
  import org.semanticweb.owlapi.apibinding.OWLManager;
  import org.semanticweb.owlapi.model.parameters.Imports;
  import org.semanticweb.owlapi.model.parameters.*;
  import org.semanticweb.owlapi.formats.OWLXMLDocumentFormat;
  import org.semanticweb.owlapi.formats.RDFXMLDocumentFormat;
  import org.semanticweb.owlapi.model.*;
  import org.semanticweb.owlapi.reasoner.*;
  import org.semanticweb.HermiT.ReasonerFactory;
  import java.util.logging.Logger;
  import java.util.logging.ConsoleHandler;
  import java.util.logging.Level;
  import java.util.logging.*;
  import org.semanticweb.elk.owlapi.ElkReasonerFactory;
  import org.semanticweb.owlapi.change.AxiomChangeData;
  import org.semanticweb.owlapi.search.EntitySearcher; 
  
  import org.jgrapht.*;
  import org.jgrapht.graph.*;
  import org.jgrapht.traverse.*;
  
  
  import uk.ac.manchester.cs.owlapi.modularity.SyntacticLocalityModuleExtractor;
  import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
  
  public class ClassFrameGroundGeneralisation {
      private OWLClassExpression entity;
  
      private HashMap<SyntaxTree,Integer> superclasses;
      private HashMap<SyntaxTree,Integer> equivalences;
      private HashMap<SyntaxTree,Integer> disjointUnion;
      private HashMap<SyntaxTree,Integer> disjointClasses;
  
      private int size;
      private int chunks;
  
      private TreeMap<Integer,Set<SyntaxTree>> weight2superclasses;
      private TreeMap<Integer,Set<SyntaxTree>> weight2equivalences;
      private TreeMap<Integer,Set<SyntaxTree>> weight2disjointUnion;
      private TreeMap<Integer,Set<SyntaxTree>> weight2disjointClasses;
  
      private SyntaxTreeBuilder treeBuilder;
  
      public ClassFrameGroundGeneralisation(ClassFrame frame){
          this.treeBuilder = new SyntaxTreeBuilder();
          this.initialise(frame); 
          this.chunks = this.superclasses.size() +
                        this.equivalences.size() + 
                        this.disjointUnion.size() +
                        this.disjointClasses.size();
          this.size = frame.getSuperClasses().size() + 
                      frame.getEquivalentClasses().size() + 
                      frame.getDisjointUnion().size() + 
                      frame.getDisjointClasses().size();
      }
  
      public String toString(){
          String res = "Superclasses \n";
          for(SyntaxTree t : this.superclasses.keySet()){
              res += "\t" + t.getRoot().toString() + "[" + superclasses.get(t) + "]" + "\n";
          }
          res += "Equivalences \n";
          for(SyntaxTree t : this.equivalences.keySet()){
              res += "\t" + t.getRoot().toString() + "[" + equivalences.get(t) + "]" + "\n";
          }
          res += "DisjointUnion \n";
          for(SyntaxTree t : this.disjointUnion.keySet()){
              res += "\t" + t.getRoot().toString() + "[" + disjointUnion.get(t) + "]" + "\n";
          }
          res += "DisjointClasses \n";
          for(SyntaxTree t : this.disjointClasses.keySet()){
              res += "\t" + t.getRoot().toString() + "[" + disjointClasses.get(t) + "]" + "\n";
          }
          return res;
  
      }
  
      public TreeMap<Integer,Set<SyntaxTree>> getWeight2superclasses(){
          return this.weight2superclasses;
      }
      public TreeMap<Integer,Set<SyntaxTree>> getWeight2equivalences(){
          return this.weight2equivalences;
      }
      public TreeMap<Integer,Set<SyntaxTree>> getWeight2disjointUnion(){
          return this.weight2disjointUnion;
      }
      public TreeMap<Integer,Set<SyntaxTree>> getWeight2disjointClasses(){
          return this.weight2disjointClasses;
      }
      public Map<SyntaxTree,Integer> getSuperClasses(){
          return this.superclasses;
      }
      public Map<SyntaxTree,Integer> getEquivalences(){
          return this.equivalences;
      }
      public Map<SyntaxTree,Integer> getDisjointUnions(){
          return this.disjointUnion;
      }
      public Map<SyntaxTree,Integer> getDisjointClasses(){
          return this.disjointClasses;
      }
      public Map<SyntaxTree,Integer> getTrees(){ 
          Map<SyntaxTree,Integer> res = new HashMap<>();
          res.putAll(this.superclasses);
          res.putAll(this.equivalences);
          res.putAll(this.disjointUnion);
          res.putAll(this.disjointClasses);
          return res;
      }
  
      private void initialise(ClassFrame d){
          this.entity = d.getClassExpression();
  
          this.superclasses = new HashMap<>();
          this.equivalences = new HashMap<>();
          this.disjointUnion = new HashMap<>();
          this.disjointClasses = new HashMap<>();
  
          this.initMap(this.superclasses, new HashSet<OWLAxiom>(d.getSuperClasses()));
          this.initMap(this.equivalences, new HashSet<OWLAxiom>(d.getEquivalentClasses()));
          this.initMap(this.disjointUnion, new HashSet<OWLAxiom>(d.getDisjointUnion()));
          this.initMap(this.disjointClasses, new HashSet<OWLAxiom>(d.getDisjointClasses()));
  
          this.weight2superclasses = new TreeMap<>();
          this.weight2equivalences = new TreeMap<>();
          this.weight2disjointUnion = new TreeMap<>();
          this.weight2disjointClasses = new TreeMap<>();
  
          this.initTreeMap(this.weight2superclasses, this.superclasses);
          this.initTreeMap(this.weight2equivalences, this.equivalences);
          this.initTreeMap(this.weight2disjointUnion, this.disjointUnion);
          this.initTreeMap(this.weight2disjointClasses, this.disjointClasses); 
      }
  
      public OWLClassExpression getClassExpression(){
          return this.entity;
      }
  
      public int size(){
          return this.size;
      }
  
      public int chunks(){
          return this.chunks;
      }
  
  
      public boolean isIsomorphic(ClassFrameGroundGeneralisation f){
          if(f.size() != this.size)
              return false;
          if(f.chunks() != this.chunks)
              return false;
  
          if(!isIsomorphic(this.weight2superclasses, f.getWeight2superclasses()))
              return false;
          if(!isIsomorphic(this.weight2equivalences, f.getWeight2equivalences()))
              return false;
          if(!isIsomorphic(this.weight2disjointUnion, f.getWeight2disjointUnion()))
              return false;
          if(!isIsomorphic(this.weight2disjointClasses, f.getWeight2disjointClasses()))
              return false; 
  
          return true;
      }
  
      private boolean isIsomorphic(TreeMap<Integer,Set<SyntaxTree>> here, 
              TreeMap<Integer,Set<SyntaxTree>> there){
  
          for(Map.Entry<Integer,Set<SyntaxTree>> entry : here.entrySet()){
  
                  int weight = entry.getKey();
                  Set<SyntaxTree> trees = entry.getValue();
  
                  if(!there.containsKey(weight)){
                      return false;
                  } else { 
                      if(!SetGroundGeneralisation.exists(trees,there.get(weight)))
                          return false;
                  }
          }
          return true;
      }
  
      public boolean coveredBy(ClassFrameGroundGeneralisation f){
          if(!coveredBy(this.getSuperClasses(), f.getSuperClasses())){
              return false;
          }
          if(!coveredBy(this.getEquivalences(), f.getEquivalences())){
              return false;
          }
          if(!coveredBy(this.getDisjointUnions(), f.getDisjointUnions())){
              return false;
          }
          if(!coveredBy(this.getDisjointClasses(), f.getDisjointClasses())){
              return false;
          }
          return true; 
      } 
  
      private boolean coveredBy(Map<SyntaxTree,Integer> smaller, Map<SyntaxTree,Integer> bigger){
          for(SyntaxTree t : smaller.keySet()){ 
              boolean covered = false;
              int tWeight = smaller.get(t);
  
              for(SyntaxTree ct : bigger.keySet()){
                  int ctWeight = bigger.get(ct);
  
                  if(ctWeight >= tWeight){
                      if(GroundGeneralisation.exists(t,ct)){
                          covered = true;
                          break;
                      } 
                  }
              } 
              if(!covered){
                  return false;
              } 
          }
          return true; 
      }
  
  
      private void initMap(Map<SyntaxTree,Integer> map, Set<OWLAxiom> set){
          for(OWLAxiom a : set){
              SyntaxTree insert = this.treeBuilder.build(a);
              boolean found = false;
              for(Map.Entry<SyntaxTree,Integer> entry : map.entrySet()){
                  SyntaxTree t = entry.getKey();
                  int weight = entry.getValue();
                  if(GroundGeneralisation.exists(t,insert)){
                      found = true;
                      map.replace(t,weight+1); 
                      break;
                  }
              }
              if(!found){
                  map.put(insert,1);
              } 
          }
      }
  
      private void initTreeMap(TreeMap<Integer,Set<SyntaxTree>> tm, Map<SyntaxTree,Integer> m){
          for(Map.Entry<SyntaxTree,Integer> entry : m.entrySet()){
              SyntaxTree t = entry.getKey();
              int weight = entry.getValue();
              tm.putIfAbsent(weight,new HashSet<>());
              tm.get(weight).add(t);
          }
      }
  
  }
  