from stdint import * import t, c import stdio # String copy function @life(noescape(dest)) def strcpy(dest: str, src: str) -> str: if dest == None or src == None: return None original_dest: str = dest for s in src: dest[0] = s dest += 1 dest[0] = 0 return original_dest # String concatenate function @life(noescape(dest)) def strcat(dest: str, src: str) -> str: if dest == None or src == None: return dest original_dest: str = dest # Find end of dest while dest[0] != '\0': dest += 1 # Copy src for s in src: dest[0] = s dest += 1 dest[0] = 0 return original_dest # String copy function with limited length @life(noescape(dest)) def strncpy(dest: str, src: str, n: sizet) -> str: if dest == None or src == None or n == 0: return dest original_dest: str = dest i: sizet = 0 for s in src: if i >= n: break dest[0] = s dest += 1 i += 1 # Fill remaining space with '\0' while i < n: dest[0] = 0 dest += 1 i += 1 return original_dest # String concatenate function with limited length @life(noescape(dest)) def strncat(dest: str, src: str, n: sizet) -> str: if dest == None or src == None or n == 0: return dest original_dest: str = dest # Find end of dest while dest[0] != '\0': dest += 1 # Copy up to n chars from src i: sizet = 0 for s in src: if i >= n: break dest[0] = s dest += 1 i += 1 dest[0] = 0 return original_dest # String length function export def strlen(src: str) -> sizet: length: sizet = 0 for _ in src: length += 1 return length # String comparison function # Note: str1[0] - str2[0] is an i8 subtraction that wraps (0xFF means -1), so both operands # must be promoted to i32 before subtracting (as in memcmp below); otherwise strcmp("abc","abd") returns 255 instead of -1. def strcmp(str1: str, str2: str) -> int: # Pointer-equality short-circuit: identical pointers guarantee identical contents, so return 0 directly and skip the full byte loop. # Semantics strictly unchanged (same pointer => same content => strcmp result is 0); different pointers fall through to the loop below. if str1 == str2: return 0 while str1[0] != '\0' and str2[0] != '\0': if str1[0] != str2[0]: return int(str1[0]) - int(str2[0]) str1 += 1 str2 += 1 return int(str1[0]) - int(str2[0]) def samestr(str1: str, str2: str) -> bool: return strcmp(str1, str2) == 0 # String compare function with limited length def strncmp(str1: str, str2: str, n: sizet) -> int: # Pointer-equality short-circuit (same as strcmp): identical pointers compare equal for any n, so return 0. # Semantics strictly unchanged. if str1 == str2: return 0 i: sizet = 0 while str1[0] != '\0' and str2[0] != '\0' and i < n: if str1[0] != str2[0]: return int(str1[0]) - int(str2[0]) str1 += 1 str2 += 1 i += 1 if i < n: return int(str1[0]) - int(str2[0]) return 0 # Memory compare function def memcmp(ptr1: t.CPtr, ptr2: t.CPtr, n: sizet) -> int: p1: BYTEPTR = BYTEPTR(ptr1) p2: BYTEPTR = BYTEPTR(ptr2) for i in range(int(n)): if p1[i] != p2[i]: return int(p1[i]) - int(p2[i]) return 0 # String find function def strchr(s: str, cr: int) -> str: while s[0] != '\0': if s[0] == char(cr): return str(s) s += 1 if cr == '\0': return str(s) return None # String reverse find function (find last occurrence of character) @life(noescape(s)) def strrchr(s: str, cr: int) -> str: last: str = None while s[0] != '\0': if s[0] == char(cr): last = s s += 1 if cr == '\0': return s return last # Substring search function (find first occurrence of needle in s, return pointer to that position, or None if not found) # Hot-path optimization: use strchr to jump to first-char candidates instead of calling strncmp at every byte, and drop the upfront strlen(s) (strncmp stops at '\0'). # Semantics unchanged: an empty needle returns s; not found returns None. @life(noescape(s, needle)) def strstr(s: str, needle: str) -> str: if needle[0] == 0: return s needle_len: sizet = strlen(needle) if needle_len == 0: return s first_ch: int = int(needle[0]) cur: str = s while True: hit: str = strchr(cur, first_ch) if hit is None: return None if strncmp(hit, needle, needle_len) == 0: return hit cur = hit + 1 # String skip function def strspn(s: str, skip: str = " ") -> int: si: int = 0 while s[si] != 0: found: int = 0 sj: int = 0 while skip[sj] != 0: if s[si] == skip[sj]: found = 1 break sj += 1 if found == 0: break si += 1 return si # Memory set function @life(noescape(ptr)) export def memset(ptr: t.CPtr, value: int, num: sizet) -> t.CPtr: # Original implementation: # p: BYTEPTR = BYTEPTR(ptr) # i: sizet # for i in range(num): # p[i] = t.CUnsignedChar(value) if num == 0: return ptr c.Asm(f"""mov rdi, {c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)} mov eax, {c.AsmInp(value, t.ASM_DESCR.REG_ANY)} mov rcx, {c.AsmInp(num, t.ASM_DESCR.REG_ANY)} rep stosb""", op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDI]) return ptr @life(noescape(ptr)) def memset32(ptr: t.CPtr, value: uint32, count: sizet) -> t.CPtr: # Original implementation: # p: UINT32PTR = UINT32PTR(ptr) # i: sizet # for i in range(count): # p[i] = value if count == 0: return ptr c.Asm(f"""mov rdi, {c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)} mov eax, {c.AsmInp(value, t.ASM_DESCR.REG_ANY)} mov rcx, {c.AsmInp(count, t.ASM_DESCR.REG_ANY)} rep stosd""", op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDI]) return ptr # Memory copy function @life(noescape(dest)) export def memcpy(dest: t.CPtr, src: t.CPtr, num: sizet) -> t.CPtr: # Original implementation: # d: BYTEPTR = BYTEPTR(dest) # s: BYTEPTR = BYTEPTR(src) # i: sizet # for i in range(num): # d[i] = s[i] if num == 0: return dest c.Asm(f"""mov rdi, {c.AsmInp(dest, t.ASM_DESCR.REG_ANY)} mov rsi, {c.AsmInp(src, t.ASM_DESCR.REG_ANY)} mov rcx, {c.AsmInp(num, t.ASM_DESCR.REG_ANY)} rep movsb""", op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDI, ASM_DESCR.CLOBBER_RSI]) return dest # Memory move function @life(noescape(dest)) def memmove(dest: t.CPtr, src: t.CPtr, num: sizet) -> t.CPtr: # Original implementation: # d: BYTEPTR = BYTEPTR(dest) # s: BYTEPTR = BYTEPTR(src) # i: sizet # if d < s: # for i in range(num): # d[i] = s[i] # elif d > s: # for i in range(num, 0, -1): # d[i - 1] = s[i - 1] if num == 0: return dest c.Asm(f"""mov rdi, {c.AsmInp(dest, t.ASM_DESCR.REG_ANY)} mov rsi, {c.AsmInp(src, t.ASM_DESCR.REG_ANY)} mov rcx, {c.AsmInp(num, t.ASM_DESCR.REG_ANY)} cmp rdi, rsi jb 2f std add rdi, rcx dec rdi add rsi, rcx dec rsi rep movsb cld jmp 3f 2: rep movsb 3:""", op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDI, ASM_DESCR.CLOBBER_RSI]) return dest def atoi(src: str) -> int: num: int = 0 sign: int = 1 s: str = src if s == None: return 0 # Skip whitespace while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r': s += 1 # Sign if s[0] == '-': sign = -1 s += 1 elif s[0] == '+': s += 1 for ch in s: if ch < '0' or ch > '9': break digit: int = ch - '0' num = num * 10 + digit return num * sign def atoll(src: str) -> int64: num: int64 = 0 sign: int64 = 1 s: str = src if s == None: return 0 while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r': s += 1 if s[0] == '-': sign = -1 s += 1 elif s[0] == '+': s += 1 # Hex prefix 0x / 0X is_hex: int = 0 if s[0] == '0' and (s[1] == 'x' or s[1] == 'X'): is_hex = 1 s += 2 if is_hex: for ch in s: d: int = 0 if ch >= '0' and ch <= '9': d = ch - '0' elif ch >= 'a' and ch <= 'f': d = ch - 'a' + 10 elif ch >= 'A' and ch <= 'F': d = ch - 'A' + 10 else: break digit: int64 = int64(d) num = num * 16 + digit else: for ch in s: if ch < '0' or ch > '9': break d: int = ch - '0' digit: int64 = int64(d) num = num * 10 + digit return num * sign # ============================================================ # Correctly-rounded decimal literal -> double conversion (N4): mantissa in u64 (<=19 digits, 20th digit rounds once), # scaled once by an exact power of ten in double-double (hi+lo, ~106 bits) then rounded a single final time. # Convention: no global table (10^0..10^22 built by repeated multiplication of 10, each step exact). # For p<0 compute 10^p*2^600 in the normal range and scale back exactly, avoiding subnormal loss in the double-double low part below ~1e-292. # ============================================================ # _dd_split_hi - upper half of the Dekker split (hi = top 26 bits). # Magnitude rescaling: multiply by 2^-28 when |a| > 6.7e292 and by 2^28 when |a| < 1e-292 (powers of two, lossless for normals); caller gets the lower half via lo = a - hi. def _dd_split_hi(a: double) -> double: aa: double = a big: int = 0 small: int = 0 if a > 6.7e292 or a < -6.7e292: aa = a * 3.725290298461914e-09 big = 1 elif (a > 0.0 and a < 1.0e-292) or (a < 0.0 and a > -1.0e-292): aa = a * 268435456.0 small = 1 tt: double = aa * 134217729.0 hi: double = tt - (tt - aa) if big != 0: return hi * 268435456.0 if small != 0: return hi * 3.725290298461914e-09 return hi # _dd_mul - double-double multiply: out = a × b (out[0]=hi, out[1]=lo) def _dd_mul(ah: double, al: double, bh: double, bl: double, out: t.CArray[double, 2]): p: double = ah * bh a_hi: double = _dd_split_hi(ah) a_lo: double = ah - a_hi b_hi: double = _dd_split_hi(bh) b_lo: double = bh - b_hi e: double = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo e = e + (ah * bl + al * bh) s: double = p + e out[0] = s out[1] = e - (s - p) # _dd_add - double-double add: out = a + b def _dd_add(ah: double, al: double, bh: double, bl: double, out: t.CArray[double, 2]): s: double = ah + bh bb: double = s - ah e: double = (ah - (s - bb)) + (bh - bb) e = e + (al + bl) r0: double = s + e out[0] = r0 out[1] = e - (r0 - s) # _dd_div - double-double divide: out = a / b (q0 plus one residual correction step) def _dd_div(ah: double, al: double, bh: double, bl: double, out: t.CArray[double, 2], tmp: t.CArray[double, 2]): q0: double = ah / bh _dd_mul(bh, bl, q0, 0.0, tmp) _dd_add(ah, al, -tmp[0], -tmp[1], tmp) q1: double = tmp[0] / bh _dd_add(q0, 0.0, q1, 0.0, out) # _dd_from_u64 - u64 -> double-double (exact: hi is the rounded value, lo the exact residual) def _dd_from_u64(m: uint64, out: t.CArray[double, 2]): hi: double = double(m) lo: double = 0.0 if m >= uint64(9007199254740992): lo = double(m - uint64(hi)) s: double = hi + lo bb: double = s - hi out[0] = s out[1] = (hi - (s - bb)) + (lo - bb) # _dd_pow10 - double-double 10^e (e >= 0; chunked by <=22 so each chunk is exact) def _dd_pow10(e: int, out: t.CArray[double, 2], tmp: t.CArray[double, 2]): out[0] = 1.0 out[1] = 0.0 k: int = e while k > 0: step: int = 22 if k < 22: step = k p10: double = 1.0 for i in range(int(step)): p10 = p10 * 10.0 _dd_mul(out[0], out[1], p10, 0.0, tmp) out[0] = tmp[0] out[1] = tmp[1] k -= step # _pow2 - 2^n (|n| <= 1000): repeated multiply by 2/0.5, exact at every step def _pow2(n: int) -> double: r: double = 1.0 i: int = int(n) if i >= 0: while i > 0: r = r * 2.0 i -= 1 else: while i < 0: r = r * 0.5 i += 1 return r # _atof_scale_stepwise - stepwise ×/÷10 scaling (fallback for subnormal/overflow cases) def _atof_scale_stepwise(x: double, p: int) -> double: r: double = x e: int = p while e > 0: r = r * 10.0 e -= 1 while e < 0: r = r / 10.0 e += 1 return r def atof(src: str) -> double: s: str = src if s == None: return 0.0 # Skip whitespace while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r': s += 1 # Sign sign: double = 1.0 if s[0] == '-': sign = -1.0 s += 1 elif s[0] == '+': s += 1 # Mantissa: significant digits packed into u64 (<=19 digits), tracking the decimal exponent mant: uint64 = 0 nd: int = 0 dexp: int = 0 seen_dot: int = 0 started: int = 0 rounded: int = 0 while s[0] != '\0': ch: char = s[0] if ch >= '0' and ch <= '9': d: int = ch - '0' if started == 0: # Leading zeros are not stored in the mantissa; leading zeros in the fraction still advance the decimal exponent if d != 0: started = 1 mant = uint64(d) nd = 1 if seen_dot != 0: dexp -= 1 elif seen_dot != 0: dexp -= 1 elif nd < 19: mant = mant * uint64(10) + uint64(d) nd += 1 if seen_dot != 0: dexp -= 1 else: # From the 20th digit on: dropped integer digits bump the exponent; dropped fraction digits do not. # Only the 20th digit is used for one rounding (dropped amount <= 1e-19 relative error). if seen_dot == 0: dexp += 1 elif rounded == 0: if d >= 5: mant += uint64(1) rounded = 1 elif ch == '.': seen_dot = 1 else: break s += 1 if started == 0: return sign * 0.0 # Carry: rounding may turn 19 digits into 20 if mant >= uint64(10000000000000000000): mant = mant // uint64(10) dexp += 1 # Exponent part exp: int = 0 if s[0] == 'e' or s[0] == 'E': s += 1 exp_sign: int = 1 if s[0] == '-': exp_sign = -1 s += 1 elif s[0] == '+': s += 1 while s[0] >= '0' and s[0] <= '9': exp = exp * 10 + (s[0] - '0') s += 1 exp = exp * exp_sign p: int = dexp + exp if p == 0: return sign * double(mant) # Double-double scaling: mant × 10^p, single final rounding ta: t.CArray[double, 2] tb: t.CArray[double, 2] tc: t.CArray[double, 2] tr: t.CArray[double, 2] _dd_from_u64(mant, ta) v: double = 0.0 if p > 0: _dd_pow10(p, tb, tr) _dd_mul(ta[0], ta[1], tb[0], tb[1], tr) v = tr[0] + tr[1] else: # p < 0: compute 2^600 / 10^(-p) (= 10^p * 2^600) in the normal range, then scale back exactly. # Direct 10^p loses effective bits to subnormals for large |p|; divide the denominator in chunks of <=308 (10^313 overflows to inf and yields NaN). tb[0] = _pow2(600) tb[1] = 0.0 q: int = -p while q > 0: step2: int = 308 if q < 308: step2 = q _dd_pow10(step2, tc, tr) _dd_div(tb[0], tb[1], tc[0], tc[1], tr, tc) tb[0] = tr[0] tb[1] = tr[1] q -= step2 _dd_mul(ta[0], ta[1], tb[0], tb[1], tr) v = (tr[0] + tr[1]) * _pow2(-600) # Normal range (not NaN / not inf / not subnormal) -> the correctly rounded result if v == v and v <= 1.7976931348623157e308 and v >= -1.7976931348623157e308 \ and (v == 0.0 or v >= 2.2250738585072014e-308 or v <= -2.2250738585072014e-308): return sign * v # Overflow / subnormal / NaN -> fall back to stepwise scaling return sign * _atof_scale_stepwise(double(mant), p) def atof_out(src: str, out: double*) -> int: """Write the atof result to out[0] (out-parameter form, avoiding the cross-module double return bug). The return value only signals success (1) or failure (0); the value is passed via the out pointer.""" if out is None: return 0 v: double = atof(src) out[0] = v return 1 def split(s: str, delim: str, result: t.CArray[str], cap: int) -> int: """Split s by a single-character delimiter, writing token pointers to result[0..] and a None sentinel at the end. cap is the declared capacity (element count); token count is limited to <= cap-1, leaving one slot for the sentinel.""" count: int = 0 start: str = s if not s or not delim or not result: return 0 if cap <= 0: return 0 d: char = delim[0] # single delimiter while start[0]: # Skip delimiters while start[0] == d: start += 1 if not start[0]: break # Capacity guard: keep the last slot for the end sentinel if count >= cap - 1: break # Record token start result[count] = start count += 1 # Find the next delimiter and truncate while start[0] and start[0] != d: start += 1 if start[0]: start[0] = '\0' start += 1 result[count] = None # end marker return count ascii_lowercase: str = "abcdefghijklmnopqrstuvwxyz" ascii_uppercase: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ascii_letters: str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" digits: str = "0123456789" hexdigits: str = "0123456789abcdefABCDEF" octdigits: str = "01234567" punctuation: str = "!\"#$%&'()*+,-./:;<=>?@[]^_`{|}~" whitespace: str = " \t\n\r\f\v" printable: str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\f\v"