unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: zacromero@posteo.net
To: Stefan Kangas <stefankangas@gmail.com>
Cc: 68232@debbugs.gnu.org, Lars Magne Ingebrigtsen <larsi@gnus.org>
Subject: bug#68232: [PATCH] Fix range-intersection implementation
Date: Fri, 12 Jan 2024 02:44:29 +0000	[thread overview]
Message-ID: <b8439c43400c963a433f8ed03d2a8bee@posteo.com> (raw)
In-Reply-To: <CADwFkmkviGnBEb2WJD9tnQLFQfnECZA_EYJebLH+FmH1qGR2mg@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1213 bytes --]

Thanks for pointing me to the tests. Attached is an updated patch with 
the added test cases and the range tests all pass.

On 11.01.2024 21:56, Stefan Kangas wrote:
> Zachary Romero <zacromero@posteo.net> writes:
> 
>> Hello Emacs maintainers,
>> 
>> I was using the range package when I encountered a bug in the
>> implementation of range-intersection.  The bug seems to occur when the
>> ranges involve a mix of integers and cons pairs.  The following are 
>> some
>> cases where the current implementation fails to compute the correct
>> intersection:
>> 
>> (range-intersection '((1 . 10) 11) '((11 . 12)))
>> ;; Expects (11), returns nil
>> 
>> (range-intersection '(11 (13 . 15)) '((13 . 15)))
>> ;; Expects (13 . 15), returns nil
>> 
>> (range-intersection '(1 11 13 15) '((1 . 2) (10 . 20)))
>> ;; Expects (1 11 13 15), returns (1)
>> 
>> 
>> I also refactored this function using pcase to try to make the steps 
>> of
>> the algorithm more understandable.
>> 
>> Let me know you thoughts and if there's any further changes I should
>> make.
> 
> Thanks for the patch.  Could you please add tests for this as well?
> See the file range-tests.el.
> 
> Did you check that the existing tests all still pass?

