use super::semantics::*;
use grabapl::graph::GraphTrait;
use grabapl::operation::signature::parameter::AbstractOutputNodeMarker;
use grabapl::prelude::*;
use proptest::proptest;
use proptest::test_runner::Config;
use std::cmp::Ordering;
use std::collections::BinaryHeap;
const MAX_HEAP_REMOVE_ID: OperationId = 0;
const MAX_HEAP_REMOVE_HELPER_ID: OperationId = 1;
fn populate_max_heap_remove_op(op_ctx: &mut OperationContext<TestSemantics>) {
populate_max_heap_remove_helper_op(op_ctx);
let mut builder = OperationBuilder::new(&op_ctx, MAX_HEAP_REMOVE_ID);
builder
.expect_parameter_node("sentinel", NodeType::Object)
.unwrap();
let sentinel = AbstractNodeId::param("sentinel");
builder
.add_named_operation(
"max_value".into(),
BuilderOpLike::LibBuiltin(LibBuiltinOperation::AddNode {
value: NodeValue::Integer(-1), }),
vec![],
)
.unwrap();
let max_value = AbstractNodeId::dynamic_output("max_value", "new");
builder.start_shape_query("q").unwrap();
builder
.expect_shape_node("root".into(), NodeType::Integer)
.unwrap();
let root_aid = AbstractNodeId::dynamic_output("q", "root");
builder
.expect_shape_edge(sentinel, root_aid, EdgeType::Wildcard)
.unwrap();
builder.enter_false_branch().unwrap();
builder.enter_true_branch().unwrap();
builder
.add_operation(
BuilderOpLike::FromOperationId(MAX_HEAP_REMOVE_HELPER_ID),
vec![root_aid, max_value],
)
.unwrap();
builder.end_query().unwrap();
builder
.return_node(max_value, "max_value".into(), NodeType::Integer)
.unwrap();
let op = builder.build().unwrap();
op_ctx.add_custom_operation(MAX_HEAP_REMOVE_ID, op);
}
fn populate_max_heap_remove_helper_op(op_ctx: &mut OperationContext<TestSemantics>) {
let mut builder = OperationBuilder::new(&op_ctx, MAX_HEAP_REMOVE_HELPER_ID);
builder
.expect_parameter_node("root", NodeType::Integer)
.unwrap();
let root = AbstractNodeId::param("root");
builder
.expect_parameter_node("max_value", NodeType::Integer)
.unwrap();
let max_value = AbstractNodeId::param("max_value");
builder
.add_operation(
BuilderOpLike::Builtin(TestOperation::CopyValueFromTo),
vec![root, max_value],
)
.unwrap();
builder.start_shape_query("q").unwrap();
builder
.expect_shape_node("left".into(), NodeType::Integer)
.unwrap();
let left_aid = AbstractNodeId::dynamic_output("q", "left");
builder
.expect_shape_edge(root, left_aid, EdgeType::Wildcard)
.unwrap();
builder
.expect_shape_node("right".into(), NodeType::Integer)
.unwrap();
let right_aid = AbstractNodeId::dynamic_output("q", "right");
builder
.expect_shape_edge(root, right_aid, EdgeType::Wildcard)
.unwrap();
builder.enter_true_branch().unwrap();
builder
.start_query(
TestQuery::CmpFstSnd(Ordering::Greater.into()),
vec![left_aid, right_aid],
)
.unwrap();
builder.enter_true_branch().unwrap();
builder
.add_named_operation(
"temp_max".into(),
BuilderOpLike::LibBuiltin(LibBuiltinOperation::AddNode {
value: NodeValue::Integer(-1), }),
vec![],
)
.unwrap();
let temp_max = AbstractNodeId::dynamic_output("temp_max", "new");
builder
.add_operation(BuilderOpLike::Recurse, vec![left_aid, temp_max])
.unwrap();
builder
.add_operation(
BuilderOpLike::Builtin(TestOperation::CopyValueFromTo),
vec![temp_max, root],
)
.unwrap();
builder
.add_operation(
BuilderOpLike::LibBuiltin(LibBuiltinOperation::RemoveNode {
param: NodeType::Object,
}),
vec![temp_max],
)
.unwrap();
builder.enter_false_branch().unwrap();
builder
.add_named_operation(
"temp_max".into(),
BuilderOpLike::LibBuiltin(LibBuiltinOperation::AddNode {
value: NodeValue::Integer(-1), }),
vec![],
)
.unwrap();
let temp_max = AbstractNodeId::dynamic_output("temp_max", "new");
builder
.add_operation(BuilderOpLike::Recurse, vec![right_aid, temp_max])
.unwrap();
builder
.add_operation(
BuilderOpLike::Builtin(TestOperation::CopyValueFromTo),
vec![temp_max, root],
)
.unwrap();
builder
.add_operation(
BuilderOpLike::LibBuiltin(LibBuiltinOperation::RemoveNode {
param: NodeType::Object,
}),
vec![temp_max],
)
.unwrap();
builder.end_query().unwrap();
builder.enter_false_branch().unwrap();
builder.start_shape_query("q").unwrap();
builder
.expect_shape_node("child".into(), NodeType::Integer)
.unwrap();
let child_aid = AbstractNodeId::dynamic_output("q", "child");
builder
.expect_shape_edge(root, child_aid, EdgeType::Wildcard)
.unwrap();
builder.enter_true_branch().unwrap();
builder
.add_named_operation(
"temp_max".into(),
BuilderOpLike::LibBuiltin(LibBuiltinOperation::AddNode {
value: NodeValue::Integer(-1), }),
vec![],
)
.unwrap();
let temp_max = AbstractNodeId::dynamic_output("temp_max", "new");
builder
.add_operation(BuilderOpLike::Recurse, vec![child_aid, temp_max])
.unwrap();
builder
.add_operation(
BuilderOpLike::Builtin(TestOperation::CopyValueFromTo),
vec![temp_max, root],
)
.unwrap();
builder
.add_operation(
BuilderOpLike::LibBuiltin(LibBuiltinOperation::RemoveNode {
param: NodeType::Object,
}),
vec![temp_max],
)
.unwrap();
builder.enter_false_branch().unwrap();
builder
.add_operation(
BuilderOpLike::LibBuiltin(LibBuiltinOperation::RemoveNode {
param: NodeType::Object,
}),
vec![root],
)
.unwrap();
builder.end_query().unwrap();
builder.end_query().unwrap();
let op = builder.build().unwrap();
op_ctx.add_custom_operation(MAX_HEAP_REMOVE_HELPER_ID, op);
}
fn mk_heap_from_values(values: &[i32]) -> (ConcreteGraph<TestSemantics>, NodeKey) {
let mut g = TestSemantics::new_concrete_graph();
let sentinel = g.add_node(NodeValue::String("sentinel".to_string()));
let heap = BinaryHeap::from(values.to_vec());
let mut node_vec = Vec::new();
for (i, val) in heap.iter().enumerate() {
let node = g.add_node(NodeValue::Integer(*val));
node_vec.push(node);
if i > 0 {
let parent_index = (i - 1) / 2;
let parent_node = node_vec[parent_index];
let NodeValue::Integer(parent_val) = g.get_node_attr(parent_node).unwrap() else {
unreachable!();
};
assert!(
parent_val >= val,
"Max heap property violated: parent value {} is not greater than or equal to child value {}",
parent_val,
val
);
g.add_edge(parent_node, node, "blah".to_string());
}
}
if let Some(&root) = node_vec.first() {
g.add_edge(sentinel, root, "root".to_string());
}
(g, sentinel)
}
#[test_log::test]
fn proptest_max_heap_remove_heap() {
let mut op_ctx = OperationContext::<TestSemantics>::new();
populate_max_heap_remove_op(&mut op_ctx);
eprintln!(
"serialized_op_ctx:\n{}",
serde_json::to_string_pretty(&op_ctx).unwrap()
);
proptest!(
Config::with_cases(10),
|(values in proptest::collection::vec(0..5000, 0..=10))| {
let start = std::time::Instant::now();
let mut expected_return_order: Vec<i32> = values.clone();
expected_return_order.sort_unstable_by(|a, b| b.cmp(a)); log_crate::info!("Length: {:?}", values.len());
let (mut g, sentinel) = mk_heap_from_values(&values);
for expected_max_value in expected_return_order {
let op_result = run_from_concrete(&mut g, &op_ctx, MAX_HEAP_REMOVE_ID, &[sentinel]).unwrap();
let max_value_node = op_result.new_nodes.get(&AbstractOutputNodeMarker::from("max_value")).unwrap();
let max_value = g.get_node_attr(*max_value_node).unwrap();
assert_eq!(
max_value,
&NodeValue::Integer(expected_max_value),
"Expected max value node to have value {}, but got {:?}",
expected_max_value,
max_value
);
}
g.edges().for_each(|(src, _, _)| {
assert_ne!(src, sentinel, "Expected no edges from the sentinel node after all removals");
});
log_crate::info!("Time taken: {:?}", start.elapsed());
}
);
}