% -*- coding: utf-8 -*- % ---------------------------------------------------------------------------- % Author: Jianrui Lyu % Website: https://lvjr.bitbucket.io/jnuexam.html % License: The LaTeX Project Public License 1.3c % ---------------------------------------------------------------------------- \NeedsTeXFormat{LaTeX2e} \ProvidesClass{jnuexam}[2023/12/18 v2.1 An exam class for Jinan University] %% 旧版本的 LaTeX 不能识别 2022-11-01 这种日期格式 %\@ifl@t@r\fmtversion{2022-11-01}{}{ \@ifl@t@r\fmtversion{2022/11/01}{}{ \ClassError{jnuexam}{% Your current TeX distribution is quite old.\MessageBreak We need CTeX 3.0+ or MiKTeX 2023+ or TeXLive 2023+% }{Please update your TeX distribution first.} } \newif\ifplain \plainfalse % 是否添加装订线和草稿纸 \newif\iftwoinone \twoinonefalse % 是否使用 A3 纸张 \newif\ifoneside \onesidefalse % 是否单面印刷试卷 \newif\ifresetnumber \resetnumbertrue % 是否对各题型小题分别编号 \newif\ifrandom \randomfalse % 是否乱序出题 \newif\ifanswer \answertrue % 是否显示答案 \newif\ifamsfonts \amsfontsfalse % 切换数学字体 \newif\ifsourcehan \sourcehanfalse % 切换思源字体 \newif\ifsolidot \solidotfalse % 是否替换空心句号为实心句号 \newif\ifcellspace \cellspacefalse % 增加表格列间距 \newif\ifmedmath \medmathfalse % 切换公式尺寸 \DeclareOption{plain}{\plaintrue} \DeclareOption{a3paper}{\twoinonetrue} \DeclareOption{a3input}{\twoinonetrue\plaintrue} \DeclareOption{oneside}{\onesidetrue} \DeclareOption{random}{\randomtrue} \DeclareOption{noanswer}{\answerfalse} \DeclareOption{amsfonts}{\amsfontstrue} \DeclareOption{sourcehan}{\sourcehantrue} \DeclareOption{solidot}{\solidottrue} \DeclareOption{medmath}{\cellspacetrue\medmathtrue} \DeclareOption*{\PassOptionsToClass{\CurrentOption}{ctexart}} %其它选项 \ProcessOptions %不可省略 % 四号 小四号 五号 小五号 % 14bp 12bp 10.5bp 9bp % 实际上,在旧版本 ctex 中只能用 cs4size 和 c5size 选项 % 而新版本 ctex 中,可以利用 zihao 选项指定各种中文字号 \PassOptionsToPackage{CJKnumber}{xeCJK} \LoadClass[cs4size,UTF8,noindent]{ctexart} % 在旧版本 xeCJK 中,必须用 CJKnumber 选项载入 CJKnumb 包,后面才载入会报错 % 但在新版本 xeCJK 中 CJKnumber 选项已经被废弃,需要在后面自行载入它 % 在 xeCJK 中已经禁止载入 CJK,但是在新版本 ctex 宏包中却失效了 % 我们假装 CJK 已经载入,再载入 CJKnumb,避免出现 \CJKglue 重复定义的问题 % 注意用 PDFLaTeX 编译时需要用到 CJK,所以只在未定义时才作修改 \ifdefined\else \@namedef{ver@CJK.sty}{} %\@namedef{opt@CJK.sty}{} \fi \RequirePackage{CJKnumb} % 新版本 xeCJK 已经废弃并禁用 CJKfntef,改用 xeCJKfntef 取代,我们需要载入后者 % 注意要保证能在较旧的 TeX 系统中编译,我们只能用 \ifXeTeX 而不能用 \ifxetex % 因为旧版本 iftex 宏包只有 \ifXeTeX 命令,而 ifxetex 宏包才有 \ifxetex 命令 % 在 2019 年 10 月,LaTeX 开发团队接管了 iftex 宏包,新版本同时提供这两个命令 \RequirePackage{CJKfntef} \RequirePackage{iftex} \ifXeTeX\@ifpackagelater{xeCJK}{2020/02/10}{\RequirePackage{xeCJKfntef}}{}\fi \iftwoinone \RequirePackage[a3paper,landscape,twocolumn,columnsep=60mm,left=30mm,right=30mm,top=25mm,bottom=25mm]{geometry} \else \RequirePackage[a4paper,left=30mm,right=30mm,top=25mm,bottom=25mm]{geometry} \fi \RequirePackage{amsmath} \RequirePackage{array} \RequirePackage{calc} \RequirePackage{comment} \RequirePackage[inline]{enumitem} \RequirePackage{environ} \RequirePackage{etoolbox} \RequirePackage{fancyhdr} \RequirePackage{zref-user,zref-lastpage} \RequirePackage{tabularx} \RequirePackage{xcolor} \RequirePackage{xkeyval} \ifplain \allowdisplaybreaks[4] \fi \ifamsfonts \RequirePackage{amssymb} \else \RequirePackage[utopia]{mathdesign} % charter, utopia \renewcommand\bfdefault{bx} \let\oldoiint\oiint\renewcommand{\oiint}{\oldoiint\nolimits} \DeclareTextCommandDefault{\nobreakspace}{\leavevmode\nobreak\ } \fi \newcolumntype{Y}{>{\centering\arraybackslash}X} \newcolumntype{n}[1]{>{\centering\arraybackslash}m{#1}} \setlength{\parindent}{0em} \setlength{\lineskiplimit}{4pt} \setlength{\lineskip}{4pt} %% --------------------------------------------------------------------------- %% 装订线命令 \addbindingline %% 草稿纸命令 \addscratchpaper %% 这两个命令需要编译两次才能得到正确结果 %% --------------------------------------------------------------------------- \newcommand{\setexam}[1]{\setkeys{jnuexam@setup}{#1}} \ifplain\else \RequirePackage{tikz} \RequirePackage{everypage} \fi \newcommand{\bindinglineleft}{ \path (current page.north west) +(25mm,-25mm) coordinate (a1); \path (current page.south west) +(25mm,25mm) coordinate (a2); \draw[dashed] (a1) -- node[left=1mm,text width=1em,inner sep=0pt]{\1{线}\0\1{订}\0\1{装}} (a2); } \newcommand{\bindinglineright}{ \path (current page.north east) +(-25mm,-25mm) coordinate (b1); \path (current page.south east) +(-25mm,25mm) coordinate (b2); \draw[dashed] (b1) -- node[right=1mm,text width=1em,inner sep=0pt]{\2{装}\0\2{订}\0\2{线}} (b2); } \newcommand{\bindinglineone}{% \def\0{\\[70mm]}\def\1{\rotatebox{90}}\def\2{\rotatebox{-90}}% \begin{tikzpicture}[remember picture,overlay,very thick] \ifnumodd{\value{page}}{\bindinglineleft}{\bindinglineright} \end{tikzpicture}} \newcommand{\bindinglinetwo}{% \def\0{\\[70mm]}\def\1{\rotatebox{90}}\def\2{\rotatebox{-90}}% \begin{tikzpicture}[remember picture,overlay,very thick] \bindinglineleft\bindinglineright \end{tikzpicture}} \iftwoinone \let\bindingline=\bindinglinetwo \else \let\bindingline=\bindinglineone \fi \def\zhuangdingxian{1} \define@key{jnuexam@setup}{binding}[2]{\def\zhuangdingxian{#1}} \newcommand{\addbindingline}{% \ifcase\zhuangdingxian % 0 \or % 1 \ifbool{answer}{}{\bindingline}% \or % 2 \bindingline \fi } \ifplain\else \AddEverypageHook{\addbindingline} \fi \newcommand{\scratchpaperone}{% \begin{tikzpicture}[remember picture,overlay,font=\sffamily\fontsize{120pt}{120pt}\selectfont] \node[text=lightgray!40,text width=120pt] at (current page.center) {草\\ 稿\\ 纸}; \end{tikzpicture}} \newcommand{\scratchpapertwo}{% \begin{tikzpicture}[remember picture,overlay,font=\sffamily\fontsize{180pt}{180pt}\selectfont] \node[text=lightgray!40] at (current page.center) {草\quad 稿\quad 纸}; \end{tikzpicture}} \def\caogaozhi{0} \define@key{jnuexam@setup}{scratch}[1]{\def\caogaozhi{#1}} \newcounter{my@empty@page} % 空白页数 \newcounter{my@scratch@page} % 草稿页数 \newcounter{my@extra@page} % 空白页数+草稿页数 % 添加空白页,使得草稿纸前页数在单面印刷时为偶数,双面印刷时为4的倍数 % 添加草稿纸,页数在单面印刷时等于设置值的两倍,双面印刷时等于设置值的4倍 \newcommand{\doscratchpaperone}{% \clearpage\pagestyle{empty}\let\addbindingline=\relax \ifnumgreater{\caogaozhi}{0}{% \ifbool{oneside}{% \ifnumodd{\value{page}}% {\setcounter{my@empty@page}{0}}% {\setcounter{my@empty@page}{1}}% \setcounter{my@scratch@page}{\caogaozhi*2}% }{% \setcounter{my@empty@page}{(\value{page}+2)/4*4+1-\value{page}}% \setcounter{my@scratch@page}{\caogaozhi*4}% }% \my@add@extra@page }{}% } % 注意在 twocolumn 文档中,\newpage 到下一栏,\clearpage 到下一页 % 另外注意第一行的 \clearpage 等不能移动到 \my@add@extra@page 命令里 % 因为 \clearpage 后本页已经结束,page 表示的是下一页的页码 \newcommand{\doscratchpapertwo}{% \clearpage\pagestyle{empty}\let\addbindingline=\relax \ifnumgreater{\caogaozhi}{0}{% \ifbool{oneside}{% \setcounter{my@empty@page}{0}% \setcounter{my@scratch@page}{\caogaozhi}% }{% \setcounter{my@empty@page}{(\value{page}/2)-(\value{page}/2)/2*2}% \setcounter{my@scratch@page}{\caogaozhi*2}% }% \my@add@extra@page }{}% } \newcommand{\my@add@extra@page}{% \setcounter{my@extra@page}{\value{my@empty@page}+\value{my@scratch@page}}% \whileboolexpr{ test{\ifnumgreater{\value{my@empty@page}}{0}} }{% \addtocounter{my@empty@page}{-1}% \leavevmode\clearpage }% \whileboolexpr{ test{\ifnumgreater{\value{my@scratch@page}}{0}} }{% \addtocounter{my@scratch@page}{-1}% \leavevmode\scratchpaper\clearpage }% \addtocounter{page}{-\value{my@extra@page}}% } \iftwoinone \let\scratchpaper=\scratchpapertwo \let\doscratchpaper=\doscratchpapertwo \else \let\scratchpaper=\scratchpaperone \let\doscratchpaper=\doscratchpaperone \fi % 在旧版本 ctex 宏包中不能用 \AtEndDocument 添加含中文的内容,即不能用 \appto 而要用 \preto \newcommand{\addscratchpaper}{% \preto{\@enddocumenthook}{\doscratchpaper}% } \ifplain\else\ifanswer\else \addscratchpaper \fi\fi \iftwoinone \RequirePackage{pdfpages} % 新版本 pdfpages 将 \includepdf 放在文档开头时会有命令未定义的错误 % 见 https://tex.stackexchange.com/questions/352007/ieeetran-and-pdfpages % 另外从 LaTeX release 2018 开始 \@ifundefined 不再将未定义命令定义为 \relax % 见 https://www.latex-project.org/news/latex2e-news/ltnews28.pdf \@ifundefined{@setmarks}{\let\@setmarks\relax}{} \fi %% --------------------------------------------------------------------------- %% 试卷表头命令 \makehead %% --------------------------------------------------------------------------- \newcommand{\niandu}{2016-2017} \newcommand{\xueqi}{2} \newcommand{\kecheng}{数学课程} \newcommand{\zhuanye}{} % 专业学分 \newcommand{\jiaoshi}{吕荐瑞} \newcommand{\shijian}{} % 考试日期 \newcommand{\bixiu}{1} % 1 为必修,0 为选修 \newcommand{\bijuan}{1} % 1 为闭卷,0 为开卷 \newcommand{\shijuan}{A} % A 或 B 或 C 卷 \newcommand{\neizhao}{1} % 1 打勾,0 不勾 \newcommand{\waizhao}{1} % 1 打勾,0 不勾 \newcommand{\underspace}[1]{\kern0pt\underline{\hspace{#1}}\kern0pt\relax} \newcommand{\underbox}[2]{\kern0pt\underline{\makebox[#1]{#2}}\kern0pt\relax} \newcommand{\underparbox}[2]{\kern0pt\underline{\parbox[b]{#1}{#2}}\kern0pt\relax} \newcommand{\ischeck}[1]{\ifnum#1>0\,$\checkmark$\,\else\quad\fi} \newcommand{\isquad}[1]{\ifnum#1=0\,$\checkmark$\,\else\quad\fi} \newcommand\my@temp@a{A} \newcommand\my@temp@c{C} \newcommand\my@empty{} \newcommand{\head@table@a}{% \begin{tabular}{l} \underbox{11\ccwd}{\niandu}学年度第\underbox{5.5\ccwd}{\xueqi}学期 \\ 课程名称:\underbox{17.5\ccwd}{\kecheng\ifx\zhuanye\my@empty\else\kern0pt(\zhuanye)\fi} \\ 授课教师:\underparbox{17.5\ccwd}{\centering\rule{0pt}{3ex}\jiaoshi} \\ 考试时间:\underbox{17.5\ccwd}{\shijian} \\ \end{tabular} } \newcommand{\head@table@b}{% \renewcommand{\arraystretch}{1}% \begin{tabular}{l} \textbf{课程类别} \\ 必修~[\ischeck{\bixiu}]\quad 选修~[\isquad{\bixiu}] \\ \hline \textbf{考试方式} \\ 开卷~[\isquad{\bijuan}]\quad 闭卷~[\ischeck{\bijuan}] \\ \hline \textbf{试卷类别}~(\,A, B, C\,)\\\relax [\,\shijuan\,]\hfill 共~\zpageref{LastPage}~页 \end{tabular} } \newcommand{\head@table@c}{% \begin{tabular}{l} \underspace{10\ccwd}学院\underspace{8\ccwd}专业\underspace{7.5\ccwd}班\kern0pt(\kern0pt{}级\kern0pt) \\[1em] 姓名\underspace{8\ccwd}学号\underspace{10\ccwd}\hfill \bfseries 内招~[\ischeck{\neizhao}]~~外招~[\ischeck{\waizhao}] %\\[0.8em] \end{tabular} } \newcommand{\makehead}{ \thispagestyle{plain} \centerline{% \LARGE\bfseries\ifanswer\color{red!80!black}\fi 暨\quad 南\quad 大\quad 学\quad 考\quad 试\quad 试\quad 卷\ifanswer\quad 答\quad 案\fi }% \vspace{2em}% \ifrandom \ifx\shijuan\my@temp@a\renewcommand{\shijuan}{B}\fi \ifx\shijuan\my@temp@c\renewcommand{\shijuan}{D}\fi \fi \noindent \begin{tabularx}{\linewidth}{|@{}X@{}|} \hline\renewcommand{\arraystretch}{1.5}% {\begin{tabularx}{\linewidth}{@{}>{\bfseries}l@{}|@{}X@{}|@{}l@{}} \begin{tabular}{l}教\\ 师\\ 填\\ 写\end{tabular} & \head@table@a & \head@table@b \end{tabularx}}\\ \hline\renewcommand{\arraystretch}{0.9}% {\begin{tabularx}{\linewidth}{@{}>{\bfseries}l@{}|@{}X@{}} \begin{tabular}{l}考\\ 生\\ 填\\ 写\end{tabular} & \head@table@c \end{tabularx}}\\ \hline \end{tabularx} \par\vspace{1em} \noindent\begin{tabularx}{\linewidth}{|*{8}{Y|}} \hline \textbf{题\quad 号} & 一 & 二 & 三 & 四 & 五 & 六 & 总分\\ \hline \textbf{得\quad 分}\rule[-0.75em]{0pt}{2.5em} & & & & & & & \\ \hline \textbf{评阅人}\rule[-0.75em]{0pt}{2.5em} & & & & & & & \\ \hline \end{tabularx} } %% --------------------------------------------------------------------------- %% 页眉页脚设定 %% --------------------------------------------------------------------------- \newcommand{\my@columnbox}[1]{\makebox[\columnwidth]{#1}} \newcommand{\my@headleft}{\hspace{-0.3em}《\kecheng》\kern-0.3em 试卷\,\shijuan} \newcommand{\my@headright}{姓名\hspace{6em}学号\hspace{6em}} \newcommand{\my@headtext}{\my@headleft\hfill \my@headright} \newcommand{\my@foottext}{第~\thepage~页\quad 共~\zpageref{LastPage}~页} % fancy page style \fancyhf{} % 清空页眉页脚 \iftwoinone \renewcommand{\headrulewidth}{0pt}% \lhead{\small\underline{\my@columnbox{\my@headtext}\strut}} \rhead{\small\underline{\my@columnbox{\my@headtext}\strut}} \lfoot{\small\my@columnbox{\my@foottext}} \rfoot{\small\my@columnbox{\stepcounter{page}\my@foottext}} \else \lhead{\small\my@headleft} \rhead{\small\my@headright} \cfoot{\small\my@foottext} \fi % plain page style \fancypagestyle{plain}{ \renewcommand{\headrulewidth}{0pt}% \fancyhf{} \iftwoinone \rhead{\small\underline{\my@columnbox{\my@headtext\strut}}} \lfoot{\small\my@columnbox{\my@foottext}} \rfoot{\small\my@columnbox{\stepcounter{page}\my@foottext}} \else \cfoot{\small\my@foottext} \fi } \ifplain \pagestyle{plain} \else \pagestyle{fancy} \fi %% --------------------------------------------------------------------------- %% 乱序排列选项 random %% 随机种子选项 seed %% --------------------------------------------------------------------------- %% 随机数种子不能超过 2147483647 = "7FFFFFFF \def\my@random@seed{19061116} \define@key{jnuexam@setup}{seed}[19061116]{\def\my@random@seed{#1}} \ifrandom \RequirePackage{pgf} \RequirePackage{pgffor} \newcommand*\exam@set@seed{% %% 当\pgfmathrandom的参数为3的倍数时,对相邻种子生成的多个随机数分布不均匀 %\pgfmathsetseed{\numexpr\my@random@seed+\value{section}-1\relax}% %% 因此我们改用下面的方法,用随机数种子生成下一个随机数种子 \pgfmathsetseed{\my@random@seed}% \pgfmathrandominteger\my@random@seed{1}{2147483647}% } \fi %% --------------------------------------------------------------------------- %% 题型命令 \makepart %% 附录命令 \makedata %% 题目环境 problem %% 解答环境 solution %% --------------------------------------------------------------------------- \newif\ifonlyoneproblem \onlyoneproblemfalse % 此部分仅有一道题时不显示题目编号 \xdef\allproblems{} \xdef\lastproblem{} \newcounter{problem} % 当前题型的小题编号 \newcounter{problemreal} % 实际显示的小题编号,在各题型小题统一编号时使用 \newcounter{totalproblems} % 之前各题型小题总数,在各题型小题统一编号时使用 \newcommand{\solutionname}{解} \newcounter{choice} % 后面选择题的 abcd 环境要用到 \newcommand{\hangtext}{} \newlength{\hanglength} \colorlet{part number}{black} \colorlet{problem number}{blue!80!black} \colorlet{solution name}{blue!80!black} \newcounter{my@shuffle@temp@cnt} \newcounter{my@list@temp@cnt} \newcommand\my@list@print[1]{% \par\renewcommand*{\do}[1]{(##1)}% \dolistloop#1% } \newcommand\my@list@remove[2]{% %\my@list@print\my@shuffle@list \setcounter{my@list@temp@cnt}{0}% \global\let\my@tmpa@list=#1% \gdef#1{}% \par\renewcommand*{\do}[1]{% \stepcounter{my@list@temp@cnt}% \ifnumequal{\value{my@list@temp@cnt}}{#2}{% \def\my@list@item{##1}% %[##1]% }{ \listxadd#1{##1}% }% }% \dolistloop\my@tmpa@list } \newcommand\my@shuffle@problems{% \exam@set@seed \ifnumgreater{\value{problem}}{2}{% \gdef\my@shuffle@list{}% \foreach \i in {1,...,\value{problem}} {\listxadd\my@shuffle@list{\i}}% %% 首尾两个小题的位置总要改变 \pgfmathrandom{2,\value{problem}}% \my@list@remove\my@shuffle@list{\pgfmathresult}% \global\csletcs{my@problem@b@1}{my@problem@a@\my@list@item}% \ifnumequal{\my@list@item}{\value{problem}}{ \pgfmathrandom{\numexpr\value{problem}-1\relax}% \my@list@remove\my@shuffle@list{\pgfmathresult}% \global\csletcs{my@problem@b@\the\value{problem}}{my@problem@a@\my@list@item}% }{% \pgfmathrandom{\numexpr\value{problem}-2\relax}% \my@list@remove\my@shuffle@list{\pgfmathresult}% \global\csletcs{my@problem@b@\the\value{problem}}{my@problem@a@\my@list@item}% }% %% 其他小题的位置没有任何限制 \setcounter{my@shuffle@temp@cnt}{1}% \whileboolexpr{% test{\ifnumless{\value{my@shuffle@temp@cnt}}{\numexpr\value{problem}-1\relax}}% }{% \stepcounter{my@shuffle@temp@cnt}% \pgfmathrandom{\numexpr\value{problem}-\value{my@shuffle@temp@cnt}\relax}% \my@list@remove\my@shuffle@list{\pgfmathresult}% \global\csletcs{my@problem@b@\the\value{my@shuffle@temp@cnt}}{% my@problem@a@\my@list@item }% }% }{% \ifnumequal{\value{problem}}{2}{% \global\csletcs{my@problem@b@1}{my@problem@a@2}% \global\csletcs{my@problem@b@2}{my@problem@a@1}% }{}% }% } \newcommand{\printproblems}{% \ifrandom \my@appto@problems \my@shuffle@problems \setcounter{problem}{0}% \allproblems \fi \xdef\allproblems{}% \xdef\lastproblem{}% } \newcommand{\makepart}[2]{% \printproblems \setcounter{totalproblems}{\value{totalproblems}+\value{problem}}% \setcounter{problem}{0}% \stepcounter{section}% \vspace{1em}% \noindent\textbf{\textcolor{part number}{\Chinese{section}}、#1}(#2)% \par\nopagebreak \if\relax\detokenize{#1}\relax % #1 is empty \onlyoneproblemtrue \else \onlyoneproblemfalse \vspace{1em}% \fi %其中设定了\@nobreaktrue,保证在列表前也不分页,详情见 source2e \@afterheading } \newcommand{\makedata}[1]{% \printproblems\my@stop@random \centerline{\textbf{附录}\quad #1}\smallskip } \preto{\@enddocumenthook}{\printproblems\my@stop@random} \newcommand\ignorepars{\@ifnextchar\par{\expandafter\ignorepars\@gobble}{}} % 局部定义,仅在当前题目内有效 \define@key{jnuexam@problem}{points}[-1]{\def\my@problem@points{#1}} \define@key{jnuexam@problem}{level}[]{\def\my@problem@level{#1}} \define@key{jnuexam@problem}{year}[]{\def\my@problem@year{#1}} \newcommand{\problempointstext}[1]{(#1 分)} \newcommand\my@hook@exec@other@keys{} \newcommand{\execute@problem@keys}[1]{% \setkeys{jnuexam@problem}{#1}% \my@hook@exec@other@keys \ifdefvoid{\my@problem@points}{}{\problempointstext{\my@problem@points}}% } \newenvironment{problemreal}[1][]{% \stepcounter{problem}\setcounter{choice}{0}% \ifresetnumber \ifonlyoneproblem \renewcommand{\hangtext}{\qquad}% \else \renewcommand{\hangtext}{\textbf{\textsf{\textcolor{problem number}{\arabic{problem}}.}}\;\,}% \fi \else \setcounter{problemreal}{\value{totalproblems}+\value{problem}}% \renewcommand{\hangtext}{\textbf{\textsf{\textcolor{problem number}{\arabic{problemreal}}.}}\;\,}% \fi \settowidth{\hanglength}{\hangtext}% \description[leftmargin=\hanglength,labelwidth=0pt,labelsep=0pt,topsep=0pt,parsep=0pt] \item[\hangtext]\execute@problem@keys{#1}% }{\enddescription} \newenvironment{solutionreal}{% \renewcommand{\hangtext}{\textbf{\textsf{\textcolor{solution name}{\solutionname}.}}\;\,}% \settowidth{\hanglength}{\hangtext}% \description[leftmargin=\hanglength,labelwidth=0pt,labelsep=0pt,topsep=0pt,parsep=0pt] \item[\hangtext] }{\enddescription} \let \oldnewpage = \newpage \let \oldvfill = \vfill \let \oldsmallskip = \smallskip \let \oldmedskip = \medskip \let \oldbigskip = \bigskip \ifrandom \newcommand\my@appto@problems{% \xappto\allproblems{\expandonce\lastproblem}% }% \NewEnviron{problem}{% \stepcounter{problem}% \my@appto@problems \csxdef{my@problem@a@\the\value{problem}}{% \unexpanded{\begin{problemreal}}% \unexpanded\expandafter{\BODY}% \unexpanded{\end{problemreal}}% }% \csxdef{my@problem@b@\the\value{problem}}{% \expandonce{\csname my@problem@a@\the\value{problem}\endcsname}% }% \xdef\lastproblem{% \expandonce{\csname my@problem@b@\the\value{problem}\endcsname}% }% } \NewEnviron{solution}{% \csxappto{my@problem@a@\the\value{problem}}{% \unexpanded{\begin{solutionreal}}% \expandonce{\BODY}% \unexpanded{\end{solutionreal}}% }% } \renewcommand{\newpage}{\gappto\lastproblem{\oldnewpage}} \renewcommand{\vfill}{\csgappto{my@problem@a@\the\value{problem}}{\oldvfill}} \renewcommand{\smallskip}{\csgappto{my@problem@a@\the\value{problem}}{\oldsmallskip}} \renewcommand{\medskip}{\csgappto{my@problem@a@\the\value{problem}}{\oldmedskip}} \renewcommand{\bigskip}{\csgappto{my@problem@a@\the\value{problem}}{\oldbigskip}} \else \newenvironment{problem}[1][]{\problemreal[#1]}{\endproblemreal} %\newenvironment{solution}{\solutionreal}{\endsolutionreal} \NewEnviron{solution}{\begin{solutionreal}\BODY\end{solutionreal}} \fi \newcommand{\my@stop@random}{% \ifrandom \renewenvironment{problem}{\problemreal}{\endproblemreal}% \renewenvironment{solution}{\solutionreal}{\endsolutionreal}% \let \newpage = \oldnewpage \let \vfill = \oldvfill \let \smallskip = \oldsmallskip \let \medskip = \oldmedskip \let \bigskip = \oldbigskip \fi } \def\CommentCutFile{\jobname.cut} \AtBeginDocument{% \ifanswer\else \excludecomment{solution} \fi } %% --------------------------------------------------------------------------- %% 答题栏命令 \answertable %% --------------------------------------------------------------------------- \gdef\answer@lines@temp{}% \newcommand{\answer@lines@add}[1]{% \xdef\answer@lines@temp{\answer@lines@temp#1}% } \newrobustcmd{\answer@number@hided}[1]{小题} % 在 PDFLaTeX 中需要保护中文 \newrobustcmd{\answer@cell@strut}[1]{\parbox[c][#1][c]{2em}{\hbox{答案}}} \newcounter{answer@col} \newcounter{answer@row} \newcounter{answer@total} \newcommand{\answer@lines}[3]{% % #1 答题栏各栏指定高度 % #2 答题栏总共答案个数 % #3 答题栏每行答案个数 \setcounter{answer@row}{(#2-1)/#3+1}% 除法向下取整,改为向上取整 \begingroup \let\hline=\relax \let\\=\relax % 禁止展开 \gdef\answer@lines@temp{}% \setcounter{answer@total}{1}% \whileboolexpr{ test{\ifnumgreater{\value{answer@row}}{0}} }{% \addtocounter{answer@row}{-1}% \answer@lines@add{\answer@number@hided}% \setcounter{answer@col}{1}% \unlessboolexpr{% test{\ifnumgreater{\value{answer@col}}{#3}}% }{% \answer@lines@add{&}% \ifnumgreater{\value{answer@total}}{#2}{}{% \answer@lines@add{\arabic{answer@total}}% }% \stepcounter{answer@col}% \stepcounter{answer@total}% }% \answer@lines@add{\\ \hline \answer@cell@strut{#1}}% \setcounter{answer@col}{1}% \unlessboolexpr{ test{\ifnumgreater{\value{answer@col}}{#3}} }{% \answer@lines@add{&}% \stepcounter{answer@col}% }% \answer@lines@add{\\ \hline}% }% \endgroup \answer@lines@temp } \newcommand{\answertable}[3][1em]{% 答题须知:本题答案必须写在如下表格中,否则不给分.\par \begin{tabularx}{\linewidth}{|c|*{#3}{Y|}} \hline \answer@lines{#1}{#2}{#3} \end{tabularx}% \par\vspace{0.8em}% } %% --------------------------------------------------------------------------- %% 答案切换命令 \answer %% 判断命令 \tickin 和 \tickout %% 填空命令 \fillin 和 \fillout %% 选择命令 \pickin 和 \pickout %% --------------------------------------------------------------------------- \newcommand{\answer}[1]{\ifanswer#1\else\phantom{#1}\fi} \newcommand*{\cdotfill}{\leavevmode\xleaders\hbox to 0.5em{\hss$\cdot$\hss}\hfill\kern0pt\relax} \newcommand*{\tick@box}[1]{[\makebox[1.5em]{\color{blue}\answer{#1}}]} \newcommand*{\tick@text@t}{$\checkmark$} \newcommand*{\tick@text@f}{{\large$\times$}} \newcommand*{\tick@text@T}{\sffamily T} \newcommand*{\tick@text@F}{\sffamily F} \newcommand*{\tickin}[1]{\tick@box{\csname tick@text@#1\endcsname}} \newcommand*{\tickout}[1]{\unskip\nobreak\cdotfill\tick@box{\csname tick@text@#1\endcsname}} \newcommand*{\ulinefill}[1]{\xleaders\hbox{\underline{\vphantom{#1}\kern1pt}}\hfill\kern0pt} \newcommand*{\minwidthbox}[2]{\makebox[{\ifdim#1<\width\width\else#1\fi}]{#2}} \newcommand*{\fillout}[1]{\allowbreak\hbox{}\nobreak\ulinefill{#1}\underline{\color{blue}\answer{#1}}\ulinefill{#1}} \newcommand*{\fillin}[1]{\underline{\hspace{1em}\color{blue}\minwidthbox{2em}{\answer{#1}}\hspace{1em}}} \newcommand*\pickoutreal[1]{% \unskip\nobreak\cdotfill(\makebox[1.5em]{\color{blue}\answer{#1}})% } \newcommand*\pickinreal[1]{% \unskip\nobreak \hspace{0.3em}(\makebox[1.5em]{\color{blue}\answer{#1}})\hspace{0.3em}% \ignorespaces } %% 选择题四个选项打乱顺序,用三种方法即可保证答案不同 %% 1:ABCD -> CDAB; 2:ABCD -> BADC; 3: ABCD -> DCBA %% 即在两行排列时仅用到上下交换以及左右交换,这样可以保持两行长度不变 \csdef{my@shuffle@1@A}{C} \csdef{my@shuffle@2@A}{B} \csdef{my@shuffle@3@A}{D} \csdef{my@shuffle@1@B}{D} \csdef{my@shuffle@2@B}{A} \csdef{my@shuffle@3@B}{C} \csdef{my@shuffle@1@C}{A} \csdef{my@shuffle@2@C}{D} \csdef{my@shuffle@3@C}{B} \csdef{my@shuffle@1@D}{B} \csdef{my@shuffle@2@D}{C} \csdef{my@shuffle@3@D}{A} \def\@my@choice@random{0} \newcommand\my@shuffle@abcd[1]{\csuse{my@shuffle@\@my@choice@random @#1}} \newcommand*\pickout[1]{% \ifbool{random}{% \exam@set@seed \pgfmathrandominteger\@my@choice@random{1}{3}% %\@my@choice@random \pickoutreal{\my@shuffle@abcd{#1}}% }{% \pickoutreal{#1}% }% } \newcommand*\pickoutfixed[1]{% \pickoutreal{#1}% \randomfalse } \newcommand*\pickin[1]{% \ifbool{random}{% \exam@set@seed \pgfmathrandominteger\@my@choice@random{1}{3}% %\@my@choice@random \pickinreal{\my@shuffle@abcd{#1}}% }{% \pickinreal{#1}% }% } \newcommand*\pickinfixed[1]{% \pickinreal{#1}% \randomfalse } %% --------------------------------------------------------------------------- %% 选择题四个选项排版环境,根据四个选项的长度自动排成一行、两行或四行 %% 其中 abcd 环境各列平分整行宽度,而 abcd* 环境各列平分剩余空白 %% --------------------------------------------------------------------------- \newlength{\my@item@len} \newlength{\my@label@len} \newcommand\my@item@temp{% \unskip\cr\stepcounter{choice}(\Alph{choice})\ % } \newcommand\my@item@box{% \hfill\egroup\hfill\hbox to \my@item@len\bgroup \stepcounter{choice}(\Alph{choice})\ \ignorespaces } \newcommand\my@item@par{% \stepcounter{choice}% \def\my@label@text{(\Alph{choice})\ }% \settowidth{\my@label@len}{\my@label@text}% \par \parshape 2 \hanglength \linewidth \dimexpr\hanglength + \my@label@len\relax \dimexpr\linewidth - \my@label@len\relax \my@label@text\ignorespaces } \NewEnviron{abcdreal}{ \unskip \setlength{\parindent}{0pt}% \setlength{\parskip}{0pt}% \setcounter{choice}{0}% \let\item=\my@item@temp \settowidth{\my@item@len}{\vbox{\halign{##\hfil\cr\BODY\crcr}}}% \setcounter{choice}{0}% \ifdim\my@item@len>0.486\linewidth \setlength{\my@item@len}{\linewidth}% \let\item=\my@item@par \BODY\par \else \ifdim\my@item@len>.243\linewidth \setlength{\my@item@len}{0.5\linewidth}% \else \setlength{\my@item@len}{0.25\linewidth}% \fi \let\item=\my@item@box \par\bgroup\BODY\hfill\egroup\par \fi } \newcommand\my@item@one@line{% \unskip \ifnumequal{\value{choice}}{0}{}{\hfill} \stepcounter{choice}(\Alph{choice})\ % } \newcommand\my@item@two@line{% \unskip \ifnumodd{\value{choice}}{&}{\unskip\cr}% \stepcounter{choice}(\Alph{choice})\ % } \NewEnviron{abcd*real}{ \unskip \setlength{\parindent}{0pt}% \setlength{\parskip}{0pt}% \setcounter{choice}{0}% \let\item=\my@item@one@line \settowidth{\my@item@len}{\BODY}% \ifdim\my@item@len<0.95\linewidth \setcounter{choice}{0}% \par\bgroup\BODY\hfill\hfill\par\egroup\par \else \setcounter{choice}{0}% \let\item=\my@item@two@line \settowidth{\my@item@len}{\vbox{\halign{##&##\hfil\cr\BODY\crcr}}}% \ifdim\my@item@len<0.975\linewidth \setcounter{choice}{0}% \par\bgroup\nointerlineskip \vbox{\halign to\linewidth{##\hfil\tabskip=0pt plus 1fil&##\hfil\cr\BODY\crcr}}% \egroup\par \else \setcounter{choice}{0}% \let\item=\my@item@par \par\bgroup\BODY\hfill\egroup\par \fi \fi } \ifbool{random}{% \csdef{my@swap@items@1}#1#2#3#4{\item#3\item#4\item#1\item#2} \csdef{my@swap@items@2}#1#2#3#4{\item#2\item#1\item#4\item#3} \csdef{my@swap@items@3}#1#2#3#4{\item#4\item#3\item#2\item#1} \long\def\my@swap@items#1\item#2\item#3\item#4\item#5\@my@stop@mark{% #1\csuse{my@swap@items@\@my@choice@random}{#2}{#3}{#4}{#5}% } }{} \NewDocumentEnvironment{abcd}{+b}{% \ifbool{random}{% \begin{abcdreal}\my@swap@items#1\@my@stop@mark\end{abcdreal}% }{% \begin{abcdreal}#1\end{abcdreal}% }% }{} \NewDocumentEnvironment{abcd*}{+b}{% \ifbool{random}{% \begin{abcd*real}\my@swap@items#1\@my@stop@mark\end{abcd*real}% }{% \begin{abcd*real}#1\end{abcd*real}% }% }{} %% --------------------------------------------------------------------------- %% 设定有序列表使用悬挂缩进,并指定前两级有序列表的标签格式 %% 标签宽度按最宽者自动设定,左边距自动计算,竖直空白全部去掉 %% 最后的 itemjoin 设定行内有序列表 enumerate* 两项之间的空白 %% --------------------------------------------------------------------------- \setlist[enumerate]{labelindent=0pt,labelsep=0.2em,itemindent=0pt,leftmargin=*,nosep,itemjoin=\quad} \setlist[enumerate,1]{label=(\arabic*)} \setlist[enumerate,2]{label=(\alph*),widest*=1} %% --------------------------------------------------------------------------- %% 自由对齐命令 \tabpoint, \tabto, \tableft %% 命令 \tabpoint 记录当前的水平位置,也可以简写为 \? %% 命令 \tabto 跳到之前记录的位置,也可以简写为 \+ %% 命令 \tableft 跳到之前记录的位置的左侧,也可以简写为 \< %% 这些自由对齐命令需要编译两次才能生效 %% --------------------------------------------------------------------------- \usepackage{zref-savepos} \@ifundefined{zsaveposx}{\let\zsaveposx\zsavepos}{} % 旧版本无 \zsaveposx 命令 \newcounter{saveposcnt} \newcounter{useposcnt} \renewcommand*{\thesaveposcnt}{savepos\number\value{saveposcnt}} \renewcommand*{\theuseposcnt}{usepos\number\value{useposcnt}} \def\my@alignment@offset{} \def\my@alignment@list{} \forcsvlist{\listadd\my@alignment@list}{=,<,>,\le,\ge,\leq,\geq,\approx} \newlength{\my@alignment@kern} \newcommand*{\my@alignment@check}[1]{% \ifx\my@let@token #1% \def\my@alignment@offset{5}% \listbreak \fi } \newcommand{\my@alignment@next}{% \ifdefempty{\my@alignment@offset}{% \def\my@alignment@offset{0}% \forlistloop{\my@alignment@check}{\my@alignment@list}% }{}% \settowidth{\my@alignment@kern}{$\mkern\my@alignment@offset mu$}% \stepcounter{saveposcnt}% \rlap{\kern\my@alignment@kern\zsaveposx{\thesaveposcnt}}% } \newcommand*{\tabpoint}[1][]{% \leavevmode \def\my@alignment@offset{#1}% \futurelet\my@let@token\my@alignment@next } \let \? = \tabpoint \newcommand*{\tabto}{% \stepcounter{useposcnt}% \zsaveposx{\theuseposcnt}% \noindent \hskip\zposx{\thesaveposcnt}sp\relax \hskip-\zposx{\theuseposcnt}sp\relax \ignorespaces } \let \+ = \tabto \newcommand*{\tableft}{% \settowidth{\my@alignment@kern}{$=\mkern5mu$}% \stepcounter{useposcnt}% \zsaveposx{\theuseposcnt}% \noindent \hskip\zposx{\thesaveposcnt}sp\relax \hskip-\zposx{\theuseposcnt}sp\relax \hskip-\my@alignment@kern \ignorespaces } \let \< = \tableft %% --------------------------------------------------------------------------- %% 评分命令 \points %% --------------------------------------------------------------------------- \PassOptionsToPackage{tbtags}{amsmath} \RequirePackage{amsmath} \newcommand{\mypoints}[1]{\textcolor{red}{#1\kern0.15em 分}} \newcommand{\pointstext}[1]{\mbox{}\nobreak\hfill$\cdots\cdots$\mypoints{#1}\par\noindent\ignorespaces} \newcommand{\pointseqno}[1]{\eqno{\cdots\cdots\text{\mypoints{#1}}}} \newcommand{\pointstag}[1]{\tag*{$\cdots\cdots$\mypoints{#1}}} \newrobustcmd{\points}[1]{% \ifbool{mmode}{% \ifdefstrequal{\tag}{\dft@tag}{\pointseqno{#1}}{\pointstag{#1}}% }{% \pointstext{#1}% }% } %% --------------------------------------------------------------------------- %% 载入个人定制文件 jnuexam.cfg %% 中文字体切换选项 sourcehan %% 实心句号替换选项 solidot %% --------------------------------------------------------------------------- \InputIfFileExists{jnuexam.cfg}{}{} \newcommand{\my@set@sourcehan}{ \setCJKmainfont[BoldFont=Source Han Sans SC]{Source Han Serif SC} \setCJKsansfont{Source Han Sans SC} % 用中文字体名时 LuaTeX 找不到该字体,XeTeX 正常 %\setCJKmainfont[BoldFont=思源黑体]{思源宋体} %\setCJKsansfont{思源黑体} } \AtBeginDocument{% \ifbool{sourcehan}{% % https://sourceforge.net/p/xetex/code/ci/master/tree/source/texk/web2c/xetexdir/NEWS \ifbool{XeTeX}{% TeXLive 2015 \ifdimless{\the\XeTeXversion\XeTeXrevision pt}{0.99992pt}{}{\my@set@sourcehan}% }{}% \ifbool{LuaTeX}{\my@set@sourcehan}{}% }{}% } %% 这里不能用 \ifbool,因为涉及到 catcode 的改变 \ifsolidot \ifXeTeX \catcode`。=\active\def。{.}% \else\ifLuaTeX \catcode`。=\active\def。{.}% \fi \fi \fi %% --------------------------------------------------------------------------- %% 在 tabular 和 array 等表格环境中添加列间距 %% 避免单元格里出现的分式太过接近上面和下面行 %% --------------------------------------------------------------------------- \newcommand{\my@do@cellspace}{% \RequirePackage[math]{cellspace}% \setlength\cellspacetoplimit{2pt}% \setlength\cellspacebottomlimit{2pt}% \addparagraphcolumntypes{X}% \newcolumntype{0}[1]{>{\bcolumn ##1\@nil}##1<{\ecolumn}}% \newcolumntype{5}[1]{>{$}0{##1}<{$}}% % Fix cellspace bug before version 1.7 % See https://tex.stackexchange.com/a/385581 \@ifpackagelater{cellspace}{2017/08/12}{}{% \patchcmd{\@endpbox}{\color@endgroup}{\expandafter\color@endgroup}{}{}% }% } \AtBeginDocument{% \ifcellspace \my@do@cellspace \fi } %% --------------------------------------------------------------------------- %% 统一行间公式和行内公式的巨算符和分式的尺寸 %% 利用开头的 medmath 选项可以启用此部分设定 %% --------------------------------------------------------------------------- \newcommand{\my@do@medmath}{% \RequirePackage[mediummath]{nccmath}% %% 补充 \oiint 命令的调整 \ifdef{\oiint}{% \let\NCC@op@oiint=\oiint \DeclareRobustCommand{\oiintop}{\mathop{\medmath{\NCC@op@oiint}}}% \def\oiint{\DOTSI\NCC@op@prepare{\oiintop}}% }{}% %% 设定 nccmath 的积分号校正尺寸 %% 后面已经改用相对尺寸,不再需要 %\ifamsfonts % \medintcorr{0.5em} %\else % \medintcorr{0.3em} %\fi %% 使用 nccmath 宏包后,cases 环境包含定积分时将无法编译,这里重新定义此环境 %% 注意相比 amsmath 的原始定义,我们这里将 \quad 从两列之间移动到第二列最前面 %% 这是因为,我们常将 cases 用于只有一列的方程组,这样处理末尾不会有多余空白 \ifbool{cellspace}{% \renewenvironment{cases}{% \left\{\linespread{1.0}\selectfont\def\arraystretch{1.2}% \begin{array}{@{}5l@{}>{\quad}5l@{}}% }{% \end{array}\right.% }% }{% \renewenvironment{cases}{% \left\{\linespread{1.0}\selectfont\def\arraystretch{1.2}% \begin{array}{@{}l@{}>{\quad}l@{}}% }{% \end{array}\right.% }% } %% 当 minipage 或 \parbox 仅包含行间公式时,盒子的右边距丢失,这里修正它 %% 其他类似问题的描述见 http://tex.stackexchange.com/q/22170 \let\start@gather=\NCC@startgather \let\start@align=\NCC@startalign \let\start@multline=\NCC@startmultline \let\mathdisplay=\NCC@startdisplay } \newcommand{\my@do@medmath@fix}{% %% 修正在角标处的非积分巨算符尺寸 %% 参考了 scalerel 宏包对数学样式的保存方法 % 非角标巨算符保持为 \displaystyle 巨算符的 80% 大小 % 一级角标巨算符修改为 \textstyle 巨算符的 80% 大小 % 二级角标巨算符修改为 \scriptstyle 巨算符的 80% 大小 \def\@my@style@D{\displaystyle}% \def\@my@style@T{\displaystyle}% \def\@my@style@S{\textstyle}% \def\@my@style@s{\scriptstyle}% \def\my@style@saved{\csname @my@style@\@my@style@switch\endcsname}% \newcommand{\my@style@this}[1]{% \mathchoice{\def\@my@style@switch{D}##1}{\def\@my@style@switch{T}##1} {\def\@my@style@switch{S}##1}{\def\@my@style@switch{s}##1}% }% \DeclareRobustCommand*\medmath[1]{\NCC@select@msize \mathord{\my@style@this{\raise\@tempdima\hbox{\NCC@prepare@msize$\my@style@saved ##1$}}}% } %% 修正在角标处的积分算符尺寸和上下限位置 \newlength{\@my@em}% \setlength{\@my@em}{1em}% \ifamsfonts \medintcorr{0.5\@my@em}% \else \medintcorr{0.3\@my@em}% \fi \newcommand{\my@style@unit}[1]{% \mathchoice{\setlength{\@my@em}{1em}##1}{\setlength{\@my@em}{1em}##1} {\setlength{\@my@em}{0.5em}##1}{\setlength{\@my@em}{0.3em}##1}% }% \let\my@saved@op@printm=\NCC@op@printm \def\NCC@op@printm{\my@style@unit{\my@saved@op@printm}}% %% 修正在角标位置的分式和嵌套分式 %% 总是使用当前样式尺寸来排版分式的分子和分母 \newcommand{\my@larger@frac}[2]{% \mathchoice{\genfrac{}{}{}{0}{##1}{##2}}{\genfrac{}{}{}{0}{##1}{##2}}% {\genfrac{}{}{}{1}{##1}{##2}}{\genfrac{}{}{}{2}{##1}{##2}}% }% \DeclareRobustCommand{\frac}[2]{% \mathchoice{\mfrac{##1}{##2}}{\mfrac{##1}{##2}}% {\my@larger@frac{##1}{##2}}{\my@larger@frac{##1}{##2}}% }% \patchcmd{\NCC@prepare@msize}{% \def\frac{\protect\NCC@innerfrac{}}% }{% \let\frac=\my@larger@frac }{}{}% } \AtBeginDocument{% \ifmedmath \my@do@medmath \my@do@medmath@fix \fi } %% --------------------------------------------------------------------------- %% 载入常用宏包,定义常用命令 %% --------------------------------------------------------------------------- \AtBeginDocument{ \setlength{\abovedisplayskip}{4pt minus 2pt} \setlength{\belowdisplayskip}{4pt minus 2pt} \setlength{\abovedisplayshortskip}{2pt} \setlength{\belowdisplayshortskip}{2pt} } \setlength\arraycolsep{4pt} \ifbool{XeTeX}{ % https://en.wikipedia.org/wiki/Number_Forms % Ⅰ、Ⅱ、Ⅲ、Ⅳ、Ⅴ、Ⅵ、Ⅶ、Ⅷ、Ⅸ、Ⅹ、Ⅺ、Ⅻ \xeCJKsetcharclass{"2150}{"218F}{1} % 斜线分数,全角罗马数字等 % https://en.wikipedia.org/wiki/Enclosed_Alphanumerics \xeCJKsetcharclass{"2460}{"24FF}{1} % 带圈数字字母,括号数字字母,带点数字等 }{} \RequirePackage{multirow} \RequirePackage{tabu} \RequirePackage{diagbox} %% 修正 \diagbox 在 array 环境中使用的问题 \newrobustcmd{\diagboxtwo}[3][]{% \ifbool{mmode}{% \hbox{\let\tabcolsep=\arraycolsep\diagbox[#1]{$#2$}{$#3$}}% }{% \diagbox[#1]{#2}{#3}% } } \newrobustcmd{\diagboxthree}[4][]{% \ifbool{mmode}{% \hbox{\let\tabcolsep=\arraycolsep\diagbox[#1]{$#2$}{$#3$}{$#4$}}% }{% \diagbox[#1]{#2}{#3}{#4}% } } \RequirePackage{mathtools} % \mathllap 命令,pmatrix* 环境等 \RequirePackage{extarrows} \RequirePackage{relsize} \newcommand{\Int}{\mathop{\mathlarger{\int}}} \AtBeginDocument{% \let\my@saved@lim=\lim \def\lim{\my@saved@lim\limits}% \let\my@saved@sum=\sum \def\sum{\my@saved@sum\limits}% \let\my@saved@prod=\prod \def\prod{\my@saved@prod\limits}% } \newcommand{\e}{\mathrm{e}} \newcommand{\R}{\mathbb{R}} \DeclareMathOperator{\arccot}{arccot} \DeclareMathOperator{\Corr}{\rho} \DeclareMathOperator{\Cov}{Cov} \DeclareMathOperator{\diag}{diag} \DeclareMathOperator{\grad}{grad} \DeclareMathOperator{\Prj}{Prj} \DeclareMathOperator{\tr}{tr} \DeclareMathOperator{\Var}{Var} \DeclareMathOperator{\diver}{div} \let\division=\div \let\div=\diver \newcommand{\diff}{\mathop{}\!\mathrm{d}} \newcommand{\dx}{\diff x} \newcommand{\dy}{\diff y} \def\dz{\diff z} % 不确定命令是否已经定义 \newcommand{\du}{\diff u} \newcommand{\dv}{\diff v} \newcommand{\dr}{\diff r} \newcommand{\ds}{\diff s} \newcommand{\dt}{\diff t} \newcommand{\dS}{\diff S} % 有些宏包比如 hyperref 会修改 \d 的定义,所以放在 document 开始处 % 利用 etoolbox 将 \d 定义为健壮命令,以避免在 align 等环境中错误地展开 \AtBeginDocument{% \let\oldd=\d \renewrobustcmd{\d}{\ifbool{mmode}{\diff}{\oldd}}% } \let\pd=\partial \newcommand{\pdf}{\pd f} \newcommand{\pdg}{\pd g} \newcommand{\pdh}{\pd h} \newcommand{\pdl}{\pd l} \newcommand{\pdn}{\pd n} \newcommand{\pdu}{\pd u} \newcommand{\pdv}{\pd v} \newcommand{\pdx}{\pd x} \newcommand{\pdy}{\pd y} \newcommand{\pdz}{\pd z} \newcommand{\pdF}{\pd F} \newcommand{\pdL}{\pd L} \newcommand{\pdP}{\pd P} \newcommand{\pdQ}{\pd Q} \newcommand{\pdR}{\pd R} % from mathabx package \DeclareFontFamily{U}{mathx}{\hyphenchar\font45} \DeclareFontShape{U}{mathx}{m}{n}{<-> mathx10}{} \DeclareSymbolFont{mathx}{U}{mathx}{m}{n} \DeclareMathAccent{\widebar}{0}{mathx}{"73} \newcommand{\va}{\vec{a}} \newcommand{\vb}{\vec{b}} \newcommand{\vc}{\vec{c}} \newcommand{\vd}{\vec{d}} \newcommand{\ve}{\vec{e}} \newcommand{\vi}{\vec{i}} \newcommand{\vj}{\vec{j}} \newcommand{\vk}{\vec{k}} \newcommand{\vn}{\vec{n}} \newcommand{\vs}{\vec{s}} \newcommand{\vv}{\vec{v}} \let\ov=\overrightarrow \let\le=\leqslant \let\ge=\geqslant \let\lb=\{ \let\rb=\} \def\T{\mathrm{T}\kern-.5pt} % 分数线长一点的分数,\wfrac[2pt]{x}{y} 表示左右加 2pt % 和前面的 medmath 一样,将代码放在 \AtBeginDocument 里 \AtBeginDocument{% \newrobustcmd{\wfrac}[3][2pt]{% \frac{\hspace{#1}#2\hspace{#1}}{\hspace{#1}#3\hspace{#1}}% }% \newrobustcmd{\wdfrac}[3][2pt]{% \dfrac{\hspace{#1}#2\hspace{#1}}{\hspace{#1}#3\hspace{#1}}% }% \newrobustcmd{\wtfrac}[3][2pt]{% \tfrac{\hspace{#1}#2\hspace{#1}}{\hspace{#1}#3\hspace{#1}}% }% } % 使用 stix font 中的 white arrows \ifxetex %\IfFileExists{STIX-Regular.otf}{% 在 TeXLive 中无效 \IfFileExists{stix.sty}{% \newfontfamily{\mystix}{STIX} % stix v1.1 }{% \newfontfamily{\mystix}{STIXGeneral} % stix v1.0 } \newrobustcmd\leftwhitearrow{% \mathrel{\text{\normalfont\mystix\symbol{"21E6}}}% } \newrobustcmd\upwhitearrow{% \mathrel{\text{\normalfont\mystix\symbol{"21E7}}}% } \newrobustcmd\rightwhitearrow{% \mathrel{\text{\normalfont\mystix\symbol{"21E8}}}% } \newrobustcmd\downwhitearrow{% \mathrel{\text{\normalfont\mystix\symbol{"21E9}}}% } \else \let \leftwhitearrow = \Leftarrow \let \rightwhitearrow = \Rightarrow \let \upwhitearrow = \Uparrow \let \downwhitearrow = \Downarrow \fi