[-- Attachment #2: 0001-fix-range-intersection-edge-cases.patch --]
[-- Type: application/octet-stream, Size: 5286 bytes --]

From 1beecd1296ff252b41df12d7c68dfb91b50341f2 Mon Sep 17 00:00:00 2001
From: Zachary Romero <zacromero@posteo.net>
Date: Thu, 11 Jan 2024 19:36:36 -0700
Subject: [PATCH] fix range intersection edge cases

---
 lisp/emacs-lisp/range.el            | 81 ++++++++++++++---------------
 test/lisp/emacs-lisp/range-tests.el | 14 +++++
 2 files changed, 52 insertions(+), 43 deletions(-)

diff --git a/lisp/emacs-lisp/range.el b/lisp/emacs-lisp/range.el
index 19a6da34acb..f27649146d6 100644
--- a/lisp/emacs-lisp/range.el
+++ b/lisp/emacs-lisp/range.el
@@ -89,49 +89,44 @@ range-difference
 
 (defun range-intersection (range1 range2)
   "Return intersection of RANGE1 and RANGE2."
-  (let* (out
-         (min1 (car range1))
-         (max1 (if (numberp min1)
-                   (if (numberp (cdr range1))
-                       (prog1 (cdr range1)
-                         (setq range1 nil)) min1)
-                 (prog1 (cdr min1)
-                   (setq min1 (car min1)))))
-         (min2 (car range2))
-         (max2 (if (numberp min2)
-                   (if (numberp (cdr range2))
-                       (prog1 (cdr range2)
-                         (setq range2 nil)) min2)
-                 (prog1 (cdr min2)
-                   (setq min2 (car min2))))))
-    (setq range1 (cdr range1)
-          range2 (cdr range2))
-    (while (and min1 min2)
-      (cond ((< max1 min2)              ; range1 precedes range2
-             (setq range1 (cdr range1)
-                   min1 nil))
-            ((< max2 min1)              ; range2 precedes range1
-             (setq range2 (cdr range2)
-                   min2 nil))
-            (t                     ; some sort of overlap is occurring
-             (let ((min (max min1 min2))
-                   (max (min max1 max2)))
-               (setq out (if (= min max)
-                             (cons min out)
-                           (cons (cons min max) out))))
-             (if (< max1 max2)          ; range1 ends before range2
-                 (setq min1 nil)        ; incr range1
-               (setq min2 nil))))       ; incr range2
-      (unless min1
-        (setq min1 (car range1)
-              max1 (if (numberp min1) min1
-                     (prog1 (cdr min1) (setq min1 (car min1))))
-              range1 (cdr range1)))
-      (unless min2
-        (setq min2 (car range2)
-              max2 (if (numberp min2) min2
-                     (prog1 (cdr min2) (setq min2 (car min2))))
-              range2 (cdr range2))))
+  (let ((out))
+    (while (and range1 range2)
+      (let* ((elt1 (car range1))
+             (elt2 (car range2)))
+        (pcase (list elt1 elt2)
+          (`((,min1 . ,max1) (,min2 . ,max2))
+           (let ((min (max min1 min2))
+                 (max (min max1 max2)))
+             (cond
+              ((< min max)
+               (setq out (cons (cons min max) out)))
+              ((= min max)
+               (setq out (cons min out))))
+             (if (< max1 max2)
+                 (setq range1 (cdr range1))
+               (setq range2 (cdr range2)))))
+          ((and `(,num1 (,min2 . ,max2))
+                (guard (numberp num1)))
+           (when (<= min2 num1 max2)
+             (setq out (cons num1 out)))
+           (if (< max2 num1)
+               (setq range2 (cdr range2))
+             (setq range1 (cdr range1))))
+          ((and `((,min1 . ,max1) ,num2)
+                (guard (numberp num2)))
+           (when (<= min1 num2 max1)
+             (setq out (cons num2 out)))
+           (if (< max1 num2)
+               (setq range1 (cdr range1))
+             (setq range2 (cdr range2))))
+          ((and `(,num1 ,num2)
+                (guard (and (numberp num1)
+                            (numberp num2))))
+           (when (= num1 num2)
+             (setq out (cons num1 out)))
+           (if (< num1 num2)
+               (setq range1 (cdr range1))
+             (setq range2 (cdr range2)))))))
     (cond ((cdr out)
            (nreverse out))
           ((numberp (car out))
diff --git a/test/lisp/emacs-lisp/range-tests.el b/test/lisp/emacs-lisp/range-tests.el
index c680ab5a9cd..dbfce0d8ddf 100644
--- a/test/lisp/emacs-lisp/range-tests.el
+++ b/test/lisp/emacs-lisp/range-tests.el
@@ -40,6 +40,20 @@ ranges
   (should (equal (range-intersection '((2 . 5) 9 (11 . 13))
                                      '((5 . 12)))
                  '(5 9 (11 . 12))))
+  (should (equal (range-intersection '(1 11 13 15)
+                                     '((1 . 2) (10 . 20)))
+                 '(1 11 13 15)))
+  (should (equal (range-intersection '(11 (13 . 15))
+                                     '((13 . 15)))
+                 '(13 . 15)))
+  (should (equal (range-intersection '((1 . 10) 11) '((11 . 12)))
+                 '(11)))
+  (should (equal (range-intersection '((2 . 5) 9 (11 . 13))
+                                     '((2 . 13)))
+                 '((2 . 5) 9 (11 . 13))))
+  (should (equal (range-intersection '((11 . 13))
+                                     '((2 . 10)))
+                 nil))
   (should (equal (range-add-list '((2 . 5) 9 (11 . 13))
                                  '(10 11 12 15 16 17))
                  '((2 . 5) (9 . 10) (11 . 13) (15 . 17))))
-- 
2.37.0 (Apple Git-136)


      reply	other threads:[~2024-01-12  2:44 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-03  3:30 bug#68232: [PATCH] Fix range-intersection implementation Zachary Romero
2024-01-11 20:56 ` Stefan Kangas
2024-01-12  2:44   ` zacromero [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b8439c43400c963a433f8ed03d2a8bee@posteo.com \
    --to=zacromero@posteo.net \
    --cc=68232@debbugs.gnu.org \
    --cc=larsi@gnus.org \
    --cc=stefankangas@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).