]> Lady’s Gitweb - Sutra/blobdiff - xsd/functions.js
Add all X·S·D non‐auxillary functions
[Sutra] / xsd / functions.js
index 4ad5b5c5e23ee093dacc777b902e34e2f6183bd2..08acc8e17d862a4d3138ff6b2b64f16a639619cb 100644 (file)
@@ -8,16 +8,33 @@
 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 
 import {
+  abs,
   bind,
   call,
+  get8BitUnsignedIntegralItem,
+  getByteLength,
+  getFirstSubstringIndex,
   getPrototype,
   isArrayBuffer,
   isFiniteNumber,
   isIntegralNumber,
   ITERATOR,
+  Matcher,
+  min,
   objectCreate,
+  rawString,
   sameValue,
+  set8BitIntegralItem,
+  stringCatenate,
+  stringIncludes,
+  stringPadEnd,
+  stringPadStart,
+  stringSlice,
+  stringSplit,
+  substring,
+  toExponentialNotation,
   toFloat32,
+  toNumber,
   type,
 } from "../deps.js";
 import { div, mod } from "./operators.js";
@@ -438,3 +455,2736 @@ const {
       ),
   };
 })();
+
+/**
+ * Maps each digit to its numerical value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-digitVal>.
+ *
+ * ☡ This function throws if the provided value is not a digit string.
+ *
+ * ※ This function is not exposed.
+ */
+const digitValue = (d) => +ensureMatches(lexicalDigit, d);
+
+/**
+ * Maps a sequence of digits to the position‐weighted sum of the terms
+ * numerical values.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-digitSeqVal>.
+ *
+ * ☡ This function throws if the provided value is not an iterable of
+ * digit strings.
+ *
+ * ※ Although elsewhere the X·S·D specification claims that sequences
+ * count from zero, this sequence clearly must count from one.
+ *
+ * ※ This function is not exposed.
+ */
+const digitSequenceValue = (S) => {
+  let sum = 0;
+  for (const S_i of S) {
+    sum = sum * 10 + digitValue(S_i);
+  }
+  return sum;
+};
+
+/**
+ * Maps a sequence of digits to the position‐weighted sum of the terms
+ * numerical values, weighted appropriately for fractional digits.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitSeqVal>.
+ *
+ * ☡ This function throws if the provided value is not an iterable of
+ * digit strings.
+ *
+ * ※ The X·S·D specification erroneously specifies ·digitValue·(Si) −
+ * 10^(−i), but ·digitValue·(Si) × 10^(−i) is correct.
+ *
+ * ※ Although elsewhere the X·S·D specification claims that sequences
+ * count from zero, this sequence clearly must count from one.
+ *
+ * ※ This function is not exposed.
+ */
+const fractionDigitSequenceValue = (S) => {
+  let sum = 0;
+  let i = 0;
+  for (const S_i of S) {
+    sum += digitValue(S_i) * 10 ** --i;
+  }
+  return sum;
+};
+
+/**
+ * Maps a fracFrag to the appropriate fractional decimal number.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-fracFragVal>.
+ *
+ * ☡ This function throws if the provided value is not a fracFrag
+ * string.
+ *
+ * ※ This function is not exposed.
+ */
+//deno-lint-ignore no-unused-vars
+const fractionFragValue = (N) =>
+  fractionDigitSequenceValue(
+    literalSequence(ensureMatches(fracFrag, N)),
+  );
+
+/**
+ * Maps an unsignedNoDecimalPtNumeral to its numerical value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-unsNoDecVal>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * unsignedNoDecimalPtNumeral string.
+ */
+export const unsignedNoDecimalMap = (N) =>
+  digitSequenceValue(
+    literalSequence(ensureMatches(unsignedNoDecimalPtNumeral, N)),
+  );
+
+/**
+ * Maps an noDecimalPtNumeral to its numerical value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-noDecVal>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * noDecimalPtNumeral string.
+ */
+export const noDecimalMap = (N) => {
+  switch (ensureMatches(noDecimalPtNumeral, N)[0]) {
+    case "-":
+      return -unsignedNoDecimalMap(substring(N, 1)) || 0;
+    case "+":
+      return unsignedNoDecimalMap(substring(N, 1));
+    default:
+      return unsignedNoDecimalMap(N);
+  }
+};
+
+/**
+ * Maps an unsignedDecimalPtNumeral to its numerical value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-unsDecVal>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * unsignedDecimalPtNumeral string.
+ *
+ * ※ This function makes use of the built·in Ecmascript float parsing
+ * to avoid rounding errors.
+ */
+export const unsignedDecimalPtMap = (D) => {
+  // const N = substring(
+  //   ensureMatches(unsignedDecimalPtNumeral, D),
+  //   0,
+  //   getFirstSubstringIndex(D, "."),
+  // );
+  // const F = substring(D, N.length + 1);
+  // if (F === "") {
+  //   return unsignedNoDecimalMap(N);
+  // } else if (N === "") {
+  //   return fractionFragValue(F);
+  // } else {
+  //   return unsignedNoDecimalMap(N) + fractionFragValue(F);
+  // }
+  return stringToFloat(ensureMatches(unsignedDecimalPtNumeral, D));
+};
+
+/**
+ * Maps an decimalPtNumeral to its numerical value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-decVal>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * decimalPtNumeral string.
+ */
+export const decimalPtMap = (N) => {
+  switch (ensureMatches(decimalPtNumeral, N)[0]) {
+    case "-":
+      return -unsignedDecimalPtMap(substring(N, 1)) || 0;
+    case "+":
+      return unsignedDecimalPtMap(substring(N, 1));
+    default:
+      return unsignedDecimalPtMap(N);
+  }
+};
+
+/**
+ * Maps a scientificNotationNumeral to its numerical value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-sciVal>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * scientificNotationNumeral string.
+ *
+ * ※ The X·S·D specification erroneously specifies
+ * ·unsignedDecimalPtMap·(E), but ·noDecimalMap·(E) is correct.
+ *
+ * ※ This function makes use of the built·in Ecmascript float parsing
+ * to avoid rounding errors.
+ */
+export const scientificMap = (N) => {
+  // const C = substring(
+  //   ensureMatches(scientificNotationNumeral, N),
+  //   0,
+  //   (() => {
+  //     let index = 0;
+  //     for (const char of literalSequence(N)) {
+  //       if (char === "e" || char === "E") {
+  //         return index;
+  //       } else {
+  //         ++index;
+  //         continue;
+  //       }
+  //     }
+  //   })(),
+  // );
+  // const E = substring(N, C.length + 1);
+  // return getFirstSubstringIndex(N, ".") !== -1
+  //   ? decimalPtMap(C) * 10 ** noDecimalMap(E)
+  //   : noDecimalMap(C) * 10 ** noDecimalMap(E);
+  return stringToFloat(ensureMatches(scientificNotationNumeral, N)) ||
+    0;
+};
+
+/**
+ * Maps each integer between 0 and 9 to the corresponding digit.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-digit>.
+ *
+ * ☡ This function throws if the provided value is not an integral
+ * number between 0 and 9 inclusive.
+ *
+ * ※ This function is not exposed.
+ */
+const digit = (i) => {
+  if (!(isIntegralNumber(i) && i >= 0 && i <= 9)) {
+    throw new TypeError(
+      `सूत्र: Expected an integral number between 0 and 9 inclusive, but got: ${i}.`,
+    );
+  } else {
+    return `${i}`;
+  }
+};
+
+/**
+ * Maps each nonnegative integer to a sequence of integers used by
+ * ·digitSeq· to ultimately create an unsignedNoDecimalPtNumeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-digitRemSeq>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ *
+ * ※ The sequence produced by this function is infinite.
+ *
+ * ※ This function is not exposed.
+ */
+const digitRemainderSeq = (i) => {
+  nonnegativeInteger(i);
+  return generatorSequence(function* () {
+    let s_j = i;
+    while (true) {
+      yield s_j;
+      s_j = div(s_j, 10);
+    }
+  });
+};
+
+/**
+ * Maps each nonnegative integer to a sequence of integers used by
+ * ·unsignedNoDecimalPtCanonicalMap· to create an
+ * unsignedNoDecimalPtNumeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-digitSeq>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ *
+ * ※ The sequence produced by this function is infinite.
+ *
+ * ※ This function is not exposed.
+ */
+const digitSeq = (i) => {
+  nonnegativeInteger(i);
+  return generatorSequence(function* () {
+    for (const i_j of digitRemainderSeq(i)) {
+      yield mod(i_j, 10);
+    }
+  });
+};
+
+/**
+ * Maps a sequence of nonnegative integers to the index before the
+ * first zero term.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-lastSigDigit>.
+ *
+ * ☡ This function throws if the values of the provided sequence are
+ * not nonnegative integers.
+ *
+ * ※ The X·S·D specification erroneously describes this function as
+ * giving the index *of* the first zero term, but it is actually the
+ * index of the last nonzero term.
+ *
+ * ※ The X·S·D specification erroneously describes this function as
+ * taking a sequence of nonnegative integers, but it is called with
+ * ·FractionDigitRemainderSeq·, which is a list of nonnegative decimal
+ * numbers.
+ *
+ * ※ This function is not exposed.
+ */
+const lastSignificantDigit = (s) => {
+  let j = 0;
+  for (const s_j of s) {
+    if (nonnegativeDecimalNumber(s_j) === 0) {
+      return j === 0 ? 0 : j - 1;
+    } else {
+      ++j;
+      continue;
+    }
+  }
+};
+
+/**
+ * Maps each nonnegative decimal number less than 1 to a sequence of
+ * decimal numbers used by ·fractionDigitSeq· to ultimately create an
+ * unsignedNoDecimalPtNumeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitRemSeq>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number less than 1.
+ *
+ * ※ The X·S·D specification erroneously specifies s_0 = f − 10 and
+ * s_(j+1) = (s_j ·mod· 1) − 10, but s_0 = f × 10 and s_(j+1) = (s_j
+ * ·mod· 1) × 10 is correct.
+ *
+ * ※ The implementation of this function uses string operations
+ * because Ecmascript does not (currently) have a lossless decimal
+ * type.
+ *
+ * ※ The sequence produced by this function is infinite.
+ *
+ * ※ This function is not exposed.
+ */
+const FractionDigitRemainderSeq = (f) => {
+  if (!(isFiniteNumber(f) && f >= 0 && f < 1)) {
+    throw new TypeError(
+      `सूत्र: Expected a nonnegative finite number less than 1, but got: ${f}.`,
+    );
+  } else {
+    return generatorSequence(function* () {
+      let s_j = f * 10;
+      while (true) {
+        yield s_j;
+        s_j = mod(s_j, 1) * 10;
+      }
+    });
+  }
+};
+
+/**
+ * Maps each nonnegative decimal number less than 1 to a sequence of
+ * integers used by ·fractionDigitsCanonicalFragmentMap· to ultimately
+ * create an unsignedNoDecimalPtNumeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitSeq>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number less than 1.
+ *
+ * ※ The sequence produced by this function is infinite.
+ *
+ * ※ This function is not exposed.
+ */
+const fractionDigitSeq = (f) => {
+  if (!(isFiniteNumber(f) && f >= 0 && f < 1)) {
+    throw new TypeError(
+      `सूत्र: Expected a nonnegative finite number less than 1, but got: ${f}.`,
+    );
+  } else {
+    return generatorSequence(function* () {
+      for (const s_j of FractionDigitRemainderSeq(f)) {
+        yield div(s_j, 1);
+      }
+    });
+  }
+};
+
+/**
+ * Maps each nonnegative decimal number less than 1 to a ·literal· used
+ * by ·unsignedDecimalPtCanonicalMap· to create an
+ * unsignedDecimalPtNumeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitsMap>.
+ *
+ * ☡ This function throws if the provided value is not nonnegative and
+ * less than 1.
+ *
+ * ※ This function is not exposed.
+ */
+//deno-lint-ignore no-unused-vars
+const fractionDigitsCanonicalFragmentMap = (f) =>
+  stringCatenate(...generatorSequence(function* () {
+    const l = lastSignificantDigit(FractionDigitRemainderSeq(f));
+    let j = 0;
+    for (const f_j of fractionDigitSeq(f)) {
+      yield digit(f_j);
+      if (j === l) {
+        break;
+      } else {
+        ++j;
+        continue;
+      }
+    }
+  }));
+
+/**
+ * Maps a nonnegative integer to a unsignedNoDecimalPtNumeral, its
+ * ·canonical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-unsNoDecCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ */
+export const unsignedNoDecimalPtCanonicalMap = (i) => {
+  nonnegativeInteger(i);
+  return stringCatenate(...generatorSequence(function* () {
+    const l = lastSignificantDigit(digitRemainderSeq(i));
+    const digits = new Array(l + 1);
+    let j = 0;
+    for (const i_j of digitSeq(i)) {
+      digits[l - j] = digit(i_j);
+      if (j === l) {
+        break;
+      } else {
+        ++j;
+        continue;
+      }
+    }
+    for (j = 0; j <= l; ++j) {
+      yield digits[j];
+    }
+  }));
+};
+
+/**
+ * Maps an integer to a noDecimalPtNumeral, its ·canonical
+ * representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-noDecCanMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer.
+ */
+export const noDecimalPtCanonicalMap = (i) =>
+  integer(i) < 0
+    ? stringCatenate("-", unsignedNoDecimalPtCanonicalMap(-i))
+    : unsignedNoDecimalPtCanonicalMap(i);
+
+/**
+ * Maps a nonnegative decimal number to a unsignedDecimalPtNumeral, its
+ * ·canonical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-unsDecCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number.
+ *
+ * ※ This function makes use of the built·in Ecmascript float
+ * serialization to avoid rounding errors.
+ */
+export const unsignedDecimalPtCanonicalMap = (n) => {
+  // nonnegativeDecimalNumber(n);
+  // return stringCatenate(
+  //   unsignedNoDecimalPtCanonicalMap(div(n, 1)),
+  //   ".",
+  //   fractionDigitsCanonicalFragmentMap(mod(n, 1)),
+  // );
+  const exp = toExponentialNotation(nonnegativeDecimalNumber(n));
+  const eIndex = getFirstSubstringIndex(exp, "e");
+  const exponent = +substring(exp, eIndex + 1);
+  const zeroPaddedMantissa = stringPadEnd(
+    stringPadStart(
+      exp[0],
+      -exponent + 1,
+      "0",
+    ) + (eIndex > 1 ? substring(exp, 2, eIndex) : ""),
+    exponent + 2,
+    "0",
+  );
+  const decimalPoint = exponent < 0 ? 1 : exponent + 1;
+  return stringCatenate(
+    substring(zeroPaddedMantissa, 0, decimalPoint),
+    ".",
+    substring(zeroPaddedMantissa, decimalPoint),
+  );
+};
+
+/**
+ * Maps a decimal number to a decimalPtNumeral, its ·canonical
+ * representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-decCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a finite number.
+ */
+export const decimalPtCanonicalMap = (n) =>
+  decimalNumber(n) < 0
+    ? stringCatenate("-", unsignedDecimalPtCanonicalMap(-n))
+    : unsignedDecimalPtCanonicalMap(n);
+
+/**
+ * Maps a nonnegative decimal number to a
+ * unsignedScientificNotationNumeral, its ·canonical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-unsSciCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number.
+ *
+ * ※ This function makes use of the built·in Ecmascript float
+ * serialization to avoid rounding errors.
+ */
+export const unsignedScientificCanonicalMap = (n) => {
+  // nonnegativeDecimalNumber(n);
+  // return stringCatenate(
+  //   unsignedDecimalPtCanonicalMap(n / 10 ** div(log10(n), 1)),
+  //   "E",
+  //   noDecimalPtCanonicalMap(div(log10(n), 1)),
+  // );
+  const exp = toExponentialNotation(nonnegativeDecimalNumber(n));
+  const [mantissa, exponent] = stringSplit(exp, "e");
+  return stringCatenate(
+    mantissa,
+    stringIncludes(mantissa, ".") ? "E" : ".0E",
+    exponent[0] === "+" ? substring(exponent, 1) : exponent,
+  );
+};
+
+/**
+ * Maps a decimal number to a scientificNotationNumeral, its ·canonical
+ * representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-sciCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a finite number.
+ */
+export const scientificCanonicalMap = (n) =>
+  decimalNumber(n) < 0
+    ? stringCatenate("-", unsignedScientificCanonicalMap(-n))
+    : unsignedScientificCanonicalMap(n);
+
+/**
+ * Maps the ·lexical representations· of ·special values· used with
+ * some numerical datatypes to those ·special values·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-specRepVal>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * numericalSpecialRep string.
+ */
+export const specialRepValue = (c) => {
+  switch (ensureMatches(numericalSpecialRep, c)) {
+    case "INF":
+    case "+INF":
+      return positiveInfinity;
+    case "-INF":
+      return negativeInfinity;
+    case "NaN":
+      return notANumber;
+  }
+};
+
+/**
+ * Maps the ·special values· used with some numerical datatypes to
+ * their ·canonical representations·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-specValCanMap>.
+ *
+ * ☡ This function throws if the provided value is not nan or positive
+ * or negative infinity.
+ */
+export const specialRepCanonicalMap = (c) => {
+  if (sameValue(c, positiveInfinity)) {
+    return "INF";
+  } else if (sameValue(c, negativeInfinity)) {
+    return "-INF";
+  } else if (sameValue(c, notANumber)) {
+    return "NaN";
+  } else {
+    throw new TypeError(
+      `सूत्र: Expected a special value, but got: ${c}.`,
+    );
+  }
+};
+
+/**
+ * Maps a decimalLexicalRep onto a decimal value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-decimalLexmap>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * decimalLexicalRep string.
+ */
+export const decimalLexicalMap = (LEX) => {
+  ensureMatches(decimalLexicalRep, LEX);
+  if (noDecimalPtNumeral(LEX)) {
+    return noDecimalMap(LEX);
+  } else if (decimalPtNumeral(LEX)) {
+    return decimalPtMap(LEX);
+  }
+};
+
+/**
+ * Maps a decimal to its ·canonical representation·, a
+ * decimalLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-decimalCanmap>.
+ *
+ * ☡ This function throws if the provided value is not a finite number.
+ */
+export const decimalCanonicalMap = (d) =>
+  isIntegralNumber(decimalNumber(d))
+    ? noDecimalPtCanonicalMap(d)
+    : decimalPtCanonicalMap(d);
+
+/**
+ * Rounds a non-zero decimal number to the nearest floating-point
+ * value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-floatPtRound>.
+ *
+ * ☡ This function throws if the provided value is zero or is not a
+ * finite number.
+ *
+ * ※ This function uses native Ecmascript float rounding methods and
+ * only supports a cWidth of 24 or 53.
+ *
+ * ※ This function is not exposed.
+ */
+const floatingPointRound = (nV, cWidth, eMin, eMax) => {
+  if (!isFiniteNumber(nV) || nV == 0) {
+    throw new TypeError(
+      `सूत्र: Expected a finite nonzero number, but got: ${nV}.`,
+    );
+  } else if (cWidth !== 24 && cWidth !== 53) {
+    throw new TypeError(
+      `सूत्र: Unsupported cWidth: ${cWidth}.`,
+    );
+  } else if (
+    cWidth === 24 && (eMin !== -149 || eMax !== 104) ||
+    cWidth === 53 && (eMin !== -1074 || eMax !== 971)
+  ) {
+    throw new TypeError(
+      `सूत्र: Unsupported eMin and eMax for cWidth: ${cWidth}.`,
+    );
+  } else {
+    return cWidth <= 24 ? toFloat32(nV) : +nV;
+  }
+};
+
+/**
+ * Maps a decimal number to that value rounded by some power of 10.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-round>.
+ *
+ * ☡ This function throws if the provided value is not a finite number.
+ *
+ * ☡ This function throws if the provided power of 10 is not a
+ * nonnegative integer.
+ *
+ * ※ This function is not exposed.
+ */
+const round = (n, k) => {
+  decimalNumber(n);
+  nonnegativeInteger(k);
+  return div(n / 10 ** k + 0.5, 1) * 10 ** k;
+};
+
+/**
+ * Maps a decimal number (c × 10e) to successive approximations.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-round>.
+ *
+ * ☡ This function throws if the provided values are not a nonnegative
+ * integer, an integer, an a nonnegative integer.
+ *
+ * ※ This function is not exposed.
+ */
+//deno-lint-ignore no-unused-vars
+const floatApprox = (c, e, j) => {
+  nonnegativeInteger(c);
+  integer(e);
+  nonnegativeInteger(j);
+  return round(c, j) * 10 ** e;
+};
+
+/**
+ * Maps a floatRep onto a float value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-floatLexmap>.
+ *
+ * ☡ This function throws if the provided value is not a floatRep
+ * string.
+ */
+export const floatLexicalMap = (LEX) => {
+  ensureMatches(floatRep, LEX);
+  if (numericalSpecialRep(LEX)) {
+    return specialRepValue(LEX);
+  } else {
+    const nV = noDecimalPtNumeral(LEX)
+      ? noDecimalMap(LEX)
+      : decimalPtNumeral(LEX)
+      ? decimalPtMap(LEX)
+      : scientificMap(LEX);
+    const v = nV == 0 ? 0 : floatingPointRound(nV, 24, -149, 104);
+    return v == 0 ? LEX[0] === "-" ? negativeZero : positiveZero : v;
+  }
+};
+
+/**
+ * Maps a doubleRep onto a double value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-doubleLexmap>.
+ *
+ * ☡ This function throws if the provided value is not a doubleRep
+ * string.
+ */
+export const doubleLexicalMap = (LEX) => {
+  ensureMatches(doubleRep, LEX);
+  if (numericalSpecialRep(LEX)) {
+    return specialRepValue(LEX);
+  } else {
+    const nV = noDecimalPtNumeral(LEX)
+      ? noDecimalMap(LEX)
+      : decimalPtNumeral(LEX)
+      ? decimalPtMap(LEX)
+      : scientificMap(LEX);
+    const v = nV == 0 ? 0 : floatingPointRound(nV, 53, -1074, 971);
+    return v == 0 ? LEX[0] === "-" ? negativeZero : positiveZero : v;
+  }
+};
+
+/**
+ * Maps a float to its ·canonical representation·, a floatRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-floatCanmap>.
+ *
+ * ☡ This function throws if the provided value is not a 32‐bit float.
+ */
+export const floatCanonicalMap = (f) => {
+  float(f);
+  return f === positiveInfinity || f === negativeInfinity ||
+      sameValue(f, notANumber)
+    ? specialRepCanonicalMap(f)
+    : sameValue(f, 0)
+    ? "0.0E0"
+    : sameValue(f, -0)
+    ? "-0.0E0"
+    : scientificCanonicalMap(f);
+};
+
+/**
+ * Maps a double to its ·canonical representation·, a doubleRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-doubleCanmap>.
+ *
+ * ☡ This function throws if the provided value is not a number.
+ */
+export const doubleCanonicalMap = (f) => {
+  double(f);
+  return f === positiveInfinity || f === negativeInfinity ||
+      sameValue(f, notANumber)
+    ? specialRepCanonicalMap(f)
+    : sameValue(f, 0)
+    ? "0.0E0"
+    : sameValue(f, -0)
+    ? "-0.0E0"
+    : scientificCanonicalMap(f);
+};
+
+/**
+ * Maps a duYearFrag to an integer, intended as part of the value of
+ * the ·months· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duYrMap>.
+ *
+ * ☡ This function throws if the provided value is not a duYearFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously specifies the numeral as
+ * following the letter, but it precedes it.
+ *
+ * ※ This function is not exposed.
+ */
+const duYearFragmentMap = (Y) =>
+  noDecimalMap(stringSlice(ensureMatches(duYearFrag, Y), 0, -1));
+
+/**
+ * Maps a duMonthFrag to an integer, intended as part of the value of
+ * the ·months· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duMoMap>.
+ *
+ * ☡ This function throws if the provided value is not a duMonthFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously specifies the numeral as
+ * following the letter, but it precedes it.
+ *
+ * ※ This function is not exposed.
+ */
+const duMonthFragmentMap = (M) =>
+  noDecimalMap(stringSlice(ensureMatches(duMonthFrag, M), 0, -1));
+
+/**
+ * Maps a duDayFrag to an integer, intended as part of the value of the
+ * ·seconds· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duDaMap>.
+ *
+ * ☡ This function throws if the provided value is not a duDayFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously specifies the numeral as
+ * following the letter, but it precedes it.
+ *
+ * ※ This function is not exposed.
+ */
+const duDayFragmentMap = (D) =>
+  noDecimalMap(stringSlice(ensureMatches(duDayFrag, D), 0, -1));
+
+/**
+ * Maps a duHourFrag to an integer, intended as part of the value of
+ * the ·seconds· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duHrMap>.
+ *
+ * ☡ This function throws if the provided value is not a duHourFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously specifies the numeral as
+ * following the letter, but it precedes it.
+ *
+ * ※ The X·S·D specification has a copypasta typo in the definition of
+ * this function; it takes an argument H which necessarily is followed
+ * by an "H". There is no D which necessarily begins with "D".
+ *
+ * ※ This function is not exposed.
+ */
+const duHourFragmentMap = (H) =>
+  noDecimalMap(stringSlice(ensureMatches(duHourFrag, H), 0, -1));
+
+/**
+ * Maps a duMinuteFrag to an integer, intended as part of the value of
+ * the ·seconds· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duMiMap>.
+ *
+ * ☡ This function throws if the provided value is not a duMinuteFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously specifies the numeral as
+ * following the letter, but it precedes it.
+ *
+ * ※ This function is not exposed.
+ */
+const duMinuteFragmentMap = (M) =>
+  noDecimalMap(stringSlice(ensureMatches(duMinuteFrag, M), 0, -1));
+
+/**
+ * Maps a duSecondFrag to an integer, intended as part of the value of
+ * the ·seconds· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duSeMap>.
+ *
+ * ☡ This function throws if the provided value is not a duMinuteFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously specifies the numeral as
+ * following the letter, but it precedes it.
+ *
+ * ※ This function is not exposed.
+ */
+const duSecondFragmentMap = (S) =>
+  getFirstSubstringIndex(ensureMatches(duSecondFrag, S), ".") !== -1
+    ? decimalPtMap(stringSlice(S, 0, -1))
+    : noDecimalMap(stringSlice(S, 0, -1));
+
+/**
+ * Maps a duYearMonthFrag into an integer, intended as part of the
+ * ·months· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duYMMap>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * duYearMonthFrag string.
+ *
+ * ※ This function is not exposed.
+ */
+const duYearMonthFragmentMap = (YM) => {
+  let Y, M;
+  new Matcher(
+    rawString`(?<Y>\d+Y)?(?<M>\d+M)?`,
+    undefined,
+    (_, { groups }) => {
+      Y = groups.Y;
+      M = groups.M;
+      return true;
+    },
+  )(ensureMatches(duYearMonthFrag, YM));
+  const y = Y ? duYearFragmentMap(Y) : 0;
+  const m = M ? duMonthFragmentMap(M) : 0;
+  return 12 * y + m;
+};
+
+/**
+ * Maps a duTimeFrag into an integer, intended as part of the ·seconds·
+ * property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duTMap>.
+ *
+ * ☡ This function throws if the provided value is not a duTimeFrag
+ * string.
+ *
+ * ※ The X·S·D specification erroneously applies ·duDayFragmentMap· to
+ * H; ·duHourFragmentMap· is correct.
+ *
+ * ※ This function is not exposed.
+ */
+const duTimeFragmentMap = (T) => {
+  let H, M, S;
+  new Matcher(
+    rawString`T(?<H>\d+H)?(?<M>\d+M)?(?<S>[\d.]+S)?`,
+    undefined,
+    (_, { groups }) => {
+      H = groups.H;
+      M = groups.M;
+      S = groups.S;
+      return true;
+    },
+  )(ensureMatches(duTimeFrag, T));
+  const h = H ? duHourFragmentMap(H) : 0;
+  const m = M ? duMinuteFragmentMap(M) : 0;
+  const s = S ? duSecondFragmentMap(S) : 0;
+  return 3600 * h + 60 * m + s;
+};
+
+/**
+ * Maps a duDayTimeFrag into a decimal number, which is the potential
+ * value of the ·seconds· property of a duration value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duDTMap>.
+ *
+ * ☡ This function throws if the provided value is not a duDayTimeFrag
+ * string.
+ *
+ * ※ This function is not exposed.
+ */
+const duDayTimeFragmentMap = (DT) => {
+  let D, T;
+  new Matcher(
+    rawString`(?<D>\d+D)?(?<T>T.+)?`,
+    undefined,
+    (_, { groups }) => {
+      D = groups.D;
+      T = groups.T;
+      return true;
+    },
+  )(ensureMatches(duDayTimeFrag, DT));
+  const d = D ? duDayFragmentMap(D) : 0;
+  const t = T ? duTimeFragmentMap(T) : 0;
+  return 86400 * d + t;
+};
+
+/**
+ * Separates the durationLexicalRep into the month part and the seconds
+ * part, then maps them into the ·months· and ·seconds· of the duration
+ * value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-durationMap>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * durationLexicalRep string.
+ */
+export const durationMap = (DUR) => {
+  let Y, D;
+  new Matcher(
+    rawString`-?P(?<Y>(?:\d+Y)?(?:\d+M)?)(?<D>(?:\d+D)?(?:T.+)?)`,
+    undefined,
+    (_, { groups }) => {
+      Y = groups.Y;
+      D = groups.D;
+      return true;
+    },
+  )(ensureMatches(durationLexicalRep, DUR));
+  const s = DUR[0] === "-" ? -1 : 1;
+  return {
+    months: Y ? s * duYearMonthFragmentMap(Y) + 0 : 0, // cast −0 to 0
+    seconds: D ? s * duDayTimeFragmentMap(D) + 0 : 0, // cast −0 to 0
+  };
+};
+
+/**
+ * Maps the lexical representation into the ·months· of a
+ * yearMonthDuration value.  (A yearMonthDuration's ·seconds· is always
+ * zero.)  ·yearMonthDurationMap· is a restriction of ·durationMap·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-yearMonthDurationMap>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * yearMonthDurationLexicalRep string.
+ */
+export const yearMonthDurationMap = (YM) => {
+  const s = YM[0] === "-" ? -1 : 1;
+  const Y = substring(
+    ensureMatches(yearMonthDurationLexicalRep, YM),
+    s === -1 ? 2 : 1,
+  );
+  return {
+    months: s * duYearMonthFragmentMap(Y) + 0, // cast −0 to 0
+    seconds: 0,
+  };
+};
+
+/**
+ * Maps the lexical representation into the ·seconds· of a
+ * dayTimeDuration value.  (A dayTimeDuration's ·months· is always
+ * zero.)  ·dayTimeDurationMap· is a restriction of ·durationMap·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-yearMonthDurationMap>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * dayTimeDurationLexicalRep string.
+ *
+ * ※ The X·S·D specification erroneously describes the argument as a
+ * dayTimeDuration value rather than a dayTimeDurationLexicalRep.
+ */
+export const dayTimeDurationMap = (DT) => {
+  const s = DT[0] === "-" ? -1 : 1;
+  const D = substring(
+    ensureMatches(dayTimeDurationLexicalRep, DT),
+    s === -1 ? 2 : 1,
+  );
+  return {
+    months: 0,
+    seconds: s * duDayTimeFragmentMap(D) + 0, // cast −0 to 0
+  };
+};
+
+/**
+ * Maps a nonnegative integer, presumably the absolute value of the
+ * ·months· of a duration value, to a duYearMonthFrag, a fragment of a
+ * duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duYMCan>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ *
+ * ※ This function is not exposed.
+ */
+const duYearMonthCanonicalFragmentMap = (ym) => {
+  nonnegativeInteger(ym);
+  const y = div(ym, 12);
+  const m = mod(ym, 12);
+  return y !== 0 && m !== 0
+    ? stringCatenate(
+      unsignedNoDecimalPtCanonicalMap(y),
+      "Y",
+      unsignedNoDecimalPtCanonicalMap(m),
+      "M",
+    )
+    : y !== 0
+    ? stringCatenate(
+      unsignedNoDecimalPtCanonicalMap(y),
+      "Y",
+    )
+    : stringCatenate(
+      unsignedNoDecimalPtCanonicalMap(m),
+      "M",
+    );
+};
+
+/**
+ * Maps a nonnegative integer, presumably the day normalized value from
+ * the ·seconds· of a duration value, to a duDayFrag, a fragment of a
+ * duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duDCan>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ *
+ * ※ Despite the description, this function does not necessarily
+ * return a duDayFrag (it can return an empty string).
+ *
+ * ※ This function is not exposed.
+ */
+const duDayCanonicalFragmentMap = (d) =>
+  d !== 0
+    ? stringCatenate(
+      unsignedNoDecimalPtCanonicalMap(d),
+      "D",
+    )
+    : "";
+
+/**
+ * Maps a nonnegative integer, presumably the hour normalized value
+ * from the ·seconds· of a duration value, to a duHourFrag, a fragment
+ * of a duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duHCan>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ *
+ * ※ Despite the description, this function does not necessarily
+ * return a duHourFrag (it can return an empty string).
+ *
+ * ※ This function is not exposed.
+ */
+const duHourCanonicalFragmentMap = (h) =>
+  h !== 0
+    ? stringCatenate(
+      unsignedNoDecimalPtCanonicalMap(h),
+      "H",
+    )
+    : "";
+
+/**
+ * Maps a nonnegative integer, presumably the minute normalized value
+ * from the ·seconds· of a duration value, to a duMinuteFrag, a
+ * fragment of a duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duMCan>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer.
+ *
+ * ※ Despite the description, this function does not necessarily
+ * return a duMinuteFrag (it can return an empty string).
+ *
+ * ※ This function is not exposed.
+ */
+const duMinuteCanonicalFragmentMap = (m) =>
+  m !== 0
+    ? stringCatenate(
+      unsignedNoDecimalPtCanonicalMap(m),
+      "M",
+    )
+    : "";
+
+/**
+ * Maps a nonnegative decimal number, presumably the second normalized
+ * value from the ·seconds· of a duration value, to a duSecondFrag, a
+ * fragment of a duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duSCan>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number.
+ *
+ * ※ Despite the description, this function does not necessarily
+ * return a duSecondFrag (it can return an empty string).
+ *
+ * ※ This function is not exposed.
+ */
+const duSecondCanonicalFragmentMap = (s) =>
+  s !== 0
+    ? stringCatenate(
+      isIntegralNumber(s)
+        ? unsignedNoDecimalPtCanonicalMap(s)
+        : unsignedDecimalPtCanonicalMap(s),
+      "S",
+    )
+    : "";
+
+/**
+ * Maps three nonnegative numbers, presumably the hour, minute, and
+ * second normalized values from a duration's ·seconds·, to a
+ * duTimeFrag, a fragment of a duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duTCan>.
+ *
+ * ☡ This function throws if either of the first two provided values
+ * are not nonnegative integers, or if the final provided value is not
+ * a nonnegative finite number.
+ *
+ * ※ Despite the description, this function does not necessarily
+ * return a duTimeFrag (it can return an empty string).
+ *
+ * ※ This function is not exposed.
+ */
+const duTimeCanonicalFragmentMap = (h, m, s) =>
+  h !== 0 || m !== 0 || s !== 0
+    ? stringCatenate(
+      "T",
+      duHourCanonicalFragmentMap(h),
+      duMinuteCanonicalFragmentMap(m),
+      duSecondCanonicalFragmentMap(s),
+    )
+    : "";
+
+/**
+ * Maps a nonnegative decimal number, presumably the absolute value of
+ * the ·seconds· of a duration value, to a duDayTimeFrag, a fragment of
+ * a duration ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-duDTCan>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number.
+ *
+ * ※ This function is not exposed.
+ */
+const duDayTimeCanonicalFragmentMap = (ss) => {
+  nonnegativeDecimalNumber(ss);
+  const d = div(ss, 86400);
+  const h = div(mod(ss, 86400), 3600);
+  const m = div(mod(ss, 3600), 60);
+  const s = mod(ss, 60);
+  return ss !== 0
+    ? stringCatenate(
+      duDayCanonicalFragmentMap(d),
+      duTimeCanonicalFragmentMap(h, m, s),
+    )
+    : "T0S";
+};
+
+/**
+ * Maps a duration's property values to durationLexicalRep fragments
+ * and combines the fragments into a complete durationLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-durationCanMap>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * duration value.
+ */
+export const durationCanonicalMap = (v) => {
+  const { months: m, seconds: s } = duration(v);
+  const sgn = m < 0 || s < 0 ? "-" : "";
+  return m !== 0 && s !== 0
+    ? stringCatenate(
+      sgn,
+      "P",
+      duYearMonthCanonicalFragmentMap(abs(m)),
+      duDayTimeCanonicalFragmentMap(abs(s)),
+    )
+    : m !== 0
+    ? stringCatenate(
+      sgn,
+      "P",
+      duYearMonthCanonicalFragmentMap(abs(m)),
+    )
+    : stringCatenate(
+      sgn,
+      "P",
+      duDayTimeCanonicalFragmentMap(abs(s)),
+    );
+};
+
+/**
+ * Maps a yearMonthDuration's ·months· value to a
+ * yearMonthDurationLexicalRep.  (The ·seconds· value is necessarily
+ * zero and is ignored.)  ·yearMonthDurationCanonicalMap· is a
+ * restriction of ·durationCanonicalMap·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-yearMonthDurationCanMap>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * yearMonthDuration value.
+ */
+export const yearMonthDurationCanonicalMap = (ym) => {
+  const { months: m, seconds: s } = duration(ym);
+  if (s !== 0) {
+    throw new TypeError(
+      `सूत्र: Expected the provided yearMonthDuration to have a value of zero for seconds, but got: ${s}.`,
+    );
+  } else {
+    const sgn = m < 0 ? "-" : "";
+    return stringCatenate(
+      sgn,
+      "P",
+      duYearMonthCanonicalFragmentMap(abs(m)),
+    );
+  }
+};
+
+/**
+ * Maps a dayTimeDuration's ·seconds· value to a
+ * dayTimeDurationLexicalRep.  (The ·months· value is necessarily zero
+ * and is ignored.)  ·dayTimeDurationCanonicalMap· is a restriction of
+ * ·durationCanonicalMap·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dayTimeDurationCanMap>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * dayTimeDuration value.
+ */
+export const dayTimeDurationCanonicalMap = (dt) => {
+  const { months: m, seconds: s } = duration(dt);
+  if (m !== 0) {
+    throw new TypeError(
+      `सूत्र: Expected the provided dayTimeDuration to have a value of zero for months, but got: ${m}.`,
+    );
+  } else {
+    const sgn = s < 0 ? "-" : "";
+    return stringCatenate(
+      sgn,
+      "P",
+      duDayTimeCanonicalFragmentMap(abs(s)),
+    );
+  }
+};
+
+/**
+ * If month (mo) is out of range, adjust month and year (yr)
+ * accordingly; otherwise, make no change.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normMo>.
+ *
+ * ☡ This function throws if the provided values are not integers.
+ *
+ * ※ This function is not exposed.
+ */
+const normalizeMonth = (yr, mo) => {
+  integer(yr);
+  integer(mo);
+  return [yr + div(mo - 1, 12), mod(mo - 1, 12) + 1];
+};
+
+/**
+ * If month (mo) is out of range, or day (da) is out of range for the
+ * appropriate month, then adjust values accordingly, otherwise make no
+ * change.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normDa>.
+ *
+ * ☡ This function throws if the provided values are not integers.
+ *
+ * ※ This function is not exposed.
+ */
+const normalizeDay = (yr, mo, da) => {
+  integer(yr);
+  integer(mo);
+  integer(da);
+  [yr, mo] = normalizeMonth(yr, mo);
+  let limit = daysInMonth(yr, mo);
+  while (da > limit || da <= 0) {
+    if (da > limit) {
+      da -= limit;
+      mo += 1;
+      [yr, mo] = normalizeMonth(yr, mo);
+      limit = daysInMonth(yr, mo);
+    }
+    if (da <= 0) {
+      mo -= 1;
+      [yr, mo] = normalizeMonth(yr, mo);
+      limit = daysInMonth(yr, mo);
+      da += limit;
+    }
+  }
+  return [yr, mo, da];
+};
+
+/**
+ * Normalizes minute, hour, month, and year values to values that obey
+ * the appropriate constraints.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normMi>.
+ *
+ * ☡ This function throws if the provided values are not integers.
+ *
+ * ※ This function is not exposed.
+ */
+const normalizeMinute = (yr, mo, da, hr, mi) => {
+  integer(yr);
+  integer(mo);
+  integer(da);
+  integer(hr);
+  integer(mi);
+  hr += div(mi, 60);
+  mi = mod(mi, 60);
+  da += div(hr, 24);
+  hr = mod(hr, 24);
+  [yr, mo, da] = normalizeDay(yr, mo, da);
+  return [yr, mo, da, hr, mi];
+};
+
+/**
+ * Normalizes minute, hour, month, and year values to values that obey
+ * the appropriate constraints.  (This algorithm ignores leap seconds.)
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normSe>.
+ *
+ * ☡ This function throws if the first five provided values are not
+ * integers or the sixth is not a finite number.
+ *
+ * ※ This function is not exposed.
+ */
+const normalizeSecond = (yr, mo, da, hr, mi, se) => {
+  integer(yr);
+  integer(mo);
+  integer(da);
+  integer(hr);
+  integer(mi);
+  decimalNumber(se);
+  mi += div(se, 60);
+  se = mod(se, 60);
+  [yr, mo, da, hr, mi] = normalizeMinute(yr, mo, da, hr, mi);
+  return [yr, mo, da, hr, mi, se];
+};
+
+/**
+ * Normalizes minute, hour, month, and year values to values that obey
+ * the appropriate constraints.  (This algorithm ignores leap seconds.)
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-daysInMonth>.
+ *
+ * ☡ This function throws if the first value is not absent or an
+ * integer or if the second value is not an integer between 1 and 12
+ * inclusive.
+ */
+export const daysInMonth = (y, m) => {
+  if (y !== absent && !isIntegralNumber(y)) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer, but got: ${y}.`,
+    );
+  } else if (!(isIntegralNumber(m) && m >= 1 && m <= 12)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer between 1 and 12 inclusive, but got: ${m}.`,
+    );
+  } else {
+    return m === 2
+      ? y === absent || y % 4 || !(y % 100) && y % 400 ? 28 : 29
+      : m === 4 || m === 6 || m === 9 || m === 11
+      ? 30
+      : 31;
+  }
+};
+
+/**
+ * Returns an instance of the date/timeSevenPropertyModel with property
+ * values as specified in the arguments. If an argument is omitted, the
+ * corresponding property is set to absent.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#p-setDTFromRaw>.
+ *
+ * ※ The X·S·D specification erroneously fails to account for the
+ * value `"--02-29"`, which `·newDateTime·` treats as 01 March because
+ * it assumes February has 28 days when the year is absent. This is a
+ * correct assumption generally, but February should be treated as
+ * having 29 days when a value of `"--02-29"` is explicity given.
+ *
+ * ☡ This function throws if the provided values do not match the
+ * ranges expected by the Date∕time Seven‐Property Model.
+ */
+export const newDateTime = (Yr, Mo, Da, Hr, Mi, Se, Tz) => {
+  if (Yr !== absent && !isIntegralNumber(Yr)) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer, but got: ${Yr}.`,
+    );
+  } else if (
+    Mo !== absent && !(isIntegralNumber(Mo) && Mo >= 1 && Mo <= 12)
+  ) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer between 1 and 12 inclusive, but got: ${Mo}.`,
+    );
+  } else if (
+    Da !== absent && !(isIntegralNumber(Da) && Da >= 1 && Da <= 31)
+  ) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer between 1 and 31 inclusive, but got: ${Da}.`,
+    );
+  } else if (
+    Hr !== absent && !(isIntegralNumber(Hr) && Hr >= 0 && Hr <= 24)
+  ) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer between 0 and 24 inclusive, but got: ${Hr}.`,
+    );
+  } else if (
+    Mi !== absent && !(isIntegralNumber(Mi) && Mi >= 0 && Mi <= 59)
+  ) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer between 0 and 59 inclusive, but got: ${Mi}.`,
+    );
+  } else if (
+    Se !== absent && !(isFiniteNumber(Se) && Se >= 0 && Se < 60)
+  ) {
+    throw new TypeError(
+      `सूत्र: Expected an optional finite number greater than or equal to 0 and less than 60, but got: ${Se}.`,
+    );
+  } else if (
+    Tz !== absent && !(isIntegralNumber(Tz) && Tz >= -840 && Tz <= 840)
+  ) {
+    throw new TypeError(
+      `सूत्र: Expected an optional integer between -840 and 840 inclusive, but got: ${Tz}.`,
+    );
+  } else {
+    let yr = Yr !== absent ? Yr : Mo === 2 && Da === 29 ? 0 : 1;
+    let mo = Mo !== absent ? Mo : 1;
+    let da = Da !== absent ? Da : 1;
+    let hr = Hr !== absent ? Hr : 0;
+    let mi = Mi !== absent ? Mi : 0;
+    let se = Se !== absent ? Se : 0;
+    [yr, mo, da, hr, mi, se] = normalizeSecond(yr, mo, da, hr, mi, se);
+    return {
+      year: Yr === absent ? absent : yr,
+      month: Mo === absent ? absent : mo,
+      day: Da === absent ? absent : da,
+      hour: Hr === absent ? absent : hr,
+      minute: Mi === absent ? absent : mi,
+      second: Se === absent ? absent : se,
+      timezoneOffset: Tz,
+    };
+  }
+};
+
+/**
+ * Adds a duration to a dateTime value, producing another dateTime
+ * value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-dt-dateTimePlusDuration>.
+ *
+ * ※ The X·S·D specification erroneously fails to account for the
+ * value `"--02-29"`, which `·dateTimePlusDuration·` treats as 01 March
+ * because it assumes February has 28 days when the year is absent.
+ * This is a correct assumption generally, but February should be
+ * treated as having 29 days when a value of `"--02-29"` is explicity
+ * given.
+ *
+ * ☡ This function throws if the provided duration or
+ * date/timeSevenPropertyModel values are not valid.
+ *
+ * ※ Despite the name and description, this function can add a
+ * duration to any date/timeSevenPropertyModel value.
+ */
+export const dateTimePlusDuration = (du, dt) => {
+  const { months, seconds } = duration(du);
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(dt);
+  let yr = year !== absent ? year : month === 2 && day === 29 ? 0 : 1;
+  let mo = month !== absent ? month : 1;
+  let da = day !== absent ? day : 1;
+  let hr = hour !== absent ? hour : 0;
+  let mi = minute !== absent ? minute : 0;
+  let se = second !== absent ? second : 0;
+  mo += months;
+  [yr, mo] = normalizeMonth(yr, mo);
+  da = min(da, daysInMonth(yr, mo));
+  se += seconds;
+  [yr, mo, da, hr, mi, se] = normalizeSecond(yr, mo, da, hr, mi, se);
+  return newDateTime(
+    year === absent ? absent : yr,
+    month === absent ? absent : mo,
+    day === absent ? absent : da,
+    hour === absent ? absent : hr,
+    minute === absent ? absent : mi,
+    second === absent ? absent : se,
+    timezoneOffset,
+  );
+};
+
+/**
+ * Maps a date/timeSevenPropertyModel value to the decimal number
+ * representing its position on the "time line".
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-dt-timeOnTimeline>.
+ *
+ * ☡ This function throws if the provided date/timeSevenPropertyModel
+ * value is not valid.
+ */
+export const timeOnTimeline = (dt) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(dt);
+  const yr = year === absent ? 1971 : year - 1;
+  const mo = month === absent ? 12 : month;
+  const da = day === absent ? daysInMonth(yr + 1, mo) - 1 : day - 1;
+  const hr = hour === absent ? 0 : hour;
+  const mi = (minute === absent ? 0 : minute) -
+    (timezoneOffset === absent ? 0 : timezoneOffset);
+  const se = second === absent ? 0 : second;
+  let ToTI = 31536000 * yr;
+  ToTI += 86400 * (div(yr, 400) - div(yr, 100) + div(yr, 4));
+  for (let m = 1; m < mo; ++m) {
+    ToTI += 86400 * daysInMonth(yr + 1, m);
+  }
+  ToTI += 86400 * da;
+  ToTI += 3600 * hr + 60 * mi + se;
+  return ToTI;
+};
+
+/**
+ * Maps a yearFrag, part of a date/timeSevenPropertyModel's ·lexical
+ * representation·, onto an integer, presumably the ·year· property of
+ * a date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-yrMap>.
+ *
+ * ☡ This function throws if the provided value is not a yearFrag
+ * string.
+ */
+export const yearFragValue = (YR) =>
+  noDecimalMap(ensureMatches(yearFrag, YR));
+
+/**
+ * Maps a monthFrag, part of a date/timeSevenPropertyModel's ·lexical
+ * representation·, onto an integer, presumably the ·month· property of
+ * a date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-moMap>.
+ *
+ * ☡ This function throws if the provided value is not a monthFrag
+ * string.
+ */
+export const monthFragValue = (MO) =>
+  unsignedNoDecimalMap(ensureMatches(monthFrag, MO));
+
+/**
+ * Maps a dayFrag, part of a date/timeSevenPropertyModel's ·lexical
+ * representation·, onto an integer, presumably the ·day· property of a
+ * date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-daMap>.
+ *
+ * ☡ This function throws if the provided value is not a dayFrag
+ * string.
+ */
+export const dayFragValue = (DA) =>
+  unsignedNoDecimalMap(ensureMatches(dayFrag, DA));
+
+/**
+ * Maps a hourFrag, part of a date/timeSevenPropertyModel's ·lexical
+ * representation·, onto an integer, presumably the ·hour· property of
+ * a date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-hrMap>.
+ *
+ * ☡ This function throws if the provided value is not a hourFrag
+ * string.
+ */
+export const hourFragValue = (HR) =>
+  unsignedNoDecimalMap(ensureMatches(hourFrag, HR));
+
+/**
+ * Maps a minuteFrag, part of a date/timeSevenPropertyModel's ·lexical
+ * representation·, onto an integer, presumably the ·minute· property
+ * of a date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-miMap>.
+ *
+ * ☡ This function throws if the provided value is not a minuteFrag
+ * string.
+ */
+export const minuteFragValue = (MI) =>
+  unsignedNoDecimalMap(ensureMatches(minuteFrag, MI));
+
+/**
+ * Maps a secondFrag, part of a date/timeSevenPropertyModel's ·lexical
+ * representation·, onto a decimal number, presumably the ·second·
+ * property of a date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-seMap>.
+ *
+ * ☡ This function throws if the provided value is not a secondFrag
+ * string.
+ */
+export const secondFragValue = (SE) =>
+  getFirstSubstringIndex(ensureMatches(secondFrag, SE), ".") === -1
+    ? unsignedNoDecimalMap(SE)
+    : unsignedDecimalPtMap(SE);
+
+/**
+ * Maps a timezoneFrag, part of a date/timeSevenPropertyModel's
+ * ·lexical representation·, onto an integer, presumably the
+ * ·timezoneOffset· property of a date/timeSevenPropertyModel value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-tzMap>.
+ *
+ * ※ The X·S·D specification erroneously specifies
+ * ·unsignedDecimalPtMap·(H) and ·unsignedDecimalPtMap·(M), but
+ * ·unsignedNoDecimalMap·(H) and ·unsignedDecimalPtMap·(M) is correct.
+ *
+ * ☡ This function throws if the provided value is not a timezoneFrag
+ * string.
+ */
+export const timezoneFragValue = (TZ) => {
+  const s = ensureMatches(timezoneFrag, TZ)[0];
+  if (s === "Z") {
+    return 0;
+  } else {
+    let H, M;
+    new Matcher(
+      rawString`[+-](?<H>\d{2}):(?<M>\d{2})`,
+      undefined,
+      (_, { groups }) => {
+        H = groups.H;
+        M = groups.M;
+        return true;
+      },
+    )(TZ);
+    return s === "-"
+      ? -(unsignedNoDecimalMap(H) * 60 + unsignedNoDecimalMap(M)) + 0
+      : unsignedNoDecimalMap(H) * 60 + unsignedNoDecimalMap(M);
+  }
+};
+
+/**
+ * Maps a dateTimeLexicalRep to a dateTime value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateTimeLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * dateTimeLexicalRep string.
+ */
+export const dateTimeLexicalMap = (LEX) => {
+  let Y, MO, D, H, MI, S, T;
+  new Matcher(
+    rawString`(?<Y>-?\d+)-(?<MO>\d+)-(?<D>\d+)T(?:24:00:00(?:\.0+)?|(?<H>\d+):(?<MI>\d+):(?<S>[\d.]+))(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      Y = groups.Y;
+      MO = groups.MO;
+      D = groups.D;
+      H = groups.H ?? absent;
+      MI = groups.MI ?? absent;
+      S = groups.S ?? absent;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(dateTimeLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return H === absent
+    ? newDateTime(
+      yearFragValue(Y),
+      monthFragValue(MO),
+      dayFragValue(D),
+      24,
+      0,
+      0,
+      tz,
+    )
+    : newDateTime(
+      yearFragValue(Y),
+      monthFragValue(MO),
+      dayFragValue(D),
+      hourFragValue(H),
+      minuteFragValue(MI),
+      secondFragValue(S),
+      tz,
+    );
+};
+
+/**
+ * Maps a timeLexicalMap to a time value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-timeLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a timeLexicalMap
+ * string.
+ */
+export const timeLexicalMap = (LEX) => {
+  let H, MI, S, T;
+  new Matcher(
+    rawString`(?:24:00:00(?:\.0+)?|(?<H>\d+):(?<MI>\d+):(?<S>[\d.]+))(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      H = groups.H ?? absent;
+      MI = groups.MI ?? absent;
+      S = groups.S ?? absent;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(timeLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return H === absent
+    ? newDateTime(
+      absent,
+      absent,
+      absent,
+      0,
+      0,
+      0,
+      tz,
+    )
+    : newDateTime(
+      absent,
+      absent,
+      absent,
+      hourFragValue(H),
+      minuteFragValue(MI),
+      secondFragValue(S),
+      tz,
+    );
+};
+
+/**
+ * Maps a dateLexicalRep to a date value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a dateLexicalRep
+ * string.
+ */
+export const dateLexicalMap = (LEX) => {
+  let Y, M, D, T;
+  new Matcher(
+    rawString`(?<Y>-?\d+)-(?<M>\d+)-(?<D>\d+)(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      Y = groups.Y;
+      M = groups.M;
+      D = groups.D;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(dateLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return newDateTime(
+    yearFragValue(Y),
+    monthFragValue(M),
+    dayFragValue(D),
+    absent,
+    absent,
+    absent,
+    tz,
+  );
+};
+
+/**
+ * Maps a gYearMonthLexicalRep to a date value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearMonthLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * gYearMonthLexicalRep string.
+ */
+export const gYearMonthLexicalMap = (LEX) => {
+  let Y, M, T;
+  new Matcher(
+    rawString`(?<Y>-?\d+)-(?<M>\d+)(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      Y = groups.Y;
+      M = groups.M;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(gYearMonthLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return newDateTime(
+    yearFragValue(Y),
+    monthFragValue(M),
+    absent,
+    absent,
+    absent,
+    absent,
+    tz,
+  );
+};
+
+/**
+ * Maps a gYearLexicalRep to a date value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * gYearLexicalRep string.
+ */
+export const gYearLexicalMap = (LEX) => {
+  let Y, T;
+  new Matcher(
+    rawString`(?<Y>-?\d+)(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      Y = groups.Y;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(gYearLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return newDateTime(
+    yearFragValue(Y),
+    absent,
+    absent,
+    absent,
+    absent,
+    absent,
+    tz,
+  );
+};
+
+/**
+ * Maps a gMonthDayLexicalRep to a date value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthDayLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * gMonthDayLexicalRep string.
+ */
+export const gMonthDayLexicalMap = (LEX) => {
+  let M, D, T;
+  new Matcher(
+    rawString`--(?<M>\d+)-(?<D>\d+)(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      M = groups.M;
+      D = groups.D;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(gMonthDayLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return newDateTime(
+    absent,
+    monthFragValue(M),
+    dayFragValue(D),
+    absent,
+    absent,
+    absent,
+    tz,
+  );
+};
+
+/**
+ * Maps a gDayLexicalRep to a date value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gDayLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a gDayLexicalRep
+ * string.
+ */
+export const gDayLexicalMap = (LEX) => {
+  let D, T;
+  new Matcher(
+    rawString`---(?<D>\d+)(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      D = groups.D;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(gDayLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return newDateTime(
+    absent,
+    absent,
+    dayFragValue(D),
+    absent,
+    absent,
+    absent,
+    tz,
+  );
+};
+
+/**
+ * Maps a gMonthLexicalRep to a date value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthLexRep>.
+ *
+ * ☡ This function throws if the provided value is not a
+ * gMonthLexicalRep string.
+ */
+export const gMonthLexicalMap = (LEX) => {
+  let M, T;
+  new Matcher(
+    rawString`--(?<M>\d+)(?<T>.+)?`,
+    undefined,
+    (_, { groups }) => {
+      M = groups.M;
+      T = groups.T ?? absent;
+      return true;
+    },
+  )(ensureMatches(gMonthLexicalRep, LEX));
+  const tz = T === absent ? absent : timezoneFragValue(T);
+  return newDateTime(
+    absent,
+    monthFragValue(M),
+    absent,
+    absent,
+    absent,
+    absent,
+    tz,
+  );
+};
+
+/**
+ * Maps a nonnegative integer less than 100 onto an unsigned
+ * always-two-digit numeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-unsTwoDigCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * integer less than 100.
+ *
+ * ※ This function is not exposed.
+ */
+const unsTwoDigitCanonicalFragmentMap = (i) => {
+  if (!(isIntegralNumber(i) && i >= 0 && i < 100)) {
+    throw new TypeError(
+      `सूत्र: Expected a nonnegative integer less than 100, but got: ${i}.`,
+    );
+  } else {
+    return stringCatenate(digit(div(i, 10)), digit(mod(i, 10)));
+  }
+};
+
+/**
+ * Maps an integer between -10000 and 10000 onto an always-four-digit numeral.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-fourDigCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer whose
+ * absolute value is less than 10000.
+ *
+ * ※ This function is not exposed.
+ */
+const fourDigitCanonicalFragmentMap = (i) => {
+  if (!(isIntegralNumber(i) && abs(i) < 10000)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer whose absolute value is less than 10000, but got: ${i}.`,
+    );
+  } else {
+    return i < 0
+      ? stringCatenate(
+        "-",
+        unsTwoDigitCanonicalFragmentMap(div(-i, 100)),
+        unsTwoDigitCanonicalFragmentMap(mod(-i, 100)),
+      )
+      : stringCatenate(
+        unsTwoDigitCanonicalFragmentMap(div(i, 100)),
+        unsTwoDigitCanonicalFragmentMap(mod(i, 100)),
+      );
+  }
+};
+
+/**
+ * Maps an integer, presumably the ·year· property of a
+ * date/timeSevenPropertyModel value, onto a yearFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-yrCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer.
+ */
+export const yearCanonicalFragmentMap = (y) =>
+  abs(integer(y)) > 9999
+    ? noDecimalPtCanonicalMap(y)
+    : fourDigitCanonicalFragmentMap(y);
+
+/**
+ * Maps an integer, presumably the ·month· property of a
+ * date/timeSevenPropertyModel value, onto a monthFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-moCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer
+ * between 1 and 12 inclusive.
+ */
+export const monthCanonicalFragmentMap = (m) => {
+  if (!(isIntegralNumber(m) && m >= 1 && m <= 12)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer between 1 and 12 inclusive, but got: ${m}.`,
+    );
+  } else {
+    return unsTwoDigitCanonicalFragmentMap(m);
+  }
+};
+
+/**
+ * Maps an integer, presumably the ·day· property of a
+ * date/timeSevenPropertyModel value, onto a dayFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-daCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer
+ * between 1 and 31 inclusive.
+ */
+export const dayCanonicalFragmentMap = (d) => {
+  if (!(isIntegralNumber(d) && d >= 1 && d <= 31)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer between 1 and 31 inclusive, but got: ${d}.`,
+    );
+  } else {
+    return unsTwoDigitCanonicalFragmentMap(d);
+  }
+};
+
+/**
+ * Maps an integer, presumably the ·hour· property of a
+ * date/timeSevenPropertyModel value, onto an hourFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hrCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer
+ * between 0 and 23 inclusive.
+ */
+export const hourCanonicalFragmentMap = (h) => {
+  if (!(isIntegralNumber(h) && h >= 0 && h <= 23)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer between 0 and 23 inclusive, but got: ${h}.`,
+    );
+  } else {
+    return unsTwoDigitCanonicalFragmentMap(h);
+  }
+};
+
+/**
+ * Maps an integer, presumably the ·minute· property of a
+ * date/timeSevenPropertyModel value, onto a minuteFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-miCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer
+ * between 0 and 59 inclusive.
+ */
+export const minuteCanonicalFragmentMap = (m) => {
+  if (!(isIntegralNumber(m) && m >= 0 && m <= 59)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer between 0 and 59 inclusive, but got: ${m}.`,
+    );
+  } else {
+    return unsTwoDigitCanonicalFragmentMap(m);
+  }
+};
+
+/**
+ * Maps a decimal number, presumably the ·second· property of a
+ * date/timeSevenPropertyModel value, onto a secondFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-seCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not a nonnegative
+ * finite number less than 60.
+ *
+ * ※ The X·S·D specification identifies the argument to this function
+ * as “a nonnegative decimal number less than 70”, but second values 60
+ * or larger are not supported by X·S·D.
+ *
+ * ※ This function makes use of the built·in Ecmascript float
+ * serialization to avoid rounding errors.
+ */
+export const secondCanonicalFragmentMap = (s) => {
+  if (!(isFiniteNumber(s) && s < 60)) {
+    throw new TypeError(
+      `सूत्र: Expected a nonnegative finite number less than 60, but got: ${s}.`,
+    );
+  } else {
+    // return isIntegralNumber(s)
+    //  ? unsTwoDigitCanonicalFragmentMap(s)
+    //  : stringCatenate(
+    //    unsTwoDigitCanonicalFragmentMap(div(s, 1)),
+    //    ".",
+    //    fractionDigitsCanonicalFragmentMap(mod(s, 1)),
+    //  );
+    return isIntegralNumber(s)
+      ? unsTwoDigitCanonicalFragmentMap(s)
+      : s < 10
+      ? stringCatenate("0", decimalPtCanonicalMap(s))
+      : decimalPtCanonicalMap(s);
+  }
+};
+
+/**
+ * Maps an integer, presumably the ·timezoneOffset· property of a
+ * date/timeSevenPropertyModel value, onto a timezoneFrag, part of a
+ * date/timeSevenPropertyModel's ·lexical representation·.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-tzCanFragMap>.
+ *
+ * ☡ This function throws if the provided value is not an integer
+ * between −840 and 840 inclusive.
+ */
+export const timezoneCanonicalFragmentMap = (t) => {
+  if (!(isIntegralNumber(t) && t >= -840 && t <= 840)) {
+    throw new TypeError(
+      `सूत्र: Expected an integer between −840 and 840 inclusive, but got: ${t}.`,
+    );
+  } else {
+    return t === 0 ? "Z" : t < 0
+      ? stringCatenate(
+        "-",
+        unsTwoDigitCanonicalFragmentMap(div(-t, 60)),
+        ":",
+        unsTwoDigitCanonicalFragmentMap(mod(-t, 60)),
+      )
+      : stringCatenate(
+        "+",
+        unsTwoDigitCanonicalFragmentMap(div(t, 60)),
+        ":",
+        unsTwoDigitCanonicalFragmentMap(mod(t, 60)),
+      );
+  }
+};
+
+/**
+ * Maps a dateTime value to a dateTimeLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateTimeCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * dateTime.
+ */
+export const dateTimeCanonicalMap = (dt) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(dt);
+  ensurePresence("year", year, true);
+  ensurePresence("month", month, true);
+  ensurePresence("day", day, true);
+  ensurePresence("hour", hour, true);
+  ensurePresence("minute", minute, true);
+  ensurePresence("second", second, true);
+  const DT = stringCatenate(
+    yearCanonicalFragmentMap(year),
+    "-",
+    monthCanonicalFragmentMap(month),
+    "-",
+    dayCanonicalFragmentMap(day),
+    "T",
+    hourCanonicalFragmentMap(hour),
+    ":",
+    minuteCanonicalFragmentMap(minute),
+    ":",
+    secondCanonicalFragmentMap(second),
+  );
+  return timezoneOffset === absent
+    ? DT
+    : stringCatenate(DT, timezoneCanonicalFragmentMap(timezoneOffset));
+};
+
+/**
+ * Maps a time value to a timeLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-timeCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete time.
+ */
+export const timeCanonicalMap = (ti) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(ti);
+  ensurePresence("year", year, false);
+  ensurePresence("month", month, false);
+  ensurePresence("day", day, false);
+  ensurePresence("hour", hour, true);
+  ensurePresence("minute", minute, true);
+  ensurePresence("second", second, true);
+  const T = stringCatenate(
+    hourCanonicalFragmentMap(hour),
+    ":",
+    minuteCanonicalFragmentMap(minute),
+    ":",
+    secondCanonicalFragmentMap(second),
+  );
+  return timezoneOffset === absent
+    ? T
+    : stringCatenate(T, timezoneCanonicalFragmentMap(timezoneOffset));
+};
+
+/**
+ * Maps a date value to a dateLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete date.
+ */
+export const dateCanonicalMap = (da) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(da);
+  ensurePresence("year", year, true);
+  ensurePresence("month", month, true);
+  ensurePresence("day", day, true);
+  ensurePresence("hour", hour, false);
+  ensurePresence("minute", minute, false);
+  ensurePresence("second", second, false);
+  const D = stringCatenate(
+    yearCanonicalFragmentMap(year),
+    "-",
+    monthCanonicalFragmentMap(month),
+    "-",
+    dayCanonicalFragmentMap(day),
+  );
+  return timezoneOffset === absent
+    ? D
+    : stringCatenate(D, timezoneCanonicalFragmentMap(timezoneOffset));
+};
+
+/**
+ * Maps a gYearMonth value to a gYearMonthLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearMonthCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * gYearMonth.
+ */
+export const gYearMonthCanonicalMap = (ym) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(ym);
+  ensurePresence("year", year, true);
+  ensurePresence("month", month, true);
+  ensurePresence("day", day, false);
+  ensurePresence("hour", hour, false);
+  ensurePresence("minute", minute, false);
+  ensurePresence("second", second, false);
+  const YM = stringCatenate(
+    yearCanonicalFragmentMap(year),
+    "-",
+    monthCanonicalFragmentMap(month),
+  );
+  return timezoneOffset === absent
+    ? YM
+    : stringCatenate(YM, timezoneCanonicalFragmentMap(timezoneOffset));
+};
+
+/**
+ * Maps a gYear value to a gYearLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * gYear.
+ */
+export const gYearCanonicalMap = (gY) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(gY);
+  ensurePresence("year", year, true);
+  ensurePresence("month", month, false);
+  ensurePresence("day", day, false);
+  ensurePresence("hour", hour, false);
+  ensurePresence("minute", minute, false);
+  ensurePresence("second", second, false);
+  return timezoneOffset === absent
+    ? yearCanonicalFragmentMap(year)
+    : stringCatenate(
+      yearCanonicalFragmentMap(year),
+      timezoneCanonicalFragmentMap(timezoneOffset),
+    );
+};
+
+/**
+ * Maps a gMonthDay value to a gMonthDayLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthDayCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * gMonthDay.
+ */
+export const gMonthDayCanonicalMap = (md) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(md);
+  ensurePresence("year", year, false);
+  ensurePresence("month", month, true);
+  ensurePresence("day", day, true);
+  ensurePresence("hour", hour, false);
+  ensurePresence("minute", minute, false);
+  ensurePresence("second", second, false);
+  const MD = stringCatenate(
+    "--",
+    monthCanonicalFragmentMap(month),
+    "-",
+    dayCanonicalFragmentMap(day),
+  );
+  return timezoneOffset === absent
+    ? MD
+    : stringCatenate(MD, timezoneCanonicalFragmentMap(timezoneOffset));
+};
+
+/**
+ * Maps a gDay value to a gDayLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gDayCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete gDay.
+ */
+export const gDayCanonicalMap = (gD) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(gD);
+  ensurePresence("year", year, false);
+  ensurePresence("month", month, false);
+  ensurePresence("day", day, true);
+  ensurePresence("hour", hour, false);
+  ensurePresence("minute", minute, false);
+  ensurePresence("second", second, false);
+  return timezoneOffset === absent
+    ? stringCatenate("---", dayCanonicalFragmentMap(day))
+    : stringCatenate(
+      "---",
+      dayCanonicalFragmentMap(day),
+      timezoneCanonicalFragmentMap(timezoneOffset),
+    );
+};
+
+/**
+ * Maps a gMonth value to a gMonthLexicalRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthCanRep>.
+ *
+ * ☡ This function throws if the provided value is not a complete
+ * gMonth.
+ */
+export const gMonthCanonicalMap = (gM) => {
+  const {
+    year,
+    month,
+    day,
+    hour,
+    minute,
+    second,
+    timezoneOffset,
+  } = date·timeSevenPropertyModel(gM);
+  ensurePresence("year", year, false);
+  ensurePresence("month", month, true);
+  ensurePresence("day", day, false);
+  ensurePresence("hour", hour, false);
+  ensurePresence("minute", minute, false);
+  ensurePresence("second", second, false);
+  return timezoneOffset === absent
+    ? stringCatenate("--", dayCanonicalFragmentMap(month))
+    : stringCatenate(
+      "--",
+      dayCanonicalFragmentMap(month),
+      timezoneCanonicalFragmentMap(timezoneOffset),
+    );
+};
+
+/**
+ * Maps a ·literal· matching the stringRep production to a string
+ * value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-stringLexmap>.
+ *
+ * ☡ This function throws if the provided value is not a stringRep
+ * string.
+ */
+export const stringLexicalMap = (LEX) => ensureMatches(stringRep, LEX);
+
+/**
+ * Maps a ·literal· matching the booleanRep production to a boolean
+ * value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-booleanLexmap>.
+ *
+ * ☡ This function throws if the provided value is not a booleanRep
+ * string.
+ */
+export const booleanLexicalMap = (LEX) => {
+  ensureMatches(booleanRep, LEX);
+  return LEX === "true" || LEX === "1";
+};
+
+/**
+ * Maps a string value to a stringRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-stringCanmap>.
+ *
+ * ☡ This function throws if the provided value is not a valid string.
+ */
+export const stringCanonicalMap = (s) => ensureMatches(stringRep, s);
+
+/**
+ * Maps a boolean value to a booleanRep.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-booleanCanmap>.
+ *
+ * ☡ This function throws if the provided value is not a boolean.
+ */
+export const booleanCanonicalMap = (b) => {
+  if (typeof b !== "boolean") {
+    throw new TypeError(`सूत्र: Expected a boolean, but got: ${b}.`);
+  } else {
+    return b ? "true" : "false";
+  }
+};
+
+/**
+ * Maps a ·literal· matching the hexBinary production to a sequence of
+ * octets in the form of a hexBinary value.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hexBinaryMap>.
+ *
+ * ☡ This function throws if the provided value is not a hexBinary
+ * string.
+ *
+ * ※ This function returns an `ArrayBuffer`.
+ */
+export const hexBinaryMap = (LEX) => {
+  ensureMatches(hexBinary, LEX);
+  const bufferLength = LEX.length / 2;
+  const o = new ArrayBuffer(bufferLength);
+  for (let bufferIndex = 0, index = 0; bufferIndex < bufferLength;) {
+    set8BitIntegralItem(
+      o,
+      bufferIndex,
+      hexOctetMap(substring(LEX, index, index = ++bufferIndex * 2)),
+    );
+  }
+  return o;
+};
+
+/**
+ * Maps a ·literal· matching the hexOctet production to a single octet.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hexOctetMap>.
+ *
+ * ☡ This function throws if the provided value is not a hexOctet
+ * string.
+ *
+ * ※ This function is not exposed.
+ */
+const hexOctetMap = (LEX) => {
+  const [d0, d1] = ensureMatches(hexOctet, LEX);
+  let octet = 0;
+  let bit = 8;
+  for (const digit of hexDigitMap(d0)) {
+    octet |= digit << --bit;
+  }
+  for (const digit of hexDigitMap(d1)) {
+    octet |= digit << --bit;
+  }
+  return octet;
+};
+
+/**
+ * Maps a hexadecimal digit (a character matching the hexDigit
+ * production) to a sequence of four binary digits.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hexDigitMap>.
+ *
+ * ☡ This function throws if the provided value is not a hexDigit
+ * string.
+ *
+ * ※ It would be way simpler and more efficient to just return a
+ * single value here rather than a sequence of digits.
+ *
+ * ※ This function is not exposed.
+ */
+const hexDigitMap = (d) => {
+  switch (ensureMatches(hexDigit, d)) {
+    case "0":
+      return binaryDigitSequence("0000");
+    case "1":
+      return binaryDigitSequence("0001");
+    case "2":
+      return binaryDigitSequence("0010");
+    case "3":
+      return binaryDigitSequence("0011");
+    case "4":
+      return binaryDigitSequence("0100");
+    case "5":
+      return binaryDigitSequence("0101");
+    case "6":
+      return binaryDigitSequence("0110");
+    case "7":
+      return binaryDigitSequence("0111");
+    case "8":
+      return binaryDigitSequence("1000");
+    case "9":
+      return binaryDigitSequence("1001");
+    case "A":
+    case "a":
+      return binaryDigitSequence("1010");
+    case "B":
+    case "b":
+      return binaryDigitSequence("1011");
+    case "C":
+    case "c":
+      return binaryDigitSequence("1100");
+    case "D":
+    case "d":
+      return binaryDigitSequence("1101");
+    case "E":
+    case "e":
+      return binaryDigitSequence("1110");
+    case "F":
+    case "f":
+      return binaryDigitSequence("1111");
+  }
+};
+
+/**
+ * Maps a hexBinary value to a literal matching the hexBinary
+ * production.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hexBinaryCanonical>.
+ *
+ * ☡ This function throws if the provided value is not an
+ * `ArrayBuffer`.
+ */
+export const hexBinaryCanonical = (o) => {
+  hexBinaryValue(o);
+  const length = getByteLength(o);
+  return stringCatenate(
+    "",
+    ...generatorSequence(function* () {
+      for (let index = 0; index < length; ++index) {
+        yield hexOctetCanonical(
+          toNumber(get8BitUnsignedIntegralItem(o, index)),
+        );
+      }
+    }),
+  );
+};
+
+/**
+ * Maps a binary octet to a literal matching the hexOctet production.
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hexOctetCanonical>.
+ *
+ * ☡ This function throws if the provided value is not a hexOctet
+ * string.
+ *
+ * ※ This function is not exposed.
+ */
+const hexOctetCanonical = (o) => {
+  if (!(isIntegralNumber(o) && o >= 0 && o <= 0xFF)) {
+    throw new TypeError(
+      `सूत्र: Expected a binary octet but got: ${o}.`,
+    );
+  } else {
+    const lo = generatorSequence(function* () {
+      for (let i = 1; i <= 4; ++i) {
+        const offset = 4 - i;
+        yield (o & 1 << offset) >> offset;
+      }
+    });
+    const hi = generatorSequence(function* () {
+      for (let i = 1; i <= 4; ++i) {
+        const offset = 8 - i;
+        yield (o & 1 << offset) >> offset;
+      }
+    });
+    return hexDigitCanonical(hi) + hexDigitCanonical(lo);
+  }
+};
+
+/**
+ * Maps a four-bit sequence to a hexadecimal digit (a literal matching
+ * the hexDigit production).
+ *
+ * See <https://www.w3.org/TR/xmlschema11-2/#f-hexDigitCanonical>.
+ *
+ * ☡ This function throws if the provided value is not an iterable of
+ * binary digits.
+ *
+ * ※ It would be way simpler and more efficient to just call this with
+ * a single value rather than a sequence of digits.
+ *
+ * ※ This function is not exposed.
+ */
+const hexDigitCanonical = (d) => {
+  let sum = 0;
+  for (const digit of d) {
+    if (digit != 0 && digit != 1) {
+      throw new TypeError(
+        `सूत्र: Expected a binary digit but got: ${digit}.`,
+      );
+    } else {
+      sum = (sum << 1) | digit;
+    }
+  }
+  switch (sum) {
+    case 0b0000:
+      return "0";
+    case 0b0001:
+      return "1";
+    case 0b0010:
+      return "2";
+    case 0b0011:
+      return "3";
+    case 0b0100:
+      return "4";
+    case 0b0101:
+      return "5";
+    case 0b0110:
+      return "6";
+    case 0b0111:
+      return "7";
+    case 0b1000:
+      return "8";
+    case 0b1001:
+      return "9";
+    case 0b1010:
+      return "A";
+    case 0b1011:
+      return "B";
+    case 0b1100:
+      return "C";
+    case 0b1101:
+      return "D";
+    case 0b1110:
+      return "E";
+    case 0b1111:
+      return "F";
+  }
+};
This page took 0.181495 seconds and 4 git commands to generate.