Post Scarcity
A prototype for a post scarcity programming environment
Loading...
Searching...
No Matches
integer.c
Go to the documentation of this file.
1/*
2 * integer.c
3 *
4 * functions for integer cells.
5 *
6 * (c) 2017 Simon Brooke <simon@journeyman.cc>
7 * Licensed under GPL version 2.0, or, at your option, any later version.
8 */
9
10#define _GNU_SOURCE
11#include <limits.h>
12#include <math.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <inttypes.h>
16/*
17 * wide characters
18 */
19#include <wchar.h>
20#include <wctype.h>
21
22#include "arith/integer.h"
23#include "arith/peano.h"
24#include "debug.h"
25#include "memory/conspage.h"
27#include "ops/equal.h"
28#include "ops/lispops.h"
29
30/**
31 * hexadecimal digits for printing numbers.
32 */
33const char *hex_digits = "0123456789ABCDEF";
34
35/*
36 * Doctrine from here on in is that ALL integers are bignums, it's just
37 * that integers less than 61 bits are bignums of one cell only.
38 * that integers less than 61 bits are bignums of one cell only.
39 * TODO: why do I not have confidence to make this 64 bits?
40 */
41
42 /*
43 * A small_int_cache array of pointers to the integers 0...23,
44 * used only by functions `acquire_integer(int64) => cons_pointer` and
45 * `release_integer(cons_pointer) => NULL` which, if the value desired is
46 * in the cache, supplies it from the cache, and, otherwise, calls
47 * make_integer() and dec_ref() respectively.
48 */
49
50#define SMALL_INT_LIMIT 24
53
54 /**
55 * Low level integer arithmetic, do not use elsewhere.
56 *
57 * @param c a pointer to a cell, assumed to be an integer cell;
58 * @param op a character representing the operation: expected to be either
59 * '+' or '*'; behaviour with other values is undefined.
60 * @param is_first_cell true if this is the first cell in a bignum
61 * chain, else false.
62 * \see multiply_integers
63 * \see add_integers
64 */
65__int128_t cell_value( struct cons_pointer c, char op, bool is_first_cell ) {
66 long int val = nilp( c ) ? 0 : pointer2cell( c ).payload.integer.value;
67
68 long int carry = is_first_cell ? 0 : ( INT_CELL_BASE );
69
70 __int128_t result = ( __int128_t ) integerp( c ) ?
71 ( val == 0 ) ? carry : val : op == '*' ? 1 : 0;
73 L"cell_value: raw value is %ld, is_first_cell = %s; '%4.4s'; returning ",
74 val, is_first_cell ? "true" : "false",
75 pointer2cell( c ).tag.bytes );
78
79 return result;
80}
81
82
83/**
84 * Allocate an integer cell representing this `value` and return a cons_pointer to it.
85 * @param value an integer value;
86 * @param more `NIL`, or a pointer to the more significant cell(s) of this number.
87 * *NOTE* that if `more` is not `NIL`, `value` *must not* exceed `MAX_INTEGER`.
88 */
89struct cons_pointer make_integer( int64_t value, struct cons_pointer more ) {
90 struct cons_pointer result = NIL;
91 debug_print( L"Entering make_integer\n", DEBUG_ALLOC );
92
93 if ( integerp( more )
94 && ( pointer2cell( more ).payload.integer.value < 0 ) ) {
95 printf( "WARNING: negative value %" PRId64
96 " passed as `more` to `make_integer`\n",
97 pointer2cell( more ).payload.integer.value );
98 }
99
100 if ( integerp( more ) || nilp( more ) ) {
101 result = allocate_cell( INTEGERTV );
102 struct cons_space_object *cell = &pointer2cell( result );
103 cell->payload.integer.value = value;
104 cell->payload.integer.more = more;
105 }
106
107 debug_print( L"make_integer: returning\n", DEBUG_ALLOC );
109 return result;
110}
111
112/**
113 * @brief Supply small valued integers from the small integer cache, if available.
114 *
115 * The pattern here is intended to be that, at least within this file, instead of
116 * calling make_integer when an integer is required and dec_ref when it's no longer
117 * required, we call acquire_integer and release_integer respectively, in order to
118 * reduce allocation churn.
119 *
120 * In the initial implementation, acquire_integer supplies the integer from the
121 * small integer cache if available, else calls make_integer. Later, more
122 * sophisticated caching of integers which are currently in play may be enabled.
123 *
124 * @param value the value of the integer desired.
125 * @param more if this value is a bignum, the rest (less significant bits) of the
126 * value.
127 * @return struct cons_pointer a pointer to the integer acquired.
128 */
129struct cons_pointer acquire_integer( int64_t value, struct cons_pointer more ) {
130 struct cons_pointer result;
131
132 if ( !nilp( more ) || value < 0 || value >= SMALL_INT_LIMIT ) {
134 ( L"acquire_integer passing to make_integer (outside small int range)\n",
135 DEBUG_ALLOC );
136 result = make_integer( value, more );
137 } else {
139 for ( int64_t i = 0; i < SMALL_INT_LIMIT; i++ ) {
141 pointer2cell( small_int_cache[i] ).count = UINT32_MAX; // lock it in so it can't be GC'd
142 }
144 debug_print( L"small_int_cache initialised.\n", DEBUG_ALLOC );
145 }
146
147 debug_printf( DEBUG_ALLOC, L"acquire_integer: returning %" PRId64 "\n",
148 value );
149 result = small_int_cache[value];
150 }
151 return result;
152}
153
154/**
155 * @brief if the value of p is less than the size of the small integer cache
156 * (and thus it was presumably supplied from there), suppress dec_ref.
157 *
158 * **NOTE THAT** at this stage it's still safe to dec_ref an arbitrary integer,
159 * because those in the cache are locked and can't be dec_refed.
160 *
161 * @param p a pointer, expected to be to an integer.
162 */
164 struct cons_space_object o = pointer2cell( p );
165 if ( !integerp( p ) || // what I've been passed isn't an integer;
166 !nilp( o.payload.integer.more ) || // or it's a bignum;
167 o.payload.integer.value >= SMALL_INT_LIMIT || // or it's bigger than the small int cache limit;
168 !eq( p, small_int_cache[o.payload.integer.value] ) // or it's simply not the copy in the cache...
169 ) {
170 dec_ref( p );
171 } else {
172 debug_printf( DEBUG_ALLOC, L"release_integer: releasing %" PRId64 "\n",
173 o.payload.integer.value );
174 }
175}
176
177
178/**
179 * @brief Overwrite the value field of the integer indicated by `new` with
180 * the least significant INTEGER_BITS bits of `val`, and return the
181 * more significant bits (if any) right-shifted by INTEGER_BITS places.
182 *
183 * Destructive, primitive, DO NOT USE in any context except primitive
184 * operations on integers. The value passed as `new` MUST be constructed
185 * with `make_integer`, NOT acquired with `acquire_integer`.
186 *
187 * @param val the value to represent;
188 * @param less_significant the less significant words of this bignum, if any,
189 * else NIL;
190 * @param new a newly created integer, which will be destructively changed.
191 * @return carry, if any, else 0.
192 */
193__int128_t int128_to_integer( __int128_t val,
194 struct cons_pointer less_significant,
195 struct cons_pointer new ) {
196 __int128_t carry = 0;
197
198 if ( MAX_INTEGER >= val ) {
199 carry = 0;
200 } else {
201 carry = val % INT_CELL_BASE;
203 L"int128_to_integer: 64 bit overflow; setting carry to %ld\n",
204 ( int64_t ) carry );
205 val /= INT_CELL_BASE;
206 }
207
208 struct cons_space_object *newc = &pointer2cell( new );
209 newc->payload.integer.value = ( int64_t ) val;
210
211 if ( integerp( less_significant ) ) {
212 struct cons_space_object *lsc = &pointer2cell( less_significant );
213 inc_ref( new );
214 lsc->payload.integer.more = new;
215 }
216
217 return carry;
218}
219
220/**
221 * Return a pointer to an integer representing the sum of the integers
222 * pointed to by `a` and `b`. If either isn't an integer, will return nil.
223 */
225 struct cons_pointer b ) {
226 struct cons_pointer result = NIL;
227 struct cons_pointer cursor = NIL;
228
229 debug_print( L"add_integers: a = ", DEBUG_ARITH );
231 debug_print( L"; b = ", DEBUG_ARITH );
234
235 __int128_t carry = 0;
236 bool is_first_cell = true;
237
238 if ( integerp( a ) && integerp( b ) ) {
239 debug_print( L"add_integers: \n", DEBUG_ARITH );
241 debug_print( L" plus \n", DEBUG_ARITH );
244
245 while ( !nilp( a ) || !nilp( b ) || carry != 0 ) {
246 __int128_t av = cell_value( a, '+', is_first_cell );
247 __int128_t bv = cell_value( b, '+', is_first_cell );
248 __int128_t rv = ( av + bv ) + carry;
249
250 debug_print( L"add_integers: av = ", DEBUG_ARITH );
252 debug_print( L"; bv = ", DEBUG_ARITH );
254 debug_print( L"; carry = ", DEBUG_ARITH );
256 debug_print( L"; rv = ", DEBUG_ARITH );
258 debug_print( L"\n", DEBUG_ARITH );
259
260 if ( carry == 0 && ( rv >= 0 || rv < SMALL_INT_LIMIT ) ) {
261 result =
262 acquire_integer( ( int64_t ) ( rv & 0xffffffff ), NIL );
263 break;
264 } else {
265 struct cons_pointer new = make_integer( 0, NIL );
266 carry = int128_to_integer( rv, cursor, new );
267 cursor = new;
268
269 if ( nilp( result ) ) {
270 result = cursor;
271 }
272
273 a = pointer2cell( a ).payload.integer.more;
274 b = pointer2cell( b ).payload.integer.more;
275 is_first_cell = false;
276 }
277 }
278 }
279
280 debug_print( L"add_integers returning: ", DEBUG_ARITH );
283
284 return result;
285}
286
287// TODO: I have really no idea what I was trying to do here, or why it could possibly be a good idea.
288struct cons_pointer base_partial( int depth ) {
289 struct cons_pointer result = NIL;
290
291 debug_printf( DEBUG_ARITH, L"base_partial: depth = %d\n", depth );
292
293 for ( int i = 0; i < depth; i++ ) {
294 result = acquire_integer( 0, result );
295 }
296
297 return result;
298}
299
300/**
301 * @brief Return a copy of this `partial` with this `digit` appended.
302 *
303 * @param partial the more significant bits of a possible bignum.
304 * @param digit the less significant bits of that possible bignum. NOTE: the
305 * name `digit` is technically correct but possibly misleading, because the
306 * numbering system here is base INT_CELL_BASE, currently x0fffffffffffffffL
307 */
308struct cons_pointer append_cell( struct cons_pointer partial,
309 struct cons_pointer digit ) {
310 struct cons_space_object cell = pointer2cell( partial );
311 // TODO: I should recursively copy the whole bignum chain, because
312 // we're still destructively modifying the end of it.
313 struct cons_pointer c = make_integer( cell.payload.integer.value,
314 cell.payload.integer.more );
315 struct cons_pointer result = partial;
316
317 if ( nilp( partial ) ) {
318 result = digit;
319 } else {
320 // find the last digit in the chain...
321 while ( !nilp( pointer2cell( c ).payload.integer.more ) ) {
322 c = pointer2cell( c ).payload.integer.more;
323 }
324
325 ( pointer2cell( c ) ).payload.integer.more = digit;
326 }
327 return result;
328}
329
330
331
332/**
333 * Return a pointer to an integer representing the product of the integers
334 * pointed to by `a` and `b`. If either isn't an integer, will return nil.
335 *
336 * Yes, this is one of Muhammad ibn Musa al-Khwarizmi's original recipes, so
337 * you'd think it would be easy; the reason that each step is documented is
338 * because I did not find it so.
339 *
340 * @param a an integer;
341 * @param b an integer.
342 */
344 struct cons_pointer b ) {
345 struct cons_pointer result = acquire_integer( 0, NIL );
346 bool neg = is_negative( a ) != is_negative( b );
347 bool is_first_b = true;
348 int i = 0;
349
350 debug_print( L"multiply_integers: a = ", DEBUG_ARITH );
352 debug_print( L"; b = ", DEBUG_ARITH );
355
356 if ( integerp( a ) && integerp( b ) ) {
357 /* for each digit in a, starting with the least significant (ai) */
358
359 for ( struct cons_pointer ai = a; !nilp( ai );
360 ai = pointer2cell( ai ).payload.integer.more ) {
361 /* set carry to 0 */
362 __int128_t carry = 0;
363
364 /* set least significant digits for result ri for this iteration
365 * to i zeros */
366 struct cons_pointer ri = base_partial( i++ );
367
368 /* for each digit in b, starting with the least significant (bj) */
369 for ( struct cons_pointer bj = b; !nilp( bj );
370 bj = pointer2cell( bj ).payload.integer.more ) {
371
373 L"multiply_integers: a[i] = %Ld, b[j] = %Ld, i = %d\n",
374 pointer2cell( ai ).payload.integer.value,
375 pointer2cell( bj ).payload.integer.value, i );
376
377 /* multiply ai with bj and add the carry, resulting in a
378 * value xj which may exceed one digit */
379 __int128_t xj = pointer2cell( ai ).payload.integer.value *
380 pointer2cell( bj ).payload.integer.value;
381 xj += carry;
382
383 /* if xj exceeds one digit, break it into the digit dj and
384 * the carry */
385 carry = xj >> INTEGER_BIT_SHIFT;
386 struct cons_pointer dj =
388
389 replace_integer_p( ri, append_cell( ri, dj ) );
390 // struct cons_pointer new_ri = append_cell( ri, dj );
391 // release_integer( ri);
392 // ri = new_ri;
393 } /* end for bj */
394
395 /* if carry is not equal to zero, append it as a final cell
396 * to ri */
397 if ( carry != 0 ) {
398 replace_integer_i( ri, carry )
399 }
400
401 /* add ri to result */
402 result = add_integers( result, ri );
403
404 debug_print( L"multiply_integers: result is ", DEBUG_ARITH );
407 } /* end for ai */
408 }
409
410 debug_print( L"multiply_integers returning: ", DEBUG_ARITH );
413
414 return result;
415}
416
417/**
418 * don't use; private to integer_to_string, and somewhat dodgy.
419 */
420struct cons_pointer integer_to_string_add_digit( int digit, int digits,
421 struct cons_pointer tail ) {
422 wint_t character = btowc( hex_digits[digit] );
424 L"integer_to_string_add_digit: digit is %d, digits is %d; returning: ",
425 digit, digits );
426 struct cons_pointer r =
427 ( digits % 3 == 0 ) ? make_string( L',', make_string( character,
428 tail ) ) :
429 make_string( character, tail );
430
433
434 return r;
435}
436
437/**
438 * @brief return a string representation of this integer, which may be a
439 * bignum.
440 *
441 * The general principle of printing a bignum is that you print the least
442 * significant digit in whatever base you're dealing with, divide through
443 * by the base, print the next, and carry on until you've none left.
444 * Obviously, that means you print from right to left. Given that we build
445 * strings from right to left, 'printing' an integer to a lisp string
446 * would seem reasonably easy. The problem is when you jump from one integer
447 * object to the next. 64 bit integers don't align with decimal numbers, so
448 * when we get to the last digit from one integer cell, we have potentially
449 * to be looking to the next. H'mmmm.
450 *
451 * @param int_pointer cons_pointer to the integer to print,
452 * @param base the base to print it in.
453 */
454struct cons_pointer integer_to_string( struct cons_pointer int_pointer,
455 int base ) {
456 struct cons_pointer result = NIL;
457
458 if ( integerp( int_pointer ) ) {
459 struct cons_pointer next =
460 pointer2cell( int_pointer ).payload.integer.more;
461 __int128_t accumulator =
462 llabs( pointer2cell( int_pointer ).payload.integer.value );
463 bool is_negative =
464 pointer2cell( int_pointer ).payload.integer.value < 0;
465 int digits = 0;
466
467 if ( accumulator == 0 && nilp( next ) ) {
468 result = c_string_to_lisp_string( L"0" );
469 } else {
470 while ( accumulator > 0 || !nilp( next ) ) {
471 if ( accumulator < MAX_INTEGER && !nilp( next ) ) {
472 accumulator +=
473 ( pointer2cell( next ).payload.integer.value %
475 next = pointer2cell( next ).payload.integer.more;
476 }
477 int offset = ( int ) ( accumulator % base );
479 L"integer_to_string: digit is %ld, hexadecimal is %c, accumulator is: ",
481 debug_print_128bit( accumulator, DEBUG_IO );
482 debug_print( L"; result is: ", DEBUG_IO );
483 debug_print_object( result, DEBUG_IO );
485
486 result =
487 integer_to_string_add_digit( offset, ++digits, result );
488 accumulator = accumulator / base;
489 }
490
491 if ( stringp( result )
492 && pointer2cell( result ).payload.string.character == L',' ) {
493 /* if the number of digits in the string is divisible by 3, there will be
494 * an unwanted comma on the front. */
495 result = pointer2cell( result ).payload.string.cdr;
496 }
497
498
499 if ( is_negative ) {
500 result = make_string( L'-', result );
501 }
502 }
503 }
504
505 return result;
506}
507
508/**
509 * true if a and be are both integers whose value is the same value.
510 */
512 bool result = false;
513
514 if ( integerp( a ) && integerp( b ) ) {
515 struct cons_space_object *cell_a = &pointer2cell( a );
516 struct cons_space_object *cell_b = &pointer2cell( b );
517
518 result =
519 cell_a->payload.integer.value == cell_b->payload.integer.value;
520 }
521
522 return result;
523}
struct cons_pointer allocate_cell(uint32_t tag)
Allocates a cell with the specified tag.
Definition conspage.c:222
struct cons_pointer inc_ref(struct cons_pointer pointer)
increment the reference count of the object at this cons pointer.
struct cons_pointer dec_ref(struct cons_pointer pointer)
Decrement the reference count of the object at this cons pointer.
union cons_space_object::@3 payload
#define NIL
a cons pointer which points to the special NIL cell
#define INTEGERTV
The string INTR, considered as an unsigned int.
struct cons_pointer make_string(wint_t c, struct cons_pointer tail)
Construct a string from the character c and this tail.
#define nilp(conspoint)
true if conspoint points to the special cell NIL, else false (there should only be one of these so it...
uint32_t offset
the index of the cell within the page
struct cons_pointer c_string_to_lisp_string(wchar_t *string)
Return a lisp string representation of this wide character string.
#define stringp(conspoint)
true if conspoint points to a string cell, else false
#define pointer2cell(pointer)
given a cons_pointer as argument, return the cell.
#define integerp(conspoint)
true if conspoint points to an integer cell, else false
An indirect pointer to a cons cell.
an object in cons space.
void debug_print_128bit(__int128_t n, int level)
print a 128 bit integer value to stderr, if verbosity matches level.
Definition debug.c:58
void debug_println(int level)
print a line feed to stderr, if verbosity matches level.
Definition debug.c:85
void debug_dump_object(struct cons_pointer pointer, int level)
Like dump_object, q.v., but protected by the verbosity mechanism.
Definition debug.c:136
void debug_printf(int level, wchar_t *format,...)
wprintf adapted for the debug logging system.
Definition debug.c:101
void debug_print(wchar_t *message, int level)
print this debug message to stderr, if verbosity matches level.
Definition debug.c:41
void debug_print_object(struct cons_pointer pointer, int level)
print the object indicated by this pointer to stderr, if verbosity matches level.
Definition debug.c:119
#define DEBUG_ARITH
Print messages debugging arithmetic operations.
Definition debug.h:28
#define DEBUG_IO
Print messages debugging input/output operations.
Definition debug.h:56
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:21
bool eq(struct cons_pointer a, struct cons_pointer b)
Shallow, and thus cheap, equality: true if these two objects are the same object, else false.
Definition equal.c:24
struct cons_pointer append_cell(struct cons_pointer partial, struct cons_pointer digit)
Return a copy of this partial with this digit appended.
Definition integer.c:308
bool small_int_cache_initialised
Definition integer.c:51
bool equal_integer_integer(struct cons_pointer a, struct cons_pointer b)
true if a and be are both integers whose value is the same value.
Definition integer.c:511
struct cons_pointer multiply_integers(struct cons_pointer a, struct cons_pointer b)
Return a pointer to an integer representing the product of the integers pointed to by a and b.
Definition integer.c:343
#define SMALL_INT_LIMIT
Definition integer.c:50
const char * hex_digits
hexadecimal digits for printing numbers.
Definition integer.c:33
void release_integer(struct cons_pointer p)
if the value of p is less than the size of the small integer cache (and thus it was presumably suppli...
Definition integer.c:163
__int128_t int128_to_integer(__int128_t val, struct cons_pointer less_significant, struct cons_pointer new)
Overwrite the value field of the integer indicated by new with the least significant INTEGER_BITS bit...
Definition integer.c:193
__int128_t cell_value(struct cons_pointer c, char op, bool is_first_cell)
Low level integer arithmetic, do not use elsewhere.
Definition integer.c:65
struct cons_pointer add_integers(struct cons_pointer a, struct cons_pointer b)
Return a pointer to an integer representing the sum of the integers pointed to by a and b.
Definition integer.c:224
struct cons_pointer small_int_cache[SMALL_INT_LIMIT]
Definition integer.c:52
struct cons_pointer integer_to_string(struct cons_pointer int_pointer, int base)
return a string representation of this integer, which may be a bignum.
Definition integer.c:454
struct cons_pointer integer_to_string_add_digit(int digit, int digits, struct cons_pointer tail)
don't use; private to integer_to_string, and somewhat dodgy.
Definition integer.c:420
struct cons_pointer acquire_integer(int64_t value, struct cons_pointer more)
Supply small valued integers from the small integer cache, if available.
Definition integer.c:129
struct cons_pointer base_partial(int depth)
Definition integer.c:288
struct cons_pointer make_integer(int64_t value, struct cons_pointer more)
Allocate an integer cell representing this value and return a cons_pointer to it.
Definition integer.c:89
#define replace_integer_i(p, i)
Definition integer.h:17
#define replace_integer_p(p, q)
Definition integer.h:18
bool is_negative(struct cons_pointer arg)
does this arg point to a negative number?
Definition peano.c:70
#define MAX_INTEGER
The maximum value we will allow in an integer cell: one less than 2^60: (let ((s (make-string-output-...
Definition peano.h:25
#define INT_CELL_BASE
Definition peano.h:26
#define INTEGER_BIT_SHIFT
Number of value bits in an integer cell.
Definition peano.h:32