diff --git a/src/buildHTML.js b/src/buildHTML.js
index d64e2bb2..8255b017 100644
--- a/src/buildHTML.js
+++ b/src/buildHTML.js
@@ -915,6 +915,59 @@ groupTypes.op = function(group, options) {
}
};
+groupTypes.mod = function(group, options) {
+ var inner = [];
+
+ if (group.value.modType === "bmod") {
+ // “\nonscript\mskip-\medmuskip\mkern5mu”
+ if (!options.style.isTight()) {
+ inner.push(makeSpan(
+ ["mspace", "negativemediumspace"], [], options));
+ }
+ inner.push(makeSpan(["mspace", "thickspace"], [], options));
+ } else if (options.style.size === Style.DISPLAY.size) {
+ inner.push(makeSpan(["mspace", "quad"], [], options));
+ } else if (group.value.modType === "mod") {
+ inner.push(makeSpan(["mspace", "twelvemuspace"], [], options));
+ } else {
+ inner.push(makeSpan(["mspace", "eightmuspace"], [], options));
+ }
+
+ if (group.value.modType === "pod" || group.value.modType === "pmod") {
+ inner.push(buildCommon.mathsym("(", group.mode));
+ }
+
+ if (group.value.modType !== "pod") {
+ var modInner = [
+ buildCommon.mathsym("m", group.mode),
+ buildCommon.mathsym("o", group.mode),
+ buildCommon.mathsym("d", group.mode)];
+ if (group.value.modType === "bmod") {
+ inner.push(makeSpan(["mbin"], modInner, options));
+ // “\mkern5mu\nonscript\mskip-\medmuskip”
+ inner.push(makeSpan(["mspace", "thickspace"], [], options));
+ if (!options.style.isTight()) {
+ inner.push(makeSpan(
+ ["mspace", "negativemediumspace"], [], options));
+ }
+ } else {
+ Array.prototype.push.apply(inner, modInner);
+ inner.push(makeSpan(["mspace", "sixmuspace"], [], options));
+ }
+ }
+
+ if (group.value.value) {
+ Array.prototype.push.apply(inner,
+ buildExpression(group.value.value, options, false));
+ }
+
+ if (group.value.modType === "pod" || group.value.modType === "pmod") {
+ inner.push(buildCommon.mathsym(")", group.mode));
+ }
+
+ return buildCommon.makeFragment(inner);
+};
+
groupTypes.katex = function(group, options) {
// The KaTeX logo. The offsets for the K and a were chosen to look
// good, but the offsets for the T, E, and X were taken from the
diff --git a/src/buildMathML.js b/src/buildMathML.js
index 795f402c..6c17d418 100644
--- a/src/buildMathML.js
+++ b/src/buildMathML.js
@@ -348,6 +348,31 @@ groupTypes.op = function(group, options) {
return node;
};
+groupTypes.mod = function(group, options) {
+ var inner = [];
+
+ if (group.value.modType === "pod" || group.value.modType === "pmod") {
+ inner.push(new mathMLTree.MathNode(
+ "mo", [makeText("(", group.mode)]));
+ }
+ if (group.value.modType !== "pod") {
+ inner.push(new mathMLTree.MathNode(
+ "mo", [makeText("mod", group.mode)]));
+ }
+ if (group.value.value) {
+ var space = new mathMLTree.MathNode("mspace");
+ space.setAttribute("width", "0.333333em");
+ inner.push(space);
+ inner = inner.concat(buildExpression(group.value.value, options));
+ }
+ if (group.value.modType === "pod" || group.value.modType === "pmod") {
+ inner.push(new mathMLTree.MathNode(
+ "mo", [makeText(")", group.mode)]));
+ }
+
+ return new mathMLTree.MathNode("mo", inner);
+};
+
groupTypes.katex = function(group) {
var node = new mathMLTree.MathNode(
"mtext", [new mathMLTree.TextNode("KaTeX")]);
diff --git a/src/functions.js b/src/functions.js
index 7e50cccf..20e7134e 100644
--- a/src/functions.js
+++ b/src/functions.js
@@ -279,6 +279,28 @@ defineFunction("\\stackrel", {
};
});
+// \mod-type functions
+defineFunction("\\bmod", {
+ numArgs: 0,
+}, function(context, args) {
+ return {
+ type: "mod",
+ modType: "bmod",
+ value: null,
+ };
+});
+
+defineFunction(["\\pod", "\\pmod", "\\mod"], {
+ numArgs: 1,
+}, function(context, args) {
+ var body = args[0];
+ return {
+ type: "mod",
+ modType: context.funcName.substr(1),
+ value: ordargument(body),
+ };
+});
+
// Extra data needed for the delimiter handler down below
var delimiterSizes = {
"\\bigl" : {mclass: "mopen", size: 1},
diff --git a/static/katex.less b/static/katex.less
index 764e4463..b935d296 100644
--- a/static/katex.less
+++ b/static/katex.less
@@ -105,9 +105,9 @@
@ptperem: 10.0;
@nulldelimiterspace: 1.2em / @ptperem;
- @thinspace: 0.16667em;
- @mediumspace: 0.22222em;
- @thickspace: 0.27778em;
+ @thinspace: 0.16667em; // 3mu
+ @mediumspace: 0.22222em; // 4mu
+ @thickspace: 0.27778em; // 5mu
// These spacings apply in textstyle and displaystyle.
.mord {
@@ -300,6 +300,10 @@
width: @thinspace;
}
+ &.negativemediumspace {
+ margin-left: -@mediumspace;
+ }
+
&.mediumspace {
width: @mediumspace;
}
@@ -308,10 +312,22 @@
width: @thickspace;
}
+ &.sixmuspace {
+ width: 0.333333em;
+ }
+
+ &.eightmuspace {
+ width: 0.444444em;
+ }
+
&.enspace {
width: 0.5em;
}
+ &.twelvemuspace {
+ width: 0.666667em;
+ }
+
&.quad {
width: 1em;
}
diff --git a/test/screenshotter/images/Mod-chrome.png b/test/screenshotter/images/Mod-chrome.png
new file mode 100644
index 00000000..c5b72d28
Binary files /dev/null and b/test/screenshotter/images/Mod-chrome.png differ
diff --git a/test/screenshotter/images/Mod-firefox.png b/test/screenshotter/images/Mod-firefox.png
new file mode 100644
index 00000000..7787a882
Binary files /dev/null and b/test/screenshotter/images/Mod-firefox.png differ
diff --git a/test/screenshotter/images/ModScript-chrome.png b/test/screenshotter/images/ModScript-chrome.png
new file mode 100644
index 00000000..accb05bd
Binary files /dev/null and b/test/screenshotter/images/ModScript-chrome.png differ
diff --git a/test/screenshotter/images/ModScript-firefox.png b/test/screenshotter/images/ModScript-firefox.png
new file mode 100644
index 00000000..27ecd68c
Binary files /dev/null and b/test/screenshotter/images/ModScript-firefox.png differ
diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml
index cd5fbb29..1b986633 100644
--- a/test/screenshotter/ss_data.yaml
+++ b/test/screenshotter/ss_data.yaml
@@ -92,6 +92,18 @@ MathRm: \mathrm{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
MathSf: \mathsf{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
MathScr: \mathscr{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
MathTt: \mathtt{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
+Mod: |
+ \begin{array}{cc}
+ a \bmod 2 & b \pod 3 \\
+ c \pmod{4} & d \mod{56} \\
+ \displaystyle a\bmod 2 & \displaystyle b \pod 3 \\
+ \displaystyle c\pmod{4} & \displaystyle d \mod{56}
+ \end{array}
+ModScript: |
+ \begin{array}{cc}
+ \scriptstyle a\bmod 2 & \scriptstyle b \pod 3 \\
+ \scriptstyle c\pmod{4} & \scriptstyle d \mod{56}
+ \end{array}
NegativeSpaceBetweenRel: A =\!= B
NestedFractions: |
\dfrac{\frac{a}{b}}{\frac{c}{d}}\dfrac{\dfrac{a}{b}}