PhysicsMethods.ts 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2018-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Methods for TeX parsing of the physics package.
  19. *
  20. * @author v.sorge@mathjax.org (Volker Sorge)
  21. */
  22. import {ParseMethod} from '../Types.js';
  23. import BaseMethods from '../base/BaseMethods.js';
  24. import TexParser from '../TexParser.js';
  25. import TexError from '../TexError.js';
  26. import {TEXCLASS, MmlNode} from '../../../core/MmlTree/MmlNode.js';
  27. import ParseUtil from '../ParseUtil.js';
  28. import NodeUtil from '../NodeUtil.js';
  29. import {NodeFactory} from '../NodeFactory.js';
  30. import {Macro} from '../Symbol.js';
  31. let PhysicsMethods: Record<string, ParseMethod> = {};
  32. /***********************
  33. * Physics package section 2.1
  34. * Automatic bracing
  35. */
  36. /**
  37. * Pairs open and closed fences.
  38. * @type {{[fence: string]: string}}
  39. */
  40. const pairs: {[fence: string]: string} = {
  41. '(': ')',
  42. '[': ']',
  43. '{': '}',
  44. '|': '|',
  45. };
  46. /**
  47. * Regular expression for matching big fence arguments.
  48. * @type {RegExp}
  49. */
  50. const biggs: RegExp = /^(b|B)i(g{1,2})$/;
  51. /**
  52. * Automatic sizing of fences, e.g., \\qty(x). Some with content.
  53. * @param {TexParser} parser The calling parser.
  54. * @param {string} name The macro name.
  55. * @param {string=} open Opening fence.
  56. * @param {string=} close Closing fence.
  57. * @param {boolean=} arg Fences contain an argument.
  58. * @param {string=} named Name operator.
  59. * @param {string=} variant A font for the mathvariant.
  60. */
  61. PhysicsMethods.Quantity = function(parser: TexParser, name: string,
  62. open: string = '(', close: string = ')',
  63. arg: boolean = false, named: string = '',
  64. variant: string = '') {
  65. let star = arg ? parser.GetStar() : false;
  66. let next = parser.GetNext();
  67. let position = parser.i;
  68. let big = null;
  69. if (next === '\\') {
  70. parser.i++;
  71. big = parser.GetCS();
  72. if (!big.match(biggs)) {
  73. // empty
  74. let empty = parser.create('node', 'mrow');
  75. parser.Push(ParseUtil.fenced(parser.configuration, open, empty, close));
  76. parser.i = position;
  77. return;
  78. }
  79. next = parser.GetNext();
  80. }
  81. let right = pairs[next];
  82. if (arg && next !== '{') {
  83. throw new TexError('MissingArgFor', 'Missing argument for %1', parser.currentCS);
  84. }
  85. if (!right) {
  86. let empty = parser.create('node', 'mrow');
  87. parser.Push(ParseUtil.fenced(parser.configuration, open, empty, close));
  88. parser.i = position;
  89. return;
  90. }
  91. // Get the fences
  92. if (named) {
  93. const mml = parser.create('token', 'mi', {texClass: TEXCLASS.OP}, named);
  94. if (variant) {
  95. NodeUtil.setAttribute(mml, 'mathvariant', variant);
  96. }
  97. parser.Push(parser.itemFactory.create('fn', mml));
  98. }
  99. if (next === '{') {
  100. let argument = parser.GetArgument(name);
  101. next = arg ? open : '\\{';
  102. right = arg ? close : '\\}';
  103. // TODO: Make all these fenced expressions.
  104. argument = star ? next + ' ' + argument + ' ' + right :
  105. (big ?
  106. '\\' + big + 'l' + next + ' ' + argument + ' ' + '\\' + big + 'r' + right :
  107. '\\left' + next + ' ' + argument + ' ' + '\\right' + right);
  108. parser.Push(new TexParser(argument, parser.stack.env,
  109. parser.configuration).mml());
  110. return;
  111. }
  112. if (arg) {
  113. next = open;
  114. right = close;
  115. }
  116. parser.i++;
  117. parser.Push(parser.itemFactory.create('auto open')
  118. .setProperties({open: next, close: right, big: big}));
  119. };
  120. /**
  121. * The evaluate macro.
  122. * @param {TexParser} parser The calling parser.
  123. * @param {string} name The macro name.
  124. */
  125. PhysicsMethods.Eval = function(parser: TexParser, name: string) {
  126. let star = parser.GetStar();
  127. let next = parser.GetNext();
  128. if (next === '{') {
  129. let arg = parser.GetArgument(name);
  130. let replace = '\\left. ' +
  131. (star ? '\\smash{' + arg + '}' : arg) +
  132. ' ' + '\\vphantom{\\int}\\right|';
  133. parser.string = parser.string.slice(0, parser.i) + replace +
  134. parser.string.slice(parser.i);
  135. return;
  136. }
  137. if (next === '(' || next === '[') {
  138. parser.i++;
  139. parser.Push(parser.itemFactory.create('auto open')
  140. .setProperties(
  141. {open: next, close: '|',
  142. smash: star, right: '\\vphantom{\\int}'}));
  143. return;
  144. }
  145. throw new TexError('MissingArgFor', 'Missing argument for %1', parser.currentCS);
  146. };
  147. /**
  148. * The anti/commutator and poisson macros.
  149. * @param {TexParser} parser The calling parser.
  150. * @param {string} name The macro name.
  151. * @param {string=} open Opening fence.
  152. * @param {string=} close Closing fence.
  153. */
  154. PhysicsMethods.Commutator = function(parser: TexParser, name: string,
  155. open: string = '[', close: string = ']') {
  156. let star = parser.GetStar();
  157. let next = parser.GetNext();
  158. let big = null;
  159. if (next === '\\') {
  160. parser.i++;
  161. big = parser.GetCS();
  162. if (!big.match(biggs)) {
  163. // Actually a commutator error arg1 error.
  164. throw new TexError('MissingArgFor', 'Missing argument for %1', parser.currentCS);
  165. }
  166. next = parser.GetNext();
  167. }
  168. if (next !== '{') {
  169. throw new TexError('MissingArgFor', 'Missing argument for %1', parser.currentCS);
  170. }
  171. let arg1 = parser.GetArgument(name);
  172. let arg2 = parser.GetArgument(name);
  173. let argument = arg1 + ',' + arg2;
  174. argument = star ? open + ' ' + argument + ' ' + close :
  175. (big ?
  176. '\\' + big + 'l' + open + ' ' + argument + ' ' + '\\' + big + 'r' + close :
  177. '\\left' + open + ' ' + argument + ' ' + '\\right' + close);
  178. parser.Push(new TexParser(argument, parser.stack.env,
  179. parser.configuration).mml());
  180. };
  181. /***********************
  182. * Physics package section 2.2
  183. * Vector notation
  184. */
  185. let latinCap: [number, number] = [0x41, 0x5A];
  186. let latinSmall: [number, number] = [0x61, 0x7A];
  187. let greekCap: [number, number] = [0x391, 0x3A9];
  188. let greekSmall: [number, number] = [0x3B1, 0x3C9];
  189. let digits: [number, number] = [0x30, 0x39];
  190. /**
  191. * Checks if a value is in a given numerical interval.
  192. * @param {number} value The value.
  193. * @param {[number, number]} range The closed interval.
  194. */
  195. function inRange(value: number, range: [number, number]) {
  196. return (value >= range[0] && value <= range[1]);
  197. }
  198. /**
  199. * Method to create a token for the vector commands. It creates a vector token
  200. * with the specific vector font (e.g., bold) in case it is a Latin or capital
  201. * Greek character, accent or small Greek character if command is starred. This
  202. * is a replacement for the original token method in the node factory.
  203. * @param {NodeFactory} factory The current node factory.
  204. * @param {string} kind The type of token to create.
  205. * @param {any} def The attributes for the node.
  206. * @param {string} text The text contained in the token node.
  207. * @return {MmlNode} The newly create token node.
  208. */
  209. function createVectorToken(factory: NodeFactory, kind: string,
  210. def: any, text: string): MmlNode {
  211. let parser = factory.configuration.parser;
  212. let token = NodeFactory.createToken(factory, kind, def, text);
  213. let code: number = text.codePointAt(0);
  214. if (text.length === 1 && !parser.stack.env.font &&
  215. parser.stack.env.vectorFont &&
  216. (inRange(code, latinCap) || inRange(code, latinSmall) ||
  217. inRange(code, greekCap) || inRange(code, digits) ||
  218. (inRange(code, greekSmall) && parser.stack.env.vectorStar) ||
  219. NodeUtil.getAttribute(token, 'accent'))) {
  220. NodeUtil.setAttribute(token, 'mathvariant', parser.stack.env.vectorFont);
  221. }
  222. return token;
  223. }
  224. /**
  225. * Bold vector notation.
  226. * @param {TexParser} parser The calling parser.
  227. * @param {string} name The macro name.
  228. */
  229. PhysicsMethods.VectorBold = function(parser: TexParser, name: string) {
  230. let star = parser.GetStar();
  231. let arg = parser.GetArgument(name);
  232. let oldToken = parser.configuration.nodeFactory.get('token');
  233. let oldFont = parser.stack.env.font;
  234. delete parser.stack.env.font;
  235. parser.configuration.nodeFactory.set('token', createVectorToken);
  236. parser.stack.env.vectorFont = star ? 'bold-italic' : 'bold';
  237. parser.stack.env.vectorStar = star;
  238. let node = new TexParser(arg, parser.stack.env, parser.configuration).mml();
  239. if (oldFont) {
  240. parser.stack.env.font = oldFont;
  241. }
  242. delete parser.stack.env.vectorFont;
  243. delete parser.stack.env.vectorStar;
  244. parser.configuration.nodeFactory.set('token', oldToken);
  245. parser.Push(node);
  246. };
  247. /**
  248. * Macros that can have an optional star which is propagated.
  249. * @param {TexParser} parser The calling parser.
  250. * @param {string} name The macro name.
  251. * @param {number} argcount Number of arguments.
  252. * @param {string[]} ...parts List of parts from which to assemble the macro.
  253. * If the original command is starred, a star will be injected at each part.
  254. */
  255. PhysicsMethods.StarMacro = function(parser: TexParser, name: string,
  256. argcount: number, ...parts: string[]) {
  257. let star = parser.GetStar();
  258. const args: string[] = [];
  259. if (argcount) {
  260. for (let i = args.length; i < argcount; i++) {
  261. args.push(parser.GetArgument(name));
  262. }
  263. }
  264. let macro = parts.join(star ? '*' : '');
  265. macro = ParseUtil.substituteArgs(parser, args, macro);
  266. parser.string = ParseUtil.addArgs(parser, macro, parser.string.slice(parser.i));
  267. parser.i = 0;
  268. ParseUtil.checkMaxMacros(parser);
  269. };
  270. /**
  271. * Computes the application of a vector operation.
  272. * @param {TexParser} parser The calling parser.
  273. * @param {string} kind The type of stack item to parse the operator into.
  274. * @param {string} name The macro name.
  275. * @param {string} operator The operator expression.
  276. * @param {string[]} ...fences List of opening fences that should be
  277. * automatically sized and paired to its corresponding closing fence.
  278. */
  279. let vectorApplication = function(
  280. parser: TexParser, kind: string, name: string, operator: string,
  281. fences: string[]) {
  282. let op = new TexParser(operator, parser.stack.env,
  283. parser.configuration).mml();
  284. parser.Push(parser.itemFactory.create(kind, op));
  285. let left = parser.GetNext();
  286. let right = pairs[left];
  287. if (!right) {
  288. return;
  289. }
  290. let lfence = '', rfence = '', arg = '';
  291. let enlarge = fences.indexOf(left) !== -1;
  292. if (left === '{') {
  293. arg = parser.GetArgument(name);
  294. lfence = enlarge ? '\\left\\{' : '';
  295. rfence = enlarge ? '\\right\\}' : '';
  296. let macro = lfence + ' ' + arg + ' ' + rfence;
  297. parser.string = macro + parser.string.slice(parser.i);
  298. parser.i = 0;
  299. return;
  300. }
  301. if (!enlarge) {
  302. return;
  303. }
  304. parser.i++;
  305. parser.Push(parser.itemFactory.create('auto open')
  306. .setProperties({open: left, close: right}));
  307. };
  308. /**
  309. * An operator that needs to be parsed (e.g., a Greek letter or nabla) and
  310. * applied to a possibly fenced expression. By default automatic fences are
  311. * parentheses and brakets, with braces being ignored.
  312. * @param {TexParser} parser The calling parser.
  313. * @param {string} name The macro name.
  314. * @param {string} operator The operator expression.
  315. * @param {string[]} ...fences List of opening fences that should be
  316. * automatically sized and paired to its corresponding closing fence.
  317. */
  318. PhysicsMethods.OperatorApplication = function(
  319. parser: TexParser, name: string, operator: string,
  320. ...fences: string[]) {
  321. vectorApplication(parser, 'fn', name, operator, fences);
  322. };
  323. /**
  324. * A vector operator that needs to be parsed (e.g., a Greek letter or nabla with
  325. * a crossproduct) and connected to a possibly fenced expression. By default
  326. * automatic fences are parentheses and brakets.
  327. * @param {TexParser} parser The calling parser.
  328. * @param {string} name The macro name.
  329. * @param {string} operator The operator expression.
  330. * @param {string[]} ...fences List of opening fences that should be
  331. * automatically sized and paired to its corresponding closing fence.
  332. */
  333. PhysicsMethods.VectorOperator = function(
  334. parser: TexParser, name: string, operator: string,
  335. ...fences: string[]) {
  336. vectorApplication(parser, 'mml', name, operator, fences);
  337. };
  338. /***********************
  339. * Physics package section 2.3
  340. * Operators
  341. */
  342. /**
  343. * Operator expression with automatic fences and optional exponent.
  344. * @param {TexParser} parser The calling parser.
  345. * @param {string} name The macro name.
  346. * @param {boolean=} opt Set to false if no optional exponent is allowed.
  347. * @param {string=} id The name of the function if different from name.
  348. */
  349. PhysicsMethods.Expression = function(parser: TexParser, name: string,
  350. opt: boolean = true, id: string = '') {
  351. id = id || name.slice(1);
  352. const exp = opt ? parser.GetBrackets(name) : null;
  353. let mml = parser.create('token', 'mi', {texClass: TEXCLASS.OP}, id);
  354. if (exp) {
  355. const sup = new TexParser(exp,
  356. parser.stack.env, parser.configuration).mml();
  357. mml = parser.create('node', 'msup', [mml, sup]);
  358. }
  359. parser.Push(parser.itemFactory.create('fn', mml));
  360. if (parser.GetNext() !== '(') {
  361. return;
  362. }
  363. parser.i++;
  364. parser.Push(parser.itemFactory.create('auto open')
  365. .setProperties({open: '(', close: ')'}));
  366. };
  367. /***********************
  368. * Physics package section 2.4
  369. * Quick quad text
  370. */
  371. /**
  372. * Quad text macros.
  373. * @param {TexParser} parser The calling parser.
  374. * @param {string} name The macro name.
  375. * @param {string} text The text that is to be padded with quad spaces.
  376. */
  377. PhysicsMethods.Qqtext = function(parser: TexParser, name: string,
  378. text: string) {
  379. let star = parser.GetStar();
  380. let arg = text ? text : parser.GetArgument(name);
  381. let replace = (star ? '' : '\\quad') + '\\text{' + arg + '}\\quad ';
  382. parser.string = parser.string.slice(0, parser.i) + replace +
  383. parser.string.slice(parser.i);
  384. };
  385. /***********************
  386. * Physics package section 2.5
  387. * Derivatives
  388. */
  389. /**
  390. * The differential and variation macros.
  391. * @param {TexParser} parser The calling parser.
  392. * @param {string} name The macro name.
  393. * @param {string} op The operator. It will be parsed.
  394. */
  395. PhysicsMethods.Differential = function(parser: TexParser, name: string,
  396. op: string) {
  397. const optArg = parser.GetBrackets(name);
  398. const power = optArg != null ? '^{' + optArg + '}' : ' ';
  399. const parens = parser.GetNext() === '(';
  400. const braces = parser.GetNext() === '{';
  401. let macro = op + power;
  402. if (!(parens || braces)) {
  403. macro += parser.GetArgument(name, true) || '';
  404. let mml = new TexParser(macro, parser.stack.env,
  405. parser.configuration).mml();
  406. parser.Push(mml);
  407. return;
  408. }
  409. if (braces) {
  410. macro += parser.GetArgument(name);
  411. const mml = new TexParser(macro, parser.stack.env,
  412. parser.configuration).mml();
  413. parser.Push(parser.create('node', 'TeXAtom', [mml], {texClass: TEXCLASS.OP}));
  414. return;
  415. }
  416. parser.Push(new TexParser(macro, parser.stack.env,
  417. parser.configuration).mml());
  418. parser.i++;
  419. parser.Push(parser.itemFactory.create('auto open')
  420. .setProperties({open: '(', close: ')'}));
  421. };
  422. /**
  423. * The derivative macro. Its behaviour depends on the number of arguments
  424. * provided. In case of
  425. * 1 argument: will be part of the denominator.
  426. * 2 arguments: argument one is numerator, argument two is denominator.
  427. * 3+ arguments: arguments above 2 will be part of the denominator and the
  428. * exponent of the enumerator will depend on the number of denominator
  429. * arguments. In particular, the optional exponent argument will be ignored!
  430. * @param {TexParser} parser The calling parser.
  431. * @param {string} name The macro name.
  432. * @param {number} argMax The maximum number of arguments for the macro.
  433. * @param {string} op The derivative operator.
  434. */
  435. PhysicsMethods.Derivative = function(parser: TexParser, name: string,
  436. argMax: number, op: string) {
  437. const star = parser.GetStar();
  438. const optArg = parser.GetBrackets(name);
  439. let argCounter = 1;
  440. const args = [];
  441. args.push(parser.GetArgument(name));
  442. while (parser.GetNext() === '{' && argCounter < argMax) {
  443. args.push(parser.GetArgument(name));
  444. argCounter++;
  445. }
  446. let ignore = false;
  447. let power1 = ' ';
  448. let power2 = ' ';
  449. if (argMax > 2 && args.length > 2) {
  450. power1 = '^{' + (args.length - 1) + '}';
  451. ignore = true;
  452. } else if (optArg != null) {
  453. if (argMax > 2 && args.length > 1) {
  454. ignore = true;
  455. }
  456. power1 = '^{' + optArg + '}';
  457. power2 = power1;
  458. }
  459. const frac = star ? '\\flatfrac' : '\\frac';
  460. const first = args.length > 1 ? args[0] : '';
  461. const second = args.length > 1 ? args[1] : args[0];
  462. let rest = '';
  463. for (let i = 2, arg; arg = args[i]; i++) {
  464. rest += op + ' ' + arg;
  465. }
  466. const macro = frac + '{' + op + power1 + first + '}' +
  467. '{' + op + ' ' + second + power2 + ' ' + rest + '}';
  468. parser.Push(new TexParser(macro, parser.stack.env,
  469. parser.configuration).mml());
  470. if (parser.GetNext() === '(') {
  471. parser.i++;
  472. parser.Push(parser.itemFactory.create('auto open')
  473. .setProperties({open: '(', close: ')', ignore: ignore}));
  474. }
  475. };
  476. /***********************
  477. * Physics package section 2.6
  478. * Dirac bra-ket notation
  479. */
  480. /**
  481. * The bra macro.
  482. * @param {TexParser} parser The calling parser.
  483. * @param {string} name The macro name.
  484. */
  485. PhysicsMethods.Bra = function(parser: TexParser, name: string) {
  486. let starBra = parser.GetStar();
  487. let bra = parser.GetArgument(name);
  488. let ket = '';
  489. let hasKet = false;
  490. let starKet = false;
  491. if (parser.GetNext() === '\\') {
  492. let saveI = parser.i;
  493. parser.i++;
  494. // This ensures that bra-ket also works if \let bound versions of \ket.
  495. let cs = parser.GetCS();
  496. let symbol = parser.lookup('macro', cs) as Macro;
  497. if (symbol && symbol.symbol === 'ket') {
  498. hasKet = true;
  499. saveI = parser.i;
  500. starKet = parser.GetStar();
  501. if (parser.GetNext() === '{') {
  502. ket = parser.GetArgument(cs, true);
  503. } else {
  504. parser.i = saveI;
  505. starKet = false;
  506. }
  507. } else {
  508. parser.i = saveI;
  509. }
  510. }
  511. let macro = '';
  512. if (hasKet) {
  513. macro = (starBra || starKet) ?
  514. `\\langle{${bra}}\\vert{${ket}}\\rangle` :
  515. `\\left\\langle{${bra}}\\middle\\vert{${ket}}\\right\\rangle`;
  516. } else {
  517. macro = (starBra || starKet) ?
  518. `\\langle{${bra}}\\vert` : `\\left\\langle{${bra}}\\right\\vert{${ket}}`;
  519. }
  520. parser.Push(new TexParser(macro, parser.stack.env,
  521. parser.configuration).mml());
  522. };
  523. /**
  524. * The ket macro.
  525. * @param {TexParser} parser The calling parser.
  526. * @param {string} name The macro name.
  527. */
  528. PhysicsMethods.Ket = function(parser: TexParser, name: string) {
  529. let star = parser.GetStar();
  530. let ket = parser.GetArgument(name);
  531. let macro = star ? `\\vert{${ket}}\\rangle` :
  532. `\\left\\vert{${ket}}\\right\\rangle`;
  533. parser.Push(new TexParser(macro, parser.stack.env,
  534. parser.configuration).mml());
  535. };
  536. /**
  537. * The braket macro.
  538. * @param {TexParser} parser The calling parser.
  539. * @param {string} name The macro name.
  540. */
  541. PhysicsMethods.BraKet = function(parser: TexParser, name: string) {
  542. let star = parser.GetStar();
  543. let bra = parser.GetArgument(name);
  544. let ket = null;
  545. if (parser.GetNext() === '{') {
  546. ket = parser.GetArgument(name, true);
  547. }
  548. let macro = '';
  549. if (ket == null) {
  550. macro = star ?
  551. `\\langle{${bra}}\\vert{${bra}}\\rangle` :
  552. `\\left\\langle{${bra}}\\middle\\vert{${bra}}\\right\\rangle`;
  553. } else {
  554. macro = star ?
  555. `\\langle{${bra}}\\vert{${ket}}\\rangle` :
  556. `\\left\\langle{${bra}}\\middle\\vert{${ket}}\\right\\rangle`;
  557. }
  558. parser.Push(new TexParser(macro, parser.stack.env,
  559. parser.configuration).mml());
  560. };
  561. /**
  562. * The ketbra macro.
  563. * @param {TexParser} parser The calling parser.
  564. * @param {string} name The macro name.
  565. */
  566. PhysicsMethods.KetBra = function(parser: TexParser, name: string) {
  567. let star = parser.GetStar();
  568. let ket = parser.GetArgument(name);
  569. let bra = null;
  570. if (parser.GetNext() === '{') {
  571. bra = parser.GetArgument(name, true);
  572. }
  573. let macro = '';
  574. if (bra == null) {
  575. macro = star ?
  576. `\\vert{${ket}}\\rangle\\!\\langle{${ket}}\\vert` :
  577. `\\left\\vert{${ket}}\\middle\\rangle\\!\\middle\\langle{${ket}}\\right\\vert`;
  578. } else {
  579. macro = star ?
  580. `\\vert{${ket}}\\rangle\\!\\langle{${bra}}\\vert` :
  581. `\\left\\vert{${ket}}\\middle\\rangle\\!\\middle\\langle{${bra}}\\right\\vert`;
  582. }
  583. parser.Push(new TexParser(macro, parser.stack.env,
  584. parser.configuration).mml());
  585. };
  586. /**
  587. * Generates the expanded braket LaTeX code for matrix operations.
  588. * @param {[string, string, string]} [arg1, arg2, arg3] The three arguments
  589. * <arg1|arg2|arg3>.
  590. * @param {boolean} star1 No automatic sizing of fences.
  591. * @param {boolean} star2 Automatic sizing of fences wrt. to arg1 & arg3 only.
  592. */
  593. function outputBraket([arg1, arg2, arg3]: [string, string, string],
  594. star1: boolean, star2: boolean) {
  595. return (star1 && star2) ?
  596. `\\left\\langle{${arg1}}\\middle\\vert{${arg2}}\\middle\\vert{${arg3}}\\right\\rangle` :
  597. (star1 ? `\\langle{${arg1}}\\vert{${arg2}}\\vert{${arg3}}\\rangle` :
  598. `\\left\\langle{${arg1}}\\right\\vert{${arg2}}\\left\\vert{${arg3}}\\right\\rangle`);
  599. }
  600. /**
  601. * The expectation value macro.
  602. * @param {TexParser} parser The calling parser.
  603. * @param {string} name The macro name.
  604. */
  605. PhysicsMethods.Expectation = function(parser: TexParser, name: string) {
  606. let star1 = parser.GetStar();
  607. let star2 = star1 && parser.GetStar();
  608. let arg1 = parser.GetArgument(name);
  609. let arg2 = null;
  610. if (parser.GetNext() === '{') {
  611. arg2 = parser.GetArgument(name, true);
  612. }
  613. let macro = (arg1 && arg2) ?
  614. outputBraket([arg2, arg1, arg2], star1, star2) :
  615. // Braces for semantics, similar to braket package.
  616. (star1 ? `\\langle {${arg1}} \\rangle` :
  617. `\\left\\langle {${arg1}} \\right\\rangle`);
  618. parser.Push(new TexParser(macro, parser.stack.env,
  619. parser.configuration).mml());
  620. };
  621. /**
  622. * The matrix element macro.
  623. * @param {TexParser} parser The calling parser.
  624. * @param {string} name The macro name.
  625. */
  626. PhysicsMethods.MatrixElement = function(parser: TexParser, name: string) {
  627. const star1 = parser.GetStar();
  628. const star2 = star1 && parser.GetStar();
  629. const arg1 = parser.GetArgument(name);
  630. const arg2 = parser.GetArgument(name);
  631. const arg3 = parser.GetArgument(name);
  632. const macro = outputBraket([arg1, arg2, arg3], star1, star2);
  633. parser.Push(new TexParser(macro, parser.stack.env,
  634. parser.configuration).mml());
  635. };
  636. /********************
  637. * Physics package Section 2.7
  638. * Matrix macros
  639. */
  640. /**
  641. * The matrix quantity macro.
  642. * @param {TexParser} parser The calling parser.
  643. * @param {string} name The macro name.
  644. * @param {boolean=} small Use small matrix.
  645. */
  646. PhysicsMethods.MatrixQuantity = function(parser: TexParser, name: string, small?: boolean) {
  647. const star = parser.GetStar();
  648. const next = parser.GetNext();
  649. const array = small ? 'smallmatrix' : 'array';
  650. let arg = '';
  651. let open = '';
  652. let close = '';
  653. switch (next) {
  654. case '{':
  655. arg = parser.GetArgument(name);
  656. break;
  657. case '(':
  658. parser.i++;
  659. open = star ? '\\lgroup' : '(';
  660. close = star ? '\\rgroup' : ')';
  661. arg = parser.GetUpTo(name, ')');
  662. break;
  663. case '[':
  664. parser.i++;
  665. open = '[';
  666. close = ']';
  667. arg = parser.GetUpTo(name, ']');
  668. break;
  669. case '|':
  670. parser.i++;
  671. open = '|';
  672. close = '|';
  673. arg = parser.GetUpTo(name, '|');
  674. break;
  675. default:
  676. open = '(';
  677. close = ')';
  678. break;
  679. }
  680. const macro = (open ? '\\left' : '') + open +
  681. '\\begin{' + array + '}{} ' + arg + '\\end{' + array + '}' +
  682. (open ? '\\right' : '') + close;
  683. parser.Push(new TexParser(macro, parser.stack.env,
  684. parser.configuration).mml());
  685. };
  686. /**
  687. * Generation of identity matrices.
  688. * @param {TexParser} parser The calling parser.
  689. * @param {string} name The macro name.
  690. */
  691. PhysicsMethods.IdentityMatrix = function(parser: TexParser, name: string) {
  692. const arg = parser.GetArgument(name);
  693. const size = parseInt(arg, 10);
  694. if (isNaN(size)) {
  695. throw new TexError('InvalidNumber', 'Invalid number');
  696. }
  697. if (size <= 1) {
  698. parser.string = '1' + parser.string.slice(parser.i);
  699. parser.i = 0;
  700. return;
  701. }
  702. let zeros = Array(size).fill('0');
  703. let columns = [];
  704. for (let i = 0; i < size; i++) {
  705. let row = zeros.slice();
  706. row[i] = '1';
  707. columns.push(row.join(' & '));
  708. }
  709. parser.string = columns.join('\\\\ ') + parser.string.slice(parser.i);
  710. parser.i = 0;
  711. };
  712. /**
  713. * Generation of matrices with fixed value.
  714. * @param {TexParser} parser The calling parser.
  715. * @param {string} name The macro name.
  716. */
  717. PhysicsMethods.XMatrix = function(parser: TexParser, name: string) {
  718. const star = parser.GetStar();
  719. const arg1 = parser.GetArgument(name);
  720. const arg2 = parser.GetArgument(name);
  721. const arg3 = parser.GetArgument(name);
  722. let n = parseInt(arg2, 10);
  723. let m = parseInt(arg3, 10);
  724. if (isNaN(n) || isNaN(m) || m.toString() !== arg3 || n.toString() !== arg2) {
  725. throw new TexError('InvalidNumber', 'Invalid number');
  726. }
  727. n = n < 1 ? 1 : n;
  728. m = m < 1 ? 1 : m;
  729. // Elements
  730. if (!star) {
  731. const row = Array(m).fill(arg1).join(' & ');
  732. const matrix = Array(n).fill(row).join('\\\\ ');
  733. parser.string = matrix + parser.string.slice(parser.i);
  734. parser.i = 0;
  735. return;
  736. }
  737. let matrix = '';
  738. if (n === 1 && m === 1) {
  739. // Case 1: n=m=1, no index.
  740. matrix = arg1;
  741. } else if (n === 1) {
  742. // Case 2: n=1, row vector, single index.
  743. let row = [];
  744. for (let i = 1; i <= m; i++) {
  745. row.push(`${arg1}_{${i}}`);
  746. }
  747. matrix = row.join(' & ');
  748. } else if (m === 1) {
  749. // Case 3: m=1, column vector, single index.
  750. let row = [];
  751. for (let i = 1; i <= n; i++) {
  752. row.push(`${arg1}_{${i}}`);
  753. }
  754. matrix = row.join('\\\\ ');
  755. } else {
  756. // Case 4: matrix, double index. Note the extra mrows for indices.
  757. let rows = [];
  758. for (let i = 1; i <= n; i++) {
  759. let row = [];
  760. for (let j = 1; j <= m; j++) {
  761. row.push(`${arg1}_{{${i}}{${j}}}`);
  762. }
  763. rows.push(row.join(' & '));
  764. }
  765. matrix = rows.join('\\\\ ');
  766. }
  767. parser.string = matrix + parser.string.slice(parser.i);
  768. parser.i = 0;
  769. return;
  770. };
  771. /**
  772. * Generation of Pauli matrices. Matrix 0 is the 2x2 identity.
  773. * @param {TexParser} parser The calling parser.
  774. * @param {string} name The macro name.
  775. */
  776. PhysicsMethods.PauliMatrix = function(parser: TexParser, name: string) {
  777. const arg = parser.GetArgument(name);
  778. let matrix = arg.slice(1);
  779. switch (arg[0]) {
  780. case '0':
  781. matrix += ' 1 & 0\\\\ 0 & 1';
  782. break;
  783. case '1':
  784. case 'x':
  785. matrix += ' 0 & 1\\\\ 1 & 0';
  786. break;
  787. case '2':
  788. case 'y':
  789. matrix += ' 0 & -i\\\\ i & 0';
  790. break;
  791. case '3':
  792. case 'z':
  793. matrix += ' 1 & 0\\\\ 0 & -1';
  794. break;
  795. default:
  796. }
  797. parser.string = matrix + parser.string.slice(parser.i);
  798. parser.i = 0;
  799. };
  800. /**
  801. * Generation of anti/diagonal matrices.
  802. * @param {TexParser} parser The calling parser.
  803. * @param {string} name The macro name.
  804. * @param {boolean=} anti True if constructing anti-diagonal matrix.
  805. */
  806. PhysicsMethods.DiagonalMatrix = function(parser: TexParser, name: string,
  807. anti?: boolean) {
  808. if (parser.GetNext() !== '{') {
  809. return;
  810. }
  811. let startI = parser.i;
  812. /* let arg =*/ parser.GetArgument(name);
  813. let endI = parser.i;
  814. parser.i = startI + 1;
  815. let elements = [];
  816. let element = '';
  817. let currentI = parser.i;
  818. while (currentI < endI) {
  819. try {
  820. element = parser.GetUpTo(name, ',');
  821. } catch (e) {
  822. parser.i = endI;
  823. elements.push(parser.string.slice(currentI, endI - 1));
  824. break;
  825. }
  826. if (parser.i >= endI) {
  827. elements.push(parser.string.slice(currentI, endI));
  828. break;
  829. }
  830. currentI = parser.i;
  831. elements.push(element);
  832. }
  833. parser.string = makeDiagMatrix(elements, anti) + parser.string.slice(endI);
  834. parser.i = 0;
  835. };
  836. /**
  837. * Creates the a (anti)diagonal matrix string.
  838. * @param {string[]} elements The elements on the diagonal.
  839. * @param {boolean} anti True if constructing anti-diagonal matrix.
  840. */
  841. function makeDiagMatrix(elements: string[], anti: boolean) {
  842. let length = elements.length;
  843. let matrix = [];
  844. for (let i = 0; i < length; i++) {
  845. matrix.push(Array(anti ? length - i : i + 1).join('&') +
  846. '\\mqty{' + elements[i] + '}');
  847. }
  848. return matrix.join('\\\\ ');
  849. }
  850. /**
  851. * Closes an automatic fence if one was opened.
  852. * @param {TexParser} parser The calling parser.
  853. * @param {string} fence The fence.
  854. * @param {number} texclass The TeX class.
  855. */
  856. PhysicsMethods.AutoClose = function(parser: TexParser, fence: string, _texclass: number) {
  857. const mo = parser.create('token', 'mo', {stretchy: false}, fence);
  858. const item = parser.itemFactory.create('mml', mo).
  859. setProperties({autoclose: fence});
  860. parser.Push(item);
  861. };
  862. /**
  863. * Generates the vector nabla depending on the arrowdel option.
  864. * @param {TexParser} parser The calling parser.
  865. * @param {string} name The macro name.
  866. */
  867. PhysicsMethods.Vnabla = function(parser: TexParser, _name: string) {
  868. let argument = parser.options.physics.arrowdel ?
  869. '\\vec{\\gradientnabla}' : '{\\gradientnabla}';
  870. return parser.Push(new TexParser(argument, parser.stack.env,
  871. parser.configuration).mml());
  872. };
  873. /**
  874. * Generates the differential d depending on the italicdiff option.
  875. * @param {TexParser} parser The calling parser.
  876. * @param {string} name The macro name.
  877. */
  878. PhysicsMethods.DiffD = function(parser: TexParser, _name: string) {
  879. let argument = parser.options.physics.italicdiff ? 'd' : '{\\rm d}';
  880. return parser.Push(new TexParser(argument, parser.stack.env,
  881. parser.configuration).mml());
  882. };
  883. /**
  884. * Methods taken from Base package.
  885. */
  886. PhysicsMethods.Macro = BaseMethods.Macro;
  887. PhysicsMethods.NamedFn = BaseMethods.NamedFn;
  888. PhysicsMethods.Array = BaseMethods.Array;
  889. export default PhysicsMethods;