<?xml version="1.0"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">

<sch:pattern>
    <sch:title>UPC</sch:title>
    <sch:rule context="test/upc">
        <sch:assert test="number()">UPC number can only have digits.</sch:assert>
        <sch:assert test="string-length() eq 12">UPC number must have 12 digits.</sch:assert>
        <sch:let name="checksum" value="number(substring(., string-length(.), 1))"/>
        <sch:let name="digits" value="for $i in string-to-codepoints(substring(., 1, string-length(.) - 1))
        return $i - 48"/>
        <sch:let name="calculation" value="sum((for $i in $digits[(position()) mod 2 = 1] return $i*3, for $i in $digits[(position()) mod 2 = 0] return $i)) mod 10"/>
        <sch:assert test="(if ($calculation ne 0) then (10 - $calculation) else 0) eq $checksum">UPC is not correct. Could be a typo?</sch:assert>
    </sch:rule>
</sch:pattern>

    <sch:pattern>
        <sch:title>EAN-13</sch:title>
        <sch:rule context="test/ean-13">
            <sch:assert test="number()">EAN number can only have digits.</sch:assert>
            <sch:assert test="string-length() eq 13">EAN-13 number must have 13 digits.</sch:assert>
            <sch:let name="digits"
                value="for $i in reverse(string-to-codepoints(substring(., 1, string-length() -1)))
            return $i - 48"/>
            <sch:let name="calculation"
                value="sum((for $i in $digits[(position()) mod 2 = 1] return $i *3, for $i in $digits[(position()) mod 2 = 0] return $i)) mod 10"/>
            <sch:let name="checksum" value="xs:integer(substring(., string-length(), 1))"/>
            <sch:assert
                test="(if ($calculation ne 0) then (10 - $calculation) else 0) eq $checksum"
                    >Not correct EAN-13. Could be a typo?</sch:assert>
        </sch:rule>
    </sch:pattern>

    <sch:pattern>
        <sch:title>ISBN-13</sch:title>
        <sch:rule context="test/isbn-13">
            <sch:assert test="matches(., '^ISBN (\d{3})-(\d+)-(\d+)-(\d+)-(\d)$')">Use this pattern:
                ISBN x-x-x-x-x</sch:assert>
            <sch:assert test="string-length() eq 22">ISBN pattern must have 22 characters.</sch:assert>
            <sch:let name="x" value="replace(., 'ISBN |-', '')"/>
            <sch:let name="a"
                value="for $i in string-to-codepoints(substring($x, 1, string-length($x) -1))
            return $i - 48"/>
            <sch:let name="checksum" value="xs:integer(substring($x, string-length($x), 1))"/>
            <sch:let name="calculation"
                value="sum((for $i in $a[position() mod 2 = 1] return $i, for $i in $a[position() mod 2 = 0] return $i * 3)) mod 10"/>
            <sch:assert
                test="(if ($calculation ne 0) then (10 - $calculation) else 0) eq $checksum"
                >Not correct ISBN-13. Could be a typo?</sch:assert>
        </sch:rule>
    </sch:pattern>

    <sch:pattern>
        <sch:title>ISBN-10</sch:title>
        <sch:rule context="test/isbn-10">
            <sch:let name="digits" value="replace(., 'ISBN ', '')"/>
            <sch:assert test="string-length($digits) = 10">Use 10 digits (or X for the last one).</sch:assert>
            <sch:assert test="matches(., '^ISBN ')">Use this pattern: ISBN xxxxxxxxxx</sch:assert>
            <sch:let name="a"
                value="for $i in string-to-codepoints(substring($digits, 1, string-length($digits) - 1))
            return $i - 48"/>
            <sch:let name="calculation"
                value="sum((for $i in $a[1] return $i *10, for $i in $a[2] return $i * 9, for $i in $a[3] return $i * 8, for $i in $a[4] return $i * 7, for $i in $a[5] return $i * 6, for $i in $a[6] return $i * 5, for $i in $a[7] return $i * 4, for $i in $a[8] return $i * 3, for $i in $a[9] return $i * 2)) mod 11"/>
            <sch:let name="checksum" value="substring($digits, string-length($digits), 1)"/>
            <sch:let name="checksum10" value="if ($checksum eq 'x') then '10' else $checksum"/>
            <sch:assert
                test="(if ($calculation ne 0) then (11 - $calculation) else 0) eq xs:integer($checksum10)">Not correct ISBN-10. Could be a typo?</sch:assert>
        </sch:rule>
    </sch:pattern>

    <sch:pattern>
        <sch:title>VISA</sch:title>
        <sch:rule context="test/visa">
            <sch:assert test="number()">VISA number can only have digits</sch:assert>
            <sch:assert test="string-length() eq 16">VISA number must have 16 digits.</sch:assert>
            <sch:assert test="substring(., 1, 1) eq '4'">VISA must start with "4".</sch:assert>
            <sch:let name="calculation"
                value="sum(for $j in (for $i in reverse(string-to-codepoints(.))[position() mod 2 = 0] return ($i - 48) * 2,
        for $i in reverse(string-to-codepoints(.))[position() mod 2 = 1] return ($i - 48)) return ($j mod 10, $j idiv 10))"/>
            <sch:assert test="$calculation mod 10 eq 0">Not correct VISA. Could be a
            typo?</sch:assert>
        </sch:rule>
    </sch:pattern>

</sch:schema>

