#include /* * Fast-path (hack) implementation: all values stored in single-digit mode. * data.digit holds the absolute magnitude as u64, len sign indicates sign. * Arithmetic is done via isize with explicit overflow checks — anything * out of isize range triggers SCC_AP_PANIC. */ /* ---------- internal helpers ---------- */ /* Convert from sign-magnitude (digit mode) to isize */ static isize ap_to_isize(const scc_ap_t *ap) { SCC_AP_ASSERT(ap != nullptr); SCC_AP_ASSERT(ap->capacity == -1); uint64_t mag = ap->data.digit; if (ap->len < 0) { if (mag == 0) return 0; if (mag == (uint64_t)INTPTR_MAX + 1) return INTPTR_MIN; SCC_AP_ASSERT(mag <= (uint64_t)INTPTR_MAX); return -(isize)(int64_t)mag; } SCC_AP_ASSERT(mag <= (uint64_t)INTPTR_MAX); return (isize)(int64_t)mag; } /* Store isize value into sign-magnitude (digit mode) */ static void ap_from_isize(scc_ap_t *ap, isize val) { ap->capacity = -1; if (val < 0) { ap->len = -1; ap->data.digit = (scc_ap_digit)(-(uint64_t)(int64_t)val); } else { ap->len = 1; ap->data.digit = (scc_ap_digit)val; } } /* Checked addition */ static isize ap_add_isize(isize a, isize b) { if (b > 0 && a > INTPTR_MAX - b) SCC_AP_PANIC("scc_ap_add: positive overflow"); if (b < 0 && a < INTPTR_MIN - b) SCC_AP_PANIC("scc_ap_add: negative overflow"); return a + b; } /* Checked subtraction: a - b */ static isize ap_sub_isize(isize a, isize b) { /* a - b = a + (-b); -INTPTR_MIN overflows, handle specially */ if (b == INTPTR_MIN) { if (a < 0) SCC_AP_PANIC("scc_ap_sub: overflow (a - INTPTR_MIN, a < 0)"); SCC_AP_PANIC("scc_ap_sub: overflow (a - INTPTR_MIN, a >= 0)"); } return ap_add_isize(a, -b); } /* Checked multiplication */ static isize ap_mul_isize(isize a, isize b) { if (a == 0 || b == 0) return 0; if (a == 1) return b; if (b == 1) return a; /* Determine sign of result */ int sign = ((a < 0) == (b < 0)) ? 1 : -1; /* Absolute values as uint64 */ uint64_t ua = (a == INTPTR_MIN) ? (uint64_t)INTPTR_MAX + 1 : (uint64_t)(a < 0 ? -(int64_t)a : (int64_t)a); uint64_t ub = (b == INTPTR_MIN) ? (uint64_t)INTPTR_MAX + 1 : (uint64_t)(b < 0 ? -(int64_t)b : (int64_t)b); /* Overflow threshold */ uint64_t limit = (sign > 0) ? (uint64_t)INTPTR_MAX : (uint64_t)INTPTR_MAX + 1; if (ua != 0 && ub > limit / ua) SCC_AP_PANIC("scc_ap_mul: overflow"); isize result = (isize)(ua * ub); return (sign > 0) ? result : -result; } /* Checked division */ static isize ap_div_isize(isize a, isize b) { if (b == 0) SCC_AP_PANIC("scc_ap_div: division by zero"); if (a == INTPTR_MIN && b == -1) SCC_AP_PANIC("scc_ap_div: overflow (INTPTR_MIN / -1)"); return a / b; } /* Checked modulo */ static isize ap_mod_isize(isize a, isize b) { if (b == 0) SCC_AP_PANIC("scc_ap_mod: modulo by zero"); if (a == INTPTR_MIN && b == -1) SCC_AP_PANIC("scc_ap_mod: overflow (INTPTR_MIN %% -1)"); return a % b; } /* ---------- public API ---------- */ void scc_ap_drop(scc_ap_t *ap) { SCC_AP_ASSERT(ap != nullptr); /* In digit mode (capacity == -1), there's no heap allocation to free */ scc_ap_init(ap); } void scc_ap_with_bits(scc_ap_t *ap, int bits) { SCC_AP_ASSERT(ap != nullptr); if (bits > (int)(sizeof(scc_ap_digit) * 8)) SCC_AP_PANIC( "scc_ap_with_bits: request %d bits but digit only has %zu bits", bits, sizeof(scc_ap_digit) * 8); (void)ap; /* Fast path: always stays in digit mode */ } void scc_ap_from_string(scc_ap_t *ap, const char *str, int len, int base) { SCC_AP_ASSERT(ap != nullptr && str != nullptr); /* len < 0 means null-terminated */ if (len < 0) { len = 0; while (str[len] != '\0') len++; } if (len == 0) { ap_from_isize(ap, 0); return; } int i = 0; int sign = 1; /* Optional leading sign */ if (str[i] == '-') { sign = -1; i++; } else if (str[i] == '+') { i++; } if (i >= len) { ap_from_isize(ap, 0); return; } /* ---------- base auto-detection ---------- */ /* hex: 0x / 0X */ if ((base == 0 || base == 16) && (i + 2 < len && str[i] == '0' && (str[i + 1] == 'x' || str[i + 1] == 'X'))) { base = 16; i += 2; } /* binary: 0b / 0B */ if ((base == 0 || base == 2) && (i + 2 < len && str[i] == '0' && (str[i + 1] == 'b' || str[i + 1] == 'B'))) { base = 2; i += 2; } if (base == 0) { if (i < len && str[i] == '0') { base = 8; i++; } else { base = 10; } } /* ---------- parse digits ---------- */ uint64_t val = 0; for (; i < len; i++) { char ch = str[i]; unsigned d; if (ch >= '0' && ch <= '9') d = (unsigned)(ch - '0'); else if (ch >= 'a' && ch <= 'f') d = (unsigned)(ch - 'a' + 10); else if (ch >= 'A' && ch <= 'F') d = (unsigned)(ch - 'A' + 10); else break; if ((int)d >= base) break; /* overflow check: val * base + d <= UINT64_MAX */ if (val > (UINT64_MAX - d) / (unsigned)base) SCC_AP_PANIC("scc_ap_from_string: overflow"); val = val * (unsigned)base + d; } ap->capacity = -1; ap->data.digit = val; ap->len = (sign < 0 && val > 0) ? -1 : 1; } void scc_ap_set_digit(scc_ap_t *ap, scc_ap_digit digit) { SCC_AP_ASSERT(ap != nullptr); if (digit > (scc_ap_digit)INTPTR_MAX) SCC_AP_PANIC("scc_ap_set_digit: value %llu exceeds isize range", (unsigned long long)digit); ap->capacity = -1; ap->data.digit = digit; ap->len = 1; } void scc_ap_add(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); isize a = ap_to_isize(from_a); isize b = ap_to_isize(from_b); ap_from_isize(to, ap_add_isize(a, b)); } void scc_ap_sub(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); isize a = ap_to_isize(from_a); isize b = ap_to_isize(from_b); ap_from_isize(to, ap_sub_isize(a, b)); } void scc_ap_mul(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); isize a = ap_to_isize(from_a); isize b = ap_to_isize(from_b); ap_from_isize(to, ap_mul_isize(a, b)); } void scc_ap_div(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); isize a = ap_to_isize(from_a); isize b = ap_to_isize(from_b); ap_from_isize(to, ap_div_isize(a, b)); } void scc_ap_mod(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); isize a = ap_to_isize(from_a); isize b = ap_to_isize(from_b); ap_from_isize(to, ap_mod_isize(a, b)); } void scc_ap_shl(scc_ap_t *to, const scc_ap_t *from, unsigned shift) { SCC_AP_ASSERT(to != nullptr && from != nullptr); SCC_AP_ASSERT(from->capacity == -1); if (shift >= sizeof(scc_ap_digit) * 8) SCC_AP_PANIC("scc_ap_shl: shift %u exceeds digit width (%zu bits)", shift, sizeof(scc_ap_digit) * 8); /* left shift = multiply by 2^shift */ isize val = ap_to_isize(from); isize result = ap_mul_isize(val, (isize)1 << shift); ap_from_isize(to, result); } void scc_ap_shr(scc_ap_t *to, const scc_ap_t *from, unsigned shift) { SCC_AP_ASSERT(to != nullptr && from != nullptr); SCC_AP_ASSERT(from->capacity == -1); if (shift >= sizeof(scc_ap_digit) * 8) SCC_AP_PANIC("scc_ap_shr: shift %u exceeds digit width (%zu bits)", shift, sizeof(scc_ap_digit) * 8); /* arithmetic right shift — uses >> on isize (arithmetic on GCC/MSVC/Clang) */ isize val = ap_to_isize(from); ap_from_isize(to, val >> shift); } void scc_ap_lshr(scc_ap_t *to, const scc_ap_t *from, unsigned shift) { SCC_AP_ASSERT(to != nullptr && from != nullptr); SCC_AP_ASSERT(from->capacity == -1); if (shift >= sizeof(scc_ap_digit) * 8) SCC_AP_PANIC("scc_ap_lshr: shift %u exceeds digit width (%zu bits)", shift, sizeof(scc_ap_digit) * 8); /* * logical right shift: reinterpret the signed value as uint64 bit pattern, * shift right, store as non-negative magnitude. */ uint64_t bits; if (from->len < 0) { /* negative → two's complement bit pattern */ bits = -(uint64_t)(int64_t)from->data.digit; } else { bits = from->data.digit; } to->capacity = -1; to->data.digit = bits >> shift; to->len = 1; } /* ---------- bitwise ---------- */ void scc_ap_and(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b) { SCC_AP_ASSERT(to != nullptr && a != nullptr && b != nullptr); ap_from_isize(to, ap_to_isize(a) & ap_to_isize(b)); } void scc_ap_or(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b) { SCC_AP_ASSERT(to != nullptr && a != nullptr && b != nullptr); ap_from_isize(to, ap_to_isize(a) | ap_to_isize(b)); } void scc_ap_xor(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b) { SCC_AP_ASSERT(to != nullptr && a != nullptr && b != nullptr); ap_from_isize(to, ap_to_isize(a) ^ ap_to_isize(b)); } void scc_ap_not(scc_ap_t *to, const scc_ap_t *from) { SCC_AP_ASSERT(to != nullptr && from != nullptr); ap_from_isize(to, ~ap_to_isize(from)); } /* ---------- unary arithmetic ---------- */ void scc_ap_neg(scc_ap_t *to, const scc_ap_t *from) { SCC_AP_ASSERT(to != nullptr && from != nullptr); isize val = ap_to_isize(from); if (val == INTPTR_MIN) SCC_AP_PANIC("scc_ap_neg: overflow (INTPTR_MIN)"); ap_from_isize(to, -val); } /* ---------- comparison ---------- */ int scc_ap_cmp(const scc_ap_t *a, const scc_ap_t *b) { SCC_AP_ASSERT(a != nullptr && b != nullptr); isize va = ap_to_isize(a); isize vb = ap_to_isize(b); if (va < vb) return -1; if (va > vb) return 1; return 0; } int scc_ap_is_zero(const scc_ap_t *ap) { SCC_AP_ASSERT(ap != nullptr); return ap->data.digit == 0; } int scc_ap_eql(const scc_ap_t *a, const scc_ap_t *b) { SCC_AP_ASSERT(a != nullptr && b != nullptr); SCC_AP_ASSERT(a->capacity == -1 && b->capacity == -1); if (a->len != b->len) { /* -0 == +0 */ if (a->data.digit == 0 && b->data.digit == 0) return 1; return 0; } return a->data.digit == b->data.digit; } int scc_ap_dump(scc_ap_t *ap, ap_dump_fn dump_fn, void *user_data) { SCC_AP_ASSERT(ap != nullptr && dump_fn != nullptr); SCC_AP_ASSERT(ap->capacity == -1); isize val = ap_to_isize(ap); if (val == 0) { dump_fn('0', user_data); return 0; } /* Buffer large enough for INT64_MIN "-9223372036854775808" */ char buf[24]; int pos = sizeof(buf); if (val < 0) { dump_fn('-', user_data); /* Work with positive magnitude to avoid overflow when printing */ uint64_t mag = ap->data.digit; while (mag > 0) { buf[--pos] = (char)('0' + (mag % 10)); mag /= 10; } } else { uint64_t mag = (uint64_t)val; while (mag > 0) { buf[--pos] = (char)('0' + (mag % 10)); mag /= 10; } } for (int i = pos; i < (int)sizeof(buf); i++) dump_fn(buf[i], user_data); return 0; }