Subject: 25.2; [PATCH] Add cl-set-equal to test for set equality

From: Damien Cassou <[email-masked]>
Date: Mon, 17 Apr 2017 11:16:10 +0200

This patch adds cl-seq-equal to test whether two lists have the 
same elements. I.e., if every element of LIST1 also appears in 
LIST2 and if every element of LIST2 also appears in LIST1. 

-- 
Damien Cassou
http://damiencassou.seasidehosting.st

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill
>From 2f00c34ecec39c5c90e6c3ef2f5ab40fa60979e9 Mon Sep 17 00:00:00 2001
From: Damien Cassou <[email-masked]>
Date: Mon, 17 Apr 2017 11:01:39 +0200
Subject: [PATCH] Add cl-set-equal to test for set equality

* lisp/emacs-lisp/cl-seq.el (cl-set-equal): Add function to compare
  two lists as if they were sets.

* test/lisp/emacs-lisp/cl-seq-tests.el (cl-set-equal): Add test for
  cl-set-equal.
---
 doc/misc/cl.texi                     |  6 ++++++
 etc/NEWS                             |  3 +++
 lisp/emacs-lisp/cl-seq.el            | 10 ++++++++++
 test/lisp/emacs-lisp/cl-seq-tests.el | 16 ++++++++++++++++
 4 files changed, 35 insertions(+)

diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi
index 2339d57..aa64ae2 100644
--- a/doc/misc/cl.texi
+++ b/doc/misc/cl.texi
@@ -3917,6 +3917,12 @@ Lists as Sets
 also appears in @var{list2}.
 @end defun
 
+@defun cl-set-equal list1 list2 @t{&key :test :key}
+This function checks whether every element of @var{list1} also appears
+in @var{list2} and if every element of @var{list2} also appears in
+@var{list1}.
+@end defun
+
 @node Association Lists
 @section Association Lists
 
diff --git a/etc/NEWS b/etc/NEWS
index 76c9dbc..e22a440 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -874,6 +874,9 @@ instead of its first.
 
 * Lisp Changes in Emacs 26.1
 
+** New function 'cl-set-equal' to check if every element of LIST1 also
+appears in LIST2 and if every element of LIST2 also appears in LIST1.
+
 +++
 ** Emacs now supports records for user-defined types, via the new
 functions 'make-record', 'record', and 'recordp'.  Records are now
diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index 67ff1a0..db4a145 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -923,6 +923,16 @@ cl-subsetp
 	     (null cl-list1)))))
 
 ;;;###autoload
+(defun cl-set-equal (cl-list1 cl-list2 &rest cl-keys)
+  "Return true if LIST1 and LIST2 have same elements.
+I.e., if every element of LIST1 also appears in LIST2 and if
+every element of LIST2 also appears in LIST1.
+\nKeywords supported: :test :key \n(fn LIST1 LIST2
+[KEYWORD VALUE]...)"
+  (and (apply 'cl-subsetp cl-list1 cl-list2 cl-keys)
+       (apply 'cl-subsetp cl-list2 cl-list1 cl-keys)))
+
+;;;###autoload
 (defun cl-subst-if (cl-new cl-pred cl-tree &rest cl-keys)
   "Substitute NEW for elements matching PREDICATE in TREE (non-destructively).
 Return a copy of TREE with all matching elements replaced by NEW.
diff --git a/test/lisp/emacs-lisp/cl-seq-tests.el b/test/lisp/emacs-lisp/cl-seq-tests.el
index 61e3d72..0347ca4 100644
--- a/test/lisp/emacs-lisp/cl-seq-tests.el
+++ b/test/lisp/emacs-lisp/cl-seq-tests.el
@@ -292,6 +292,22 @@ cl-seq--with-side-effects
     (should (= 1 (cl-search (nthcdr 2 list) (nthcdr 2 list2))))
     (should (= 3 (cl-search (nthcdr 2 list) list2)))))
 
+;; keywords supported: :test :key
+(ert-deftest cl-set-equal ()
+  (should     (cl-set-equal '(1 2 3) '(1 2 3)))
+  (should     (cl-set-equal '(1 2 3) '(3 2 1)))
+  (should     (cl-set-equal '(3 2 1) '(1 2 3)))
+  (should-not (cl-set-equal '(2 3) '(3 2 1)))
+  (should-not (cl-set-equal '(1 2 3) '(2 3)))
+  (should-not (cl-set-equal '("1" "2") '("2" "1") :test #'eq))
+  (should     (cl-set-equal '("1" "2") '("2" "1") :test #'equal))
+  (should-not (cl-set-equal '(1 2) '(-1 -2)))
+  (should     (cl-set-equal '(1 2) '(-1 -2) :key #'abs))
+  (should-not (cl-set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2))))
+  (should-not (cl-set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2)) :key #'car))
+  (should-not (cl-set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2)) :test #'equal))
+  (should     (cl-set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2)) :key #'car :test #'equal)))
+
 (ert-deftest cl-seq-test-bug24264 ()
   "Test for http://debbugs.gnu.org/24264 ."
   (let ((list  (append (make-list 8000005 1) '(8)))
-- 
2.9.3