# How do I convert a matrix in one form to a matrix in another form (like bmatrix environment)

I'm attempting to take faster linear algebra notes within org-mode. The latex of a matrix is complex to write, so I'm trying to develop a shorthand expansion.

My current workflow is to highlight a shorthand and run M-x latex-matrixify. Unfortunately, it seems to complain:

let: Invalid search bound (wrong side of point)


I believe because I'm adding things into the region that's selected, the bounds are not being updated to contain all of the new information.

Here's the current code:

(defun latex-matrixify ()
"turn a shorthand matrix of [2,3,4|2\\9,2,1|9] into the proper latex form"
(interactive)
(if (not (use-region-p))
(message "You have to select a region to operate on.")
(let ((pairs (list
(cons "]" "\\end{bmatrix}")
(cons "[" "\\begin{bmatrix}")
(cons "," " & ")
(cons "\\" "\\
")
(cons "|" " & | &")
)))
(dolist (p pairs)
(let ((beg   (save-excursion (goto-char (region-beginning))
(line-beginning-position)))
(end   (save-excursion (goto-char (region-end)) (line-end-position))))

(replace-string-in-region (car p) (cdr p) beg end))))))


It should turn something like:

[1,2,3\\4,5,6\\7,8,9] into

\begin{bmatrix}
1 & 2 & 3\\
4 & 5 & 6\\
7 & 8 & 9\\
\end{bmatrix}


I'm not seeing a lot of details on that error.

• I suspect your code has the same bug as the code in this question: as you are replacing, the end-of-buffer position changes, but you are using the same position and sometimes you end up pointing beyond the end of the buffer. If that answer fixes this problem, we can mark this question as a duplicate. Commented Oct 20, 2022 at 11:40
• You would be better off building on what people already have bullt ... LaTeX Input for Impatient Scholars. The second video in the article is captioned Matrices in a hurry.
– user31220
Commented Oct 23, 2022 at 7:37

EDIT

There is/was a bug in the replace-string-in-region function. Using the newest version of that function, your code works alright, except for some unwanted newlines due to the first backslash in the double backslash functioning as an escape character. To fix it, just replace the double backslashes \\ by quadruple backslashes \\\\

END EDIT

It looks like you have encountered a bug in the replace-string-in-region. If you closely look at the code of that function then you will find that the end argument passed to the search-forward function, does not move while the 'original region' expands (due to replacing a short string with a longer string).

Of course, I did not find this error by reading the source directly, but I was using edebug to investigate the behavior (I strongly advise you to read about edebug, as it is very simple to use, while it is a very powerful tool).

The error you are seeing seems to be triggered because, due to this bug, the position of point is after the position passed as end to the search-forward function.

I guess to make your code work, you would have to fix the replace-string-in-region to update the position passed as end to the search-forward function, or rewrite your algorithm otherwise. Luckily, it looks like fixing that replace-string-in-region function should not be too difficult.

It would be great if subsequently you could contribute a patch with the fix (or otherwise create a bug report).

• It looks like the replace-string-in-region function has been changed a few times in the last year(s), so I am not sure how exactly the version you are using looks like. I guess the explanation is relevant for most versions, but it would probably be wise to create the patch against the latest version (or just report the bug otherwise). Commented Oct 20, 2022 at 7:32
• Actually, it looks like the function already has been fixed on 2022-09-11, so you should probably just obtain that fixed function from the latest Emacs source code. Commented Oct 20, 2022 at 7:48
• It's probably NOT a replace-string-in-region bug: see my comment. Commented Oct 20, 2022 at 11:40
• @NickD It probably really is a replace-string-in-region, as I have edebugged the function with a 'simple testcase', i.e. using (replace-string-in-region "," " &" 1 22) in a buffer containing only the 'example matrix', where the bug was easy to see. Furthermore (I forgot to mention), when simply using the latest version of that function, then the latex-matrixify function produces the correct result (except for some misplaced newlines). Finally, here is a link to the fix... Commented Oct 20, 2022 at 12:51
• I could not easily find the bug report, but I have now found that it is just mentioned in that commit message. So finally here also is the original bug report. Commented Oct 20, 2022 at 12:55

I suggest you use Calc. You can set language mode to latex, temporarily by opening a Calc session, or permanently by saving the configuration in the calc.el file.

Enable calc-embedded when the point is on this formula

 [1,2,3\\4,5,6\\7,8,9]


results in

 \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix}


You must exit calc-embedded-mode to continue editing.

Just search and replace pmatrix with bmatrix.

A keyboard macro should do the job quickly.

There is really no need to write any elisp.

The name-last-kbd-macro and insert-kbd-macro will write the elisp for you.

1. emacs -Q (-Q here is important)
2. Copy the following to *scratch* buffer
 [1,2,3\\4,5,6\\7,8,9]

1. Install the following snippet
(defalias 'latex-matrixify
(kbd
"C-SPC C-M-f C-x n n ! C-SPC C-SPC
<escape> < M-^ M-% [ <return> \\ b e g i n { b m a t r i x } C-q C-j <return> !
<escape> < M-% , <return> SPC & SPC <return> !
<escape> < M-% \\ \\ <return> \\ \\ C-q C-j <return> !
<escape> < M-% ] <return> C-q C-j \\ e n d { b m a t r x <backspace> i x } C-q C-j <return> !
<escape> < C-x n w"
))


1. Put the cursor just before the [ in the math fragment
2. Do M-x latex-matrixify

name-last-kbd-macro is an alias for kmacro-name-last-macro in loaddefs.el.


(name-last-kbd-macro SYMBOL)


Assign a name to the last keyboard macro defined. Argument SYMBOL is the name to define. The symbols function definition becomes the keyboard macro string. Such a "function" cannot be called from Lisp, but it is a valid editor command.

insert-kbd-macro is an autoloaded interactive Lisp function in macros.el.


(insert-kbd-macro MACRONAME &optional KEYS)


Insert in buffer the definition of kbd macro MACRONAME, as Lisp code. MACRONAME should be a symbol. Optional second arg KEYS means also record the keys it is on (this is the prefix argument, when calling interactively).

This Lisp code will, when executed, define the kbd macro with the same definition it has now. If you say to record the keys, the Lisp code will also rebind those keys to the macro. Only global key bindings are recorded since executing this Lisp code always makes global bindings.

To save a kbd macro, visit a file of Lisp code such as your ~/.emacs`, use this command, and then save the file.

Probably introduced at or before Emacs version 18.