Post Scarcity
A prototype for a post scarcity programming environment
Loading...
Searching...
No Matches
equal.c
Go to the documentation of this file.
1/*
2 * equal.c
3 *
4 * Checks for shallow and deep equality
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#include <math.h>
11#include <stdbool.h>
12#include <string.h>
13
14#include "arith/integer.h"
15#include "arith/peano.h"
16#include "arith/ratio.h"
17#include "debug.h"
18#include "memory/conspage.h"
20#include "memory/vectorspace.h"
21#include "ops/equal.h"
22#include "ops/intern.h"
23
24/**
25 * Shallow, and thus cheap, equality: true if these two objects are
26 * the same object, else false.
27 */
28bool eq( struct cons_pointer a, struct cons_pointer b ) {
29 return ( ( a.page == b.page ) && ( a.offset == b.offset ) );
30}
31
32/**
33 * True if the objects at these two cons pointers have the same tag, else false.
34 * @param a a pointer to a cons-space object;
35 * @param b another pointer to a cons-space object.
36 * @return true if the objects at these two cons pointers have the same tag,
37 * else false.
38 */
39bool same_type( struct cons_pointer a, struct cons_pointer b ) {
40 struct cons_space_object *cell_a = &pointer2cell( a );
41 struct cons_space_object *cell_b = &pointer2cell( b );
42
43 return cell_a->tag.value == cell_b->tag.value;
44}
45
46/**
47 * Some strings will be null terminated and some will be NIL terminated... ooops!
48 * @param string the string to test
49 * @return true if it's the end of a string.
50 */
51bool end_of_string( struct cons_pointer string ) {
52 return nilp( string ) ||
53 pointer2cell( string ).payload.string.character == '\0';
54}
55
56/**
57 * @brief compare two long doubles and returns true if they are the same to
58 * within a tolerance of one part in a billion.
59 *
60 * @param a
61 * @param b
62 * @return true if `a` and `b` are equal to within one part in a billion.
63 * @return false otherwise.
64 */
65bool equal_ld_ld( long double a, long double b ) {
66 long double fa = fabsl( a );
67 long double fb = fabsl( b );
68 /* difference of magnitudes */
69 long double diff = fabsl( fa - fb );
70 /* average magnitude of the two */
71 long double av = ( fa > fb ) ? ( fa - diff ) : ( fb - diff );
72 /* amount of difference we will tolerate for equality */
73 long double tolerance = av * 0.000000001;
74
75 bool result = ( fabsl( a - b ) < tolerance );
76
77 debug_printf( DEBUG_ARITH, L"\nequal_ld_ld returning %d\n", result );
78
79 return result;
80}
81
82/**
83 * @brief Private function, don't use. It depends on its arguments being
84 * numbers and doesn't sanity check them.
85 *
86 * @param a a lisp integer -- if it isn't an integer, things will break.
87 * @param b a lisp real -- if it isn't a real, things will break.
88 * @return true if the two numbers have equal value.
89 * @return false if they don't.
90 */
91bool equal_integer_real( struct cons_pointer a, struct cons_pointer b ) {
92 debug_print( L"\nequal_integer_real: ", DEBUG_ARITH );
94 debug_print( L" = ", DEBUG_ARITH );
96 bool result = false;
97 struct cons_space_object *cell_a = &pointer2cell( a );
98 struct cons_space_object *cell_b = &pointer2cell( b );
99
100 if ( nilp( cell_a->payload.integer.more ) ) {
101 result =
102 equal_ld_ld( ( long double ) cell_a->payload.integer.value,
103 cell_b->payload.real.value );
104 } else {
105 fwprintf( stderr,
106 L"\nequality is not yet implemented for bignums compared to reals." );
107 }
108
109 debug_printf( DEBUG_ARITH, L"\nequal_integer_real returning %d\n",
110 result );
111
112 return result;
113}
114
115/**
116 * @brief Private function, don't use. It depends on its arguments being
117 * numbers and doesn't sanity check them.
118 *
119 * @param a a lisp integer -- if it isn't an integer, things will break.
120 * @param b a lisp number.
121 * @return true if the two numbers have equal value.
122 * @return false if they don't.
123 */
125 debug_print( L"\nequal_integer_number: ", DEBUG_ARITH );
127 debug_print( L" = ", DEBUG_ARITH );
129 bool result = false;
130 struct cons_space_object *cell_b = &pointer2cell( b );
131
132 switch ( cell_b->tag.value ) {
133 case INTEGERTV:
134 result = equal_integer_integer( a, b );
135 break;
136 case REALTV:
137 result = equal_integer_real( a, b );
138 break;
139 case RATIOTV:
140 result = false;
141 break;
142 }
143
144 debug_printf( DEBUG_ARITH, L"\nequal_integer_number returning %d\n",
145 result );
146
147 return result;
148}
149
150/**
151 * @brief Private function, don't use. It depends on its arguments being
152 * numbers and doesn't sanity check them.
153 *
154 * @param a a lisp real -- if it isn't an real, things will break.
155 * @param b a lisp number.
156 * @return true if the two numbers have equal value.
157 * @return false if they don't.
158 */
159bool equal_real_number( struct cons_pointer a, struct cons_pointer b ) {
160 debug_print( L"\nequal_real_number: ", DEBUG_ARITH );
162 debug_print( L" = ", DEBUG_ARITH );
164 bool result = false;
165 struct cons_space_object *cell_b = &pointer2cell( b );
166
167 switch ( cell_b->tag.value ) {
168 case INTEGERTV:
169 result = equal_integer_real( b, a );
170 break;
171 case REALTV:{
172 struct cons_space_object *cell_a = &pointer2cell( a );
173 result =
174 equal_ld_ld( cell_a->payload.real.value,
175 cell_b->payload.real.value );
176 }
177 break;
178 case RATIOTV:
179 struct cons_space_object *cell_a = &pointer2cell( a );
180 result =
181 equal_ld_ld( c_ratio_to_ld( b ), cell_a->payload.real.value );
182 break;
183 }
184
185 debug_printf( DEBUG_ARITH, L"\nequal_real_number returning %d\n", result );
186
187 return result;
188}
189
190/**
191 * @brief Private function, don't use. It depends on its arguments being
192 * numbers and doesn't sanity check them.
193 *
194 * @param a a number
195 * @param b a number
196 * @return true if the two numbers have equal value.
197 * @return false if they don't.
198 */
200 bool result = eq( a, b );
201
202 debug_print( L"\nequal_number_number: ", DEBUG_ARITH );
204 debug_print( L" = ", DEBUG_ARITH );
206
207 if ( !result ) {
208 struct cons_space_object *cell_a = &pointer2cell( a );
209 struct cons_space_object *cell_b = &pointer2cell( b );
210
211 switch ( cell_a->tag.value ) {
212 case INTEGERTV:
213 result = equal_integer_number( a, b );
214 break;
215 case REALTV:
216 result = equal_real_number( a, b );
217 break;
218 case RATIOTV:
219 switch ( cell_b->tag.value ) {
220 case INTEGERTV:
221 /* as ratios are simplified by make_ratio, any
222 * ratio that would simplify to an integer is an
223 * integer, TODO: no longer always true. */
224 result = false;
225 break;
226 case REALTV:
227 result = equal_real_number( b, a );
228 break;
229 case RATIOTV:
230 result = equal_ratio_ratio( a, b );
231 break;
232 /* can't throw an exception from here, but non-numbers
233 * shouldn't have been passed in anyway, so no default. */
234 }
235 break;
236 /* can't throw an exception from here, but non-numbers
237 * shouldn't have been passed in anyway, so no default. */
238 }
239 }
240
241 debug_printf( DEBUG_ARITH, L"\nequal_number_number returning %d\n",
242 result );
243
244 return result;
245}
246
247/**
248 * @brief equality of two map-like things.
249 *
250 * The list returned by `keys` on a map-like thing is not sorted, and is not
251 * guaranteed always to come out in the same order. So equality is established
252 * if:
253 * 1. the length of the keys list is the same; and
254 * 2. the value of each key in the keys list for map `a` is the same in map `a`
255 * and in map `b`.
256 *
257 * Private function, do not use outside this file, **WILL NOT** work
258 * unless both arguments are VECPs.
259 *
260 * @param a a pointer to a vector space object.
261 * @param b another pointer to a vector space object.
262 * @return true if the two objects have the same logical structure.
263 * @return false otherwise.
264 */
265bool equal_map_map( struct cons_pointer a, struct cons_pointer b ) {
266 bool result = false;
267
268 struct cons_pointer keys_a = hashmap_keys( a );
269
270 if ( c_length( keys_a ) == c_length( hashmap_keys( b ) ) ) {
271 result = true;
272
273 for ( struct cons_pointer i = keys_a; !nilp( i ); i = c_cdr( i ) ) {
274 struct cons_pointer key = c_car( i );
275 if ( !equal( hashmap_get( a, key ), hashmap_get( b, key ) ) ) {
276 result = false;
277 break;
278 }
279 }
280 }
281
282 return result;
283}
284
285/**
286 * @brief equality of two vector-space things.
287 *
288 * Expensive, but we need to be able to check for equality of at least hashmaps
289 * and namespaces.
290 *
291 * Private function, do not use outside this file, not guaranteed to work
292 * unless both arguments are VECPs pointing to map like things.
293 *
294 * @param a a pointer to a vector space object.
295 * @param b another pointer to a vector space object.
296 * @return true if the two objects have the same logical structure.
297 * @return false otherwise.
298 */
300 bool result = false;
301
302 if ( eq( a, b ) ) {
303 result = true; // same
304 /* there shouldn't ever be two separate VECP cells which point to the
305 * same address in vector space, so I don't believe it's worth checking
306 * for this.
307 */
308 } else if ( vectorp( a ) && vectorp( b ) ) {
309 struct vector_space_object *va = pointer_to_vso( a );
310 struct vector_space_object *vb = pointer_to_vso( b );
311
312 /* what we're saying here is that a namespace is not equal to a map,
313 * even if they have identical logical structure. Is this right? */
314 if ( va->header.tag.value == vb->header.tag.value ) {
315 switch ( va->header.tag.value ) {
316 case HASHTV:
317 case NAMESPACETV:
318 result = equal_map_map( a, b );
319 break;
320 }
321 }
322 }
323 // else can't throw an exception from here but TODO: should log.
324
325 return result;
326}
327
328/**
329 * Deep, and thus expensive, equality: true if these two objects have
330 * identical structure, else false.
331 */
332bool equal( struct cons_pointer a, struct cons_pointer b ) {
333 debug_print( L"\nequal: ", DEBUG_ARITH );
335 debug_print( L" = ", DEBUG_ARITH );
337
338 bool result = false;
339
340 if ( eq( a, b ) ) {
341 result = true;
342 } else if ( !numberp( a ) && same_type( a, b ) ) {
343 struct cons_space_object *cell_a = &pointer2cell( a );
344 struct cons_space_object *cell_b = &pointer2cell( b );
345
346 switch ( cell_a->tag.value ) {
347 case CONSTV:
348 case LAMBDATV:
349 case NLAMBDATV:
350 /* TODO: it is not OK to do this on the stack since list-like
351 * structures can be of indefinite extent. It *must* be done by
352 * iteration (and even that is problematic) */
353 result =
354 equal( cell_a->payload.cons.car, cell_b->payload.cons.car )
355 && equal( cell_a->payload.cons.cdr,
356 cell_b->payload.cons.cdr );
357 break;
358 case KEYTV:
359 case STRINGTV:
360 case SYMBOLTV:
361 /* slightly complex because a string may or may not have a '\0'
362 * cell at the end, but I'll ignore that for now. I think in
363 * practice only the empty string will.
364 */
365 /* TODO: it is not OK to do this on the stack since list-like
366 * structures can be of indefinite extent. It *must* be done by
367 * iteration (and even that is problematic) */
368 if ( cell_a->payload.string.hash ==
369 cell_b->payload.string.hash ) {
370 wchar_t a_buff[STRING_SHIPYARD_SIZE],
371 b_buff[STRING_SHIPYARD_SIZE];
372 uint32_t tag = cell_a->tag.value;
373 int i = 0;
374
375 memset( a_buff, 0, sizeof( a_buff ) );
376 memset( b_buff, 0, sizeof( b_buff ) );
377
378 for ( ;
379 ( i < ( STRING_SHIPYARD_SIZE - 1 ) ) && !nilp( a )
380 && !nilp( b ); i++ ) {
381 a_buff[i] = cell_a->payload.string.character;
382 a = c_cdr( a );
383 cell_a = &pointer2cell( a );
384
385 b_buff[i] = cell_b->payload.string.character;
386 b = c_cdr( b );
387 cell_b = &pointer2cell( b );
388 }
389
390#ifdef DEBUG
391 debug_print( L"Comparing '", DEBUG_ARITH );
392 debug_print( a_buff, DEBUG_ARITH );
393 debug_print( L"' to '", DEBUG_ARITH );
394 debug_print( b_buff, DEBUG_ARITH );
395 debug_print( L"'\n", DEBUG_ARITH );
396#endif
397
398 /* OK, now we have wchar string buffers loaded from the objects. We
399 * may not have exhausted either string, so the buffers being equal
400 * isn't sufficient. So we recurse at least once. */
401
402 result = ( wcsncmp( a_buff, b_buff, i ) == 0 )
403 && equal( c_cdr( a ), c_cdr( b ) );
404 }
405 break;
406 case VECTORPOINTTV:
407 if ( cell_b->tag.value == VECTORPOINTTV ) {
408 result = equal_vector_vector( a, b );
409 } else {
410 result = false;
411 }
412 break;
413 default:
414 result = false;
415 break;
416 }
417 } else if ( numberp( a ) && numberp( b ) ) {
418 result = equal_number_number( a, b );
419 }
420
421 /*
422 * there's only supposed ever to be one T and one NIL cell, so each
423 * should be caught by eq.
424 *
425 * I'm not certain what equality means for read and write streams, so
426 * I'll ignore them, too, for now.
427 */
428
429 debug_printf( DEBUG_ARITH, L"\nequal returning %d\n", result );
430
431 return result;
432}
struct cons_pointer c_cdr(struct cons_pointer arg)
Implementation of cdr in C.
int c_length(struct cons_pointer arg)
Implementation of length in C.
struct cons_pointer c_car(struct cons_pointer arg)
Implementation of car in C.
#define KEYTV
The string KEYW, considered as an unsigned int.
#define VECTORPOINTTV
The string VECP, considered as an unsigned int.
#define SYMBOLTV
The string SYMB, considered as an unsigned int.
union cons_space_object::@2 tag
union cons_space_object::@3 payload
uint32_t page
the index of the page on which this cell resides
#define STRINGTV
The string STRG, considered as an unsigned int.
#define INTEGERTV
The string INTR, considered as an unsigned int.
#define RATIOTV
The string RTIO, considered as an unsigned int.
#define CONSTV
The string CONS, considered as an unsigned int.
#define nilp(conspoint)
true if conspoint points to the special cell NIL, else false (there should only be one of these so it...
#define NLAMBDATV
The string NLMD, considered as an unsigned int.
uint32_t offset
the index of the cell within the page
#define REALTV
The string REAL, considered as an unsigned int.
#define LAMBDATV
The string LMDA, considered as an unsigned int.
#define pointer2cell(pointer)
given a cons_pointer as argument, return the cell.
#define numberp(conspoint)
true if conspoint points to some sort of a number cell, else false
An indirect pointer to a cons cell.
an object in cons space.
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:31
bool equal_ld_ld(long double a, long double b)
compare two long doubles and returns true if they are the same to within a tolerance of one part in a...
Definition equal.c:65
bool equal_integer_number(struct cons_pointer a, struct cons_pointer b)
Private function, don't use.
Definition equal.c:124
bool equal_number_number(struct cons_pointer a, struct cons_pointer b)
Private function, don't use.
Definition equal.c:199
bool same_type(struct cons_pointer a, struct cons_pointer b)
True if the objects at these two cons pointers have the same tag, else false.
Definition equal.c:39
bool equal_integer_real(struct cons_pointer a, struct cons_pointer b)
Private function, don't use.
Definition equal.c:91
bool end_of_string(struct cons_pointer string)
Some strings will be null terminated and some will be NIL terminated... ooops!
Definition equal.c:51
bool equal_real_number(struct cons_pointer a, struct cons_pointer b)
Private function, don't use.
Definition equal.c:159
bool equal_map_map(struct cons_pointer a, struct cons_pointer b)
equality of two map-like things.
Definition equal.c:265
bool equal(struct cons_pointer a, struct cons_pointer b)
Deep, and thus expensive, equality: true if these two objects have identical structure,...
Definition equal.c:332
bool equal_vector_vector(struct cons_pointer a, struct cons_pointer b)
equality of two vector-space things.
Definition equal.c:299
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:28
#define STRING_SHIPYARD_SIZE
equal.h
Definition equal.h:22
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 hashmap_get(struct cons_pointer mapp, struct cons_pointer key)
Get a value from a hashmap.
Definition intern.c:221
struct cons_pointer hashmap_keys(struct cons_pointer mapp)
return a flat list of all the keys in the hashmap indicated by map.
Definition intern.c:163
long double c_ratio_to_ld(struct cons_pointer rat)
convert a ratio to an equivalent long double.
Definition ratio.c:379
bool equal_ratio_ratio(struct cons_pointer a, struct cons_pointer b)
True if a and be are identical rationals, else false.
Definition ratio.c:357
#define pointer_to_vso(pointer)
given a pointer to a vector space object, return the object.
Definition vectorspace.h:55
#define HASHTV
Definition vectorspace.h:30
#define NAMESPACETV
Definition vectorspace.h:40
struct vector_space_header header
the header of this object
union vector_space_header::@4 tag
the tag (type) of this vector-space object.
#define vectorp(conspoint)
Definition vectorspace.h:50
a vector_space_object is just a vector_space_header followed by a lump of bytes; what we deem to be i...