-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Range for OpStack #338
Conversation
Thanks for taking this on! Unfortunately, there is a logic error in the implementation. The documentation of field /// The underlying, actual stack. When manually accessing, be aware of reversed indexing:
/// while `op_stack[0]` is the top of the stack, `op_stack.stack[0]` is the lowest element in
/// the stack.
Consequently, accessing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is only generated due to a bug in proptest
. It has purposefully not been commited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added processor.txt
into .gitignore
. Does it sound good to you ?
@jan-ferdinand Sorry for delay. Thanks for your clarifications. Any thoughts on my update? |
// TODO rename this field to queue | ||
pub stack: VecDeque<BFieldElement>, | ||
/// `op_stack.stack[0]` is the lowest element in the stack. | ||
stack: VecDeque<BFieldElement>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jan-ferdinand would you like to change stack
to queue
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No; conceptually, it is still a stack. This should probably be reflected in the doc comment. How about:
/// The underlying, actual stack.
///
/// Even though the type suggests a queue, it is actually used as a stack. The
/// decision to use a [VecDeque] is motivated by indexing using ranges,
/// allowing, for example:
///
/// ```no_compile
/// let my_slice = op_stack[3..=5];
/// ```
///
/// Note that only `*_front` methods should be called on this stack. Pushing to
/// or popping from the back is almost certainly a logic error.
d2fd1ed
to
52b5057
Compare
f0346aa
to
15c7c44
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems we have another fundamental issue going on. 😫 Consider the following implementation:
impl Index<Range<usize>> for OpStack {
type Output = [BFieldElement];
fn index(&self, range: Range<usize>) -> &Self::Output {
let (front, back) = self.stack.as_slices();
if front.is_empty() {
&back[range]
} else if back.is_empty() {
&front[range]
} else {
panic!("the underlying op stack is not contiguous")
}
}
}
It is super easy to trigger the panic:
#[test]
fn indexing_with_range_produces_expected_slices() {
let mut op_stack = test_stack();
assert_eq!(op_stack[1..3], bfe_array![1, 2]); // 💥
}
/// An [`OpStack`] with [`BFieldElement`] `i` at index `i` and length
/// 2·[`OpStackElement::COUNT`].
fn test_stack() -> OpStack {
let mut op_stack = OpStack::default();
for i in (0..OpStackElement::COUNT).rev() {
op_stack.push(bfe!(i));
}
op_stack
}
For impl IndexMut<_> for OpStack
, we can easily avoid this by calling self.stack.make_contiguous()
in the index
method. However, for immutable access, this is impossible.
These are the options I see:
- Give up. (😢)
- Only support
IndexMut<_>
in combination with ranges. (😕) - Use a
VecDeque
and ensure that it is always contiguous. This might impact performance heavily and requires benchmarks. - Find (or build) a sort of reverse
Vec
.
I'm sorry that this seemingly simple issue is turning into a bit of a rabbit hole. I appreciate the effort you're putting in. At the same time, I want to emphasize that you should feel free to drop this increasingly complex endeavor at any point. 🙂
// TODO rename this field to queue | ||
pub stack: VecDeque<BFieldElement>, | ||
/// `op_stack.stack[0]` is the lowest element in the stack. | ||
stack: VecDeque<BFieldElement>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No; conceptually, it is still a stack. This should probably be reflected in the doc comment. How about:
/// The underlying, actual stack.
///
/// Even though the type suggests a queue, it is actually used as a stack. The
/// decision to use a [VecDeque] is motivated by indexing using ranges,
/// allowing, for example:
///
/// ```no_compile
/// let my_slice = op_stack[3..=5];
/// ```
///
/// Note that only `*_front` methods should be called on this stack. Pushing to
/// or popping from the back is almost certainly a logic error.
type Output = [BFieldElement]; | ||
|
||
fn index(&self, range: Range<usize>) -> &Self::Output { | ||
&self.stack.as_slices().0[range.start..range.end] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&self.stack.as_slices().0[range.start..range.end] | |
let (front, back) = self.stack.as_slices(); | |
if front.is_empty() { | |
&back[range] | |
} else if back.is_empty() { | |
&front[range] | |
} else { | |
panic!("the underlying op stack is not contiguous") | |
} |
Yeah, you are right. I have identified, that Regarding reverse Thanks for your guidance! |
Thank you for trying, and sorry for so many dead ends. 😕 |
Closes #322