Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pineforge_codegen/codegen/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,13 +659,14 @@ def _checked_array_percentrank(a: str, args: list[str]) -> str:
"sort_indices": lambda a, args: f"[&](){{ std::vector<double> idx({a}.size()); std::iota(idx.begin(),idx.end(),0); std::sort(idx.begin(),idx.end(),[&](int i,int j){{return {a}[i]<{a}[j];}}); return idx; }}()",
}

# Pine parameter order for the checked-access subset. Keeping the receiver
# Pine parameter order for the checked-index subset. Keeping the receiver
# (``id``) separate lets method syntax merge ``a.get(index = i)`` while the
# namespace-functional path merges ``array.get(id = a, index = i)``.
CHECKED_ARRAY_METHOD_KWARGS: dict[str, list[str]] = {
"get": ["index"],
"set": ["index", "value"],
"remove": ["index"],
"percentrank": ["index"],
"first": [],
"last": [],
"pop": [],
Expand Down
52 changes: 52 additions & 0 deletions tests/test_array_checked_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,29 @@ def test_percentrank_keeps_temporary_receiver_before_index_once():
assert assignment.index("}((idx()))") < assignment.index("}((receiver()))")


@pytest.mark.parametrize(
"expression",
[
"array.percentrank(id=values, index=2)",
"array.percentrank(index=2, id=values)",
"array.percentrank(values, index=2)",
"values.percentrank(index=2)",
"array.from(1.0, 2.0, 3.0).percentrank(index=2)",
],
)
def test_percentrank_keyword_forms_route_to_checked_lowering(expression: str):
cpp = _generate(
"values = array.from(1.0, 2.0, 3.0)\n"
f"rank = {expression}"
)
assignment = next(
line for line in cpp.splitlines() if line.startswith(" rank =")
)
assert "pine_runtime_error" in assignment
assert "int64_t __pf_array_index=__pf_raw_index;" in assignment
assert "__pf_array[(size_t)__pf_array_index]" in assignment


@pytest.mark.parametrize(
"access",
["array.get(pivots, i)", "array.first(pivots)", "pivots.last()"],
Expand Down Expand Up @@ -344,6 +367,7 @@ def test_checked_udt_lvalue_access_preserves_alias(access: str):
empty = array.new<float>(0)
singleton = array.from(7.0)
var calls = array.new<int>()
var keyword_order = array.new<int>()

empty_index() =>
array.push(calls, 1)
Expand All @@ -353,12 +377,28 @@ def test_checked_udt_lvalue_access_preserves_alias(access: str):
array.push(calls, 2)
-999

keyword_receiver() =>
array.push(keyword_order, 1)
array.copy(values)

keyword_index() =>
array.push(keyword_order, 2)
2

low = array.percentrank(values, 0)
high = array.percentrank(values, 2)
keyword_function = array.percentrank(id=values, index=2)
keyword_mixed = array.percentrank(values, index=2)
keyword_method = values.percentrank(index=2)
keyword_temporary = array.from(1.0, 2.0, 3.0).percentrank(index=2)
keyword_stateful = array.percentrank(
id=keyword_receiver(),
index=keyword_index())
empty_rank = array.percentrank(empty, empty_index())
singleton_rank = array.percentrank(singleton, singleton_index())
call_count = array.size(calls)
call_code = array.get(calls, 0) * 10 + array.get(calls, 1)
keyword_order_code = array.get(keyword_order, 0) * 10 + array.get(keyword_order, 1)
"""


Expand Down Expand Up @@ -458,6 +498,12 @@ def test_percentrank_valid_and_degenerate_indices_runtime():
return 2;
}
std::cout << strategy.low << " " << strategy.high << " "
<< strategy.keyword_function << " "
<< strategy.keyword_mixed << " "
<< strategy.keyword_method << " "
<< strategy.keyword_temporary << " "
<< strategy.keyword_stateful << " "
<< strategy.keyword_order_code << " "
<< std::isnan(strategy.empty_rank) << " "
<< std::isnan(strategy.singleton_rank) << " "
<< strategy.call_count << " " << strategy.call_code << "\n";
Expand All @@ -469,6 +515,12 @@ def test_percentrank_valid_and_degenerate_indices_runtime():
assert tuple(int(float(value)) for value in output.split()) == (
0,
100,
100,
100,
100,
100,
100,
12,
1,
1,
2,
Expand Down
Loading