Include <config.h> in all C files; use `#ifdef HAVE_CONFIG_H' rather than `#if'.
[bpt/guile.git] / srfi / srfi-60.c
1 /* srfi-60.c --- Integers as Bits
2 *
3 * Copyright (C) 2005, 2006, 2008 Free Software Foundation, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <libguile.h>
25 #include <srfi/srfi-60.h>
26
27
28 SCM_DEFINE (scm_srfi60_log2_binary_factors, "log2-binary-factors", 1, 0, 0,
29 (SCM n),
30 "Return a count of how many factors of 2 are present in @var{n}.\n"
31 "This is also the bit index of the lowest 1 bit in @var{n}. If\n"
32 "@var{n} is 0, the return is @math{-1}.\n"
33 "\n"
34 "@example\n"
35 "(log2-binary-factors 6) @result{} 1\n"
36 "(log2-binary-factors -8) @result{} 3\n"
37 "@end example")
38 #define FUNC_NAME s_scm_srfi60_log2_binary_factors
39 {
40 SCM ret = SCM_EOL;
41
42 if (SCM_I_INUMP (n))
43 {
44 long nn = SCM_I_INUM (n);
45 if (nn == 0)
46 return SCM_I_MAKINUM (-1);
47 nn = nn ^ (nn-1); /* 1 bits for each low 0 and lowest 1 */
48 return scm_logcount (SCM_I_MAKINUM (nn >> 1));
49 }
50 else if (SCM_BIGP (n))
51 {
52 /* no need for scm_remember_upto_here_1 here, mpz_scan1 doesn't do
53 anything that could result in a gc */
54 return SCM_I_MAKINUM (mpz_scan1 (SCM_I_BIG_MPZ (n), 0L));
55 }
56 else
57 SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
58
59 return ret;
60 }
61 #undef FUNC_NAME
62
63
64 SCM_DEFINE (scm_srfi60_copy_bit, "copy-bit", 3, 0, 0,
65 (SCM index, SCM n, SCM bit),
66 "Return @var{n} with the bit at @var{index} set according to\n"
67 "@var{newbit}. @var{newbit} should be @code{#t} to set the bit\n"
68 "to 1, or @code{#f} to set it to 0. Bits other than at\n"
69 "@var{index} are unchanged in the return.\n"
70 "\n"
71 "@example\n"
72 "(copy-bit 1 #b0101 #t) @result{} 7\n"
73 "@end example")
74 #define FUNC_NAME s_scm_srfi60_copy_bit
75 {
76 SCM r;
77 unsigned long ii;
78 int bb;
79
80 ii = scm_to_ulong (index);
81 bb = scm_to_bool (bit);
82
83 if (SCM_I_INUMP (n))
84 {
85 long nn = SCM_I_INUM (n);
86
87 /* can't set high bit ii==SCM_LONG_BIT-1, that would change the sign,
88 which is not what's wanted */
89 if (ii < SCM_LONG_BIT-1)
90 {
91 nn &= ~(1L << ii); /* zap bit at index */
92 nn |= ((long) bb << ii); /* insert desired bit */
93 return scm_from_long (nn);
94 }
95 else
96 {
97 /* bits at ii==SCM_LONG_BIT-1 and above are all copies of the sign
98 bit, if this is already the desired "bit" value then no need to
99 make a new bignum value */
100 if (bb == (nn < 0))
101 return n;
102
103 r = scm_i_long2big (nn);
104 goto big;
105 }
106 }
107 else if (SCM_BIGP (n))
108 {
109 /* if the bit is already what's wanted then no need to make a new
110 bignum */
111 if (bb == mpz_tstbit (SCM_I_BIG_MPZ (n), ii))
112 return n;
113
114 r = scm_i_clonebig (n, 1);
115 big:
116 if (bb)
117 mpz_setbit (SCM_I_BIG_MPZ (r), ii);
118 else
119 mpz_clrbit (SCM_I_BIG_MPZ (r), ii);
120
121 /* changing a high bit might put the result into range of a fixnum */
122 return scm_i_normbig (r);
123 }
124 else
125 SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
126 }
127 #undef FUNC_NAME
128
129
130 SCM_DEFINE (scm_srfi60_rotate_bit_field, "rotate-bit-field", 4, 0, 0,
131 (SCM n, SCM count, SCM start, SCM end),
132 "Return @var{n} with the bit field from @var{start} (inclusive)\n"
133 "to @var{end} (exclusive) rotated upwards by @var{count} bits.\n"
134 "\n"
135 "@var{count} can be positive or negative, and it can be more\n"
136 "than the field width (it'll be reduced modulo the width).\n"
137 "\n"
138 "@example\n"
139 "(rotate-bit-field #b0110 2 1 4) @result{} #b1010\n"
140 "@end example")
141 #define FUNC_NAME s_scm_srfi60_rotate_bit_field
142 {
143 unsigned long ss = scm_to_ulong (start);
144 unsigned long ee = scm_to_ulong (end);
145 unsigned long ww, cc;
146
147 SCM_ASSERT_RANGE (3, end, (ee >= ss));
148 ww = ee - ss;
149
150 cc = scm_to_ulong (scm_modulo (count, scm_difference (end, start)));
151
152 if (SCM_I_INUMP (n))
153 {
154 long nn = SCM_I_INUM (n);
155
156 if (ee <= SCM_LONG_BIT-1)
157 {
158 /* all within a long */
159 long below = nn & ((1L << ss) - 1); /* before start */
160 long above = nn & (-1L << ee); /* above end */
161 long fmask = (-1L << ss) & ((1L << ee) - 1); /* field mask */
162 long ff = nn & fmask; /* field */
163
164 return scm_from_long (above
165 | ((ff << cc) & fmask)
166 | ((ff >> (ww-cc)) & fmask)
167 | below);
168 }
169 else
170 {
171 /* either no movement, or a field of only 0 or 1 bits, result
172 unchanged, avoid creating a bignum */
173 if (cc == 0 || ww <= 1)
174 return n;
175
176 n = scm_i_long2big (nn);
177 goto big;
178 }
179 }
180 else if (SCM_BIGP (n))
181 {
182 mpz_t tmp;
183 SCM r;
184
185 /* either no movement, or in a field of only 0 or 1 bits, result
186 unchanged, avoid creating a new bignum */
187 if (cc == 0 || ww <= 1)
188 return n;
189
190 big:
191 r = scm_i_ulong2big (0);
192 mpz_init (tmp);
193
194 /* portion above end */
195 mpz_fdiv_q_2exp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (n), ee);
196 mpz_mul_2exp (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r), ee);
197
198 /* field high part, width-count bits from start go to start+count */
199 mpz_fdiv_q_2exp (tmp, SCM_I_BIG_MPZ (n), ss);
200 mpz_fdiv_r_2exp (tmp, tmp, ww - cc);
201 mpz_mul_2exp (tmp, tmp, ss + cc);
202 mpz_ior (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r), tmp);
203
204 /* field high part, count bits from end-count go to start */
205 mpz_fdiv_q_2exp (tmp, SCM_I_BIG_MPZ (n), ee - cc);
206 mpz_fdiv_r_2exp (tmp, tmp, cc);
207 mpz_mul_2exp (tmp, tmp, ss);
208 mpz_ior (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r), tmp);
209
210 /* portion below start */
211 mpz_fdiv_r_2exp (tmp, SCM_I_BIG_MPZ (n), ss);
212 mpz_ior (SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (r), tmp);
213
214 mpz_clear (tmp);
215
216 /* bits moved around might leave us in range of an inum */
217 return scm_i_normbig (r);
218 }
219 else
220 SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
221 }
222 #undef FUNC_NAME
223
224
225 SCM_DEFINE (scm_srfi60_reverse_bit_field, "reverse-bit-field", 3, 0, 0,
226 (SCM n, SCM start, SCM end),
227 "Return @var{n} with the bits between @var{start} (inclusive) to\n"
228 "@var{end} (exclusive) reversed.\n"
229 "\n"
230 "@example\n"
231 "(reverse-bit-field #b101001 2 4) @result{} #b100101\n"
232 "@end example")
233 #define FUNC_NAME s_scm_srfi60_reverse_bit_field
234 {
235 long ss = scm_to_long (start);
236 long ee = scm_to_long (end);
237 long swaps = (ee - ss) / 2; /* number of swaps */
238 SCM b;
239
240 if (SCM_I_INUMP (n))
241 {
242 long nn = SCM_I_INUM (n);
243
244 if (ee <= SCM_LONG_BIT-1)
245 {
246 /* all within a long */
247 long smask = 1L << ss;
248 long emask = 1L << (ee-1);
249 for ( ; swaps > 0; swaps--)
250 {
251 long sbit = nn & smask;
252 long ebit = nn & emask;
253 nn ^= sbit ^ (ebit ? smask : 0) /* zap sbit, put ebit value */
254 ^ ebit ^ (sbit ? emask : 0); /* zap ebit, put sbit value */
255
256 smask <<= 1;
257 emask >>= 1;
258 }
259 return scm_from_long (nn);
260 }
261 else
262 {
263 /* avoid creating a new bignum if reversing only 0 or 1 bits */
264 if (ee - ss <= 1)
265 return n;
266
267 b = scm_i_long2big (nn);
268 goto big;
269 }
270 }
271 else if (SCM_BIGP (n))
272 {
273 /* avoid creating a new bignum if reversing only 0 or 1 bits */
274 if (ee - ss <= 1)
275 return n;
276
277 b = scm_i_clonebig (n, 1);
278 big:
279
280 ee--;
281 for ( ; swaps > 0; swaps--)
282 {
283 int sbit = mpz_tstbit (SCM_I_BIG_MPZ (b), ss);
284 int ebit = mpz_tstbit (SCM_I_BIG_MPZ (b), ee);
285 if (sbit ^ ebit)
286 {
287 /* the two bits are different, flip them */
288 if (sbit)
289 {
290 mpz_clrbit (SCM_I_BIG_MPZ (b), ss);
291 mpz_setbit (SCM_I_BIG_MPZ (b), ee);
292 }
293 else
294 {
295 mpz_setbit (SCM_I_BIG_MPZ (b), ss);
296 mpz_clrbit (SCM_I_BIG_MPZ (b), ee);
297 }
298 }
299 ss++;
300 ee--;
301 }
302 /* swapping zero bits into the high might make us fit a fixnum */
303 return scm_i_normbig (b);
304 }
305 else
306 SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
307 }
308 #undef FUNC_NAME
309
310
311 SCM_DEFINE (scm_srfi60_integer_to_list, "integer->list", 1, 1, 0,
312 (SCM n, SCM len),
313 "Return bits from @var{n} in the form of a list of @code{#t} for\n"
314 "1 and @code{#f} for 0. The least significant @var{len} bits\n"
315 "are returned, and the first list element is the most\n"
316 "significant of those bits. If @var{len} is not given, the\n"
317 "default is @code{(integer-length @var{n})} (@pxref{Bitwise\n"
318 "Operations}).\n"
319 "\n"
320 "@example\n"
321 "(integer->list 6) @result{} (#t #t #f)\n"
322 "(integer->list 1 4) @result{} (#f #f #f #t)\n"
323 "@end example")
324 #define FUNC_NAME s_scm_srfi60_integer_to_list
325 {
326 SCM ret = SCM_EOL;
327 unsigned long ll, i;
328
329 if (SCM_UNBNDP (len))
330 len = scm_integer_length (n);
331 ll = scm_to_ulong (len);
332
333 if (SCM_I_INUMP (n))
334 {
335 long nn = SCM_I_INUM (n);
336 for (i = 0; i < ll; i++)
337 {
338 unsigned long shift =
339 (i < ((unsigned long) SCM_LONG_BIT-1))
340 ? i : ((unsigned long) SCM_LONG_BIT-1);
341 int bit = (nn >> shift) & 1;
342 ret = scm_cons (scm_from_bool (bit), ret);
343 }
344 }
345 else if (SCM_BIGP (n))
346 {
347 for (i = 0; i < ll; i++)
348 ret = scm_cons (scm_from_bool (mpz_tstbit (SCM_I_BIG_MPZ (n), i)),
349 ret);
350 scm_remember_upto_here_1 (n);
351 }
352 else
353 SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
354
355 return ret;
356 }
357 #undef FUNC_NAME
358
359
360 SCM_DEFINE (scm_srfi60_list_to_integer, "list->integer", 1, 0, 0,
361 (SCM lst),
362 "Return an integer formed bitwise from the given @var{lst} list\n"
363 "of booleans. Each boolean is @code{#t} for a 1 and @code{#f}\n"
364 "for a 0. The first element becomes the most significant bit in\n"
365 "the return.\n"
366 "\n"
367 "@example\n"
368 "(list->integer '(#t #f #t #f)) @result{} 10\n"
369 "@end example")
370 #define FUNC_NAME s_scm_srfi60_list_to_integer
371 {
372 long len;
373
374 /* strip high zero bits from lst; after this the length tells us whether
375 an inum or bignum is required */
376 while (scm_is_pair (lst) && scm_is_false (SCM_CAR (lst)))
377 lst = SCM_CDR (lst);
378
379 SCM_VALIDATE_LIST_COPYLEN (SCM_ARG1, lst, len);
380
381 if (len <= SCM_I_FIXNUM_BIT - 1)
382 {
383 /* fits an inum (a positive inum) */
384 long n = 0;
385 while (scm_is_pair (lst))
386 {
387 n <<= 1;
388 if (! scm_is_false (SCM_CAR (lst)))
389 n++;
390 lst = SCM_CDR (lst);
391 }
392 return SCM_I_MAKINUM (n);
393 }
394 else
395 {
396 /* need a bignum */
397 SCM n = scm_i_ulong2big (0);
398 while (scm_is_pair (lst))
399 {
400 len--;
401 if (! scm_is_false (SCM_CAR (lst)))
402 mpz_setbit (SCM_I_BIG_MPZ (n), len);
403 lst = SCM_CDR (lst);
404 }
405 return n;
406 }
407 }
408 #undef FUNC_NAME
409
410
411 /* note: don't put "scm_srfi60_list_to_integer" arg on its own line, a
412 newline breaks the snarfer */
413 SCM_REGISTER_PROC (s_srfi60_booleans_to_integer, "booleans->integer", 0, 0, 1, scm_srfi60_list_to_integer);
414
415
416 void
417 scm_init_srfi_60 (void)
418 {
419 #ifndef SCM_MAGIC_SNARFER
420 #include "srfi/srfi-60.x"
421 #endif
422 }