<commit_before>// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import delimiter from "../delimiter";
import mathMLTree from "../mathMLTree";
import Style from "../Style";
import {assertNodeType, assertAtomFamily, checkNodeType} from "../parseNode";
import {assert} from "../utils";

import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import {calculateSize} from "../units";

const htmlBuilder = (group, options) => {
    // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
    // Figure out what style this fraction should be in based on the
    // function used
    let style = options.style;
    if (group.size === "display") {
        // Get display style as a default.
        // If incoming style is sub/sup, use style.text() to get correct size.
        style = style.id > 3 ? style.text() : Style.DISPLAY;
    } else if (group.size === "text" &&
        style.size === Style.DISPLAY.size) {
        // We're in a \tfrac but incoming style is displaystyle, so:
        style = Style.TEXT;
    } else if (group.size === "script") {
        style = Style.SCRIPT;
    } else if (group.size === "scriptscript") {
        style = Style.SCRIPTSCRIPT;
    }

    const nstyle = style.fracNum();
    const dstyle = style.fracDen();
    let newOptions;

    newOptions = options.havingStyle(nstyle);
    const numerm = html.buildGroup(group.numer, newOptions, options);

    if (group.continued) {
        // \cfrac inserts a \strut into the numerator.
        // Get \strut dimensions from TeXbook page 353.
        const hStrut = 8.5 / options.fontMetrics().ptPerEm;
        const dStrut = 3.5 / options.fontMetrics().ptPerEm;
        numerm.height = numerm.height < hStrut ? hStrut : numerm.height;
        numerm.depth = numerm.depth < dStrut ? dStrut : numerm.depth;
    }

    newOptions = options.havingStyle(dstyle);
    const denomm = html.buildGroup(group.denom, newOptions, options);

    let rule;
    let ruleWidth;
    let ruleSpacing;
    if (group.hasBarLine) {
        if (group.barSize) {
            ruleWidth = calculateSize(group.barSize, options);
            rule = buildCommon.makeLineSpan("frac-line", options, ruleWidth);
        } else {
            rule = buildCommon.makeLineSpan("frac-line", options);
        }
        ruleWidth = rule.height;
        ruleSpacing = rule.height;
    } else {
        rule = null;
        ruleWidth = 0;
        ruleSpacing = options.fontMetrics().defaultRuleThickness;
    }

    // Rule 15b
    let numShift;
    let clearance;
    let denomShift;
    if (style.size === Style.DISPLAY.size || group.size === "display") {
        numShift = options.fontMetrics().num1;
        if (ruleWidth > 0) {
            clearance = 3 * ruleSpacing;
        } else {
            clearance = 7 * ruleSpacing;
        }
        denomShift = options.fontMetrics().denom1;
    } else {
        if (ruleWidth > 0) {
            numShift = options.fontMetrics().num2;
            clearance = ruleSpacing;
        } else {
            numShift = options.fontMetrics().num3;
            clearance = 3 * ruleSpacing;
        }
        denomShift = options.fontMetrics().denom2;
    }

    let frac;
    if (!rule) {
        // Rule 15c
        const candidateClearance =
            (numShift - numerm.depth) - (denomm.height - denomShift);
        if (candidateClearance < clearance) {
            numShift += 0.5 * (clearance - candidateClearance);
            denomShift += 0.5 * (clearance - candidateClearance);
        }

        frac = buildCommon.makeVList({
            positionType: "individualShift",
            children: [
                {type: "elem", elem: denomm, shift: denomShift},
                {type: "elem", elem: numerm, shift: -numShift},
            ],
        }, options);
    } else {
        // Rule 15d
        const axisHeight = options.fontMetrics().axisHeight;

        if ((numShift - numerm.depth) - (axisHeight + 0.5 * ruleWidth) <
                clearance) {
            numShift +=
                clearance - ((numShift - numerm.depth) -
                            (axisHeight + 0.5 * ruleWidth));
        }

        if ((axisHeight - 0.5 * ruleWidth) - (denomm.height - denomShift) <
                clearance) {
            denomShift +=
                clearance - ((axisHeight - 0.5 * ruleWidth) -
                            (denomm.height - denomShift));
        }

        const midShift = -(axisHeight - 0.5 * ruleWidth);

        frac = buildCommon.makeVList({
            positionType: "individualShift",
            children: [
                {type: "elem", elem: denomm, shift: denomShift},
                {type: "elem", elem: rule,   shift: midShift},
                {type: "elem", elem: numerm, shift: -numShift},
            ],
        }, options);
    }

    // Since we manually change the style sometimes (with \dfrac or \tfrac),
    // account for the possible size change here.
    newOptions = options.havingStyle(style);
    frac.height *= newOptions.sizeMultiplier / options.sizeMultiplier;
    frac.depth *= newOptions.sizeMultiplier / options.sizeMultiplier;

    // Rule 15e
    let delimSize;
    if (style.size === Style.DISPLAY.size) {
        delimSize = options.fontMetrics().delim1;
    } else {
        delimSize = options.fontMetrics().delim2;
    }

    let leftDelim;
    let rightDelim;
    if (group.leftDelim == null) {
        leftDelim = html.makeNullDelimiter(options, ["mopen"]);
    } else {
        leftDelim = delimiter.customSizedDelim(
            group.leftDelim, delimSize, true,
            options.havingStyle(style), group.mode, ["mopen"]);
    }

    if (group.continued) {
        rightDelim = buildCommon.makeSpan([]); // zero width for \cfrac
    } else if (group.rightDelim == null) {
        rightDelim = html.makeNullDelimiter(options, ["mclose"]);
    } else {
        rightDelim = delimiter.customSizedDelim(
            group.rightDelim, delimSize, true,
            options.havingStyle(style), group.mode, ["mclose"]);
    }

    return buildCommon.makeSpan(
        ["mord"].concat(newOptions.sizingClasses(options)),
        [leftDelim, buildCommon.makeSpan(["mfrac"], [frac]), rightDelim],
        options);
};

