[சனி மார்ச் 16, 2024] Ihor Radchenko wrote:

> Visuwesh <visuwe...@gmail.com> writes:
>
>> Attached patch adds support for passing tables as matrices (or vector)
>> to a Calc source block through the :var parameter.  There might be a
>> better way to do it than manually construct the data structure expected
>> by Calc for a matrix but given that it rarely sees changes in this area,
>> it shouldn't be a bother.
>> I also added tests for this feature, and other simple tests to ensure
>> everything works.  I checked that all the tests passed on my side.
>
> Thanks!
> Since you are adding a new feature, may you also add an entry to etc/ORG-NEWS?

Now done.

>> -       (calc-push-list (list (cdr pair)))
>> +       (let ((val (cdr pair)))
>> +         (calc-push-list
>> +          (list (if (listp val)
>> +                    (cons 'vec
>> +                          (if (null (cdr val))
>> +                              (car val)
>> +                            (mapcar (lambda (x) (if (listp x) (cons 'vec x) 
>> x))
>> +                                    val)))
>> +                  val))))
>
> It would be nice to add code comments explaining the Calc's internal
> format. Ideally, with references to Calc's manual or source code.
> Otherwise, this code looks like black magic :)

I hope the comment I added in the attached patch is clear enough.

>> +++ b/testing/examples/ob-calc-test.org
>> @@ -0,0 +1,57 @@
>> +#+TITLE: Tests for ob-calc
>> +#+OPTIONS: ^:nil
>
> Thanks a lot for adding many tests!
> Would you mind using `org-test-with-temp-text' instead of
> `org-test-at-id' as much as possible? Otherwise, looking at tests like

OK, I wasn't sure what test style to use since the README in testing/
does not talk about writing new tests.  Now I have adapted the tests to
use org-test-with-temp-text.

>From 29962bc3ec33e1e25f83f153b681d49182368592 Mon Sep 17 00:00:00 2001
From: Visuwesh <visuwe...@gmail.com>
Date: Sat, 16 Mar 2024 17:04:14 +0530
Subject: [PATCH] ob-calc.el: Add support for tables in Calc source block :var

A table with MxN dimensions is converted to a MxN matrix when given in
:var to a Calc source block.  A table with a single row is converted
to a vector (i.e., row vector).

* lisp/ob-calc.el (org-babel-execute-src-block:calc): Construct the
right data structure to pass tables as matrices to Calc.
* testing/lisp/test-ob-calc.el: Add tests for ob-calc, and this new
feature.
* etc/ORG-NEWS: Announce the feature.
---
 etc/ORG-NEWS                 |   7 +++
 lisp/ob-calc.el              |  14 ++++-
 testing/lisp/test-ob-calc.el | 115 +++++++++++++++++++++++++++++++++++
 3 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 testing/lisp/test-ob-calc.el

diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index ca73f06e7..197d7503d 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -1121,6 +1121,13 @@ Maxima's graphics packages (~draw~ or ~plot~); the default remains
 ~plot~.  The graphics terminal is now determined from the file-ending
 of the file-name set in the ~:file~ header argument.
 
+*** =ob-calc.el=: Support for tables in ~:var~
+
+=ob-calc= now supports tables in ~:var~.  They are converted to a
+matrix or a vector depending on the dimensionality of the table.  A
+table with a single row is converted to a vector, the rest are
+converted to a matrix.
+
 *** Images and files in clipboard can be pasted
 
 Org asks the user what must be done when pasting images and files
