Strange behavior of while loop in GenExpr

Joji Iwasaki's icon

Background: I was trying to write a binary search algorithm in GenExpr.

Issue: In the attached patch, if "right" is removed at line 16 of [codebox] in [gen~], an infinite loop occurs.

That is,

lower_bound(buf, val) {
    left = 1; // Buffer is 0-based
    right = dim(buf);
    
    while (left < right) {
        mid = int((left + right) / 2);
        
        if (peek(buf, mid) < val) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    
    // For some reason, removing right causes an endless loop.
    return left, right;
}

Buffer buf("buf");


out1 = lower_bound(buf, 2);

into

// ...

return left;

// ...

causes an infinite loop. Am I missing something fundamental?

lower_bound_test.maxpat
text/plain 19.36 KB

Joji Iwasaki's icon

Update: I haven't found a solution, but whatever it is, I found the cause: removing `right` from return seems to prevent the else block from compiling. Here is an excerpt of the exported C++ source:

inline void lower_bound_buf_i(Buffer& buf, int val, int& out1, int& out2) {
    int left = ((int)1);
    int buf_dim = buf.dim;
    int buf_channels = buf.channels;
    int right = buf_dim;
    while ((left < right)) {
        // abort processing if an infinite loop is suspected;
        if (((__loopcount--) <= 0)) {
            __exception = GENLIB_ERR_LOOP_OVERFLOW;
            break ;
            
        };
        int mid = int(((left + right) * ((t_sample)0.5)));
        bool index_ignore_1 = ((mid >= buf_dim) || (mid < 0));
        // samples buf channel 1;
        t_sample peek_106 = (index_ignore_1 ? 0 : buf.read(mid, 0));
        t_sample peek_107 = mid;
        if ((peek_106 < val)) {
            left = (mid + ((int)1));
            
        } else {
            right = mid;
            
        };
        
    };
    out1 = left;
    out2 = right;
    
};

The above is the source with`right`. But, without `right`:

inline int lower_bound_buf_i(Buffer& buf, int val) {
    int left = ((int)1);
    int buf_dim = buf.dim;
    int buf_channels = buf.channels;
    int right = buf_dim;
    while ((left < right)) {
        // abort processing if an infinite loop is suspected;
        if (((__loopcount--) <= 0)) {
            __exception = GENLIB_ERR_LOOP_OVERFLOW;
            break ;
            
        };
        int mid = int(((left + right) * ((t_sample)0.5)));
        bool index_ignore_1 = ((mid >= buf_dim) || (mid < 0));
        // samples buf channel 1;
        t_sample peek_118 = (index_ignore_1 ? 0 : buf.read(mid, 0));
        t_sample peek_119 = mid;
        if ((peek_118 < val)) {
            left = (mid + ((int)1));
            
        };
    };
    return left;
    
};

Oh man, the else block is missing! Does anyone know why this happened?

Diemo Schwarz's icon

same problem here: https://cycling74.com/forums/gen-ate-my-else
Why? The root cause is that gen's "optimizations" seem very ad-hoc and untested, especially with function definitions, and there is no staff assigned to work on this. You can however write to support@cycling.com with a simplified case and they will fix these bugs one-by-one.
One long-term solution would be to allow to include user-provided C code, so we could write the function code ourselves.
BTW: the code transation will be correct if you inline the function.