const mathmlBuilder = (group, options) => {
    const node = new mathMLTree.MathNode(
        "mfrac",
        [
            mml.buildGroup(group.numer, options),
            mml.buildGroup(group.denom, options),
        ]);

    if (!group.hasBarLine) {
        node.setAttribute("linethickness", "0px");
    } else if (group.barSize) {
        const ruleWidth = calculateSize(group.barSize, options);
        node.setAttribute("linethickness", ruleWidth + "em");
    }

    if (group.leftDelim != null || group.rightDelim != null) {
        const withDelims = [];

        if (group.leftDelim != null) {<commit_msg>Improve MathML for fractions (#1882)<commit_after>// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import delimiter from "../delimiter";
import mathMLTree from "../mathMLTree";
import Style from "../Style";
import {assertNodeType, assertAtomFamily, checkNodeType} from "../parseNode";
import {assert} from "../utils";

import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import {calculateSize} from "../units";

const adjustStyle = (size, originalStyle) => {
    // Figure out what style this fraction should be in based on the
    // function used
    let style = originalStyle;
    if (size === "display") {
        // Get display style as a default.
        // If incoming style is sub/sup, use style.text() to get correct size.
        style = style.id >= Style.SCRIPT.id ? style.text() : Style.DISPLAY;
    } else if (size === "text" &&
        style.size === Style.DISPLAY.size) {
        // We're in a \tfrac but incoming style is displaystyle, so:
        style = Style.TEXT;
    } else if (size === "script") {
        style = Style.SCRIPT;
    } else if (size === "scriptscript") {
        style = Style.SCRIPTSCRIPT;
    }
    return style;
};

const htmlBuilder = (group, options) => {
    // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
    const style = adjustStyle(group.size, options.style);

    const nstyle = style.fracNum();
    const dstyle = style.fracDen();
    let newOptions;

    newOptions = options.havingStyle(nstyle);
    const numerm = html.buildGroup(group.numer, newOptions, options);

    if (group.continued) {
        // \cfrac inserts a \strut into the numerator.
        // Get \strut dimensions from TeXbook page 353.
        const hStrut = 8.5 / options.fontMetrics().ptPerEm;
        const dStrut = 3.5 / options.fontMetrics().ptPerEm;
        numerm.height = numerm.height < hStrut ? hStrut : numerm.height;
        numerm.depth = numerm.depth < dStrut ? dStrut : numerm.depth;
    }

    newOptions = options.havingStyle(dstyle);
    const denomm = html.buildGroup(group.denom, newOptions, options);

    let rule;
    let ruleWidth;
    let ruleSpacing;
    if (group.hasBarLine) {
        if (group.barSize) {
            ruleWidth = calculateSize(group.barSize, options);
            rule = buildCommon.makeLineSpan("frac-line", options, ruleWidth);
        } else {
            rule = buildCommon.makeLineSpan("frac-line", options);
        }
        ruleWidth = rule.height;
        ruleSpacing = rule.height;
    } else {
        rule = null;
        ruleWidth = 0;
        ruleSpacing = options.fontMetrics().defaultRuleThickness;
    }

    // Rule 15b
    let numShift;
    let clearance;
    let denomShift;
    if (style.size === Style.DISPLAY.size || group.size === "display") {
        numShift = options.fontMetrics().num1;
        if (ruleWidth > 0) {
            clearance = 3 * ruleSpacing;
        } else {
            clearance = 7 * ruleSpacing;
        }
        denomShift = options.fontMetrics().denom1;
    } else {
        if (ruleWidth > 0) {
            numShift = options.fontMetrics().num2;
            clearance = ruleSpacing;
        } else {
            numShift = options.fontMetrics().num3;
            clearance = 3 * ruleSpacing;
        }
        denomShift = options.fontMetrics().denom2;
    }

    let frac;
    if (!rule) {
        // Rule 15c
        const candidateClearance =
            (numShift - numerm.depth) - (denomm.height - denomShift);
        if (candidateClearance < clearance) {
            numShift += 0.5 * (clearance - candidateClearance);
            denomShift += 0.5 * (clearance - candidateClearance);
        }

        frac = buildCommon.makeVList({
            positionType: "individualShift",
            children: [
                {type: "elem", elem: denomm, shift: denomShift},
                {type: "elem", elem: numerm, shift: -numShift},
            ],
        }, options);
    } else {
        // Rule 15d
        const axisHeight = options.fontMetrics().axisHeight;

        if ((numShift - numerm.depth) - (axisHeight + 0.5 * ruleWidth) <
                clearance) {
            numShift +=
                clearance - ((numShift - numerm.depth) -
                            (axisHeight + 0.5 * ruleWidth));
        }

        if ((axisHeight - 0.5 * ruleWidth) - (denomm.height - denomShift) <
                clearance) {
            denomShift +=
                clearance - ((axisHeight - 0.5 * ruleWidth) -
                            (denomm.height - denomShift));
        }

        const midShift = -(axisHeight - 0.5 * ruleWidth);

        frac = buildCommon.makeVList({
            positionType: "individualShift",
            children: [
                {type: "elem", elem: denomm, shift: denomShift},
                {type: "elem", elem: rule,   shift: midShift},
                {type: "elem", elem: numerm, shift: -numShift},
            ],
        }, options);
    }

    // Since we manually change the style sometimes (with \dfrac or \tfrac),
    // account for the possible size change here.
    newOptions = options.havingStyle(style);
    frac.height *= newOptions.sizeMultiplier / options.sizeMultiplier;
    frac.depth *= newOptions.sizeMultiplier / options.sizeMultiplier;

    // Rule 15e
    let delimSize;
    if (style.size === Style.DISPLAY.size) {
        delimSize = options.fontMetrics().delim1;
    } else {
        delimSize = options.fontMetrics().delim2;
    }

    let leftDelim;
    let rightDelim;
    if (group.leftDelim == null) {
        leftDelim = html.makeNullDelimiter(options, ["mopen"]);
    } else {
        leftDelim = delimiter.customSizedDelim(
            group.leftDelim, delimSize, true,
            options.havingStyle(style), group.mode, ["mopen"]);
    }

    if (group.continued) {
        rightDelim = buildCommon.makeSpan([]); // zero width for \cfrac
    } else if (group.rightDelim == null) {
        rightDelim = html.makeNullDelimiter(options, ["mclose"]);
    } else {
        rightDelim = delimiter.customSizedDelim(
            group.rightDelim, delimSize, true,
            options.havingStyle(style), group.mode, ["mclose"]);
    }

    return buildCommon.makeSpan(
        ["mord"].concat(newOptions.sizingClasses(options)),
        [leftDelim, buildCommon.makeSpan(["mfrac"], [frac]), rightDelim],
        options);
};

const mathmlBuilder = (group, options) => {
    let node = new mathMLTree.MathNode(
        "mfrac",
        [
            mml.buildGroup(group.numer, options),
            mml.buildGroup(group.denom, options),
        ]);

    const style = adjustStyle(group.size, options.style);
    if (style.size !== options.style.size) {
        node = new mathMLTree.MathNode("mstyle", [node]);
        const isDisplay = (style.size === Style.DISPLAY.size) ? "true" : "false";
        node.setAttribute("displaystyle", isDisplay);
        node.setAttribute("scriptlevel", "0");
    }

    if (!group.hasBarLine) {
        node.setAttribute("linethickness", "0px");
    } else if (group.barSize) {
        const ruleWidth = calculateSize(group.barSize, options);
        node.setAttribute("linethickness", ruleWidth + "em");
    }

    if (group.leftDelim != null || group.rightDelim != null) {
        const withDelims = [];

        if (group.leftDelim != null) {
{'fraction_non_alphanumeric': 0.1280701754385965, 'fraction_numerical': 0.007578947368421052, 'mean_word_length': 2.129336846728151, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5076392', 'n_tokens_mistral': 4290, 'n_tokens_neox': 4136, 'n_words': 1023}