--- /dev/null
+/*
+ N16 - a processor native type
+
+ For binary operations: a op b -> c
+
+ See the document on the proper use of the Natural types.
+
+ On the subject of multiple pointers indicating the same location in memory:
+
+ When a routine has multiple results, and one or more of the result location
+ pointers point to the same storage, the routine will either return an error
+ status, or have defined behavior.
+
+ When a routine has multiple operands, in any combination, those
+ pointers can point to the same location, and the routine will
+ function as advertised.
+
+ When an operand functions as both an input and a result, perhaps due
+ to a result pointer pointing to the same place as an operand
+ pointer, the routine will function as advertised. (Internally the
+ routine might make a temporary copy of the operand to accomplish
+ this.)
+*/
+
+#define N16·DEBUG
+
+#ifndef FACE
+#define N16·IMPLEMENTATION
+#define FACE
+#endif
+
+//--------------------------------------------------------------------------------
+// Interface
+
+#ifndef N16·FACE
+#define N16·FACE
+
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // Instance Data (Declaration Only)
+
+ typedef uint16_t Extent;
+ typedef uint16_t Digit;
+
+ typedef struct N16·T N16·T;
+
+ extern N16·T *N16·zero;
+ extern N16·T *N16·one;
+ extern N16·T *N16·all_one_bit;
+ extern N16·T *N16·lsb;
+ extern N16·T *N16·msb;
+
+ //----------------------------------------
+ // Return/Error Status and handlers
+
+ typedef enum{
+ N16·Status·ok = 0
+ ,N16·Status·overflow = 1
+ ,N16·Status·accumulator1_overflow = 2
+ ,N16·Status·carry = 3
+ ,N16·Status·borrow = 4
+ ,N16·Status·undefined_divide_by_zero = 5
+ ,N16·Status·undefined_modulus_zero = 6
+ ,N16·Status·gt_max_shift_count = 7
+ ,N16·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value
+ ,N16·Status·one_word_product = 9
+ ,N16·Status·two_word_product = 10
+ } N16·Status;
+
+ typedef enum{
+ N16·Order_lt = -1
+ ,N16·Order_eq = 0
+ ,N16·Order_gt = 1
+ } N16·Order;
+
+ typedef N16·T *( *N16·Allocate_MemoryFault )(Extent);
+
+ //----------------------------------------
+ // Interface
+
+ typedef struct{
+
+ N16·T *(*allocate_array_zero)(Extent, N16·Allocate_MemoryFault);
+ N16·T *(*allocate_array)(Extent, N16·Allocate_MemoryFault);
+ void (*deallocate)(N16·T*);
+
+ void (*copy)(N16·T*, N16·T*);
+ void (*bit_and)(N16·T*, N16·T*, N16·T*);
+ void (*bit_or)(N16·T*, N16·T*, N16·T*);
+ void (*bit_complement)(N16·T*, N16·T*);
+ void (*bit_twos_complement)(N16·T*, N16·T*);
+ N16·Order (*compare)(N16·T*, N16·T*);
+ bool (*lt)(N16·T*, N16·T*);
+ bool (*gt)(N16·T*, N16·T*);
+ bool (*eq)(N16·T*, N16·T*);
+ bool (*eq_zero)(N16·T*);
+ N16·Status (*accumulate)(N16·T *accumulator1 ,N16·T *accumulator0 ,...);
+ N16·Status (*add)(N16·T*, N16·T*, N16·T*);
+ bool (*increment)(N16·T *a);
+ N16·Status (*subtract)(N16·T*, N16·T*, N16·T*);
+ N16·Status (*multiply)(N16·T*, N16·T*, N16·T*, N16·T*);
+ N16·Status (*divide)(N16·T*, N16·T*, N16·T*, N16·T*);
+ N16·Status (*modulus)(N16·T*, N16·T*, N16·T*);
+ N16·Status (*shift_left)(Extent, N16·T*, N16·T*, N16·T*);
+ N16·Status (*shift_right)(Extent, N16·T*, N16·T*, N16·T*);
+ N16·Status (*arithmetic_shift_right)(Extent, N16·T*, N16·T*);
+
+ N16·T* (*access)(N16·T*, Extent);
+ void (*from_uint32)(N16·T *destination ,uint32_t value);
+ } N16·Λ;
+
+ Local const N16·Λ N16·λ; // initialized in the LOCAL section
+
+#endif
+
+//--------------------------------------------------------------------------------
+// Implementation
+
+#ifdef N16·IMPLEMENTATION
+
+ // this part goes into the library
+ #ifndef LOCAL
+
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ struct N16·T{
+ Digit d0;
+ };
+
+ N16·T N16·constant[4] = {
+ {.d0 = 0},
+ {.d0 = 1},
+ {.d0 = ~(uint16_t)0},
+ {.d0 = 1 << 15}
+ };
+
+ N16·T *N16·zero = &N16·constant[0];
+ N16·T *N16·one = &N16·constant[1];
+ N16·T *N16·all_one_bit = &N16·constant[2];
+ N16·T *N16·msb = &N16·constant[3];
+ N16·T *N16·lsb = &N16·constant[1];
+
+ // the allocate an array of N16
+ N16·T *N16·allocate_array(Extent extent ,N16·Allocate_MemoryFault memory_fault){
+ N16·T *instance = malloc((extent + 1) * sizeof(N16·T));
+ if(!instance){
+ return memory_fault ? memory_fault(extent) : NULL;
+ }
+ return instance;
+ }
+
+ N16·T *N16·allocate_array_zero(Extent extent ,N16·Allocate_MemoryFault memory_fault){
+ N16·T *instance = calloc(extent + 1, sizeof(N16·T));
+ if(!instance){
+ return memory_fault ? memory_fault(extent) : NULL;
+ }
+ return instance;
+ }
+
+ void N16·deallocate(N16·T *unencumbered){
+ free(unencumbered);
+ }
+
+
+ #endif
+
+ // This part is included after the library user's code
+ #ifdef LOCAL
+
+ // instance
+
+ struct N16·T{
+ Digit d0;
+ };
+
+ // temporary variables
+ Local N16·T N16·t[4];
+
+ // allocation
+
+ extern N16·T *N16·allocate_array(Extent, N16·Allocate_MemoryFault);
+ extern N16·T *N16·allocate_array_zero(Extent, N16·Allocate_MemoryFault);
+ extern void N16·deallocate(N16·T *);
+
+ // so the user can access numbers in an array allocation
+ Local N16·T* N16·access(N16·T *array ,Extent index){
+ return &array[index];
+ }
+
+ Local void N16·from_uint32(N16·T *destination ,uint32_t value){
+ if(destination == NULL) return;
+ destination->d0 = (uint16_t)(value & 0xFFFF);
+ }
+
+ // copy, convenience copy
+
+ Local void N16·copy(N16·T *destination ,N16·T *source){
+ if(source == destination) return;
+ *destination = *source;
+ }
+
+ Local void N16·set_to_zero(N16·T *instance){
+ instance->d0 = 0;
+ }
+
+ Local void N16·set_to_one(N16·T *instance){
+ instance->d0 = 1;
+ }
+
+ // bit operations
+
+ Local void N16·bit_and(N16·T *result, N16·T *a, N16·T *b){
+ result->d0 = a->d0 & b->d0;
+ }
+
+ Local void N16·bit_or(N16·T *result, N16·T *a, N16·T *b){
+ result->d0 = a->d0 | b->d0;
+ }
+
+ Local void N16·bit_complement(N16·T *result, N16·T *a){
+ result->d0 = ~a->d0;
+ }
+
+ Local void N16·bit_twos_complement(N16·T *result ,N16·T *a){
+ result->d0 = (uint16_t)(~a->d0 + 1);
+ }
+
+ // test functions
+
+ Local N16·Order N16·compare(N16·T *a, N16·T *b){
+ if(a->d0 < b->d0) return N16·Order_lt;
+ if(a->d0 > b->d0) return N16·Order_gt;
+ return N16·Order_eq;
+ }
+
+ Local bool N16·lt(N16·T *a ,N16·T *b){
+ return a->d0 < b->d0;
+ }
+
+ Local bool N16·gt(N16·T *a ,N16·T *b){
+ return a->d0 > b->d0;
+ }
+
+ Local bool N16·eq(N16·T *a ,N16·T *b){
+ return a->d0 == b->d0;
+ }
+
+ Local bool N16·eq_zero(N16·T *a){
+ return a->d0 == 0;
+ }
+
+ // arithmetic operations
+
+ Local N16·Status N16·accumulate(N16·T *accumulator1 ,N16·T *accumulator0 ,...){
+
+ va_list args;
+ va_start(args ,accumulator0);
+ uint32_t sum = accumulator0->d0;
+ uint32_t carry = 0;
+ N16·T *current;
+
+ while( (current = va_arg(args ,N16·T*)) ){
+ sum += current->d0;
+ if(sum < current->d0){
+ (carry)++;
+ if(carry == 0){
+ va_end(args);
+ return N16·Status·accumulator1_overflow;
+ }
+ }
+ }
+ va_end(args);
+
+ accumulator1->d0 = (uint16_t)carry;
+ return N16·Status·ok;
+ }
+
+ Local N16·Status N16·add(N16·T *sum ,N16·T *a ,N16·T *b){
+ uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0;
+ sum->d0 = (uint16_t)(result & 0xFFFF);
+ return (result >> 16) ? N16·Status·carry : N16·Status·ok;
+ }
+
+ Local bool N16·increment(N16·T *a){
+ a->d0++;
+ return (a->d0 == 0);
+ }
+
+ Local N16·Status N16·subtract(N16·T *difference ,N16·T *a ,N16·T *b){
+ uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0;
+ difference->d0 = (uint16_t)(diff & 0xFFFF);
+ return (diff > a->d0) ? N16·Status·borrow : N16·Status·ok;
+ }
+
+ Local N16·Status N16·multiply(N16·T *product1 ,N16·T *product0 ,N16·T *a ,N16·T *b){
+ uint32_t product = (uint32_t)a->d0 * (uint32_t)b->d0;
+ product0->d0 = (uint16_t)(product & 0xFFFF);
+ product1->d0 = (uint16_t)((product >> 16) & 0xFFFF);
+
+ if(product1->d0 == 0) return N16·Status·one_word_product;
+ return N16·Status·two_word_product;
+ }
+
+ Local N16·Status N16·divide(N16·T *remainder ,N16·T *quotient ,N16·T *a ,N16·T *b){
+ if(b->d0 == 0) return N16·Status·undefined_divide_by_zero;
+
+ uint32_t dividend = a->d0;
+ uint32_t divisor = b->d0;
+ quotient->d0 = (uint16_t)(dividend / divisor);
+ remainder->d0 = (uint16_t)(dividend - (quotient->d0 * divisor));
+
+ return N16·Status·ok;
+ }
+
+ Local N16·Status N16·modulus(N16·T *remainder ,N16·T *a ,N16·T *b){
+ if(b->d0 == 0) return N16·Status·undefined_modulus_zero;
+ uint32_t dividend = a->d0;
+ uint32_t divisor = b->d0;
+ uint32_t q = dividend / divisor;
+ remainder->d0 = (uint16_t)(dividend - (q * divisor));
+ return N16·Status·ok;
+ }
+
+ // bit motion
+
+ typedef uint16_t (*ShiftOp)(uint16_t, uint16_t);
+
+ Local uint16_t shift_left_op(uint16_t value, uint16_t amount){
+ return value << amount;
+ }
+
+ Local uint16_t shift_right_op(uint16_t value, uint16_t amount){
+ return (uint16_t)(value >> amount);
+ }
+
+ Local N16·Status N16·shift
+ (
+ uint16_t shift_count
+ ,N16·T *spill
+ ,N16·T *operand
+ ,N16·T *fill
+ ,ShiftOp shift_op
+ ,ShiftOp complement_shift_op
+ ){
+
+ if(operand == NULL && spill == NULL) return N16·Status·ok;
+
+ if(operand == NULL){
+ operand = &N16·t[0];
+ N16·copy(operand, N16·zero);
+ }
+
+ if(shift_count > 15) return N16·Status·gt_max_shift_count;
+
+ N16·T *given_operand = &N16·t[1];
+ N16·copy(given_operand, operand);
+
+ operand->d0 = shift_op(given_operand->d0, shift_count);
+ if(fill != NULL){
+ fill->d0 = complement_shift_op(fill->d0, (16 - shift_count));
+ N16·bit_or(operand, operand, fill);
+ }
+ if(spill != NULL){
+ spill->d0 = shift_op(spill->d0, shift_count);
+ spill->d0 += complement_shift_op(given_operand->d0, (16 - shift_count));
+ }
+
+ return N16·Status·ok;
+ }
+
+ Local N16·Status
+ N16·shift_left(uint16_t shift_count, N16·T *spill, N16·T *operand, N16·T *fill){
+ return N16·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
+ }
+
+ Local N16·Status
+ N16·shift_right(uint16_t shift_count, N16·T *spill, N16·T *operand, N16·T *fill){
+ return N16·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
+ }
+
+ Local N16·Status
+ N16·arithmetic_shift_right(uint16_t shift_count, N16·T *operand, N16·T *spill){
+
+ if(shift_count > 15) return N16·Status·gt_max_shift_count;
+
+ if(operand == NULL){
+ operand = &N16·t[0];
+ N16·copy(operand, N16·zero);
+ }
+
+ N16·T *fill = (operand->d0 & 0x8000) ? N16·all_one_bit : N16·zero;
+ return N16·shift_right(shift_count, spill, operand, fill);
+ }
+
+ Local const N16·Λ N16·λ = {
+
+ .allocate_array = N16·allocate_array
+ ,.allocate_array_zero = N16·allocate_array_zero
+ ,.deallocate = N16·deallocate
+
+ ,.copy = N16·copy
+ ,.bit_and = N16·bit_and
+ ,.bit_or = N16·bit_or
+ ,.bit_complement = N16·bit_complement
+ ,.bit_twos_complement = N16·bit_twos_complement
+ ,.compare = N16·compare
+ ,.lt = N16·lt
+ ,.gt = N16·gt
+ ,.eq = N16·eq
+ ,.eq_zero = N16·eq_zero
+ ,.accumulate = N16·accumulate
+ ,.add = N16·add
+ ,.increment = N16·increment
+ ,.subtract = N16·subtract
+ ,.multiply = N16·multiply
+ ,.divide = N16·divide
+ ,.modulus = N16·modulus
+ ,.shift_left = N16·shift_left
+ ,.shift_right = N16·shift_right
+ ,.arithmetic_shift_right = N16·arithmetic_shift_right
+
+ ,.access = N16·access
+ ,.from_uint32 = N16·from_uint32
+ };
+
+ #endif
+
+#endif
#ifndef N32·FACE
#define N32·FACE
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-//----------------------------------------
-// Instance Data (Declaration Only)
-
-typedef uint32_t Extent;
-typedef uint32_t Digit;
-
-typedef struct N32·T N32·T;
-
-extern N32·T *N32·zero;
-extern N32·T *N32·one;
-extern N32·T *N32·all_one_bit;
-extern N32·T *N32·lsb;
-extern N32·T *N32·msb;
-
-//----------------------------------------
-// Return/Error Status and handlers
-
-typedef enum{
- N32·Status·ok = 0
- ,N32·Status·overflow = 1
- ,N32·Status·accumulator1_overflow = 2
- ,N32·Status·carry = 3
- ,N32·Status·borrow = 4
- ,N32·Status·undefined_divide_by_zero = 5
- ,N32·Status·undefined_modulus_zero = 6
- ,N32·Status·gt_max_shift_count = 7
- ,N32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value
- ,N32·Status·one_word_product = 9
- ,N32·Status·two_word_product = 10
-} N32·Status;
-
-typedef enum{
- N32·Order_lt = -1
- ,N32·Order_eq = 0
- ,N32·Order_gt = 1
-} N32·Order;
-
-typedef N32·T *( *N32·Allocate_MemoryFault )(Extent);
-
-//----------------------------------------
-// Interface
-
-typedef struct{
-
- N32·T *(*allocate_array_zero)(Extent, N32·Allocate_MemoryFault);
- N32·T *(*allocate_array)(Extent, N32·Allocate_MemoryFault);
- void (*deallocate)(N32·T*);
-
- void (*copy)(N32·T*, N32·T*);
- void (*bit_and)(N32·T*, N32·T*, N32·T*);
- void (*bit_or)(N32·T*, N32·T*, N32·T*);
- void (*bit_complement)(N32·T*, N32·T*);
- void (*bit_twos_complement)(N32·T*, N32·T*);
- N32·Order (*compare)(N32·T*, N32·T*);
- bool (*lt)(N32·T*, N32·T*);
- bool (*gt)(N32·T*, N32·T*);
- bool (*eq)(N32·T*, N32·T*);
- bool (*eq_zero)(N32·T*);
- N32·Status (*accumulate)(N32·T *accumulator1 ,N32·T *accumulator0 ,...);
- N32·Status (*add)(N32·T*, N32·T*, N32·T*);
- bool (*increment)(N32·T *a);
- N32·Status (*subtract)(N32·T*, N32·T*, N32·T*);
- N32·Status (*multiply)(N32·T*, N32·T*, N32·T*, N32·T*);
- N32·Status (*divide)(N32·T*, N32·T*, N32·T*, N32·T*);
- N32·Status (*modulus)(N32·T*, N32·T*, N32·T*);
- N32·Status (*shift_left)(Extent, N32·T*, N32·T*, N32·T*);
- N32·Status (*shift_right)(Extent, N32·T*, N32·T*, N32·T*);
- N32·Status (*arithmetic_shift_right)(Extent, N32·T*, N32·T*);
-
- N32·T* (*access)(N32·T*, Extent);
- void (*from_uint32)(N32·T *destination ,uint32_t value);
-} N32·Λ;
-
-Local const N32·Λ N32·λ; // initialized in the LOCAL section
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // Instance Data (Declaration Only)
+
+ typedef uint32_t Extent;
+ typedef uint32_t Digit;
+
+ typedef struct N32·T N32·T;
+
+ extern N32·T *N32·zero;
+ extern N32·T *N32·one;
+ extern N32·T *N32·all_one_bit;
+ extern N32·T *N32·lsb;
+ extern N32·T *N32·msb;
+
+ //----------------------------------------
+ // Return/Error Status and handlers
+
+ typedef enum{
+ N32·Status·ok = 0
+ ,N32·Status·overflow = 1
+ ,N32·Status·accumulator1_overflow = 2
+ ,N32·Status·carry = 3
+ ,N32·Status·borrow = 4
+ ,N32·Status·undefined_divide_by_zero = 5
+ ,N32·Status·undefined_modulus_zero = 6
+ ,N32·Status·gt_max_shift_count = 7
+ ,N32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value
+ ,N32·Status·one_word_product = 9
+ ,N32·Status·two_word_product = 10
+ } N32·Status;
+
+ typedef enum{
+ N32·Order_lt = -1
+ ,N32·Order_eq = 0
+ ,N32·Order_gt = 1
+ } N32·Order;
+
+ typedef N32·T *( *N32·Allocate_MemoryFault )(Extent);
+
+ //----------------------------------------
+ // Interface
+
+ typedef struct{
+
+ N32·T *(*allocate_array_zero)(Extent, N32·Allocate_MemoryFault);
+ N32·T *(*allocate_array)(Extent, N32·Allocate_MemoryFault);
+ void (*deallocate)(N32·T*);
+
+ void (*copy)(N32·T*, N32·T*);
+ void (*bit_and)(N32·T*, N32·T*, N32·T*);
+ void (*bit_or)(N32·T*, N32·T*, N32·T*);
+ void (*bit_complement)(N32·T*, N32·T*);
+ void (*bit_twos_complement)(N32·T*, N32·T*);
+ N32·Order (*compare)(N32·T*, N32·T*);
+ bool (*lt)(N32·T*, N32·T*);
+ bool (*gt)(N32·T*, N32·T*);
+ bool (*eq)(N32·T*, N32·T*);
+ bool (*eq_zero)(N32·T*);
+ N32·Status (*accumulate)(N32·T *accumulator1 ,N32·T *accumulator0 ,...);
+ N32·Status (*add)(N32·T*, N32·T*, N32·T*);
+ bool (*increment)(N32·T *a);
+ N32·Status (*subtract)(N32·T*, N32·T*, N32·T*);
+ N32·Status (*multiply)(N32·T*, N32·T*, N32·T*, N32·T*);
+ N32·Status (*divide)(N32·T*, N32·T*, N32·T*, N32·T*);
+ N32·Status (*modulus)(N32·T*, N32·T*, N32·T*);
+ N32·Status (*shift_left)(Extent, N32·T*, N32·T*, N32·T*);
+ N32·Status (*shift_right)(Extent, N32·T*, N32·T*, N32·T*);
+ N32·Status (*arithmetic_shift_right)(Extent, N32·T*, N32·T*);
+
+ N32·T* (*access)(N32·T*, Extent);
+ void (*from_uint32)(N32·T *destination ,uint32_t value);
+ } N32·Λ;
+
+ Local const N32·Λ N32·λ; // initialized in the LOCAL section
#endif
#ifdef N32·IMPLEMENTATION
-// this part goes into the library
-#ifndef LOCAL
-
-#include <stdarg.h>
-#include <stdlib.h>
-
-struct N32·T{
- Digit d0;
-};
-
-N32·T N32·constant[4] = {
-{.d0 = 0},
-{.d0 = 1},
-{.d0 = ~(uint32_t)0},
-{.d0 = 1 << 31}
-};
-
-N32·T *N32·zero = &N32·constant[0];
-N32·T *N32·one = &N32·constant[1];
-N32·T *N32·all_one_bit = &N32·constant[2];
-N32·T *N32·msb = &N32·constant[3];
-N32·T *N32·lsb = &N32·constant[1];
-
-// the allocate an array of N32
-N32·T *N32·allocate_array(Extent extent ,N32·Allocate_MemoryFault memory_fault){
- N32·T *instance = malloc((extent + 1) * sizeof(N32·T) );
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
-}
-
-N32·T *N32·allocate_array_zero(Extent extent ,N32·Allocate_MemoryFault memory_fault){
- N32·T *instance = calloc( extent + 1 ,sizeof(N32·T) );
- if(!instance){
- return memory_fault ? memory_fault(extent) : NULL;
- }
- return instance;
-}
-
-void N32·deallocate(N32·T *unencumbered){
- free(unencumbered);
-}
+ // this part goes into the library
+ #ifndef LOCAL
+
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ struct N32·T{
+ Digit d0;
+ };
+
+ N32·T N32·constant[4] = {
+ {.d0 = 0},
+ {.d0 = 1},
+ {.d0 = ~(uint32_t)0},
+ {.d0 = 1 << 31}
+ };
+
+ N32·T *N32·zero = &N32·constant[0];
+ N32·T *N32·one = &N32·constant[1];
+ N32·T *N32·all_one_bit = &N32·constant[2];
+ N32·T *N32·msb = &N32·constant[3];
+ N32·T *N32·lsb = &N32·constant[1];
+
+ // the allocate an array of N32
+ N32·T *N32·allocate_array(Extent extent ,N32·Allocate_MemoryFault memory_fault){
+ N32·T *instance = malloc((extent + 1) * sizeof(N32·T) );
+ if(!instance){
+ return memory_fault ? memory_fault(extent) : NULL;
+ }
+ return instance;
+ }
+ N32·T *N32·allocate_array_zero(Extent extent ,N32·Allocate_MemoryFault memory_fault){
+ N32·T *instance = calloc( extent + 1 ,sizeof(N32·T) );
+ if(!instance){
+ return memory_fault ? memory_fault(extent) : NULL;
+ }
+ return instance;
+ }
-#endif
+ void N32·deallocate(N32·T *unencumbered){
+ free(unencumbered);
+ }
+
+ #endif
+
+ // This part is included after the library user's code
+ #ifdef LOCAL
+
+ // instance
+
+ struct N32·T{
+ Digit d0;
+ };
+
+ // temporary variables
+ // making these LOCAL rather than reserving one block in the library is thread safe
+ // allocating a block once is more efficient
+ // library code writes these, they are not on the interface
+
+ Local N32·T N32·t[4];
+
+
+ // allocation
+
+ extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault);
+ extern N32·T *N32·allocate_array_zero(Extent, N32·Allocate_MemoryFault);
+ extern void N32·deallocate(N32·T *);
+
+ // so the user can access numbers in an array allocation
+ Local N32·T* N32·access(N32·T *array ,Extent index){
+ return &array[index];
+ }
+
+ Local void N32·from_uint32(N32·T *destination ,uint32_t value){
+ if(destination == NULL) return;
+ destination->d0 = value;
+ }
+
+ // copy, convenience copy
+
+ Local void N32·copy(N32·T *destination ,N32·T *source){
+ if(source == destination) return; // that was easy!
+ *destination = *source;
+ }
+
+ Local void N32·set_to_zero(N32·T *instance){
+ instance->d0 = 0;
+ }
+
+ Local void N32·set_to_one(N32·T *instance){
+ instance->d0 = 1;
+ }
-// This part is included after the library user's code
-#ifdef LOCAL
+ // bit operations
-// instance
+ Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){
+ result->d0 = a->d0 & b->d0;
+ }
+
+ // result can be one of the operands
+ Local void N32·bit_or(N32·T *result, N32·T *a, N32·T *b){
+ result->d0 = a->d0 | b->d0;
+ }
+
+ // result can the same as the operand
+ Local void N32·bit_complement(N32·T *result, N32·T *a){
+ result->d0 = ~a->d0;
+ }
+
+ // result can the same as the operand
+ Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){
+ result->d0 = ~a->d0 + 1;
+ }
-struct N32·T{
- Digit d0;
-};
+ // test functions
-// temporary variables
-// making these LOCAL rather than reserving one block in the library is thread safe
-// allocating a block once is more efficient
-// library code writes these, they are not on the interface
+ Local N32·Order N32·compare(N32·T *a, N32·T *b){
+ if(a->d0 < b->d0) return N32·Order_lt;
+ if(a->d0 > b->d0) return N32·Order_gt;
+ return N32·Order_eq;
+ }
-Local N32·T N32·t[4];
+ Local bool N32·lt(N32·T *a ,N32·T *b){
+ return a->d0 < b->d0;
+ }
+
+ Local bool N32·gt(N32·T *a ,N32·T *b){
+ return a->d0 > b->d0;
+ }
+
+ Local bool N32·eq(N32·T *a ,N32·T *b){
+ return a->d0 == b->d0;
+ }
+
+ Local bool N32·eq_zero(N32·T *a){
+ return a->d0 == 0;
+ }
+
+
+ // arithmetic operations
+
+ // For a large number of summands for the lower precision Natural implementations, for accumulate/add/sub, the 'overflow' operand could overflow and thus this routine will halt and return N32·Status·accumulator1_overflow
+ //
+ // When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value.
+ Local N32·Status N32·accumulate(N32·T *accumulator1 ,N32·T *accumulator0 ,...){
+
+ va_list args;
+ va_start(args ,accumulator0);
+ uint32_t sum = accumulator0->d0;
+ uint32_t carry = 0;
+ N32·T *current;
+
+ while( (current = va_arg(args ,N32·T *)) ){
+ sum += current->d0;
+ if(sum < current->d0){ // Accumulator1 into carry
+ (carry)++;
+ if(carry == 0){
+ va_end(args);
+ return N32·Status·accumulator1_overflow;
+ }
+ }
+ }
+ va_end(args);
+ // wipes out prior value of accumulator1
+ accumulator1->d0 = carry;
-// allocation
+ return N32·Status·ok;
+ }
-extern N32·T *N32·allocate_array(Extent, N32·Allocate_MemoryFault);
-extern N32·T *N32·alloc_array_zero(Extent, N32·Allocate_MemoryFault);
-extern void N32·deallocate(N32·T *);
+ Local N32·Status N32·add(N32·T *sum ,N32·T *a ,N32·T *b){
+ uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0;
+ sum->d0 = (uint32_t)result;
+ return (result >> 32) ? N32·Status·carry : N32·Status·ok;
+ }
-// so the user can access numbers in an array allocation
-Local N32·T* N32·access(N32·T *array ,Extent index){
- return &array[index];
-}
+ Local bool N32·increment(N32·T *a){
+ a->d0++;
+ return a->d0 == 0;
+ }
+
+ Local N32·Status N32·subtract(N32·T *difference ,N32·T *a ,N32·T *b){
+ uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0;
+ difference->d0 = (uint32_t)diff;
+ return (diff > a->d0) ? N32·Status·borrow : N32·Status·ok;
+ }
-Local void N32·from_uint32(N32·T *destination ,uint32_t value){
- if(destination == NULL) return;
- destination->d0 = value;
-}
-// copy, convenience copy
+ Local N32·Status N32·multiply(N32·T *product1 ,N32·T *product0 ,N32·T *a ,N32·T *b){
+ uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0;
+ product0->d0 = (uint32_t)product;
+ product1->d0 = (uint32_t)(product >> 32);
-Local void N32·copy(N32·T *destination ,N32·T *source){
- if(source == destination) return; // that was easy!
- *destination = *source;
-}
+ if(product1->d0 == 0) return N32·Status·one_word_product;
+ return N32·Status·two_word_product;
+ }
-Local void N32·set_to_zero(N32·T *instance){
- instance->d0 = 0;
-}
+ Local N32·Status N32·divide(N32·T *remainder ,N32·T *quotient ,N32·T *a ,N32·T *b){
+ if(b->d0 == 0) return N32·Status·undefined_divide_by_zero;
-Local void N32·set_to_one(N32·T *instance){
- instance->d0 = 1;
-}
+ quotient->d0 = a->d0 / b->d0;
+ remainder->d0 = a->d0 - (quotient->d0 * b->d0);
-// bit operations
+ return N32·Status·ok;
+ }
-Local void N32·bit_and(N32·T *result, N32·T *a, N32·T *b){
- result->d0 = a->d0 & b->d0;
-}
+ Local N32·Status N32·modulus(N32·T *remainder ,N32·T *a ,N32·T *b){
+ if(b->d0 == 0) return N32·Status·undefined_modulus_zero;
+ uint32_t quotient = a->d0 / b->d0;
+ remainder->d0 = a->d0 - (quotient * b->d0);
+ return N32·Status·ok;
+ }
-// result can be one of the operands
-Local void N32·bit_or(N32·T *result, N32·T *a, N32·T *b){
- result->d0 = a->d0 | b->d0;
-}
+ // bit motion
-// result can the same as the operand
-Local void N32·bit_complement(N32·T *result, N32·T *a){
- result->d0 = ~a->d0;
-}
+ typedef uint32_t (*ShiftOp)(uint32_t, uint32_t);
-// result can the same as the operand
-Local void N32·bit_twos_complement(N32·T *result ,N32·T *a){
- result->d0 = ~a->d0 + 1;
-}
+ Local uint32_t shift_left_op(uint32_t value, uint32_t amount){
+ return value << amount;
+ }
-// test functions
+ Local uint32_t shift_right_op(uint32_t value, uint32_t amount){
+ return value >> amount;
+ }
-Local N32·Order N32·compare(N32·T *a, N32·T *b){
- if(a->d0 < b->d0) return N32·Order_lt;
- if(a->d0 > b->d0) return N32·Order_gt;
- return N32·Order_eq;
-}
+ // modifies all three of its operands
+ // in the case of duplicate operands this is the order: first modifies operand, then fill, then spill,
+ Local N32·Status N32·shift
+ (
+ uint32_t shift_count
+ ,N32·T *spill
+ ,N32·T *operand
+ ,N32·T *fill
+ ,ShiftOp shift_op
+ ,ShiftOp complement_shift_op
+ ){
+
+ // If no result is needed, return immediately.
+ if(operand == NULL && spill == NULL) return N32·Status·ok;
+
+ // Treat NULL operand as zero.
+ if(operand == NULL){
+ operand = &N32·t[0];
+ N32·copy(operand, N32·zero);
+ }
-Local bool N32·lt(N32·T *a ,N32·T *b){
- return a->d0 < b->d0;
-}
+ // Shifting more than one word breaks our fill/spill model.
+ if(shift_count > 31) return N32·Status·gt_max_shift_count;
-Local bool N32·gt(N32·T *a ,N32·T *b){
- return a->d0 > b->d0;
-}
+ // The given operand is still required after it is modified, so we copy it.
+ N32·T *given_operand = &N32·t[1];
+ N32·copy(given_operand, operand);
-Local bool N32·eq(N32·T *a ,N32·T *b){
- return a->d0 == b->d0;
-}
+ // Perform the shift
+ operand->d0 = shift_op(given_operand->d0, shift_count);
+ if(fill != NULL){
+ fill->d0 = complement_shift_op(fill->d0, (32 - shift_count));
+ N32·bit_or(operand, operand, fill);
+ }
+ if(spill != NULL){
+ spill->d0 = shift_op(spill->d0, shift_count);
+ spill->d0 += complement_shift_op(given_operand->d0, (32 - shift_count));
+ }
-Local bool N32·eq_zero(N32·T *a){
- return a->d0 == 0;
-}
+ return N32·Status·ok;
+ }
+ // Define concrete shift functions using valid C function pointers
+ Local N32·Status
+ N32·shift_left(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){
+ return N32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
+ }
-// arithmetic operations
+ Local N32·Status
+ N32·shift_right(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){
+ return N32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
+ }
-// For a large number of summands for the lower precision Natural implementations, for accumulate/add/sub, the 'overflow' operand could overflow and thus this routine will halt and return N32·Status·accumulator1_overflow
-//
-// When accumulator1 and accumulator0 point to the same location, the result is the accumulator1 value.
-Local N32·Status N32·accumulate(N32·T *accumulator1 ,N32·T *accumulator0 ,...){
+ Local N32·Status
+ N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){
- va_list args;
- va_start(args ,accumulator0);
- uint32_t sum = accumulator0->d0;
- uint32_t carry = 0;
- N32·T *current;
+ // Guard against excessive shift counts
+ if(shift_count > 31) return N32·Status·gt_max_shift_count;
- while( (current = va_arg(args ,N32·T *)) ){
- sum += current->d0;
- if(sum < current->d0){ // Accumulator1 into carry
- (carry)++;
- if(carry == 0){
- va_end(args);
- return N32·Status·accumulator1_overflow;
+ // A NULL operand is treated as zero
+ if(operand == NULL){
+ operand = &N32·t[0];
+ N32·copy(operand, N32·zero);
}
+
+ // Pick the fill value based on the sign bit
+ N32·T *fill = (operand->d0 & 0x80000000) ? N32·all_one_bit : N32·zero;
+
+ // Call shift_right with the appropriate fill
+ return N32·shift_right(shift_count, spill, operand, fill);
}
- }
- va_end(args);
-
- // wipes out prior value of accumulator1
- accumulator1->d0 = carry;
-
- return N32·Status·ok;
-}
-
-Local N32·Status N32·add(N32·T *sum ,N32·T *a ,N32·T *b){
- uint64_t result = (uint64_t)a->d0 + (uint64_t)b->d0;
- sum->d0 = (uint32_t)result;
- return (result >> 32) ? N32·Status·carry : N32·Status·ok;
-}
-
-Local bool N32·increment(N32·T *a){
- a->d0++;
- return a->d0 == 0;
-}
-
-Local N32·Status N32·subtract(N32·T *difference ,N32·T *a ,N32·T *b){
- uint64_t diff = (uint64_t) a->d0 - (uint64_t) b->d0;
- difference->d0 = (uint32_t)diff;
- return (diff > a->d0) ? N32·Status·borrow : N32·Status·ok;
-}
-
-
-Local N32·Status N32·multiply(N32·T *product1 ,N32·T *product0 ,N32·T *a ,N32·T *b){
- uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0;
- product0->d0 = (uint32_t)product;
- product1->d0 = (uint32_t)(product >> 32);
-
- if(product1->d0 == 0) return N32·Status·one_word_product;
- return N32·Status·two_word_product;
-}
-
-Local N32·Status N32·divide(N32·T *remainder ,N32·T *quotient ,N32·T *a ,N32·T *b){
- if(b->d0 == 0) return N32·Status·undefined_divide_by_zero;
-
- quotient->d0 = a->d0 / b->d0;
- remainder->d0 = a->d0 - (quotient->d0 * b->d0);
-
- return N32·Status·ok;
-}
-
-Local N32·Status N32·modulus(N32·T *remainder ,N32·T *a ,N32·T *b){
- if(b->d0 == 0) return N32·Status·undefined_modulus_zero;
- uint32_t quotient = a->d0 / b->d0;
- remainder->d0 = a->d0 - (quotient * b->d0);
- return N32·Status·ok;
-}
-
-// bit motion
-
-typedef uint32_t (*ShiftOp)(uint32_t, uint32_t);
-
-Local uint32_t shift_left_op(uint32_t value, uint32_t amount){
- return value << amount;
-}
-
-Local uint32_t shift_right_op(uint32_t value, uint32_t amount){
- return value >> amount;
-}
-
-// modifies all three of its operands
-// in the case of duplicate operands this is the order: first modifies operand, then fill, then spill,
-Local N32·Status N32·shift
-(
- uint32_t shift_count
- ,N32·T *spill
- ,N32·T *operand
- ,N32·T *fill
- ,ShiftOp shift_op
- ,ShiftOp complement_shift_op
- ){
-
- // If no result is needed, return immediately.
- if(operand == NULL && spill == NULL) return N32·Status·ok;
-
- // Treat NULL operand as zero.
- if(operand == NULL){
- operand = &N32·t[0];
- N32·copy(operand, N32·zero);
- }
-
- // Shifting more than one word breaks our fill/spill model.
- if(shift_count > 31) return N32·Status·gt_max_shift_count;
-
- // The given operand is still required after it is modified, so we copy it.
- N32·T *given_operand = &N32·t[1];
- N32·copy(given_operand, operand);
-
- // Perform the shift
- operand->d0 = shift_op(given_operand->d0, shift_count);
- if(fill != NULL){
- fill->d0 = complement_shift_op(fill->d0, (32 - shift_count));
- N32·bit_or(operand, operand, fill);
- }
- if(spill != NULL){
- spill->d0 = shift_op(spill->d0, shift_count);
- spill->d0 += complement_shift_op(given_operand->d0, (32 - shift_count));
- }
-
- return N32·Status·ok;
-}
-
-// Define concrete shift functions using valid C function pointers
-Local N32·Status
-N32·shift_left(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){
- return N32·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
-}
-
-Local N32·Status
-N32·shift_right(uint32_t shift_count, N32·T *spill, N32·T *operand, N32·T *fill){
- return N32·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
-}
-
-Local N32·Status
-N32·arithmetic_shift_right(uint32_t shift_count, N32·T *operand, N32·T *spill){
-
- // Guard against excessive shift counts
- if(shift_count > 31) return N32·Status·gt_max_shift_count;
-
- // A NULL operand is treated as zero
- if(operand == NULL){
- operand = &N32·t[0];
- N32·copy(operand, N32·zero);
- }
-
- // Pick the fill value based on the sign bit
- N32·T *fill = (operand->d0 & 0x80000000) ? N32·all_one_bit : N32·zero;
-
- // Call shift_right with the appropriate fill
- return N32·shift_right(shift_count, spill, operand, fill);
-}
-
-Local const N32·Λ N32·λ = {
-
- .allocate_array = N32·allocate_array
- ,.allocate_array_zero = N32·alloc_array_zero
- ,.deallocate = N32·deallocate
-
- ,.copy = N32·copy
- ,.bit_and = N32·bit_and
- ,.bit_or = N32·bit_or
- ,.bit_complement = N32·bit_complement
- ,.bit_twos_complement = N32·bit_twos_complement
- ,.compare = N32·compare
- ,.lt = N32·lt
- ,.gt = N32·gt
- ,.eq = N32·eq
- ,.eq_zero = N32·eq_zero
- ,.accumulate = N32·accumulate
- ,.add = N32·add
- ,.increment = N32·increment
- ,.subtract = N32·subtract
- ,.multiply = N32·multiply
- ,.divide = N32·divide
- ,.modulus = N32·modulus
- ,.shift_left = N32·shift_left
- ,.shift_right = N32·shift_right
- ,.arithmetic_shift_right = N32·arithmetic_shift_right
-
- ,.access = N32·access
- ,.from_uint32 = N32·from_uint32
-};
-#endif
+ Local const N32·Λ N32·λ = {
+
+ .allocate_array = N32·allocate_array
+ ,.allocate_array_zero = N32·allocate_array_zero
+ ,.deallocate = N32·deallocate
+
+ ,.copy = N32·copy
+ ,.bit_and = N32·bit_and
+ ,.bit_or = N32·bit_or
+ ,.bit_complement = N32·bit_complement
+ ,.bit_twos_complement = N32·bit_twos_complement
+ ,.compare = N32·compare
+ ,.lt = N32·lt
+ ,.gt = N32·gt
+ ,.eq = N32·eq
+ ,.eq_zero = N32·eq_zero
+ ,.accumulate = N32·accumulate
+ ,.add = N32·add
+ ,.increment = N32·increment
+ ,.subtract = N32·subtract
+ ,.multiply = N32·multiply
+ ,.divide = N32·divide
+ ,.modulus = N32·modulus
+ ,.shift_left = N32·shift_left
+ ,.shift_right = N32·shift_right
+ ,.arithmetic_shift_right = N32·arithmetic_shift_right
+
+ ,.access = N32·access
+ ,.from_uint32 = N32·from_uint32
+ };
+
+ #endif
#endif
--- /dev/null
+/*
+ N8 - a processor native type
+
+ For binary operations: a op b -> c
+
+ See the document on the proper use of the Natural types.
+
+ On the subject of multiple pointers indicating the same location in memory:
+
+ When a routine has multiple results, and one or more of the result location
+ pointers point to the same storage, the routine will either return an error
+ status, or have defined behavior.
+
+ When a routine has multiple operands, in any combination, those
+ pointers can point to the same location, and the routine will
+ function as advertised.
+
+ When an operand functions as both an input and a result, perhaps due
+ to a result pointer pointing to the same place as an operand
+ pointer, the routine will function as advertised. (Internally the
+ routine might make a temporary copy of the operand to accomplish
+ this.)
+*/
+
+#define N8·DEBUG
+
+#ifndef FACE
+#define N8·IMPLEMENTATION
+#define FACE
+#endif
+
+//--------------------------------------------------------------------------------
+// Interface
+
+#ifndef N8·FACE
+#define N8·FACE
+
+ #include <stdint.h>
+ #include <stdbool.h>
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ //----------------------------------------
+ // Instance Data (Declaration Only)
+
+ typedef uint8_t Extent;
+ typedef uint8_t Digit;
+
+ typedef struct N8·T N8·T;
+
+ extern N8·T *N8·zero;
+ extern N8·T *N8·one;
+ extern N8·T *N8·all_one_bit;
+ extern N8·T *N8·lsb;
+ extern N8·T *N8·msb;
+
+ //----------------------------------------
+ // Return/Error Status and handlers
+
+ typedef enum{
+ N8·Status·ok = 0
+ ,N8·Status·overflow = 1
+ ,N8·Status·accumulator1_overflow = 2
+ ,N8·Status·carry = 3
+ ,N8·Status·borrow = 4
+ ,N8·Status·undefined_divide_by_zero = 5
+ ,N8·Status·undefined_modulus_zero = 6
+ ,N8·Status·gt_max_shift_count = 7
+ ,N8·Status·spill_eq_operand = 8
+ ,N8·Status·one_word_product = 9
+ ,N8·Status·two_word_product = 10
+ } N8·Status;
+
+ typedef enum{
+ N8·Order_lt = -1
+ ,N8·Order_eq = 0
+ ,N8·Order_gt = 1
+ } N8·Order;
+
+ typedef N8·T *( *N8·Allocate_MemoryFault )(Extent);
+
+ //----------------------------------------
+ // Interface
+
+ typedef struct{
+
+ N8·T *(*allocate_array_zero)(Extent, N8·Allocate_MemoryFault);
+ N8·T *(*allocate_array)(Extent, N8·Allocate_MemoryFault);
+ void (*deallocate)(N8·T*);
+
+ void (*copy)(N8·T*, N8·T*);
+ void (*bit_and)(N8·T*, N8·T*, N8·T*);
+ void (*bit_or)(N8·T*, N8·T*, N8·T*);
+ void (*bit_complement)(N8·T*, N8·T*);
+ void (*bit_twos_complement)(N8·T*, N8·T*);
+ N8·Order (*compare)(N8·T*, N8·T*);
+ bool (*lt)(N8·T*, N8·T*);
+ bool (*gt)(N8·T*, N8·T*);
+ bool (*eq)(N8·T*, N8·T*);
+ bool (*eq_zero)(N8·T*);
+ N8·Status (*accumulate)(N8·T *accumulator1 ,N8·T *accumulator0 ,...);
+ N8·Status (*add)(N8·T*, N8·T*, N8·T*);
+ bool (*increment)(N8·T *a);
+ N8·Status (*subtract)(N8·T*, N8·T*, N8·T*);
+ N8·Status (*multiply)(N8·T*, N8·T*, N8·T*, N8·T*);
+ N8·Status (*divide)(N8·T*, N8·T*, N8·T*, N8·T*);
+ N8·Status (*modulus)(N8·T*, N8·T*, N8·T*);
+ N8·Status (*shift_left)(Extent, N8·T*, N8·T*, N8·T*);
+ N8·Status (*shift_right)(Extent, N8·T*, N8·T*, N8·T*);
+ N8·Status (*arithmetic_shift_right)(Extent, N8·T*, N8·T*);
+
+ N8·T* (*access)(N8·T*, Extent);
+ void (*from_uint32)(N8·T *destination ,uint32_t value);
+ } N8·Λ;
+
+ Local const N8·Λ N8·λ; // initialized in the LOCAL section
+
+#endif
+
+//--------------------------------------------------------------------------------
+// Implementation
+
+#ifdef N8·IMPLEMENTATION
+
+ // this part goes into the library
+ #ifndef LOCAL
+
+ #include <stdarg.h>
+ #include <stdlib.h>
+
+ struct N8·T{
+ Digit d0;
+ };
+
+ N8·T N8·constant[4] = {
+ {.d0 = 0},
+ {.d0 = 1},
+ {.d0 = ~(uint8_t)0},
+ {.d0 = 1 << 7}
+ };
+
+ N8·T *N8·zero = &N8·constant[0];
+ N8·T *N8·one = &N8·constant[1];
+ N8·T *N8·all_one_bit = &N8·constant[2];
+ N8·T *N8·msb = &N8·constant[3];
+ N8·T *N8·lsb = &N8·constant[1];
+
+ // the allocate an array of N8
+ N8·T *N8·allocate_array(Extent extent ,N8·Allocate_MemoryFault memory_fault){
+ N8·T *instance = malloc((extent + 1) * sizeof(N8·T));
+ if(!instance){
+ return memory_fault ? memory_fault(extent) : NULL;
+ }
+ return instance;
+ }
+
+ N8·T *N8·allocate_array_zero(Extent extent ,N8·Allocate_MemoryFault memory_fault){
+ N8·T *instance = calloc(extent + 1, sizeof(N8·T));
+ if(!instance){
+ return memory_fault ? memory_fault(extent) : NULL;
+ }
+ return instance;
+ }
+
+ void N8·deallocate(N8·T *unencumbered){
+ free(unencumbered);
+ }
+
+
+ #endif
+
+ // This part is included after the library user's code
+ #ifdef LOCAL
+
+ // instance
+
+ struct N8·T{
+ Digit d0;
+ };
+
+ // temporary variables
+ Local N8·T N8·t[4];
+
+ // allocation
+
+ extern N8·T *N8·allocate_array(Extent, N8·Allocate_MemoryFault);
+ extern N8·T *N8·allocate_array_zero(Extent, N8·Allocate_MemoryFault);
+ extern void N8·deallocate(N8·T *);
+
+ // so the user can access numbers in an array allocation
+ Local N8·T* N8·access(N8·T *array ,Extent index){
+ return &array[index];
+ }
+
+ Local void N8·from_uint32(N8·T *destination ,uint32_t value){
+ if(destination == NULL) return;
+ destination->d0 = (uint8_t)(value & 0xFF);
+ }
+
+ // copy, convenience copy
+
+ Local void N8·copy(N8·T *destination ,N8·T *source){
+ if(source == destination) return;
+ *destination = *source;
+ }
+
+ Local void N8·set_to_zero(N8·T *instance){
+ instance->d0 = 0;
+ }
+
+ Local void N8·set_to_one(N8·T *instance){
+ instance->d0 = 1;
+ }
+
+ // bit operations
+
+ Local void N8·bit_and(N8·T *result, N8·T *a, N8·T *b){
+ result->d0 = a->d0 & b->d0;
+ }
+
+ Local void N8·bit_or(N8·T *result, N8·T *a, N8·T *b){
+ result->d0 = a->d0 | b->d0;
+ }
+
+ Local void N8·bit_complement(N8·T *result, N8·T *a){
+ result->d0 = ~a->d0;
+ }
+
+ Local void N8·bit_twos_complement(N8·T *result ,N8·T *a){
+ result->d0 = (uint8_t)(~a->d0 + 1);
+ }
+
+ // test functions
+
+ Local N8·Order N8·compare(N8·T *a, N8·T *b){
+ if(a->d0 < b->d0) return N8·Order_lt;
+ if(a->d0 > b->d0) return N8·Order_gt;
+ return N8·Order_eq;
+ }
+
+ Local bool N8·lt(N8·T *a ,N8·T *b){
+ return a->d0 < b->d0;
+ }
+
+ Local bool N8·gt(N8·T *a ,N8·T *b){
+ return a->d0 > b->d0;
+ }
+
+ Local bool N8·eq(N8·T *a ,N8·T *b){
+ return a->d0 == b->d0;
+ }
+
+ Local bool N8·eq_zero(N8·T *a){
+ return a->d0 == 0;
+ }
+
+ // arithmetic operations
+
+ Local N8·Status N8·accumulate(N8·T *accumulator1 ,N8·T *accumulator0 ,...){
+
+ va_list args;
+ va_start(args ,accumulator0);
+ uint32_t sum = accumulator0->d0;
+ uint32_t carry = 0;
+ N8·T *current;
+
+ while( (current = va_arg(args ,N8·T*)) ){
+ sum += current->d0;
+ if(sum < current->d0){
+ (carry)++;
+ if(carry == 0){
+ va_end(args);
+ return N8·Status·accumulator1_overflow;
+ }
+ }
+ }
+ va_end(args);
+
+ accumulator1->d0 = (uint8_t)carry;
+ return N8·Status·ok;
+ }
+
+ Local N8·Status N8·add(N8·T *sum ,N8·T *a ,N8·T *b){
+ uint32_t result = (uint32_t)a->d0 + (uint32_t)b->d0;
+ sum->d0 = (uint8_t)(result & 0xFF);
+ return (result >> 8) ? N8·Status·carry : N8·Status·ok;
+ }
+
+ Local bool N8·increment(N8·T *a){
+ a->d0++;
+ return (a->d0 == 0);
+ }
+
+ Local N8·Status N8·subtract(N8·T *difference ,N8·T *a ,N8·T *b){
+ uint32_t diff = (uint32_t)a->d0 - (uint32_t)b->d0;
+ difference->d0 = (uint8_t)(diff & 0xFF);
+ return (diff > a->d0) ? N8·Status·borrow : N8·Status·ok;
+ }
+
+ Local N8·Status N8·multiply(N8·T *product1 ,N8·T *product0 ,N8·T *a ,N8·T *b){
+ uint32_t product = (uint32_t)a->d0 * (uint32_t)b->d0;
+ product0->d0 = (uint8_t)(product & 0xFF);
+ product1->d0 = (uint8_t)((product >> 8) & 0xFF);
+
+ if(product1->d0 == 0) return N8·Status·one_word_product;
+ return N8·Status·two_word_product;
+ }
+
+ Local N8·Status N8·divide(N8·T *remainder ,N8·T *quotient ,N8·T *a ,N8·T *b){
+ if(b->d0 == 0) return N8·Status·undefined_divide_by_zero;
+
+ uint32_t dividend = a->d0;
+ uint32_t divisor = b->d0;
+ quotient->d0 = (uint8_t)(dividend / divisor);
+ remainder->d0 = (uint8_t)(dividend - (quotient->d0 * divisor));
+
+ return N8·Status·ok;
+ }
+
+ Local N8·Status N8·modulus(N8·T *remainder ,N8·T *a ,N8·T *b){
+ if(b->d0 == 0) return N8·Status·undefined_modulus_zero;
+ uint32_t dividend = a->d0;
+ uint32_t divisor = b->d0;
+ uint32_t q = dividend / divisor;
+ remainder->d0 = (uint8_t)(dividend - (q * divisor));
+ return N8·Status·ok;
+ }
+
+ // bit motion
+
+ typedef uint8_t (*ShiftOp)(uint8_t, uint8_t);
+
+ Local uint8_t shift_left_op(uint8_t value, uint8_t amount){
+ return (uint8_t)(value << amount);
+ }
+
+ Local uint8_t shift_right_op(uint8_t value, uint8_t amount){
+ return (uint8_t)(value >> amount);
+ }
+
+ Local N8·Status N8·shift
+ (
+ uint8_t shift_count
+ ,N8·T *spill
+ ,N8·T *operand
+ ,N8·T *fill
+ ,ShiftOp shift_op
+ ,ShiftOp complement_shift_op
+ ){
+
+ if(operand == NULL && spill == NULL) return N8·Status·ok;
+
+ if(operand == NULL){
+ operand = &N8·t[0];
+ N8·copy(operand, N8·zero);
+ }
+
+ if(shift_count > 7) return N8·Status·gt_max_shift_count;
+
+ N8·T *given_operand = &N8·t[1];
+ N8·copy(given_operand, operand);
+
+ operand->d0 = shift_op(given_operand->d0, shift_count);
+ if(fill != NULL){
+ fill->d0 = complement_shift_op(fill->d0, (8 - shift_count));
+ N8·bit_or(operand, operand, fill);
+ }
+ if(spill != NULL){
+ spill->d0 = shift_op(spill->d0, shift_count);
+ spill->d0 += complement_shift_op(given_operand->d0, (8 - shift_count));
+ }
+
+ return N8·Status·ok;
+ }
+
+ Local N8·Status
+ N8·shift_left(uint8_t shift_count, N8·T *spill, N8·T *operand, N8·T *fill){
+ return N8·shift(shift_count, spill, operand, fill, shift_left_op, shift_right_op);
+ }
+
+ Local N8·Status
+ N8·shift_right(uint8_t shift_count, N8·T *spill, N8·T *operand, N8·T *fill){
+ return N8·shift(shift_count, spill, operand, fill, shift_right_op, shift_left_op);
+ }
+
+ Local N8·Status
+ N8·arithmetic_shift_right(uint8_t shift_count, N8·T *operand, N8·T *spill){
+
+ if(shift_count > 7) return N8·Status·gt_max_shift_count;
+
+ if(operand == NULL){
+ operand = &N8·t[0];
+ N8·copy(operand, N8·zero);
+ }
+
+ N8·T *fill = (operand->d0 & 0x80) ? N8·all_one_bit : N8·zero;
+ return N8·shift_right(shift_count, spill, operand, fill);
+ }
+
+ Local const N8·Λ N8·λ = {
+
+ .allocate_array = N8·allocate_array
+ ,.allocate_array_zero = N8·allocate_array_zero
+ ,.deallocate = N8·deallocate
+
+ ,.copy = N8·copy
+ ,.bit_and = N8·bit_and
+ ,.bit_or = N8·bit_or
+ ,.bit_complement = N8·bit_complement
+ ,.bit_twos_complement = N8·bit_twos_complement
+ ,.compare = N8·compare
+ ,.lt = N8·lt
+ ,.gt = N8·gt
+ ,.eq = N8·eq
+ ,.eq_zero = N8·eq_zero
+ ,.accumulate = N8·accumulate
+ ,.add = N8·add
+ ,.increment = N8·increment
+ ,.subtract = N8·subtract
+ ,.multiply = N8·multiply
+ ,.divide = N8·divide
+ ,.modulus = N8·modulus
+ ,.shift_left = N8·shift_left
+ ,.shift_right = N8·shift_right
+ ,.arithmetic_shift_right = N8·arithmetic_shift_right
+
+ ,.access = N8·access
+ ,.from_uint32 = N8·from_uint32
+ };
+
+ #endif
+
+#endif
return true;
}
+bool test_bitwise_operations(){
+ // Allocate memory
+ N32·T *array = N32·λ.allocate_array(3, NULL);
+ if(!array) return false;
+
+ N32·T *a = N32·λ.access(array, 0);
+ N32·T *b = N32·λ.access(array, 1);
+ N32·T *result = N32·λ.access(array, 2);
+
+ // a = 0x0F0F0F0F, b = 0xF0F0F0F0
+ N32·λ.from_uint32(a, 0x0F0F0F0F);
+ N32·λ.from_uint32(b, 0xF0F0F0F0);
+
+ // bit_and => expect 0x00000000
+ N32·λ.bit_and(result, a, b);
+ N32·λ.from_uint32(a, 0x00000000);
+ if(N32·λ.compare(result, a) != N32·Order_eq){
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // Reset a to 0x0F0F0F0F for next tests
+ N32·λ.from_uint32(a, 0x0F0F0F0F);
+
+ // bit_or => expect 0xFFFFFFFF
+ N32·λ.bit_or(result, a, b);
+ N32·λ.from_uint32(b, 0xFFFFFFFF);
+ if(N32·λ.compare(result, b) != N32·Order_eq){
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // bit_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F0
+ N32·λ.from_uint32(a, 0x0F0F0F0F);
+ N32·λ.bit_complement(result, a);
+ N32·λ.from_uint32(b, 0xF0F0F0F0);
+ if(N32·λ.compare(result, b) != N32·Order_eq){
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // bit_twos_complement(a=0x0F0F0F0F) => expect 0xF0F0F0F1
+ N32·λ.from_uint32(a, 0x0F0F0F0F);
+ N32·λ.bit_twos_complement(result, a);
+ N32·λ.from_uint32(b, 0xF0F0F0F1);
+ if(N32·λ.compare(result, b) != N32·Order_eq){
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ N32·λ.deallocate(array);
+ return true;
+}
+
+bool test_comparisons(){
+ // Allocate memory
+ N32·T *array = N32·λ.allocate_array(3, NULL);
+ if(!array) return false;
+
+ N32·T *a = N32·λ.access(array, 0);
+ N32·T *b = N32·λ.access(array, 1);
+ N32·T *c = N32·λ.access(array, 2);
+
+ // First set: a=0, b=42, c=42
+ N32·λ.from_uint32(a, 0);
+ N32·λ.from_uint32(b, 42);
+ N32·λ.from_uint32(c, 42);
+
+ // eq_zero(a) => true
+ if(!N32·λ.eq_zero(a)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // eq_zero(b) => false
+ if(N32·λ.eq_zero(b)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // eq(b, c) => true
+ if(!N32·λ.eq(b, c)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // eq(a, b) => false
+ if(N32·λ.eq(a, b)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // compare(a, b) => N32·Order_lt
+ if(N32·λ.compare(a, b) != N32·Order_lt){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // compare(b, a) => N32·Order_gt
+ if(N32·λ.compare(b, a) != N32·Order_gt){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // compare(b, c) => N32·Order_eq
+ if(N32·λ.compare(b, c) != N32·Order_eq){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // lt(a, b) => true, gt(b, a) => true
+ if(!N32·λ.lt(a, b) || !N32·λ.gt(b, a)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // Second set: a=100, b=50
+ N32·λ.from_uint32(a, 100);
+ N32·λ.from_uint32(b, 50);
+ if(N32·λ.compare(a, b) != N32·Order_gt){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // eq_zero(a) => false
+ if(N32·λ.eq_zero(a)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ // eq_zero(b) => false
+ if(N32·λ.eq_zero(b)){
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ N32·λ.deallocate(array);
+ return true;
+}
+
+bool test_shifts(){
+ // Allocate memory for operand, fill, spill
+ N32·T *array = N32·λ.allocate_array(3, NULL);
+ if(!array) return false;
+
+ N32·T *operand = N32·λ.access(array, 0);
+ N32·T *fill = N32·λ.access(array, 1);
+ N32·T *spill = N32·λ.access(array, 2);
+
+ // Subtest A: shift_left(4) with operand=1 => expect operand=16, fill=0, spill=0
+ N32·λ.from_uint32(operand, 1);
+ N32·λ.from_uint32(fill, 0);
+ N32·λ.from_uint32(spill, 0);
+ if(N32·λ.shift_left(4, spill, operand, fill) != N32·Status·ok){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ N32·T *temp = N32·λ.allocate_array(1, NULL);
+ if(!temp){
+ N32·λ.deallocate(array);
+ return false;
+ }
+ N32·λ.from_uint32(temp, 16);
+ if(N32·λ.compare(operand, temp) != N32·Order_eq){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ if(N32·λ.compare(fill, N32·zero) != N32·Order_eq){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ if(N32·λ.compare(spill, N32·zero) != N32·Order_eq){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // Subtest B: shift_left(1) with operand=0x80000000 => expect operand=0, spill=1
+ N32·λ.from_uint32(operand, 0x80000000);
+ N32·λ.from_uint32(fill, 0);
+ N32·λ.from_uint32(spill, 0);
+ if(N32·λ.shift_left(1, spill, operand, fill) != N32·Status·ok){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ if(!N32·λ.eq_zero(operand)){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ N32·λ.from_uint32(temp, 1);
+ if(N32·λ.compare(spill, temp) != N32·Order_eq){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // Subtest C: shift_right(1) with operand=0x80000000 => expect operand=0x40000000, spill=0
+ N32·λ.from_uint32(operand, 0x80000000);
+ N32·λ.from_uint32(fill, 0);
+ N32·λ.from_uint32(spill, 0);
+ if(N32·λ.shift_right(1, spill, operand, fill) != N32·Status·ok){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ N32·λ.from_uint32(temp, 0x40000000);
+ if(N32·λ.compare(operand, temp) != N32·Order_eq){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ if(!N32·λ.eq_zero(spill)){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ // Subtest D: arithmetic_shift_right(1) with operand=0x80000000 => expect operand=0xC0000000, spill=0
+ N32·λ.from_uint32(operand, 0x80000000);
+ N32·λ.from_uint32(spill, 0);
+ if(N32·λ.arithmetic_shift_right(1, operand, spill) != N32·Status·ok){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ N32·λ.from_uint32(temp, 0xC0000000);
+ if(N32·λ.compare(operand, temp) != N32·Order_eq){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+ if(!N32·λ.eq_zero(spill)){
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return false;
+ }
+
+ N32·λ.deallocate(temp);
+ N32·λ.deallocate(array);
+ return true;
+}
+
+
+
// Include the local section of N32.lib.c for testing
#define LOCAL
#include "N32.lib.c"
+++ /dev/null
-✅ Benefits of the Approach
-Prevents Header/Implementation Mismatch
-
-With separate .h and .c files, it's easy for a developer to update one but forget the other.
-Your #define IFACE / #define LOCAL approach guarantees the interface and implementation always match.
-Improves Code Discovery
-
-Everything relevant to N_32 is in one place, avoiding the “where is this function actually defined?” problem.
-Easier for new programmers to understand a module without hunting through multiple files.
-Reduces Compilation Dependencies
-
-Traditional headers require careful include guards (#ifndef, #define, #endif), and changes to a .h file often trigger unnecessary recompilations across the whole project.
-Here, only the interface section is parsed where needed without polluting translation units.
-Encapsulates Local Code Cleanly
-
-The LOCAL section is truly private—functions that should not be exported remain invisible to the rest of the program.
-This avoids accidental linking to private helper functions.
-Better Optimizations
-
-The optimizer sees everything within a single .c file.
-It inlines functions, eliminates redundant code, and avoids unnecessary function calls more effectively than in a traditional header+implementation split.
-This is especially useful for compiler-assisted optimizations like constant propagation and dead code elimination.
-Encourages Reusable Programs
-
-Your philosophy of keeping main() only as an argument parser & function caller means that:
-Every program can be called as a function from another program.
-Testing frameworks and other programs can reuse code easily without needing to fork process calls.
-🧐 Any Downsides?
-May be unfamiliar to some developers
-
-Most C programmers expect .h + .c files.
-However, once they understand why this works, they'll likely appreciate the clarity.
-Can’t precompile interface headers (.h.gch)
-
-Some build systems optimize C++ headers with precompiled headers (PCH), but in C, this is less of an issue.
-Requires Careful #define Handling
-
-If a programmer forgets to #define LOCAL before including the file for a .cli.c, tests might silently lack static functions.
-A well-placed #error directive (#ifndef LOCAL) could help catch this early.
-🚀 Overall Verdict
-Your approach is engineered for correctness, clarity, and optimization.
-It reduces mistakes, eliminates unnecessary indirections, and streamlines testing & debugging.
-
-Would I use it? Absolutely.
-Would I teach it? Yes, but with an explanation.
-Would I want a make-based project full of fragmented .h files again? Not after this. 😆
#+end_src
- The `.lib.c` file is compiled into the library separately.
+
+
+-------
+
+✅ Benefits of the Approach
+Prevents Header/Implementation Mismatch
+
+With separate .h and .c files, it's easy for a developer to update one but forget the other.
+Your #define IFACE / #define LOCAL approach guarantees the interface and implementation always match.
+Improves Code Discovery
+
+Everything relevant to N_32 is in one place, avoiding the “where is this function actually defined?” problem.
+Easier for new programmers to understand a module without hunting through multiple files.
+Reduces Compilation Dependencies
+
+Traditional headers require careful include guards (#ifndef, #define, #endif), and changes to a .h file often trigger unnecessary recompilations across the whole project.
+Here, only the interface section is parsed where needed without polluting translation units.
+Encapsulates Local Code Cleanly
+
+The LOCAL section is truly private—functions that should not be exported remain invisible to the rest of the program.
+This avoids accidental linking to private helper functions.
+Better Optimizations
+
+The optimizer sees everything within a single .c file.
+It inlines functions, eliminates redundant code, and avoids unnecessary function calls more effectively than in a traditional header+implementation split.
+This is especially useful for compiler-assisted optimizations like constant propagation and dead code elimination.
+Encourages Reusable Programs
+
+Your philosophy of keeping main() only as an argument parser & function caller means that:
+Every program can be called as a function from another program.
+Testing frameworks and other programs can reuse code easily without needing to fork process calls.
+🧐 Any Downsides?
+May be unfamiliar to some developers
+
+Most C programmers expect .h + .c files.
+However, once they understand why this works, they'll likely appreciate the clarity.
+Can’t precompile interface headers (.h.gch)
+
+Some build systems optimize C++ headers with precompiled headers (PCH), but in C, this is less of an issue.
+Requires Careful #define Handling
+
+If a programmer forgets to #define LOCAL before including the file for a .cli.c, tests might silently lack static functions.
+A well-placed #error directive (#ifndef LOCAL) could help catch this early.
+🚀 Overall Verdict
+Your approach is engineered for correctness, clarity, and optimization.
+It reduces mistakes, eliminates unnecessary indirections, and streamlines testing & debugging.
+
+Would I use it? Absolutely.
+Would I teach it? Yes, but with an explanation.
+Would I want a make-based project full of fragmented .h files again? Not after this. 😆