diff --git a/lisp/ob-calc.el b/lisp/ob-calc.el
index f36df77ff..810ed1735 100644
--- a/lisp/ob-calc.el
+++ b/lisp/ob-calc.el
@@ -64,7 +64,19 @@
 	 (var-names (mapcar #'symbol-name org--var-syms)))
     (mapc
      (lambda (pair)
-       (calc-push-list (list (cdr pair)))
+       (let ((val (cdr pair)))
+         (calc-push-list
+          ;; For a vector, Calc follows the format (vec 1 2 3 ...)  so
+          ;; a matrix becomes (vec (vec 1 2 3) (vec 4 5 6) ...).  See
+          ;; the comments in "Arithmetic routines." section of
+          ;; calc.el.
+          (list (if (listp val)
+                    (cons 'vec
+                          (if (null (cdr val))
+                              (car val)
+                            (mapcar (lambda (x) (if (listp x) (cons 'vec x) x))
+                                    val)))
+                  val))))
        (calc-store-into (car pair)))
      vars)
     (mapc
diff --git a/testing/lisp/test-ob-calc.el b/testing/lisp/test-ob-calc.el
new file mode 100644
index 000000000..6d6ca104d
--- /dev/null
+++ b/testing/lisp/test-ob-calc.el
@@ -0,0 +1,115 @@
+;;; test-ob-calc.el --- tests for ob-calc.el         -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Visuwesh
+
+;; Author: Visuwesh <visuwe...@gmail.com>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+(require 'ob-calc)
+
+(unless (featurep 'ob-calc)
+  (signal 'missing-test-dependency "Support for Calc code blocks"))
+
+(ert-deftest ob-calc/simple-program-mult ()
+  "Test of simple multiplication."
+  (org-test-with-temp-text "\
+#+BEGIN_SRC calc :results silent
+	1 * 2
+#+END_SRC"
+    (should (equal "2" (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/simple-program-arith ()
+  "Test of simple arithmetic."
+  (org-test-with-temp-text "\
+#+BEGIN_SRC calc :results silent
+	12 + 16 - 1
+#+END_SRC"
+    (should (equal "27" (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/simple-program-symbolic ()
+  "Test of simple symbolic algebra."
+  (org-test-with-temp-text "\
+#+BEGIN_SRC calc :results silent
+	inv(a)
+#+END_SRC"
+    (should (equal "1 / a" (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/matrix-inversion ()
+  "Test of a matrix inversion."
+  (org-test-with-temp-text "\
+#+NAME: ob-calc-table-1
+| 1 |  2 |  3 |
+| 5 |  6 |  7 |
+| 9 | 14 | 11 |
+
+<point>#+BEGIN_SRC calc :results silent :var a=ob-calc-table-1
+	inv(a)
+#+END_SRC "
+    (should (equal "[[-1, 0.625, -0.125], [0.25, -0.5, 0.25], [0.5, 0.125, -0.125]]"
+                   (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/matrix-algebra ()
+  "Test of simple matrix algebra."
+  (org-test-with-temp-text "\
+#+NAME: ob-calc-table-2
+| 1 | 2 | 3 | 4 | 5 |
+
+<point>#+BEGIN_SRC calc :results silent :var a=ob-calc-table-2
+	a*2 - 2
+#+END_SRC"
+    (should (equal "[0, 2, 4, 6, 8]"
+                   (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/matrix-mean ()
+  "Test of simple mean of a vector."
+  (org-test-with-temp-text "\
+#+NAME: ob-calc-table-2
+| 1 | 2 | 3 | 4 | 5 |
+
+<point>#+BEGIN_SRC calc :results silent :var a=ob-calc-table-2
+	vmean(a)
+#+END_SRC"
+    (should (equal "3"
+                   (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/matrix-correct-conv-column ()
+  "Test of conversion of column table to Calc format."
+  (org-test-with-temp-text "\
+#+NAME: ob-calc-table-3
+| 1 |
+| 2 |
+| 3 |
+
+<point>#+BEGIN_SRC calc :results silent :var a=ob-calc-table-3
+	a
+#+END_SRC"
+    (should (equal "[[1], [2], [3]]"
+                   (org-babel-execute-src-block)))))
+
+(ert-deftest ob-calc/matrix-correct-conv-row ()
+  "Test of conversion of row table to Calc format."
+  (org-test-with-temp-text "\
+#+NAME: ob-calc-table-2
+| 1 | 2 | 3 | 4 | 5 |
+
+<point>#+BEGIN_SRC calc :results silent :var a=ob-calc-table-2
+	a
+#+END_SRC"
+    (should (equal "[1, 2, 3, 4, 5]"
+                   (org-babel-execute-src-block)))))
+
+(provide 'test-ob-calc)
+;;; test-ob-calc.el ends here
-- 
2.43.0

Reply via email to