use crate::enums::tarjan_result::TarjanResult;
use crate::records::tarjan::Tarjan;
use luaur_common::macros::luau_assert::LUAU_ASSERT;
impl Tarjan {
pub fn loop_item(&mut self) -> TarjanResult {
while !self.worklist.is_empty() {
let (index, mut curr_edge, mut last_edge) = {
let top = self.worklist.last().unwrap();
(top.index, top.curr_edge, top.last_edge)
};
if curr_edge == -1 {
self.child_count += 1;
if self.child_limit > 0 && self.child_limit <= self.child_count {
return TarjanResult::TooManyChildren;
}
self.stack.push(index);
self.nodes[index as usize].on_stack = true;
curr_edge = self.edges_ty.len() as i32;
let ty = self.nodes[index as usize].ty;
if !ty.is_null() {
self.visit_children_type_id_i32(ty, index);
} else {
let tp = self.nodes[index as usize].tp;
if !tp.is_null() {
self.visit_children_type_pack_id_i32(tp, index);
}
}
last_edge = self.edges_ty.len() as i32;
if let Some(top) = self.worklist.last_mut() {
top.curr_edge = curr_edge;
top.last_edge = last_edge;
}
}
let mut found_fresh = false;
while curr_edge < last_edge {
let mut child_index: i32 = -1;
let mut fresh = false;
let edge_ty = self.edges_ty[curr_edge as usize];
if !edge_ty.is_null() {
let (ci, fr) = self.indexify_type_id(edge_ty);
child_index = ci;
fresh = fr;
} else {
let edge_tp = self.edges_tp[curr_edge as usize];
if !edge_tp.is_null() {
let (ci, fr) = self.indexify_type_pack_id(edge_tp);
child_index = ci;
fresh = fr;
} else {
LUAU_ASSERT!(false);
}
}
if fresh {
if let Some(top) = self.worklist.last_mut() {
top.curr_edge = curr_edge + 1;
}
self.worklist.push(
crate::records::tarjan_worklist_vertex::TarjanWorklistVertex {
index: child_index,
curr_edge: -1,
last_edge: -1,
},
);
found_fresh = true;
break;
} else if self.nodes[child_index as usize].on_stack {
let ll = self.nodes[index as usize].lowlink;
let other = child_index;
if other < ll {
self.nodes[index as usize].lowlink = other;
}
}
self.visit_edge(child_index, index);
curr_edge += 1;
}
if found_fresh {
continue;
}
if self.nodes[index as usize].lowlink == index {
self.visit_scc(index);
while !self.stack.is_empty() {
let popped = *self.stack.last().unwrap();
self.stack.pop();
self.nodes[popped as usize].on_stack = false;
if popped == index {
break;
}
}
}
self.worklist.pop();
if !self.worklist.is_empty() {
let (parent_index, _parent_curr_edge, parent_end_edge) = {
let top = self.worklist.last().unwrap();
(top.index, top.curr_edge, top.last_edge)
};
let new_len = parent_end_edge as usize;
self.edges_ty.truncate(new_len);
self.edges_tp.truncate(new_len);
let child_lowlink = self.nodes[index as usize].lowlink;
let parent_lowlink_ref = &mut self.nodes[parent_index as usize].lowlink;
if child_lowlink < *parent_lowlink_ref {
*parent_lowlink_ref = child_lowlink;
}
self.visit_edge(index, parent_index);
}
}
TarjanResult::Ok
}
}