N32 tests pass, addition of N16 and N8
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Fri, 14 Feb 2025 06:30:20 +0000 (06:30 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Fri, 14 Feb 2025 06:30:20 +0000 (06:30 +0000)
developer/amd64/test_N32 [new file with mode: 0755]
developer/amd64/test_setup [new file with mode: 0755]
developer/cc🖉/N16.lib.c [new file with mode: 0644]
developer/cc🖉/N32.lib.c
developer/cc🖉/N8.lib.c [new file with mode: 0644]
developer/cc🖉/test_N32.cli.c
developer/document🖉/Approach_to_headers.txt [deleted file]
developer/document🖉/SectionedFileFormat.org

diff --git a/developer/amd64/test_N32 b/developer/amd64/test_N32
new file mode 100755 (executable)
index 0000000..36150b2
Binary files /dev/null and b/developer/amd64/test_N32 differ
diff --git a/developer/amd64/test_setup b/developer/amd64/test_setup
new file mode 100755 (executable)
index 0000000..d5834b8
Binary files /dev/null and b/developer/amd64/test_setup differ
diff --git a/developer/cc🖉/N16.lib.c b/developer/cc🖉/N16.lib.c
new file mode 100644 (file)
index 0000000..3b77116
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+  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
index ff69120..fba3c21 100644 (file)
 #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
 
@@ -123,336 +123,335 @@ Local const N32·Λ N32·λ; // initialized in the LOCAL section
 
 #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
diff --git a/developer/cc🖉/N8.lib.c b/developer/cc🖉/N8.lib.c
new file mode 100644 (file)
index 0000000..521666f
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+  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
index 6e79571..563711f 100644 (file)
@@ -119,6 +119,245 @@ bool test_arithmetic(){
   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"
diff --git a/developer/document🖉/Approach_to_headers.txt b/developer/document🖉/Approach_to_headers.txt
deleted file mode 100644 (file)
index c929c4d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-✅ 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. 😆
index 46710ee..db89851 100644 (file)
      #+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. 😆