Post Scarcity 0.0.6
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_EQUAL, 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
276 ( hashmap_get( a, key, false ),
277 hashmap_get( b, key, false ) ) ) {
278 result = false;
279 break;
280 }
281 }
282 }
283
284 return result;
285}
286
287/**
288 * @brief equality of two vector-space things.
289 *
290 * Expensive, but we need to be able to check for equality of at least hashmaps
291 * and namespaces.
292 *
293 * Private function, do not use outside this file, not guaranteed to work
294 * unless both arguments are VECPs pointing to map like things.
295 *
296 * @param a a pointer to a vector space object.
297 * @param b another pointer to a vector space object.
298 * @return true if the two objects have the same logical structure.
299 * @return false otherwise.
300 */
302 bool result = false;
303
304 if ( eq( a, b ) ) {
305 result = true; // same
306 /* there shouldn't ever be two separate VECP cells which point to the
307 * same address in vector space, so I don't believe it's worth checking
308 * for this.
309 */
310 } else if ( vectorp( a ) && vectorp( b ) ) {
311 struct vector_space_object *va = pointer_to_vso( a );
312 struct vector_space_object *vb = pointer_to_vso( b );
313
314 /* what we're saying here is that a namespace is not equal to a map,
315 * even if they have identical logical structure. Is this right? */
316 if ( va->header.tag.value == vb->header.tag.value ) {
317 switch ( va->header.tag.value ) {
318 case HASHTV:
319 case NAMESPACETV:
320 result = equal_map_map( a, b );
321 break;
322 }
323 }
324 }
325 // else can't throw an exception from here but TODO: should log.
326
327 return result;
328}
329
330/**
331 * Deep, and thus expensive, equality: true if these two objects have
332 * identical structure, else false.
333 */
334bool equal( struct cons_pointer a, struct cons_pointer b ) {
335 debug_print( L"\nequal: ", DEBUG_EQUAL );
337 debug_print( L" = ", DEBUG_EQUAL );
339
340 bool result = false;
341
342 if ( eq( a, b ) ) {
343 result = true;
344 } else if ( !numberp( a ) && same_type( a, b ) ) {
345 struct cons_space_object *cell_a = &pointer2cell( a );
346 struct cons_space_object *cell_b = &pointer2cell( b );
347
348 switch ( cell_a->tag.value ) {
349 case CONSTV:
350 case LAMBDATV:
351 case NLAMBDATV:
352 /* TODO: it is not OK to do this on the stack since list-like
353 * structures can be of indefinite extent. It *must* be done by
354 * iteration (and even that is problematic) */
355 result =
356 equal( cell_a->payload.cons.car, cell_b->payload.cons.car )
357 && equal( cell_a->payload.cons.cdr,
358 cell_b->payload.cons.cdr );
359 break;
360 case KEYTV:
361 case STRINGTV:
362 case SYMBOLTV:
363 /* slightly complex because a string may or may not have a '\0'
364 * cell at the end, but I'll ignore that for now. I think in
365 * practice only the empty string will.
366 */
367 /* TODO: it is not OK to do this on the stack since list-like
368 * structures can be of indefinite extent. It *must* be done by
369 * iteration (and even that is problematic) */
370 if ( cell_a->payload.string.hash ==
371 cell_b->payload.string.hash ) {
372 wchar_t a_buff[STRING_SHIPYARD_SIZE],
373 b_buff[STRING_SHIPYARD_SIZE];
374 uint32_t tag = cell_a->tag.value;
375 int i = 0;
376
377 memset( a_buff, 0, sizeof( a_buff ) );
378 memset( b_buff, 0, sizeof( b_buff ) );
379
380 for ( ; ( i < ( STRING_SHIPYARD_SIZE - 1 ) ) && !nilp( a )
381 && !nilp( b ); i++ ) {
382 a_buff[i] = cell_a->payload.string.character;
383 a = c_cdr( a );
384 cell_a = &pointer2cell( a );
385
386 b_buff[i] = cell_b->payload.string.character;
387 b = c_cdr( b );
388 cell_b = &pointer2cell( b );
389 }
390
391#ifdef DEBUG
392 debug_print( L"Comparing '", DEBUG_EQUAL );
393 debug_print( a_buff, DEBUG_EQUAL );
394 debug_print( L"' to '", DEBUG_EQUAL );
395 debug_print( b_buff, DEBUG_EQUAL );
396 debug_print( L"'\n", DEBUG_EQUAL );
397#endif
398
399 /* OK, now we have wchar string buffers loaded from the objects. We
400 * may not have exhausted either string, so the buffers being equal
401 * isn't sufficient. So we recurse at least once. */
402
403 result = ( wcsncmp( a_buff, b_buff, i ) == 0 )
404 && equal( c_cdr( a ), c_cdr( b ) );
405 }
406 break;
407 case VECTORPOINTTV:
408 if ( cell_b->tag.value == VECTORPOINTTV ) {
409 result = equal_vector_vector( a, b );
410 } else {
411 result = false;
412 }
413 break;
414 default:
415 result = false;
416 break;
417 }
418 } else if ( numberp( a ) && numberp( b ) ) {
419 result = equal_number_number( a, b );
420 }
421
422 /*
423 * there's only supposed ever to be one T and one NIL cell, so each
424 * should be caught by eq.
425 *
426 * I'm not certain what equality means for read and write streams, so
427 * I'll ignore them, too, for now.
428 */
429
430 debug_printf( DEBUG_EQUAL, L"\nequal returning %d\n", result );
431
432 return result;
433}
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:120
void debug_print(wchar_t *message, int level)
print this debug message to stderr, if verbosity matches level.
Definition debug.c:60
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:138
#define DEBUG_ARITH
Print messages debugging arithmetic operations.
Definition debug.h:31
#define DEBUG_EQUAL
Print messages about equality tests.
Definition debug.h:87
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:334
bool equal_vector_vector(struct cons_pointer a, struct cons_pointer b)
equality of two vector-space things.
Definition equal.c:301
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:496
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
struct cons_pointer hashmap_get(struct cons_pointer mapp, struct cons_pointer key, bool return_key)
Get a value from a hashmap.
Definition intern.c:221
long double c_ratio_to_ld(struct cons_pointer rat)
convert a ratio to an equivalent long double.
Definition ratio.c:384
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:362
#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...