/* srfi-60.c --- Integers as Bits Copyright 2005-2006,2008,2010,2014,2018,2022 Free Software Foundation, Inc. This file is part of Guile. Guile is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Guile 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Guile. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include "boolean.h" #include "eq.h" #include "extensions.h" #include "gsubr.h" #include "integers.h" #include "list.h" #include "numbers.h" #include "pairs.h" #include "version.h" #include "srfi-60.h" SCM_DEFINE (scm_srfi60_log2_binary_factors, "log2-binary-factors", 1, 0, 0, (SCM n), "Return a count of how many factors of 2 are present in @var{n}.\n" "This is also the bit index of the lowest 1 bit in @var{n}. If\n" "@var{n} is 0, the return is @math{-1}.\n" "\n" "@example\n" "(log2-binary-factors 6) @result{} 1\n" "(log2-binary-factors -8) @result{} 3\n" "@end example") #define FUNC_NAME s_scm_srfi60_log2_binary_factors { SCM ret = SCM_EOL; if (SCM_I_INUMP (n)) return scm_integer_scan1_i (SCM_I_INUM (n)); else if (SCM_BIGP (n)) return scm_integer_scan1_z (scm_bignum (n)); else SCM_WRONG_TYPE_ARG (SCM_ARG1, n); return ret; } #undef FUNC_NAME SCM_DEFINE (scm_srfi60_copy_bit, "copy-bit", 3, 0, 0, (SCM index, SCM n, SCM newbit), "Return @var{n} with the bit at @var{index} set according to\n" "@var{newbit}. @var{newbit} should be @code{#t} to set the bit\n" "to 1, or @code{#f} to set it to 0. Bits other than at\n" "@var{index} are unchanged in the return.\n" "\n" "@example\n" "(copy-bit 1 #b0101 #t) @result{} 7\n" "@end example") #define FUNC_NAME s_scm_srfi60_copy_bit { unsigned long ii; int bb; ii = scm_to_ulong (index); bb = scm_to_bool (newbit); if (SCM_I_INUMP (n)) { if (scm_integer_logbit_ui (ii, SCM_I_INUM (n)) == bb) return n; } else if (SCM_BIGP (n)) { if (scm_integer_logbit_uz (ii, scm_bignum (n)) == bb) return n; } else SCM_WRONG_TYPE_ARG (SCM_ARG1, n); return scm_logxor (n, ii == 0 ? SCM_INUM1 : scm_integer_lsh_iu (1, ii)); } #undef FUNC_NAME SCM_DEFINE (scm_srfi60_rotate_bit_field, "rotate-bit-field", 4, 0, 0, (SCM n, SCM count, SCM start, SCM end), "Return @var{n} with the bit field from @var{start} (inclusive)\n" "to @var{end} (exclusive) rotated upwards by @var{count} bits.\n" "\n" "@var{count} can be positive or negative, and it can be more\n" "than the field width (it'll be reduced modulo the width).\n" "\n" "@example\n" "(rotate-bit-field #b0110 2 1 4) @result{} #b1010\n" "@end example") #define FUNC_NAME s_scm_srfi60_rotate_bit_field { unsigned long ss = scm_to_ulong (start); unsigned long ee = scm_to_ulong (end); unsigned long ww, cc; SCM_ASSERT_RANGE (3, end, (ee >= ss)); ww = ee - ss; /* we must avoid division by zero, and a field whose width is 0 or 1 will be left unchanged anyway, so in that case we set cc to 0. */ if (ww <= 1) cc = 0; else cc = scm_to_ulong (scm_modulo (count, scm_difference (end, start))); mpz_t zn; if (SCM_I_INUMP (n)) { long nn = SCM_I_INUM (n); if (ee <= SCM_LONG_BIT-1) { /* Everything fits within a long. To avoid undefined behavior when shifting negative numbers, we do all operations using unsigned values, and then convert to signed at the end. */ unsigned long unn = nn; unsigned long below = unn & ((1UL << ss) - 1); /* below start */ unsigned long above = unn & ~((1UL << ee) - 1); /* above end */ unsigned long fmask = ((1UL << ww) - 1) << ss; /* field mask */ unsigned long ff = unn & fmask; /* field */ unsigned long uresult = (above | ((ff << cc) & fmask) | ((ff >> (ww-cc)) & fmask) | below); long result; if (uresult > LONG_MAX) /* The high bit is set in uresult, so the result is negative. We have to handle the conversion to signed integer carefully, to avoid undefined behavior. First we compute ~uresult, equivalent to (ULONG_MAX - uresult), which will be between 0 and LONG_MAX (inclusive): exactly the set of numbers that can be represented as both signed and unsigned longs and thus convertible between them. We cast that difference to a signed long and then substract it from -1. */ result = -1 - (long) ~uresult; else result = (long) uresult; return scm_from_long (result); } else { /* if there's no movement, avoid creating a bignum. */ if (cc == 0) return n; mpz_init_set_si (zn, nn); } } else if (SCM_BIGP (n)) { /* if there's no movement, avoid creating a new bignum. */ if (cc == 0) return n; scm_integer_init_set_mpz_z (scm_bignum (n), zn); } else SCM_WRONG_TYPE_ARG (SCM_ARG1, n); mpz_t tmp, r; mpz_init (tmp); mpz_init_set_si (r, 0); /* portion above end */ mpz_fdiv_q_2exp (r, zn, ee); mpz_mul_2exp (r, r, ee); /* field high part, width-count bits from start go to start+count */ mpz_fdiv_q_2exp (tmp, zn, ss); mpz_fdiv_r_2exp (tmp, tmp, ww - cc); mpz_mul_2exp (tmp, tmp, ss + cc); mpz_ior (r, r, tmp); /* field low part, count bits from end-count go to start */ mpz_fdiv_q_2exp (tmp, zn, ee - cc); mpz_fdiv_r_2exp (tmp, tmp, cc); mpz_mul_2exp (tmp, tmp, ss); mpz_ior (r, r, tmp); /* portion below start */ mpz_fdiv_r_2exp (tmp, zn, ss); mpz_ior (r, r, tmp); mpz_clear (zn); mpz_clear (tmp); /* bits moved around might leave us in range of an inum */ SCM ret = scm_from_mpz (r); mpz_clear (r); return ret; } #undef FUNC_NAME SCM_DEFINE (scm_srfi60_reverse_bit_field, "reverse-bit-field", 3, 0, 0, (SCM n, SCM start, SCM end), "Return @var{n} with the bits between @var{start} (inclusive) to\n" "@var{end} (exclusive) reversed.\n" "\n" "@example\n" "(reverse-bit-field #b101001 2 4) @result{} #b100101\n" "@end example") #define FUNC_NAME s_scm_srfi60_reverse_bit_field { long ss = scm_to_long (start); long ee = scm_to_long (end); long swaps = (ee - ss) / 2; /* number of swaps */ mpz_t b; if (SCM_I_INUMP (n)) { long nn = SCM_I_INUM (n); if (ee <= SCM_LONG_BIT-1) { /* all within a long */ long smask = 1L << ss; long emask = 1L << (ee-1); for ( ; swaps > 0; swaps--) { long sbit = nn & smask; long ebit = nn & emask; nn ^= sbit ^ (ebit ? smask : 0) /* zap sbit, put ebit value */ ^ ebit ^ (sbit ? emask : 0); /* zap ebit, put sbit value */ smask <<= 1; emask >>= 1; } return scm_from_long (nn); } else { /* avoid creating a new bignum if reversing only 0 or 1 bits */ if (ee - ss <= 1) return n; mpz_init_set_si (b, nn); } } else if (SCM_BIGP (n)) { /* avoid creating a new bignum if reversing only 0 or 1 bits */ if (ee - ss <= 1) return n; scm_integer_init_set_mpz_z (scm_bignum (n), b); } else SCM_WRONG_TYPE_ARG (SCM_ARG1, n); ee--; for ( ; swaps > 0; swaps--) { int sbit = mpz_tstbit (b, ss); int ebit = mpz_tstbit (b, ee); if (sbit ^ ebit) { /* the two bits are different, flip them */ if (sbit) { mpz_clrbit (b, ss); mpz_setbit (b, ee); } else { mpz_setbit (b, ss); mpz_clrbit (b, ee); } } ss++; ee--; } SCM ret = scm_integer_from_mpz (b); mpz_clear (b); return ret; } #undef FUNC_NAME SCM_DEFINE (scm_srfi60_integer_to_list, "integer->list", 1, 1, 0, (SCM n, SCM len), "Return bits from @var{n} in the form of a list of @code{#t} for\n" "1 and @code{#f} for 0. The least significant @var{len} bits\n" "are returned, and the first list element is the most\n" "significant of those bits. If @var{len} is not given, the\n" "default is @code{(integer-length @var{n})} (@pxref{Bitwise\n" "Operations}).\n" "\n" "@example\n" "(integer->list 6) @result{} (#t #t #f)\n" "(integer->list 1 4) @result{} (#f #f #f #t)\n" "@end example") #define FUNC_NAME s_scm_srfi60_integer_to_list { SCM ret = SCM_EOL; unsigned long ll; if (SCM_UNBNDP (len)) len = scm_integer_length (n); ll = scm_to_ulong (len); if (SCM_I_INUMP (n)) { scm_t_inum nn = SCM_I_INUM (n); for (unsigned long i = 0; i < ll; i++) ret = scm_cons (scm_from_bool (scm_integer_logbit_ui (i, nn)), ret); } else if (SCM_BIGP (n)) { struct scm_bignum *nn = scm_bignum (n); for (unsigned long i = 0; i < ll; i++) ret = scm_cons (scm_from_bool (scm_integer_logbit_uz (i, nn)), ret); } else SCM_WRONG_TYPE_ARG (SCM_ARG1, n); return ret; } #undef FUNC_NAME SCM_DEFINE (scm_srfi60_list_to_integer, "list->integer", 1, 0, 0, (SCM lst), "Return an integer formed bitwise from the given @var{lst} list\n" "of booleans. Each boolean is @code{#t} for a 1 and @code{#f}\n" "for a 0. The first element becomes the most significant bit in\n" "the return.\n" "\n" "@example\n" "(list->integer '(#t #f #t #f)) @result{} 10\n" "@end example") #define FUNC_NAME s_scm_srfi60_list_to_integer { long len; /* strip high zero bits from lst; after this the length tells us whether an inum or bignum is required */ while (scm_is_pair (lst) && scm_is_false (SCM_CAR (lst))) lst = SCM_CDR (lst); SCM_VALIDATE_LIST_COPYLEN (SCM_ARG1, lst, len); if (len <= SCM_I_FIXNUM_BIT - 1) { /* fits an inum (a positive inum) */ long n = 0; while (scm_is_pair (lst)) { n <<= 1; if (! scm_is_false (SCM_CAR (lst))) n++; lst = SCM_CDR (lst); } return SCM_I_MAKINUM (n); } else { mpz_t z; mpz_init (z); while (scm_is_pair (lst)) { len--; if (! scm_is_false (SCM_CAR (lst))) mpz_setbit (z, len); lst = SCM_CDR (lst); } SCM ret = scm_from_mpz (z); mpz_clear (z); return ret; } } #undef FUNC_NAME /* note: don't put "scm_srfi60_list_to_integer" arg on its own line, a newline breaks the snarfer */ SCM_REGISTER_PROC (s_srfi60_booleans_to_integer, "booleans->integer", 0, 0, 1, scm_srfi60_list_to_integer); void scm_register_srfi_60 (void) { scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION, "scm_init_srfi_60", (scm_t_extension_init_func)scm_init_srfi_60, NULL); } void scm_init_srfi_60 (void) { #ifndef SCM_MAGIC_SNARFER #include "srfi-60.x" #endif }