"Undefined value" error in comptime mathematics

I am trying to do some mathematics at compile time (towards a library for units of measure) and got an error I don’t understand. I am afraid my attempt to shorten the code to post here is still a little long:

const std = @import("std");
const testing = std.testing;

pub const ComptimeFraction = struct {
    numerator: comptime_int,
    denominator: comptime_int,
    const Self = @This();

    pub fn init(top: comptime_int, bottom: comptime_int) Self {
        return Self{
            .numerator = top,
            .denominator = bottom,
        };
    }

    pub fn eq(self: Self, other: Self) bool {
        return (self.numerator == other.numerator) and (self.denominator == other.denominator);
    }
};

fn distinctPrimeFactorCount(number: comptime_int) comptime_int {
    comptime var remaining = number;
    comptime var count = 0;
    comptime var p = 2;
    while (remaining > 1) {
        if (remaining % p == 0) {
            count += 1;
            while (remaining % p == 0) {
                remaining /= p;
            }
        }
        p += 1;
    }
    return count;
}

const Factor = struct { prime: comptime_int, power: ComptimeFraction };

fn primeFactorization(number: comptime_int) [distinctPrimeFactorCount(number)]Factor {
    comptime var factorization: [distinctPrimeFactorCount(number)]Factor = undefined;
    comptime var remaining = number;
    comptime var p = 2;
    comptime var i = 0;
    while (i < 0) {
        if (remaining % p == 0) {
            comptime var count = 0;
            while (remaining % p == 0) {
                remaining /= p;
                count += 1;
            }
            factorization[i] = .{ .prime = p, .power = ComptimeFraction.init(count, 1) };
            i += 1;
        }
        p += 1;
    }
    return factorization;
}

test "primeFactorization" {
    try testing.expect(primeFactorization(2)[0].power.eq(ComptimeFraction.init(1, 1)));
}

zig test on this file failed with

src/comptime_fraction.zig:17:16: error: use of undefined value here causes undefined behavior
        return (self.numerator == other.numerator) and (self.denominator == other.denominator);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    test.primeFactorization: src/comptime_fraction.zig:60:57

I would appreciate any advice.

In the body of primeFactorization function you never change the undefined values of the factorization array because you never enter the while loop.

3 Likes

ugh, thanks! I think I changed that to say 0 temporarily while trying to identify some other bug.

Not answering your question but … why are you defining all this as comptime ?

I think you could write all this as not comptime and if it is called from a comptime part of your code, it will be evaluated at comptime …

3 Likes

Probably. I hit some build errors at the very start that seemed to go away when I made comptime more explicit, and decided to do it that way until it builds and does what I intended. Refactoring away unnecessary "comptime"s is in my todos, though.

I was curious so I removed all the comptime definitions and ended up with this error:

frac.zig:42:62: error: unable to resolve comptime value
fn primeFactorization(number: i64) [distinctPrimeFactorCount(number)]Factor {
                                                             ^~~~~~
frac.zig:42:62: note: argument to function being called at comptime must be comptime-known

If this error prompted you to comptime everything, I think your problem here is your primeFactorization function needs to know the number to factorize at comptime in order to deduce the size of the output array.

In this form this function can only work at comptime. But I think if you change it so it returns a slice instead of an array, the function becomes comptime agnostic, thus simplifying your code.

1 Like

I had assumed I couldn’t allocate and return a slice a slice at comptime, but am I right in thinking that FixedBufferAllocator will work?

the following seems to compile.

const std = @import("std");
const testing = std.testing;

pub const Fraction = struct {
    numerator: i64,
    denominator: i64,
    const Self = @This();

    pub fn init(top: i64, bottom: i64) Self {
        return Self{
            .numerator = top,
            .denominator = bottom,
        };
    }

    pub fn eq(self: Self, other: Self) bool {
        return (self.numerator == other.numerator) and (self.denominator == other.denominator);
    }
};

fn distinctPrimeFactorCount(number: i64) i64 {
    var remaining = number;
    var count = 0;
    var p = 2;
    while (remaining > 1) {
        if (remaining % p == 0) {
            count += 1;
            while (remaining % p == 0) {
                remaining /= p;
            }
        }
        p += 1;
    }
    return count;
}

const Factor = struct {
    prime: i64,
    power: Fraction,
};

fn primeFactorization(comptime number: i64) [distinctPrimeFactorCount(number)]Factor {
    var factorization: [distinctPrimeFactorCount(number)]Factor = undefined;
    var remaining = number;
    comptime var p = 2;
    comptime var i = 0;
    while (i < 0) {
        if (remaining % p == 0) {
            var count = 0;
            while (remaining % p == 0) {
                remaining /= p;
                count += 1;
            }
            factorization[i] = .{
                .prime = p,
                .power = Fraction.init(count, 1),
            };
            i += 1;
        }
        p += 1;
    }
    return factorization;
}

// test "primeFactorization" {
//     try testing.expect(primeFactorization(2)[0].power.eq(Fraction.init(1, 1)));
// }

// this is comptime defined
const a = primeFactorization(2);

pub fn main() void {
    std.debug.print("{any}", .{a});
}
1 Like