Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const std = @import("std");
- const TypeId = @import("builtin").TypeId;
- fn is_signed(comptime N: type) bool {
- switch (@typeInfo(N)) {
- TypeId.Int => |int| {
- return int.is_signed;
- },
- else => {
- @compileError("Is_signed is only available on integer types. Found `" ++ @typeName(N) ++ "`.");
- }
- }
- }
- fn bitcount(comptime N: type) u32 {
- switch (@typeInfo(N)) {
- TypeId.Int => |int| {
- return @intCast(u32, int.bits);
- },
- else => {
- @compileError("Bitcount only available on integer types. Found `" ++ @typeName(N) ++ "`.");
- }
- }
- }
- fn is_negative(comptime N: type, num: N) bool {
- return num < 0;
- }
- fn digits10(comptime N: type, n: N, comptime unroll: ?u8) usize {
- // If we encouter a signed int,
- // check if it's negative,
- // if so, multiply by minus one (make positive), and call digits10 on the unsigned version of the int,
- // if not negative, just call digits10 on the unsigned version,
- // and if we dont encouter a signed int, just skip this whole if :)
- if (comptime is_signed(N)) {
- const nbits = comptime bitcount(N);
- const unsigned_friend = @IntType(false, nbits);
- if (is_negative(N, n)) {
- // we can't use @intCast for some reason :(
- var num = @bitCast(unsigned_friend, n *% -1);
- return digits10(unsigned_friend, num, unroll);
- }
- else {
- return digits10(unsigned_friend, @intCast(unsigned_friend, n), unroll);
- }
- }
- comptime var digits: usize = 1;
- comptime var check: usize = 10;
- if (comptime unroll) |unroll_count| {
- var num: usize = n;
- var result: usize = 0;
- comptime var counter: usize = 0;
- while (true) {
- inline while (counter != unroll_count) : ({counter += 1; check *= 10; digits += 1;}) {
- if (num < check) {
- return result + digits;
- }
- }
- result += unroll_count;
- num /= comptime (check / 10);
- }
- } else {
- comptime var overflowed1 = false;
- comptime var overflowed2 = false;
- inline while (check <= @maxValue(N) and (!overflowed1 and !overflowed2)) : (
- {
- overflowed1 = @mulWithOverflow(usize, check, 10, &check);
- overflowed2 = @addWithOverflow(usize, digits, 1, &digits);
- }
- ) {
- if (n < check) {
- return digits;
- }
- }
- return digits;
- }
- }
- fn iter(comptime N: usize) [N]void {
- return undefined;
- }
- fn assert_value(length: usize, comptime inttype: type, input: inttype, comptime unroll_count: ?u8) void {
- std.debug.assert(length == digits10(inttype, input, unroll_count));
- }
- test "corner cases" {
- const bitcounts = []u32 {8, 16, 32, 64};
- const lenghts = []usize {3, 5, 10, 19};
- inline for(bitcounts) |nbits, idx| {
- comptime var unsigned_type = @IntType(false, @intCast(u32, nbits));
- comptime var signed_type = @IntType(true, @intCast(u32, nbits));
- const length = lenghts[idx];
- inline for (comptime iter(10)) |_, i| {
- const unroll_count = @intCast(u8, i + 1);
- assert_value(length, signed_type, @minValue(signed_type), unroll_count);
- assert_value(length, signed_type, @maxValue(signed_type), unroll_count);
- assert_value(1, unsigned_type, 0, unroll_count);
- // i64 has a length of 19, but u64 20.. :3
- if (unsigned_type == u64) {
- assert_value(length + 1, unsigned_type, @maxValue(unsigned_type), unroll_count);
- } else {
- assert_value(length, unsigned_type, @maxValue(unsigned_type), unroll_count);
- }
- }
- assert_value(1, unsigned_type, 0, null);
- if (unsigned_type == u64) {
- assert_value(length + 1, unsigned_type, @maxValue(unsigned_type), null);
- } else {
- assert_value(length, unsigned_type, @maxValue(unsigned_type), null);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement