@prefix algo-ext: <https://secorolab.github.io/metamodels/algorithm#> .
@prefix qkind-ext: <https://secorolab.github.io/metamodels/qudt/quantity-kind#> .
@prefix qkind: <http://qudt.org/vocab/quantitykind/> .
@prefix qudt: <http://qudt.org/schema/qudt/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix time: <http://www.w3.org/2006/time#> .

algo-ext:Saturation
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:Saturation ;
    sh:message "A saturation needs one quantity input, one quantity output, and exactly one valid limit form." ;
    sh:property [ sh:path algo-ext:in ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:xone (
        [
            sh:property [
                sh:path algo-ext:maximum-absolute-value ;
                sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ;
                sh:node algo-ext:PositiveValueShape
            ] ;
            sh:property [ sh:path algo-ext:lower-bound ; sh:maxCount 0 ] ;
            sh:property [ sh:path algo-ext:upper-bound ; sh:maxCount 0 ]
        ]
        [
            sh:property [ sh:path algo-ext:lower-bound ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
            sh:property [ sh:path algo-ext:upper-bound ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
            sh:property [ sh:path algo-ext:maximum-absolute-value ; sh:maxCount 0 ]
        ]
    ) .

# Addition is commutative and associative, so the addends are an unordered set.
algo-ext:Addition
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:Addition ;
    sh:message "An addition needs two or more quantity inputs and one quantity output." ;
    sh:property [ sh:path algo-ext:in ; sh:minCount 2 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] .

# Subtraction is neither commutative nor associative, so its operands are named rather than
# collected: the subtrahend is taken from the minuend, and exactly one of each says which.
algo-ext:Subtraction
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:Subtraction ;
    sh:message "A subtraction needs one quantity to take from, one to take, and one quantity output." ;
    sh:property [ sh:path algo-ext:minuend ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:subtrahend ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] .

# Multiplication is commutative and associative, so the factors are an unordered set.
algo-ext:Multiplication
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:Multiplication ;
    sh:message "A multiplication needs two or more quantity inputs and one quantity output." ;
    sh:property [ sh:path algo-ext:in ; sh:minCount 2 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] .

# Division is neither commutative nor associative, so its operands are named rather than
# collected: the dividend is divided by the divisor, and exactly one of each says which.
algo-ext:Division
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:Division ;
    sh:message "A division needs one quantity to divide, one to divide by, and one quantity output." ;
    sh:property [ sh:path algo-ext:dividend ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:divisor ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] .

# Profiled approach to a target: rate-limited by velocity, acceleration and (for an
# s-curve) jerk, it emits the setpoint the controller tracks and re-emits it every
# cycle until the target is reached.
algo-ext:VelocityProfile
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:VelocityProfile ;
    sh:message "A velocity profile needs one target and maximum velocity and acceleration, and produces one setpoint of the target's kind; a measured-velocity input and a jerk limit are optional." ;
    sh:property [ sh:path algo-ext:in ; sh:maxCount 1 ; sh:node algo-ext:LinearVelocityShape ] ;
    sh:property [ sh:path algo-ext:target ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ] ;
    sh:property [ sh:path algo-ext:maximum-velocity ; sh:minCount 1 ; sh:maxCount 1 ; sh:node algo-ext:PositiveLinearVelocityShape ] ;
    sh:property [ sh:path algo-ext:maximum-acceleration ; sh:minCount 1 ; sh:maxCount 1 ; sh:node algo-ext:PositiveLinearAccelerationShape ] ;
    sh:property [ sh:path algo-ext:maximum-jerk ; sh:maxCount 1 ; sh:node algo-ext:PositiveLinearJerkShape ] ;
    sh:property [ sh:path algo-ext:shape ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:IRI ; sh:in ( algo-ext:trapezoidal algo-ext:s-curve ) ] ;
    sh:xone (
        [
            sh:property [ sh:path algo-ext:shape ; sh:hasValue algo-ext:trapezoidal ] ;
            sh:property [ sh:path algo-ext:maximum-jerk ; sh:maxCount 0 ]
        ]
        [
            sh:property [ sh:path algo-ext:shape ; sh:hasValue algo-ext:s-curve ] ;
            sh:property [ sh:path algo-ext:maximum-jerk ; sh:minCount 1 ; sh:maxCount 1 ]
        ]
    ) .

algo-ext:LinearVelocityShape
    a sh:NodeShape ;
    sh:message "This value must be a quantity whose quantity kind is linear velocity." ;
    sh:class qudt:Quantity ;
    sh:property [ sh:path qudt:hasQuantityKind ; sh:hasValue qkind:LinearVelocity ] .

algo-ext:LinearAccelerationShape
    a sh:NodeShape ;
    sh:message "This value must be a quantity whose quantity kind is linear acceleration." ;
    sh:class qudt:Quantity ;
    sh:property [ sh:path qudt:hasQuantityKind ; sh:hasValue qkind:LinearAcceleration ] .

algo-ext:PositiveLinearVelocityShape
    a sh:NodeShape ;
    sh:node algo-ext:LinearVelocityShape ;
    sh:node algo-ext:PositiveValueShape .

algo-ext:PositiveLinearAccelerationShape
    a sh:NodeShape ;
    sh:node algo-ext:LinearAccelerationShape ;
    sh:node algo-ext:PositiveValueShape .

algo-ext:PositiveLinearJerkShape
    a sh:NodeShape ;
    sh:class qudt:Quantity ;
    sh:property [ sh:path qudt:hasQuantityKind ; sh:hasValue qkind-ext:LinearJerk ] ;
    sh:node algo-ext:PositiveValueShape .

# Force in, velocity out through a virtual mass-spring-damper.
algo-ext:Admittance
    a rdfs:Class, sh:NodeShape ;
    sh:targetClass algo-ext:Admittance ;
    sh:message "An admittance needs a force input, mass and damping, and produces one linear-velocity output; stiffness and saturation are optional." ;
    sh:property [ sh:path algo-ext:in ; sh:minCount 1 ; sh:maxCount 1 ; sh:node algo-ext:ForceShape ] ;
    sh:property [ sh:path algo-ext:out ; sh:minCount 1 ; sh:maxCount 1 ; sh:node algo-ext:LinearVelocityShape ] ;
    sh:property [ sh:path algo-ext:mass ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ; sh:node algo-ext:PositiveValueShape ] ;
    sh:property [ sh:path algo-ext:damping ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ; sh:node algo-ext:NonNegativeValueShape ] ;
    sh:property [ sh:path algo-ext:stiffness ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class qudt:Quantity ; sh:node algo-ext:NonNegativeValueShape ] ;
    sh:property [ sh:path algo-ext:limits ; sh:maxCount 1 ; sh:nodeKind sh:BlankNodeOrIRI ; sh:class algo-ext:Saturation ] .

algo-ext:ForceShape
    a sh:NodeShape ;
    sh:message "This value must be a quantity whose quantity kind is force." ;
    sh:class qudt:Quantity ;
    sh:property [ sh:path qudt:hasQuantityKind ; sh:hasValue qkind:Force ] .

algo-ext:PositiveValueShape
    a sh:NodeShape ;
    sh:property [ sh:path qudt:value ; sh:minCount 1 ; sh:maxCount 1 ; sh:minExclusive 0 ] .

algo-ext:NonNegativeValueShape
    a sh:NodeShape ;
    sh:property [ sh:path qudt:value ; sh:minCount 1 ; sh:maxCount 1 ; sh:minInclusive 0 ] .
