-module(thrifty@zigzag). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/thrifty/zigzag.gleam"). -export([decode_i32/1, decode_i64/1, encode_i32_checked/1, encode_i32/1, encode_i64_checked/1, encode_i64/1]). -export_type([zigzag_range_error/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type zigzag_range_error() :: {zigzag_range_error, integer(), integer()}. -file("src/thrifty/zigzag.gleam", 65). ?DOC( " Decode a ZigZag-encoded unsigned integer back to signed i32.\n" "\n" " Inputs\n" " - `z`: ZigZag-encoded unsigned integer.\n" "\n" " Outputs\n" " - Returns the decoded signed integer.\n" ). -spec decode_i32(integer()) -> integer(). decode_i32(Z) -> case (Z rem 2) =:= 0 of true -> Z div 2; false -> (0 - (Z div 2)) - 1 end. -file("src/thrifty/zigzag.gleam", 112). ?DOC( " Decode a ZigZag-encoded unsigned integer back to signed i64.\n" "\n" " Inputs\n" " - `z`: ZigZag-encoded unsigned integer.\n" "\n" " Outputs\n" " - Returns the decoded signed integer.\n" ). -spec decode_i64(integer()) -> integer(). decode_i64(Z) -> case (Z rem 2) =:= 0 of true -> Z div 2; false -> (0 - (Z div 2)) - 1 end. -file("src/thrifty/zigzag.gleam", 119). -spec check_range(integer(), integer(), integer(), integer()) -> {ok, integer()} | {error, zigzag_range_error()}. check_range(Value, Min, Max, Bits) -> case (Value < Min) orelse (Value > Max) of true -> {error, {zigzag_range_error, Value, Bits}}; false -> {ok, Value} end. -file("src/thrifty/zigzag.gleam", 142). ?DOC( " Validate that `value` falls within `[min, max]` inclusive and return an\n" " explicit `ZigzagRangeError` when it does not.\n" "\n" " Inputs\n" " - `value`: integer to validate.\n" " - `min`, `max`: inclusive bounds.\n" " - `bits`: bit-width used for error reporting.\n" "\n" " Outputs\n" " - `Ok(value)` when within range.\n" " - `Error(ZigzagRangeError)` when out of range.\n" ). -spec mask_uint(integer(), integer()) -> integer(). mask_uint(Value, Modulus) -> Remainder = case Modulus of 0 -> 0; Gleam@denominator -> Value rem Gleam@denominator end, case Remainder < 0 of true -> Remainder + Modulus; false -> Remainder end. -file("src/thrifty/zigzag.gleam", 158). ?DOC( " Ensure an integer is represented as a non-negative residue modulo `modulus`.\n" "\n" " Inputs\n" " - `value`: integer to reduce.\n" " - `modulus`: modulus used for reduction (e.g., 2^32, 2^64).\n" "\n" " Outputs\n" " - Non-negative integer in 0..modulus-1 representing `value mod modulus`.\n" ). -spec zigzag_encode_formula(integer()) -> integer(). zigzag_encode_formula(N) -> case N >= 0 of true -> N * 2; false -> (N * -2) - 1 end. -file("src/thrifty/zigzag.gleam", 173). ?DOC( " Core zigzag mapping formula: maps signed integers to unsigned integers\n" " such that small-magnitude signed values map to small unsigned values.\n" "\n" " Inputs\n" " - `n`: signed integer.\n" "\n" " Outputs\n" " - ZigZag-mapped integer (unsigned representation prior to masking).\n" ). -spec zigzag_range_error_to_string(zigzag_range_error()) -> binary(). zigzag_range_error_to_string(Err) -> case Err of {zigzag_range_error, Value, Bits} -> <<<<<<<<"Value "/utf8, (erlang:integer_to_binary(Value))/binary>>/binary, " is outside the supported i"/utf8>>/binary, (erlang:integer_to_binary(Bits))/binary>>/binary, " range"/utf8>> end. -file("src/thrifty/zigzag.gleam", 51). ?DOC( " Checked version of `encode_i32/1` returning an explicit error for out-of-range inputs.\n" "\n" " Inputs\n" " - `n`: signed integer to encode.\n" "\n" " Outputs\n" " - `Ok(Int)` with the ZigZag-encoded unsigned integer when `n` fits in i32.\n" " - `Error(ZigzagRangeError)` when `n` is outside the i32 representable range.\n" ). -spec encode_i32_checked(integer()) -> {ok, integer()} | {error, zigzag_range_error()}. encode_i32_checked(N) -> case check_range(N, -2147483648, 2147483647, 32) of {error, E} -> {error, E}; {ok, Value} -> {ok, mask_uint(zigzag_encode_formula(Value), 4294967296)} end. -file("src/thrifty/zigzag.gleam", 36). ?DOC( " Encode a signed 32-bit integer into an unsigned integer using ZigZag.\n" "\n" " Inputs\n" " - `n`: signed integer expected to fit in 32-bit signed range.\n" "\n" " Outputs\n" " - Returns the ZigZag-encoded unsigned integer.\n" "\n" " Error modes\n" " - Panics when `n` is outside the supported i32 range. Use\n" " `encode_i32_checked/1` to receive an explicit error instead of panicking.\n" ). -spec encode_i32(integer()) -> integer(). encode_i32(N) -> case encode_i32_checked(N) of {ok, Value} -> Value; {error, Err} -> erlang:error(#{gleam_error => panic, message => zigzag_range_error_to_string(Err), file => <>, module => <<"thrifty/zigzag"/utf8>>, function => <<"encode_i32"/utf8>>, line => 39}) end. -file("src/thrifty/zigzag.gleam", 98). ?DOC( " Checked version of `encode_i64/1` returning an explicit error for out-of-range inputs.\n" "\n" " Inputs\n" " - `n`: signed integer to encode.\n" "\n" " Outputs\n" " - `Ok(Int)` with the ZigZag-encoded unsigned integer when `n` fits in i64.\n" " - `Error(ZigzagRangeError)` when `n` is outside the i64 representable range.\n" ). -spec encode_i64_checked(integer()) -> {ok, integer()} | {error, zigzag_range_error()}. encode_i64_checked(N) -> case check_range(N, -9223372036854775808, 9223372036854775807, 64) of {error, E} -> {error, E}; {ok, Value} -> {ok, mask_uint(zigzag_encode_formula(Value), 18446744073709551616)} end. -file("src/thrifty/zigzag.gleam", 83). ?DOC( " Encode a signed 64-bit integer into an unsigned integer using ZigZag.\n" "\n" " Inputs\n" " - `n`: signed integer expected to fit in 64-bit signed range.\n" "\n" " Outputs\n" " - Returns the ZigZag-encoded unsigned integer.\n" "\n" " Error modes\n" " - Panics when `n` is outside the supported i64 range. Use\n" " `encode_i64_checked/1` to receive an explicit error instead of panicking.\n" ). -spec encode_i64(integer()) -> integer(). encode_i64(N) -> case encode_i64_checked(N) of {ok, Value} -> Value; {error, Err} -> erlang:error(#{gleam_error => panic, message => zigzag_range_error_to_string(Err), file => <>, module => <<"thrifty/zigzag"/utf8>>, function => <<"encode_i64"/utf8>>, line => 86}) end.