Strange behavior of while loop in GenExpr
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?
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?
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.