diff options
| author | Paul Eggert | 2018-08-20 15:52:29 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-20 16:01:31 -0700 |
| commit | eb83344fc7c08ec08b51e7700f1ac2632afa462c (patch) | |
| tree | 69c96f89acf42957ed98dc7ce189cb49d04abecd /src | |
| parent | 36de7bd7b0b9fcd038c440b4705e9186bfbaaa41 (diff) | |
| download | emacs-eb83344fc7c08ec08b51e7700f1ac2632afa462c.tar.gz emacs-eb83344fc7c08ec08b51e7700f1ac2632afa462c.zip | |
Speed up (nthcdr N L) when L is circular
Also, fix bug when N is a positive bignum, a problem reported
by Eli Zaretskii and Pip Cet in:
https://lists.gnu.org/r/emacs-devel/2018-08/msg00690.html
* src/fns.c (Fnthcdr): If a cycle is found, reduce the count
modulo the cycle length before continuing. This reduces the
worst-case cost of (nthcdr N L) from N to min(N, C) where C is
the number of distinct cdrs of L. Reducing modulo the cycle
length also allows us to do arithmetic with machine words
instead of with GMP.
* test/src/fns-tests.el (test-nthcdr-circular): New test.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 58 |
1 files changed, 52 insertions, 6 deletions
| @@ -1403,7 +1403,12 @@ DEFUN ("nthcdr", Fnthcdr, Snthcdr, 2, 2, 0, | |||
| 1403 | (Lisp_Object n, Lisp_Object list) | 1403 | (Lisp_Object n, Lisp_Object list) |
| 1404 | { | 1404 | { |
| 1405 | CHECK_INTEGER (n); | 1405 | CHECK_INTEGER (n); |
| 1406 | Lisp_Object tail = list; | 1406 | |
| 1407 | /* A huge but in-range EMACS_INT that can be substituted for a | ||
| 1408 | positive bignum while counting down. It does not introduce | ||
| 1409 | miscounts because a list or cycle cannot possibly be this long, | ||
| 1410 | and any counting error is fixed up later. */ | ||
| 1411 | EMACS_INT large_num = EMACS_INT_MAX; | ||
| 1407 | 1412 | ||
| 1408 | EMACS_INT num; | 1413 | EMACS_INT num; |
| 1409 | if (FIXNUMP (n)) | 1414 | if (FIXNUMP (n)) |
| @@ -1412,16 +1417,57 @@ DEFUN ("nthcdr", Fnthcdr, Snthcdr, 2, 2, 0, | |||
| 1412 | { | 1417 | { |
| 1413 | num = mpz_sgn (XBIGNUM (n)->value); | 1418 | num = mpz_sgn (XBIGNUM (n)->value); |
| 1414 | if (0 < num) | 1419 | if (0 < num) |
| 1415 | num = EMACS_INT_MAX; /* LIST cannot possibly be this long. */ | 1420 | num = large_num; |
| 1416 | } | 1421 | } |
| 1417 | 1422 | ||
| 1418 | for (; 0 < num; num--) | 1423 | EMACS_INT tortoise_num = num; |
| 1424 | Lisp_Object tail = list, saved_tail = tail; | ||
| 1425 | FOR_EACH_TAIL_SAFE (tail) | ||
| 1419 | { | 1426 | { |
| 1420 | if (! CONSP (tail)) | 1427 | if (num <= 0) |
| 1428 | return tail; | ||
| 1429 | if (tail == li.tortoise) | ||
| 1430 | tortoise_num = num; | ||
| 1431 | saved_tail = XCDR (tail); | ||
| 1432 | num--; | ||
| 1433 | rarely_quit (num); | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | tail = saved_tail; | ||
| 1437 | if (! CONSP (tail)) | ||
| 1438 | { | ||
| 1439 | CHECK_LIST_END (tail, list); | ||
| 1440 | return Qnil; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | /* TAIL is part of a cycle. Reduce NUM modulo the cycle length to | ||
| 1444 | avoid going around this cycle repeatedly. */ | ||
| 1445 | intptr_t cycle_length = tortoise_num - num; | ||
| 1446 | if (! FIXNUMP (n)) | ||
| 1447 | { | ||
| 1448 | /* Undo any error introduced when LARGE_NUM was substituted for | ||
| 1449 | N, by adding N - LARGE_NUM to NUM, using arithmetic modulo | ||
| 1450 | CYCLE_LENGTH. */ | ||
| 1451 | mpz_t z; /* N mod CYCLE_LENGTH. */ | ||
| 1452 | mpz_init (z); | ||
| 1453 | if (cycle_length <= ULONG_MAX) | ||
| 1454 | num += mpz_mod_ui (z, XBIGNUM (n)->value, cycle_length); | ||
| 1455 | else | ||
| 1421 | { | 1456 | { |
| 1422 | CHECK_LIST_END (tail, list); | 1457 | mpz_set_intmax (z, cycle_length); |
| 1423 | return Qnil; | 1458 | mpz_mod (z, XBIGNUM (n)->value, z); |
| 1459 | intptr_t iz; | ||
| 1460 | mpz_export (&iz, NULL, -1, sizeof iz, 0, 0, z); | ||
| 1461 | num += iz; | ||
| 1424 | } | 1462 | } |
| 1463 | mpz_clear (z); | ||
| 1464 | num += cycle_length - large_num % cycle_length; | ||
| 1465 | } | ||
| 1466 | num %= cycle_length; | ||
| 1467 | |||
| 1468 | /* One last time through the cycle. */ | ||
| 1469 | for (; 0 < num; num--) | ||
| 1470 | { | ||
| 1425 | tail = XCDR (tail); | 1471 | tail = XCDR (tail); |
| 1426 | rarely_quit (num); | 1472 | rarely_quit (num); |
| 1427 | } | 1473 | } |