@prefix : <https://ontology.inferal.com/modules/hypergraphs/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

:HypergraphShape
    a sh:NodeShape ;
    rdfs:label "hypergraph shape" ;
    rdfs:comment "Validates hypergraph vertices, hyperedges, hyperedge membership occurrences, incidence containment, and forward/reverse hyperedge links." ;
    sh:targetClass :Hypergraph ;
    sh:property [
        sh:path :hasVertex ;
        sh:class :Vertex ;
    ] ;
    sh:property [
        sh:path :hasHyperedge ;
        sh:class :Hyperedge ;
    ] ;
    sh:property [
        sh:path :hasHyperedgeMembership ;
        sh:class :HyperedgeMembership ;
    ] ;
    sh:sparql [
        sh:message "Every incident vertex of a hypergraph hyperedge must be listed as a vertex of the same hypergraph." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?edge ?incidence ?vertex WHERE {
                $this :hasHyperedge ?edge .
                ?edge :hasIncidence ?incidence .
                ?incidence :incidentVertex ?vertex .
                FILTER NOT EXISTS { $this :hasVertex ?vertex . }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "Every hasHyperedge assertion must be backed by a hyperedge membership occurrence." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?edge WHERE {
                $this :hasHyperedge ?edge .
                FILTER NOT EXISTS {
                    ?membership :membershipHypergraph $this ;
                                :membershipHyperedge ?edge .
                }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A hasHyperedgeMembership link must point to a membership occurrence whose membershipHypergraph is the same hypergraph." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?membership ?declaredHypergraph WHERE {
                $this :hasHyperedgeMembership ?membership .
                OPTIONAL { ?membership :membershipHypergraph ?declaredHypergraph . }
                FILTER(!BOUND(?declaredHypergraph) || ?declaredHypergraph != $this)
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A hasHyperedge link must agree with the hyperedge's edgeInHypergraph value when that value is stated." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?edge ?declaredHypergraph WHERE {
                $this :hasHyperedge ?edge .
                ?edge :edgeInHypergraph ?declaredHypergraph .
                FILTER(?declaredHypergraph != $this)
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "An edgeInHypergraph assertion must be backed by a forward hasHyperedge assertion." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?edge WHERE {
                ?edge :edgeInHypergraph $this .
                FILTER NOT EXISTS { $this :hasHyperedge ?edge . }
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A vertexInHypergraph assertion must be backed by a forward hasVertex assertion." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?vertex WHERE {
                ?vertex :vertexInHypergraph $this .
                FILTER NOT EXISTS { $this :hasVertex ?vertex . }
            }
        """ ;
    ] .

:HyperedgeMembershipShape
    a sh:NodeShape ;
    rdfs:label "hyperedge membership shape" ;
    rdfs:comment "Validates hypergraph-hyperedge membership occurrences and their matching hypergraph hasHyperedge assertions." ;
    sh:targetClass :HyperedgeMembership ;
    sh:property [
        sh:path :membershipHypergraph ;
        sh:class :Hypergraph ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :membershipHyperedge ;
        sh:class :Hyperedge ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:sparql [
        sh:message "A hyperedge membership occurrence must be backed by a hasHyperedge assertion." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?hypergraph ?edge WHERE {
                $this :membershipHypergraph ?hypergraph ;
                      :membershipHyperedge ?edge .
                FILTER NOT EXISTS { ?hypergraph :hasHyperedge ?edge . }
            }
        """ ;
    ] .

:HyperedgeShape
    a sh:NodeShape ;
    rdfs:label "hyperedge shape" ;
    rdfs:comment "Validates hyperedge graph membership, incidence links, incidence uniqueness, and forward/reverse incidence consistency." ;
    sh:targetClass :Hyperedge ;
    sh:property [
        sh:path :edgeInHypergraph ;
        sh:class :Hypergraph ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :hasIncidence ;
        sh:class :Incidence ;
    ] ;
    sh:sparql [
        sh:message "A classical hyperedge must not have duplicate incidence occurrences for the same vertex." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?vertex ?left ?right WHERE {
                ?left :incidentEdge $this ;
                      :incidentVertex ?vertex .
                ?right :incidentEdge $this ;
                       :incidentVertex ?vertex .
                FILTER(?left != ?right)
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "A hasIncidence link must point to an incidence whose incidentEdge is the same hyperedge." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?incidence ?declaredEdge WHERE {
                $this :hasIncidence ?incidence .
                OPTIONAL { ?incidence :incidentEdge ?declaredEdge . }
                FILTER(!BOUND(?declaredEdge) || ?declaredEdge != $this)
            }
        """ ;
    ] ;
    sh:sparql [
        sh:message "An incidentEdge assertion must be backed by a forward hasIncidence assertion." ;
        sh:select """
            PREFIX : <https://ontology.inferal.com/modules/hypergraphs/>
            SELECT $this ?incidence WHERE {
                ?incidence :incidentEdge $this .
                FILTER NOT EXISTS { $this :hasIncidence ?incidence . }
            }
        """ ;
    ] .

:IncidenceShape
    a sh:NodeShape ;
    rdfs:label "incidence shape" ;
    rdfs:comment "Validates that incidence resources identify exactly one hyperedge and exactly one incident vertex." ;
    sh:targetClass :Incidence ;
    sh:property [
        sh:path :incidentEdge ;
        sh:class :Hyperedge ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :incidentVertex ;
        sh:class :Vertex ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .
