diff options
| author | Mattias EngdegÄrd | 2024-03-10 13:18:22 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-03-29 11:39:38 +0100 |
| commit | 1232ab31c656b8564984a758957466f90ac10501 (patch) | |
| tree | 38a7774207a5ac8dba2612bef9a6a39f3cd0d658 | |
| parent | c3684b97885c5a1f4d0713ff45c7395e9a4c6e8a (diff) | |
| download | emacs-1232ab31c656b8564984a758957466f90ac10501.tar.gz emacs-1232ab31c656b8564984a758957466f90ac10501.zip | |
Add `value<` (bug#69709)
It's a general-purpose polymorphic ordering function, like `<` but
for any two values of the same type.
* src/data.c (syms_of_data): Add the `type-mismatch` error.
(bits_word_to_host_endian): Move...
* src/lisp.h (bits_word_to_host_endian): ...here, and declare inline.
* src/fns.c (Fstring_lessp): Extract the bulk of this function to...
(string_cmp): ...this 3-way comparison function, for use elsewhere.
(bool_vector_cmp, value_cmp, Fvaluelt): New.
* lisp/emacs-lisp/byte-opt.el (side-effect-free-fns, pure-fns):
Add `value<`, which is pure and side-effect-free.
* test/src/fns-tests.el (fns-value<-ordered, fns-value<-unordered)
(fns-value<-type-mismatch, fns-value<-symbol-with-pos)
(fns-value<-circle, ert-deftest fns-value<-bool-vector): New tests.
* doc/lispref/sequences.texi (Sequence Functions):
* doc/lispref/numbers.texi (Comparison of Numbers):
* doc/lispref/strings.texi (Text Comparison):
Document the new value< function.
* etc/NEWS: Announce.
| -rw-r--r-- | doc/lispref/numbers.texi | 1 | ||||
| -rw-r--r-- | doc/lispref/sequences.texi | 35 | ||||
| -rw-r--r-- | doc/lispref/strings.texi | 1 | ||||
| -rw-r--r-- | etc/NEWS | 10 | ||||
| -rw-r--r-- | lisp/emacs-lisp/byte-opt.el | 4 | ||||
| -rw-r--r-- | src/data.c | 26 | ||||
| -rw-r--r-- | src/fns.c | 280 | ||||
| -rw-r--r-- | src/lisp.h | 24 | ||||
| -rw-r--r-- | test/src/fns-tests.el | 218 |
9 files changed, 552 insertions, 47 deletions
diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 99b456043b9..2c093ccd6bd 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi | |||
| @@ -476,6 +476,7 @@ This function tests whether its arguments are numerically equal, and | |||
| 476 | returns @code{t} if they are not, and @code{nil} if they are. | 476 | returns @code{t} if they are not, and @code{nil} if they are. |
| 477 | @end defun | 477 | @end defun |
| 478 | 478 | ||
| 479 | @anchor{definition of <} | ||
| 479 | @defun < number-or-marker &rest number-or-markers | 480 | @defun < number-or-marker &rest number-or-markers |
| 480 | This function tests whether each argument is strictly less than the | 481 | This function tests whether each argument is strictly less than the |
| 481 | following argument. It returns @code{t} if so, @code{nil} otherwise. | 482 | following argument. It returns @code{t} if so, @code{nil} otherwise. |
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 74719d4779f..5bdf71fe02e 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi | |||
| @@ -436,6 +436,41 @@ but their relative order is also preserved: | |||
| 436 | @end example | 436 | @end example |
| 437 | @end defun | 437 | @end defun |
| 438 | 438 | ||
| 439 | @cindex comparing values | ||
| 440 | @cindex standard sorting order | ||
| 441 | @defun value< a b | ||
| 442 | This function returns non-@code{nil} if @var{a} comes before @var{b} in | ||
| 443 | the standard sorting order; this means that it returns @code{nil} when | ||
| 444 | @var{b} comes before @var{a}, or if they are equal or unordered. | ||
| 445 | |||
| 446 | @var{a} and @var{b} must have the same type. Specifically: | ||
| 447 | |||
| 448 | @itemize @bullet | ||
| 449 | @item | ||
| 450 | Numbers are compared using @code{<} (@pxref{definition of <}). | ||
| 451 | @item | ||
| 452 | Strings and symbols are compared using @code{string<} | ||
| 453 | (@pxref{definition of string<}). | ||
| 454 | @item | ||
| 455 | Conses, lists, vectors and records are compared lexicographically. | ||
| 456 | @item | ||
| 457 | Markers are compared first by buffer, then by position. | ||
| 458 | @item | ||
| 459 | Buffers and processes are compared by name. | ||
| 460 | @item | ||
| 461 | Other types are considered unordered and the return value will be @code{nil}. | ||
| 462 | @end itemize | ||
| 463 | |||
| 464 | Examples: | ||
| 465 | @example | ||
| 466 | (value< -4 3.5) @result{} t | ||
| 467 | (value< "dog" "cat") @result{} nil | ||
| 468 | (value< 'yip 'yip) @result{} nil | ||
| 469 | (value< '(3 2) '(3 2 0)) @result{} t | ||
| 470 | (value< [3 2 1] [3 2 0]) @result{} nil | ||
| 471 | @end example | ||
| 472 | @end defun | ||
| 473 | |||
| 439 | Sometimes, computation of sort keys of list or vector elements is | 474 | Sometimes, computation of sort keys of list or vector elements is |
| 440 | expensive, and therefore it is important to perform it the minimum | 475 | expensive, and therefore it is important to perform it the minimum |
| 441 | number of times. By contrast, computing the sort keys of elements | 476 | number of times. By contrast, computing the sort keys of elements |
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index a2285098aad..6a9dd589237 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi | |||
| @@ -612,6 +612,7 @@ that collation implements. | |||
| 612 | @end defun | 612 | @end defun |
| 613 | 613 | ||
| 614 | @cindex lexical comparison of strings | 614 | @cindex lexical comparison of strings |
| 615 | @anchor{definition of string<} | ||
| 615 | @defun string< string1 string2 | 616 | @defun string< string1 string2 |
| 616 | @c (findex string< causes problems for permuted index!!) | 617 | @c (findex string< causes problems for permuted index!!) |
| 617 | This function compares two strings a character at a time. It | 618 | This function compares two strings a character at a time. It |
| @@ -1760,6 +1760,16 @@ precedence over the variable when present. | |||
| 1760 | Mostly used internally to do a kind of topological sort of | 1760 | Mostly used internally to do a kind of topological sort of |
| 1761 | inheritance hierarchies. | 1761 | inheritance hierarchies. |
| 1762 | 1762 | ||
| 1763 | +++ | ||
| 1764 | ** New polymorphic comparison function 'value<'. | ||
| 1765 | This function returns non-nil if the first argument is less than the | ||
| 1766 | second. It works for any two values of the same type with reasonable | ||
| 1767 | ordering for numbers, strings, symbols, bool-vectors, markers, buffers | ||
| 1768 | and processes. Conses, lists, vectors and records are ordered | ||
| 1769 | lexicographically. | ||
| 1770 | It is intended as a convenient ordering predicate for sorting, and is | ||
| 1771 | likely to be faster than hand-written Lisp functions. | ||
| 1772 | |||
| 1763 | ** New function 'sort-on'. | 1773 | ** New function 'sort-on'. |
| 1764 | This function implements the Schwartzian transform, and is appropriate | 1774 | This function implements the Schwartzian transform, and is appropriate |
| 1765 | for sorting lists when the computation of the sort key of a list | 1775 | for sorting lists when the computation of the sort key of a list |
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 54997205edb..ea163723a3e 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el | |||
| @@ -1772,7 +1772,7 @@ See Info node `(elisp) Integer Basics'." | |||
| 1772 | string-version-lessp | 1772 | string-version-lessp |
| 1773 | substring substring-no-properties | 1773 | substring substring-no-properties |
| 1774 | sxhash-eq sxhash-eql sxhash-equal sxhash-equal-including-properties | 1774 | sxhash-eq sxhash-eql sxhash-equal sxhash-equal-including-properties |
| 1775 | take vconcat | 1775 | take value< vconcat |
| 1776 | ;; frame.c | 1776 | ;; frame.c |
| 1777 | frame-ancestor-p frame-bottom-divider-width frame-char-height | 1777 | frame-ancestor-p frame-bottom-divider-width frame-char-height |
| 1778 | frame-char-width frame-child-frame-border-width frame-focus | 1778 | frame-char-width frame-child-frame-border-width frame-focus |
| @@ -1973,7 +1973,7 @@ See Info node `(elisp) Integer Basics'." | |||
| 1973 | hash-table-p identity length length< length= | 1973 | hash-table-p identity length length< length= |
| 1974 | length> member memq memql nth nthcdr proper-list-p rassoc rassq | 1974 | length> member memq memql nth nthcdr proper-list-p rassoc rassq |
| 1975 | safe-length string-bytes string-distance string-equal string-lessp | 1975 | safe-length string-bytes string-distance string-equal string-lessp |
| 1976 | string-search string-version-lessp take | 1976 | string-search string-version-lessp take value< |
| 1977 | ;; search.c | 1977 | ;; search.c |
| 1978 | regexp-quote | 1978 | regexp-quote |
| 1979 | ;; syntax.c | 1979 | ;; syntax.c |
diff --git a/src/data.c b/src/data.c index 69b990bed76..a86f86c52f5 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -3835,30 +3835,6 @@ count_trailing_zero_bits (bits_word val) | |||
| 3835 | } | 3835 | } |
| 3836 | } | 3836 | } |
| 3837 | 3837 | ||
| 3838 | static bits_word | ||
| 3839 | bits_word_to_host_endian (bits_word val) | ||
| 3840 | { | ||
| 3841 | #ifndef WORDS_BIGENDIAN | ||
| 3842 | return val; | ||
| 3843 | #else | ||
| 3844 | if (BITS_WORD_MAX >> 31 == 1) | ||
| 3845 | return bswap_32 (val); | ||
| 3846 | if (BITS_WORD_MAX >> 31 >> 31 >> 1 == 1) | ||
| 3847 | return bswap_64 (val); | ||
| 3848 | { | ||
| 3849 | int i; | ||
| 3850 | bits_word r = 0; | ||
| 3851 | for (i = 0; i < sizeof val; i++) | ||
| 3852 | { | ||
| 3853 | r = ((r << 1 << (CHAR_BIT - 1)) | ||
| 3854 | | (val & ((1u << 1 << (CHAR_BIT - 1)) - 1))); | ||
| 3855 | val = val >> 1 >> (CHAR_BIT - 1); | ||
| 3856 | } | ||
| 3857 | return r; | ||
| 3858 | } | ||
| 3859 | #endif | ||
| 3860 | } | ||
| 3861 | |||
| 3862 | DEFUN ("bool-vector-exclusive-or", Fbool_vector_exclusive_or, | 3838 | DEFUN ("bool-vector-exclusive-or", Fbool_vector_exclusive_or, |
| 3863 | Sbool_vector_exclusive_or, 2, 3, 0, | 3839 | Sbool_vector_exclusive_or, 2, 3, 0, |
| 3864 | doc: /* Return A ^ B, bitwise exclusive or. | 3840 | doc: /* Return A ^ B, bitwise exclusive or. |
| @@ -4072,6 +4048,7 @@ syms_of_data (void) | |||
| 4072 | DEFSYM (Qminibuffer_quit, "minibuffer-quit"); | 4048 | DEFSYM (Qminibuffer_quit, "minibuffer-quit"); |
| 4073 | DEFSYM (Qwrong_length_argument, "wrong-length-argument"); | 4049 | DEFSYM (Qwrong_length_argument, "wrong-length-argument"); |
| 4074 | DEFSYM (Qwrong_type_argument, "wrong-type-argument"); | 4050 | DEFSYM (Qwrong_type_argument, "wrong-type-argument"); |
| 4051 | DEFSYM (Qtype_mismatch, "type-mismatch") | ||
| 4075 | DEFSYM (Qargs_out_of_range, "args-out-of-range"); | 4052 | DEFSYM (Qargs_out_of_range, "args-out-of-range"); |
| 4076 | DEFSYM (Qvoid_function, "void-function"); | 4053 | DEFSYM (Qvoid_function, "void-function"); |
| 4077 | DEFSYM (Qcyclic_function_indirection, "cyclic-function-indirection"); | 4054 | DEFSYM (Qcyclic_function_indirection, "cyclic-function-indirection"); |
| @@ -4163,6 +4140,7 @@ syms_of_data (void) | |||
| 4163 | PUT_ERROR (Quser_error, error_tail, ""); | 4140 | PUT_ERROR (Quser_error, error_tail, ""); |
| 4164 | PUT_ERROR (Qwrong_length_argument, error_tail, "Wrong length argument"); | 4141 | PUT_ERROR (Qwrong_length_argument, error_tail, "Wrong length argument"); |
| 4165 | PUT_ERROR (Qwrong_type_argument, error_tail, "Wrong type argument"); | 4142 | PUT_ERROR (Qwrong_type_argument, error_tail, "Wrong type argument"); |
| 4143 | PUT_ERROR (Qtype_mismatch, error_tail, "Types do not match"); | ||
| 4166 | PUT_ERROR (Qargs_out_of_range, error_tail, "Args out of range"); | 4144 | PUT_ERROR (Qargs_out_of_range, error_tail, "Args out of range"); |
| 4167 | PUT_ERROR (Qvoid_function, error_tail, | 4145 | PUT_ERROR (Qvoid_function, error_tail, |
| 4168 | "Symbol's function definition is void"); | 4146 | "Symbol's function definition is void"); |
| @@ -27,6 +27,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 27 | #include <vla.h> | 27 | #include <vla.h> |
| 28 | #include <errno.h> | 28 | #include <errno.h> |
| 29 | #include <ctype.h> | 29 | #include <ctype.h> |
| 30 | #include <math.h> | ||
| 30 | 31 | ||
| 31 | #include "lisp.h" | 32 | #include "lisp.h" |
| 32 | #include "bignum.h" | 33 | #include "bignum.h" |
| @@ -466,21 +467,10 @@ load_unaligned_size_t (const void *p) | |||
| 466 | return x; | 467 | return x; |
| 467 | } | 468 | } |
| 468 | 469 | ||
| 469 | DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0, | 470 | /* Return -1/0/1 to indicate the relation </=/> between string1 and string2. */ |
| 470 | doc: /* Return non-nil if STRING1 is less than STRING2 in lexicographic order. | 471 | static int |
| 471 | Case is significant. | 472 | string_cmp (Lisp_Object string1, Lisp_Object string2) |
| 472 | Symbols are also allowed; their print names are used instead. */) | ||
| 473 | (Lisp_Object string1, Lisp_Object string2) | ||
| 474 | { | 473 | { |
| 475 | if (SYMBOLP (string1)) | ||
| 476 | string1 = SYMBOL_NAME (string1); | ||
| 477 | else | ||
| 478 | CHECK_STRING (string1); | ||
| 479 | if (SYMBOLP (string2)) | ||
| 480 | string2 = SYMBOL_NAME (string2); | ||
| 481 | else | ||
| 482 | CHECK_STRING (string2); | ||
| 483 | |||
| 484 | ptrdiff_t n = min (SCHARS (string1), SCHARS (string2)); | 474 | ptrdiff_t n = min (SCHARS (string1), SCHARS (string2)); |
| 485 | 475 | ||
| 486 | if ((!STRING_MULTIBYTE (string1) || SCHARS (string1) == SBYTES (string1)) | 476 | if ((!STRING_MULTIBYTE (string1) || SCHARS (string1) == SBYTES (string1)) |
| @@ -489,7 +479,9 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 489 | /* Each argument is either unibyte or all-ASCII multibyte: | 479 | /* Each argument is either unibyte or all-ASCII multibyte: |
| 490 | we can compare bytewise. */ | 480 | we can compare bytewise. */ |
| 491 | int d = memcmp (SSDATA (string1), SSDATA (string2), n); | 481 | int d = memcmp (SSDATA (string1), SSDATA (string2), n); |
| 492 | return d < 0 || (d == 0 && n < SCHARS (string2)) ? Qt : Qnil; | 482 | if (d) |
| 483 | return d; | ||
| 484 | return n < SCHARS (string2) ? -1 : n > SCHARS (string2); | ||
| 493 | } | 485 | } |
| 494 | else if (STRING_MULTIBYTE (string1) && STRING_MULTIBYTE (string2)) | 486 | else if (STRING_MULTIBYTE (string1) && STRING_MULTIBYTE (string2)) |
| 495 | { | 487 | { |
| @@ -523,7 +515,7 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 523 | 515 | ||
| 524 | if (b >= nb) | 516 | if (b >= nb) |
| 525 | /* One string is a prefix of the other. */ | 517 | /* One string is a prefix of the other. */ |
| 526 | return b < nb2 ? Qt : Qnil; | 518 | return b < nb2 ? -1 : b > nb2; |
| 527 | 519 | ||
| 528 | /* Now back up to the start of the differing characters: | 520 | /* Now back up to the start of the differing characters: |
| 529 | it's the last byte not having the bit pattern 10xxxxxx. */ | 521 | it's the last byte not having the bit pattern 10xxxxxx. */ |
| @@ -535,7 +527,7 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 535 | ptrdiff_t i1_byte = b, i2_byte = b; | 527 | ptrdiff_t i1_byte = b, i2_byte = b; |
| 536 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); | 528 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); |
| 537 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); | 529 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); |
| 538 | return c1 < c2 ? Qt : Qnil; | 530 | return c1 < c2 ? -1 : c1 > c2; |
| 539 | } | 531 | } |
| 540 | else if (STRING_MULTIBYTE (string1)) | 532 | else if (STRING_MULTIBYTE (string1)) |
| 541 | { | 533 | { |
| @@ -546,9 +538,9 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 546 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); | 538 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); |
| 547 | int c2 = SREF (string2, i2++); | 539 | int c2 = SREF (string2, i2++); |
| 548 | if (c1 != c2) | 540 | if (c1 != c2) |
| 549 | return c1 < c2 ? Qt : Qnil; | 541 | return c1 < c2 ? -1 : 1; |
| 550 | } | 542 | } |
| 551 | return i1 < SCHARS (string2) ? Qt : Qnil; | 543 | return i1 < SCHARS (string2) ? -1 : i1 > SCHARS (string2); |
| 552 | } | 544 | } |
| 553 | else | 545 | else |
| 554 | { | 546 | { |
| @@ -559,12 +551,30 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 559 | int c1 = SREF (string1, i1++); | 551 | int c1 = SREF (string1, i1++); |
| 560 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); | 552 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); |
| 561 | if (c1 != c2) | 553 | if (c1 != c2) |
| 562 | return c1 < c2 ? Qt : Qnil; | 554 | return c1 < c2 ? -1 : 1; |
| 563 | } | 555 | } |
| 564 | return i1 < SCHARS (string2) ? Qt : Qnil; | 556 | return i1 < SCHARS (string2) ? -1 : i1 > SCHARS (string2); |
| 565 | } | 557 | } |
| 566 | } | 558 | } |
| 567 | 559 | ||
| 560 | DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0, | ||
| 561 | doc: /* Return non-nil if STRING1 is less than STRING2 in lexicographic order. | ||
| 562 | Case is significant. | ||
| 563 | Symbols are also allowed; their print names are used instead. */) | ||
| 564 | (Lisp_Object string1, Lisp_Object string2) | ||
| 565 | { | ||
| 566 | if (SYMBOLP (string1)) | ||
| 567 | string1 = SYMBOL_NAME (string1); | ||
| 568 | else | ||
| 569 | CHECK_STRING (string1); | ||
| 570 | if (SYMBOLP (string2)) | ||
| 571 | string2 = SYMBOL_NAME (string2); | ||
| 572 | else | ||
| 573 | CHECK_STRING (string2); | ||
| 574 | |||
| 575 | return string_cmp (string1, string2) < 0 ? Qt : Qnil; | ||
| 576 | } | ||
| 577 | |||
| 568 | DEFUN ("string-version-lessp", Fstring_version_lessp, | 578 | DEFUN ("string-version-lessp", Fstring_version_lessp, |
| 569 | Sstring_version_lessp, 2, 2, 0, | 579 | Sstring_version_lessp, 2, 2, 0, |
| 570 | doc: /* Return non-nil if S1 is less than S2, as version strings. | 580 | doc: /* Return non-nil if S1 is less than S2, as version strings. |
| @@ -2908,6 +2918,233 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind, | |||
| 2908 | 2918 | ||
| 2909 | return false; | 2919 | return false; |
| 2910 | } | 2920 | } |
| 2921 | |||
| 2922 | /* Return -1/0/1 for the </=/> lexicographic relation between bool-vectors. */ | ||
| 2923 | static int | ||
| 2924 | bool_vector_cmp (Lisp_Object a, Lisp_Object b) | ||
| 2925 | { | ||
| 2926 | ptrdiff_t na = bool_vector_size (a); | ||
| 2927 | ptrdiff_t nb = bool_vector_size (b); | ||
| 2928 | /* Skip equal words. */ | ||
| 2929 | ptrdiff_t words_min = min (na, nb) / BITS_PER_BITS_WORD; | ||
| 2930 | bits_word *ad = bool_vector_data (a); | ||
| 2931 | bits_word *bd = bool_vector_data (b); | ||
| 2932 | ptrdiff_t i = 0; | ||
| 2933 | while (i < words_min && ad[i] == bd[i]) | ||
| 2934 | i++; | ||
| 2935 | na -= i * BITS_PER_BITS_WORD; | ||
| 2936 | nb -= i * BITS_PER_BITS_WORD; | ||
| 2937 | eassume (na >= 0 && nb >= 0); | ||
| 2938 | if (nb == 0) | ||
| 2939 | return na != 0; | ||
| 2940 | if (na == 0) | ||
| 2941 | return -1; | ||
| 2942 | |||
| 2943 | bits_word aw = bits_word_to_host_endian (ad[i]); | ||
| 2944 | bits_word bw = bits_word_to_host_endian (bd[i]); | ||
| 2945 | bits_word xw = aw ^ bw; | ||
| 2946 | if (xw == 0) | ||
| 2947 | return na < nb ? -1 : na > nb; | ||
| 2948 | |||
| 2949 | bits_word d = xw & -xw; /* Isolate first difference. */ | ||
| 2950 | eassume (d != 0); | ||
| 2951 | return (d & aw) ? 1 : -1; | ||
| 2952 | } | ||
| 2953 | |||
| 2954 | /* Return -1, 0 or 1 to indicate whether a<b, a=b or a>b in the sense of value<. | ||
| 2955 | In particular 0 does not mean equality in the sense of Fequal, only | ||
| 2956 | that the arguments cannot be ordered yet they can be compared (same | ||
| 2957 | type). */ | ||
| 2958 | static int | ||
| 2959 | value_cmp (Lisp_Object a, Lisp_Object b, int maxdepth) | ||
| 2960 | { | ||
| 2961 | if (maxdepth < 0) | ||
| 2962 | error ("Maximum depth exceeded in comparison"); | ||
| 2963 | |||
| 2964 | tail_recurse: | ||
| 2965 | /* Shortcut for a common case. */ | ||
| 2966 | if (BASE_EQ (a, b)) | ||
| 2967 | return 0; | ||
| 2968 | |||
| 2969 | switch (XTYPE (a)) | ||
| 2970 | { | ||
| 2971 | case_Lisp_Int: | ||
| 2972 | { | ||
| 2973 | EMACS_INT ia = XFIXNUM (a); | ||
| 2974 | if (FIXNUMP (b)) | ||
| 2975 | return ia < XFIXNUM (b) ? -1 : 1; /* we know that aâ b */ | ||
| 2976 | if (FLOATP (b)) | ||
| 2977 | return ia < XFLOAT_DATA (b) ? -1 : ia > XFLOAT_DATA (b); | ||
| 2978 | if (BIGNUMP (b)) | ||
| 2979 | return -mpz_sgn (*xbignum_val (b)); | ||
| 2980 | } | ||
| 2981 | goto type_mismatch; | ||
| 2982 | |||
| 2983 | case Lisp_Symbol: | ||
| 2984 | if (BARE_SYMBOL_P (b)) | ||
| 2985 | return string_cmp (XBARE_SYMBOL (a)->u.s.name, | ||
| 2986 | XBARE_SYMBOL (b)->u.s.name); | ||
| 2987 | if (CONSP (b) && NILP (a)) | ||
| 2988 | return -1; | ||
| 2989 | if (SYMBOLP (b)) | ||
| 2990 | /* Slow-path branch when B is a symbol-with-pos. */ | ||
| 2991 | return string_cmp (XBARE_SYMBOL (a)->u.s.name, XSYMBOL (b)->u.s.name); | ||
| 2992 | goto type_mismatch; | ||
| 2993 | |||
| 2994 | case Lisp_String: | ||
| 2995 | if (STRINGP (b)) | ||
| 2996 | return string_cmp (a, b); | ||
| 2997 | goto type_mismatch; | ||
| 2998 | |||
| 2999 | case Lisp_Cons: | ||
| 3000 | /* FIXME: Optimise for difference in the first element? */ | ||
| 3001 | FOR_EACH_TAIL (b) | ||
| 3002 | { | ||
| 3003 | int cmp = value_cmp (XCAR (a), XCAR (b), maxdepth - 1); | ||
| 3004 | if (cmp != 0) | ||
| 3005 | return cmp; | ||
| 3006 | a = XCDR (a); | ||
| 3007 | if (!CONSP (a)) | ||
| 3008 | { | ||
| 3009 | b = XCDR (b); | ||
| 3010 | goto tail_recurse; | ||
| 3011 | } | ||
| 3012 | } | ||
| 3013 | if (NILP (b)) | ||
| 3014 | return 1; | ||
| 3015 | else | ||
| 3016 | goto type_mismatch; | ||
| 3017 | goto tail_recurse; | ||
| 3018 | |||
| 3019 | case Lisp_Vectorlike: | ||
| 3020 | if (VECTORLIKEP (b)) | ||
| 3021 | { | ||
| 3022 | enum pvec_type ta = PSEUDOVECTOR_TYPE (XVECTOR (a)); | ||
| 3023 | enum pvec_type tb = PSEUDOVECTOR_TYPE (XVECTOR (b)); | ||
| 3024 | if (ta == tb) | ||
| 3025 | switch (ta) | ||
| 3026 | { | ||
| 3027 | case PVEC_NORMAL_VECTOR: | ||
| 3028 | case PVEC_RECORD: | ||
| 3029 | { | ||
| 3030 | ptrdiff_t len_a = ASIZE (a); | ||
| 3031 | ptrdiff_t len_b = ASIZE (b); | ||
| 3032 | if (ta == PVEC_RECORD) | ||
| 3033 | { | ||
| 3034 | len_a &= PSEUDOVECTOR_SIZE_MASK; | ||
| 3035 | len_b &= PSEUDOVECTOR_SIZE_MASK; | ||
| 3036 | } | ||
| 3037 | ptrdiff_t len_min = min (len_a, len_b); | ||
| 3038 | for (ptrdiff_t i = 0; i < len_min; i++) | ||
| 3039 | { | ||
| 3040 | int cmp = value_cmp (AREF (a, i), AREF (b, i), | ||
| 3041 | maxdepth - 1); | ||
| 3042 | if (cmp != 0) | ||
| 3043 | return cmp; | ||
| 3044 | } | ||
| 3045 | return len_a < len_b ? -1 : len_a > len_b; | ||
| 3046 | } | ||
| 3047 | |||
| 3048 | case PVEC_BOOL_VECTOR: | ||
| 3049 | return bool_vector_cmp (a, b); | ||
| 3050 | |||
| 3051 | case PVEC_MARKER: | ||
| 3052 | { | ||
| 3053 | Lisp_Object buf_a = Fmarker_buffer (a); | ||
| 3054 | Lisp_Object buf_b = Fmarker_buffer (b); | ||
| 3055 | if (NILP (buf_a)) | ||
| 3056 | return NILP (buf_b) ? 0 : -1; | ||
| 3057 | if (NILP (buf_b)) | ||
| 3058 | return 1; | ||
| 3059 | int cmp = value_cmp (buf_a, buf_b, maxdepth - 1); | ||
| 3060 | if (cmp != 0) | ||
| 3061 | return cmp; | ||
| 3062 | ptrdiff_t pa = XMARKER (a)->charpos; | ||
| 3063 | ptrdiff_t pb = XMARKER (b)->charpos; | ||
| 3064 | return pa < pb ? -1 : pa > pb; | ||
| 3065 | } | ||
| 3066 | |||
| 3067 | case PVEC_PROCESS: | ||
| 3068 | a = Fprocess_name (a); | ||
| 3069 | b = Fprocess_name (b); | ||
| 3070 | goto tail_recurse; | ||
| 3071 | |||
| 3072 | case PVEC_BUFFER: | ||
| 3073 | { | ||
| 3074 | /* Killed buffers lack names and sort before those alive. */ | ||
| 3075 | Lisp_Object na = Fbuffer_name (a); | ||
| 3076 | Lisp_Object nb = Fbuffer_name (b); | ||
| 3077 | if (NILP (na)) | ||
| 3078 | return NILP (nb) ? 0 : -1; | ||
| 3079 | if (NILP (nb)) | ||
| 3080 | return 1; | ||
| 3081 | a = na; | ||
| 3082 | b = nb; | ||
| 3083 | goto tail_recurse; | ||
| 3084 | } | ||
| 3085 | |||
| 3086 | case PVEC_BIGNUM: | ||
| 3087 | return mpz_cmp (*xbignum_val (a), *xbignum_val (b)); | ||
| 3088 | |||
| 3089 | case PVEC_SYMBOL_WITH_POS: | ||
| 3090 | /* Compare by name, enabled or not. */ | ||
| 3091 | a = XSYMBOL_WITH_POS_SYM (a); | ||
| 3092 | b = XSYMBOL_WITH_POS_SYM (b); | ||
| 3093 | goto tail_recurse; | ||
| 3094 | |||
| 3095 | default: | ||
| 3096 | /* Treat other types as unordered. */ | ||
| 3097 | return 0; | ||
| 3098 | } | ||
| 3099 | } | ||
| 3100 | else if (BIGNUMP (a)) | ||
| 3101 | return -value_cmp (b, a, maxdepth); | ||
| 3102 | else if (SYMBOL_WITH_POS_P (a) && symbols_with_pos_enabled) | ||
| 3103 | { | ||
| 3104 | a = XSYMBOL_WITH_POS_SYM (a); | ||
| 3105 | goto tail_recurse; | ||
| 3106 | } | ||
| 3107 | |||
| 3108 | goto type_mismatch; | ||
| 3109 | |||
| 3110 | case Lisp_Float: | ||
| 3111 | { | ||
| 3112 | double fa = XFLOAT_DATA (a); | ||
| 3113 | if (FLOATP (b)) | ||
| 3114 | return fa < XFLOAT_DATA (b) ? -1 : fa > XFLOAT_DATA (b); | ||
| 3115 | if (FIXNUMP (b)) | ||
| 3116 | return fa < XFIXNUM (b) ? -1 : fa > XFIXNUM (b); | ||
| 3117 | if (BIGNUMP (b)) | ||
| 3118 | { | ||
| 3119 | if (isnan (fa)) | ||
| 3120 | return 0; | ||
| 3121 | return -mpz_cmp_d (*xbignum_val (b), fa); | ||
| 3122 | } | ||
| 3123 | } | ||
| 3124 | goto type_mismatch; | ||
| 3125 | |||
| 3126 | default: | ||
| 3127 | eassume (0); | ||
| 3128 | } | ||
| 3129 | type_mismatch: | ||
| 3130 | xsignal2 (Qtype_mismatch, a, b); | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | DEFUN ("value<", Fvaluelt, Svaluelt, 2, 2, 0, | ||
| 3134 | doc: /* Return non-nil if A precedes B in standard value order. | ||
| 3135 | A and B must have the same basic type. | ||
| 3136 | Numbers are compared with `<'. | ||
| 3137 | Strings and symbols are compared with `string-lessp'. | ||
| 3138 | Lists, vectors, bool-vectors and records are compared lexicographically. | ||
| 3139 | Markers are compared lexicographically by buffer and position. | ||
| 3140 | Buffers and processes are compared by name. | ||
| 3141 | Other types are considered unordered and the return value will be `nil'. */) | ||
| 3142 | (Lisp_Object a, Lisp_Object b) | ||
| 3143 | { | ||
| 3144 | int maxdepth = 20; /* FIXME: arbitrary value */ | ||
| 3145 | return value_cmp (a, b, maxdepth) < 0 ? Qt : Qnil; | ||
| 3146 | } | ||
| 3147 | |||
| 2911 | 3148 | ||
| 2912 | 3149 | ||
| 2913 | DEFUN ("fillarray", Ffillarray, Sfillarray, 2, 2, 0, | 3150 | DEFUN ("fillarray", Ffillarray, Sfillarray, 2, 2, 0, |
| @@ -6589,6 +6826,7 @@ For best results this should end in a space. */); | |||
| 6589 | defsubr (&Seql); | 6826 | defsubr (&Seql); |
| 6590 | defsubr (&Sequal); | 6827 | defsubr (&Sequal); |
| 6591 | defsubr (&Sequal_including_properties); | 6828 | defsubr (&Sequal_including_properties); |
| 6829 | defsubr (&Svaluelt); | ||
| 6592 | defsubr (&Sfillarray); | 6830 | defsubr (&Sfillarray); |
| 6593 | defsubr (&Sclear_string); | 6831 | defsubr (&Sclear_string); |
| 6594 | defsubr (&Snconc); | 6832 | defsubr (&Snconc); |
diff --git a/src/lisp.h b/src/lisp.h index f86758c88fb..5583a7e2e8e 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -1882,6 +1882,30 @@ bool_vector_bytes (EMACS_INT size) | |||
| 1882 | return (size + BOOL_VECTOR_BITS_PER_CHAR - 1) / BOOL_VECTOR_BITS_PER_CHAR; | 1882 | return (size + BOOL_VECTOR_BITS_PER_CHAR - 1) / BOOL_VECTOR_BITS_PER_CHAR; |
| 1883 | } | 1883 | } |
| 1884 | 1884 | ||
| 1885 | INLINE bits_word | ||
| 1886 | bits_word_to_host_endian (bits_word val) | ||
| 1887 | { | ||
| 1888 | #ifndef WORDS_BIGENDIAN | ||
| 1889 | return val; | ||
| 1890 | #else | ||
| 1891 | if (BITS_WORD_MAX >> 31 == 1) | ||
| 1892 | return bswap_32 (val); | ||
| 1893 | if (BITS_WORD_MAX >> 31 >> 31 >> 1 == 1) | ||
| 1894 | return bswap_64 (val); | ||
| 1895 | { | ||
| 1896 | int i; | ||
| 1897 | bits_word r = 0; | ||
| 1898 | for (i = 0; i < sizeof val; i++) | ||
| 1899 | { | ||
| 1900 | r = ((r << 1 << (CHAR_BIT - 1)) | ||
| 1901 | | (val & ((1u << 1 << (CHAR_BIT - 1)) - 1))); | ||
| 1902 | val = val >> 1 >> (CHAR_BIT - 1); | ||
| 1903 | } | ||
| 1904 | return r; | ||
| 1905 | } | ||
| 1906 | #endif | ||
| 1907 | } | ||
| 1908 | |||
| 1885 | INLINE bool | 1909 | INLINE bool |
| 1886 | BOOL_VECTOR_P (Lisp_Object a) | 1910 | BOOL_VECTOR_P (Lisp_Object a) |
| 1887 | { | 1911 | { |
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index 7437c07f156..844000cdc76 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el | |||
| @@ -1513,4 +1513,222 @@ | |||
| 1513 | (should-error (copy-alist "abc") | 1513 | (should-error (copy-alist "abc") |
| 1514 | :type 'wrong-type-argument)) | 1514 | :type 'wrong-type-argument)) |
| 1515 | 1515 | ||
| 1516 | (ert-deftest fns-value<-ordered () | ||
| 1517 | ;; values (X . Y) where X<Y | ||
| 1518 | (let* ((big (* 10 most-positive-fixnum)) | ||
| 1519 | (buf1 (get-buffer-create " *one*")) | ||
| 1520 | (buf2 (get-buffer-create " *two*")) | ||
| 1521 | (buf3 (get-buffer-create " *three*")) | ||
| 1522 | (_ (progn (with-current-buffer buf1 (insert (make-string 20 ?a))) | ||
| 1523 | (with-current-buffer buf2 (insert (make-string 20 ?b))))) | ||
| 1524 | (mark1 (set-marker (make-marker) 12 buf1)) | ||
| 1525 | (mark2 (set-marker (make-marker) 13 buf1)) | ||
| 1526 | (mark3 (set-marker (make-marker) 12 buf2)) | ||
| 1527 | (mark4 (set-marker (make-marker) 13 buf2)) | ||
| 1528 | (proc1 (make-pipe-process :name " *proc one*")) | ||
| 1529 | (proc2 (make-pipe-process :name " *proc two*"))) | ||
| 1530 | (kill-buffer buf3) | ||
| 1531 | (unwind-protect | ||
| 1532 | (dolist (c | ||
| 1533 | `( | ||
| 1534 | ;; fixnums | ||
| 1535 | (1 . 2) (-2 . -1) (-2 . 1) (-1 . 2) | ||
| 1536 | ;; bignums | ||
| 1537 | (,big . ,(1+ big)) (,(- big) . ,big) | ||
| 1538 | (,(- -1 big) . ,(- big)) | ||
| 1539 | ;; fixnums/bignums | ||
| 1540 | (1 . ,big) (-1 . ,big) (,(- big) . -1) (,(- big) . 1) | ||
| 1541 | ;; floats | ||
| 1542 | (1.5 . 1.6) (-1.3 . -1.2) (-13.0 . 12.0) | ||
| 1543 | ;; floats/fixnums | ||
| 1544 | (1 . 1.1) (1.9 . 2) (-2.0 . 1) (-2 . 1.0) | ||
| 1545 | ;; floats/bignums | ||
| 1546 | (,big . ,(float (* 2 big))) (,(float big) . ,(* 2 big)) | ||
| 1547 | ;; symbols | ||
| 1548 | (a . b) (nil . nix) (b . ba) (## . a) (A . a) | ||
| 1549 | (#:a . #:b) (a . #:b) (#:a . b) | ||
| 1550 | ;; strings | ||
| 1551 | ("" . "a") ("a" . "b") ("A" . "a") ("abc" . "abd") | ||
| 1552 | ("b" . "ba") | ||
| 1553 | |||
| 1554 | ;; lists | ||
| 1555 | ((1 2 3) . (2 3 4)) ((2) . (2 1)) (() . (0)) | ||
| 1556 | ((1 2 3) . (1 3)) ((1 2 3) . (1 3 2)) | ||
| 1557 | (((b a) (c d) e) . ((b a) (c d) f)) | ||
| 1558 | (((b a) (c D) e) . ((b a) (c d) e)) | ||
| 1559 | (((b a) (c d () x) e) . ((b a) (c d (1) x) e)) | ||
| 1560 | ((1 . 2) . (1 . 3)) ((1 2 . 3) . (1 2 . 4)) | ||
| 1561 | |||
| 1562 | ;; vectors | ||
| 1563 | ([1 2 3] . [2 3 4]) ([2] . [2 1]) ([] . [0]) | ||
| 1564 | ([1 2 3] . [1 3]) ([1 2 3] . [1 3 2]) | ||
| 1565 | ([[b a] [c d] e] . [[b a] [c d] f]) | ||
| 1566 | ([[b a] [c D] e] . [[b a] [c d] e]) | ||
| 1567 | ([[b a] [c d [] x] e] . [[b a] [c d [1] x] e]) | ||
| 1568 | |||
| 1569 | ;; bool-vectors | ||
| 1570 | (,(bool-vector) . ,(bool-vector nil)) | ||
| 1571 | (,(bool-vector nil) . ,(bool-vector t)) | ||
| 1572 | (,(bool-vector t nil t nil) . ,(bool-vector t nil t t)) | ||
| 1573 | (,(bool-vector t nil t) . ,(bool-vector t nil t nil)) | ||
| 1574 | |||
| 1575 | ;; records | ||
| 1576 | (#s(a 2 3) . #s(b 3 4)) (#s(b) . #s(b a)) | ||
| 1577 | (#s(a 2 3) . #s(a 3)) (#s(a 2 3) . #s(a 3 2)) | ||
| 1578 | (#s(#s(b a) #s(c d) e) . #s(#s(b a) #s(c d) f)) | ||
| 1579 | (#s(#s(b a) #s(c D) e) . #s(#s(b a) #s(c d) e)) | ||
| 1580 | (#s(#s(b a) #s(c d #s(u) x) e) | ||
| 1581 | . #s(#s(b a) #s(c d #s(v) x) e)) | ||
| 1582 | |||
| 1583 | ;; markers | ||
| 1584 | (,mark1 . ,mark2) (,mark1 . ,mark3) (,mark1 . ,mark4) | ||
| 1585 | (,mark2 . ,mark3) (,mark2 . ,mark4) (,mark3 . ,mark4) | ||
| 1586 | |||
| 1587 | ;; buffers | ||
| 1588 | (,buf1 . ,buf2) (,buf3 . ,buf1) (,buf3 . ,buf2) | ||
| 1589 | |||
| 1590 | ;; processes | ||
| 1591 | (,proc1 . ,proc2) | ||
| 1592 | )) | ||
| 1593 | (let ((x (car c)) | ||
| 1594 | (y (cdr c))) | ||
| 1595 | (should (value< x y)) | ||
| 1596 | (should-not (value< y x)) | ||
| 1597 | (should-not (value< x x)) | ||
| 1598 | (should-not (value< y y)))) | ||
| 1599 | |||
| 1600 | (delete-process proc2) | ||
| 1601 | (delete-process proc1) | ||
| 1602 | (kill-buffer buf2) | ||
| 1603 | (kill-buffer buf1)))) | ||
| 1604 | |||
| 1605 | (ert-deftest fns-value<-unordered () | ||
| 1606 | ;; values (X . Y) where neither X<Y nor Y<X | ||
| 1607 | |||
| 1608 | (let ((buf1 (get-buffer-create " *one*")) | ||
| 1609 | (buf2 (get-buffer-create " *two*"))) | ||
| 1610 | (kill-buffer buf2) | ||
| 1611 | (kill-buffer buf1) | ||
| 1612 | (dolist (c `( | ||
| 1613 | ;; numbers | ||
| 1614 | (0 . 0.0) (0 . -0.0) (0.0 . -0.0) | ||
| 1615 | |||
| 1616 | ;; symbols | ||
| 1617 | (a . #:a) | ||
| 1618 | |||
| 1619 | ;; (dead) buffers | ||
| 1620 | (,buf1 . ,buf2) | ||
| 1621 | |||
| 1622 | ;; unordered types | ||
| 1623 | (,(make-hash-table) . ,(make-hash-table)) | ||
| 1624 | (,(obarray-make) . ,(obarray-make)) | ||
| 1625 | ;; FIXME: more? | ||
| 1626 | )) | ||
| 1627 | (let ((x (car c)) | ||
| 1628 | (y (cdr c))) | ||
| 1629 | (should-not (value< x y)) | ||
| 1630 | (should-not (value< y x)))))) | ||
| 1631 | |||
| 1632 | (ert-deftest fns-value<-type-mismatch () | ||
| 1633 | ;; values of disjoint (incomparable) types | ||
| 1634 | (let ((incomparable | ||
| 1635 | `( 1 a "a" (a b) [a b] ,(bool-vector nil t) #s(a b) | ||
| 1636 | ,(make-char-table 'test) | ||
| 1637 | ,(make-hash-table) | ||
| 1638 | ,(obarray-make) | ||
| 1639 | ;; FIXME: more? | ||
| 1640 | ))) | ||
| 1641 | (let ((tail incomparable)) | ||
| 1642 | (while tail | ||
| 1643 | (let ((x (car tail))) | ||
| 1644 | (dolist (y (cdr tail)) | ||
| 1645 | (should-error (value< x y) :type 'type-mismatch) | ||
| 1646 | (should-error (value< y x) :type 'type-mismatch))) | ||
| 1647 | (setq tail (cdr tail)))))) | ||
| 1648 | |||
| 1649 | (ert-deftest fns-value<-symbol-with-pos () | ||
| 1650 | ;; values (X . Y) where X<Y | ||
| 1651 | (let* ((a-sp-1 (position-symbol 'a 1)) | ||
| 1652 | (a-sp-2 (position-symbol 'a 2)) | ||
| 1653 | (b-sp-1 (position-symbol 'b 1)) | ||
| 1654 | (b-sp-2 (position-symbol 'b 2))) | ||
| 1655 | |||
| 1656 | (dolist (swp '(nil t)) | ||
| 1657 | (let ((symbols-with-pos-enabled swp)) | ||
| 1658 | ;; Enabled or not, they compare by name. | ||
| 1659 | (dolist (c `((,a-sp-1 . ,b-sp-1) (,a-sp-1 . ,b-sp-2) | ||
| 1660 | (,a-sp-2 . ,b-sp-1) (,a-sp-2 . ,b-sp-2))) | ||
| 1661 | (let ((x (car c)) | ||
| 1662 | (y (cdr c))) | ||
| 1663 | (should (value< x y)) | ||
| 1664 | (should-not (value< y x)) | ||
| 1665 | (should-not (value< x x)) | ||
| 1666 | (should-not (value< y y)))) | ||
| 1667 | (should-not (value< a-sp-1 a-sp-2)) | ||
| 1668 | (should-not (value< a-sp-2 a-sp-1)))) | ||
| 1669 | |||
| 1670 | ;; When disabled, symbol-with-pos and symbols do not compare. | ||
| 1671 | (should-error (value< a-sp-1 'a) :type 'type-mismatch) | ||
| 1672 | (should-error (value< 'a a-sp-1) :type 'type-mismatch) | ||
| 1673 | |||
| 1674 | (let ((symbols-with-pos-enabled t)) | ||
| 1675 | ;; When enabled, a symbol-with-pos compares as a plain symbol. | ||
| 1676 | (dolist (c `((,a-sp-1 . b) (a . ,b-sp-1))) | ||
| 1677 | (let ((x (car c)) | ||
| 1678 | (y (cdr c))) | ||
| 1679 | (should (value< x y)) | ||
| 1680 | (should-not (value< y x)) | ||
| 1681 | (should-not (value< x x)) | ||
| 1682 | (should-not (value< y y)))) | ||
| 1683 | (should-not (value< a-sp-1 'a)) | ||
| 1684 | (should-not (value< 'a a-sp-1))))) | ||
| 1685 | |||
| 1686 | (ert-deftest fns-value<-circle () | ||
| 1687 | ;; Check that we at least don't hang when comparing two circular lists. | ||
| 1688 | (let ((a (number-sequence 1 5)) | ||
| 1689 | (b (number-sequence 1 5))) | ||
| 1690 | (setcdr (last a) (nthcdr 2 a)) | ||
| 1691 | (setcdr (last b) (nthcdr 2 b)) | ||
| 1692 | (should-error (value< a b :type 'circular)) | ||
| 1693 | (should-error (value< b a :type 'circular)))) | ||
| 1694 | |||
| 1695 | (ert-deftest fns-value<-bool-vector () | ||
| 1696 | ;; More thorough test of `value<' for bool-vectors. | ||
| 1697 | (random "my seed") | ||
| 1698 | (dolist (na '(0 1 5 8 9 32 63 64 65 200 1001 1024)) | ||
| 1699 | (let ((a (make-bool-vector na nil))) | ||
| 1700 | (dotimes (i na) | ||
| 1701 | (aset a i (zerop (random 2)))) | ||
| 1702 | (dolist (nb '(0 1 5 8 9 32 63 64 65 200 1001 1024)) | ||
| 1703 | (when (<= nb na) | ||
| 1704 | (let ((b (make-bool-vector nb nil))) | ||
| 1705 | (dotimes (i nb) | ||
| 1706 | (aset b i (aref a i))) | ||
| 1707 | ;; `b' is now a prefix of `a'. | ||
| 1708 | (should-not (value< a b)) | ||
| 1709 | (cond ((= nb na) | ||
| 1710 | (should (equal a b)) | ||
| 1711 | (should-not (value< b a))) | ||
| 1712 | (t | ||
| 1713 | (should-not (equal a b)) | ||
| 1714 | (should (value< b a)))) | ||
| 1715 | (unless (zerop nb) | ||
| 1716 | ;; Flip random bits in `b' and check how it affects the order. | ||
| 1717 | (dotimes (_ 3) | ||
| 1718 | (let ((i (random nb))) | ||
| 1719 | (let ((val (aref b i))) | ||
| 1720 | (aset b i (not val)) | ||
| 1721 | (should-not (equal a b)) | ||
| 1722 | (cond | ||
| 1723 | (val | ||
| 1724 | ;; t -> nil: `b' is now always a proper prefix of `a'. | ||
| 1725 | (should-not (value< a b)) | ||
| 1726 | (should (value< b a))) | ||
| 1727 | (t | ||
| 1728 | ;; nil -> t: `a' is now less than `b'. | ||
| 1729 | (should (value< a b)) | ||
| 1730 | (should-not (value< b a)))) | ||
| 1731 | ;; Undo the flip. | ||
| 1732 | (aset b i val))))))))))) | ||
| 1733 | |||
| 1516 | ;;; fns-tests.el ends here | 1734 | ;;; fns-tests.el ends here |