Current section
Files
Jump to
Current section
Files
src/zipper@rose_tree.erl
-module(zipper@rose_tree).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/zipper/rose_tree.gleam").
-export([from_standard_tree/1, to_standard_tree/1, from_tree/2, to_tree/2, is_leftmost/1, go_left/1, is_rightmost/1, go_right/1, go_up/1, go_to_root/1, go_down/1, get_value/1, get_standard_tree/1, get_tree/2, set_value/2, set_standard_tree/2, set_tree/3, update/2, map_focus/2, is_root/1, insert_left/2, insert_right/2, insert_child/2, insert_child_back/2, delete/1, is_leaf/1]).
-export_type([rose_tree/1, adapter/2, choice/1, zipper/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A functional zipper for rose trees (multi-way trees).\n"
"\n"
" This module provides tools to navigate and modify rose tree structures\n"
" efficiently. The zipper makes it easy to move up, down, and sideways\n"
" in a tree and to perform local modifications without traversing the\n"
" entire tree.\n"
"\n"
" Most navigation and local modification operations are $O(1)$. Going up\n"
" the tree is $O(k)$, where $k$ is the number of left siblings of the\n"
" current node. Insert children at the last position is $O(d)$, where $d$ is\n"
" the number of children. Operations that convert from or to a full tree\n"
" structure are $O(n)$, where $n$ is the number of nodes in the tree.\n"
"\n"
" It supports both a standard `RoseTree` type and can be adapted to work with\n"
" any user-defined rose tree structure via an `Adapter`.\n"
"\n"
" Unlike binary trees, rose trees do not have a separate concept of a leaf node;\n"
" a node with an empty list of children is considered a leaf. This distinction\n"
" leads to some differences in the API compared to the `zipper/tree` module.\n"
" For example, `get_value` always succeeds because every location in a rose\n"
" tree zipper has a value.\n"
"\n"
" ## Usage\n"
" ```gleam\n"
" import zipper/rose_tree\n"
"\n"
" let my_tree =\n"
" rose_tree.RoseTree(1, [\n"
" rose_tree.RoseTree(2, []),\n"
" rose_tree.RoseTree(3, []),\n"
" ])\n"
"\n"
" let zipper = rose_tree.from_standard_tree(my_tree)\n"
"\n"
" // Navigate and modify the tree\n"
" let assert Ok(zipper) = rose_tree.go_down(zipper)\n"
" let zipper = rose_tree.set_value(zipper, 4)\n"
" let assert Ok(zipper) = rose_tree.go_up(zipper)\n"
"\n"
" rose_tree.to_standard_tree(zipper)\n"
" // => RoseTree(1, [RoseTree(4, []), RoseTree(3, [])])\n"
" ```\n"
).
-type rose_tree(DRJ) :: {rose_tree, DRJ, list(rose_tree(DRJ))}.
-type adapter(DRK, DRL) :: {adapter,
fun((DRL) -> DRK),
fun((DRL) -> list(DRL)),
fun((DRK, list(DRL)) -> DRL)}.
-type choice(DRM) :: {choice, DRM, list(rose_tree(DRM)), list(rose_tree(DRM))}.
-opaque zipper(DRN) :: {zipper, list(choice(DRN)), rose_tree(DRN)}.
-file("src/zipper/rose_tree.gleam", 106).
?DOC(
" Creates a zipper from a standard rose tree.\n"
"\n"
" The initial focus of the zipper is the root of the tree.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [])\n"
" let zipper = from_standard_tree(tree)\n"
" get_value(zipper)\n"
" // => 1\n"
" ```\n"
).
-spec from_standard_tree(rose_tree(DRO)) -> zipper(DRO).
from_standard_tree(Tree) ->
{zipper, [], Tree}.
-file("src/zipper/rose_tree.gleam", 124).
?DOC(
" Converts a zipper back to a standard rose tree.\n"
"\n"
" This function reconstructs the entire tree by navigating to the root from the\n"
" current focus and rebuilding the structure along the way.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [RoseTree(2, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper)\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(1, [RoseTree(2, [])])\n"
" ```\n"
).
-spec to_standard_tree(zipper(DRR)) -> rose_tree(DRR).
to_standard_tree(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
erlang:element(3, Zipper);
[{choice, Value, Left_siblings, Right_siblings} | Thread] ->
Children = lists:append(
lists:reverse(Left_siblings),
[erlang:element(3, Zipper) | Right_siblings]
),
_pipe = {zipper, Thread, {rose_tree, Value, Children}},
to_standard_tree(_pipe)
end.
-file("src/zipper/rose_tree.gleam", 181).
-spec user_tree_to_standard_tree(DSJ, adapter(DSK, DSJ)) -> rose_tree(DSK).
user_tree_to_standard_tree(Tree, Adapter) ->
Value = (erlang:element(2, Adapter))(Tree),
Children = begin
_pipe = (erlang:element(3, Adapter))(Tree),
gleam@list:map(
_pipe,
fun(_capture) -> user_tree_to_standard_tree(_capture, Adapter) end
)
end,
{rose_tree, Value, Children}.
-file("src/zipper/rose_tree.gleam", 150).
?DOC(
" Creates a zipper from a user-defined tree using an adapter.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" // Given a user-defined tree `my_tree` and a corresponding `adapter`:\n"
" let zipper = from_tree(my_tree, adapter)\n"
"\n"
" // The zipper can now be navigated and modified.\n"
" get_value(zipper)\n"
" // => root_value\n"
" ```\n"
).
-spec from_tree(DRU, adapter(DRV, DRU)) -> zipper(DRV).
from_tree(Users_tree, Adapter) ->
_pipe = user_tree_to_standard_tree(Users_tree, Adapter),
from_standard_tree(_pipe).
-file("src/zipper/rose_tree.gleam", 172).
-spec standard_tree_to_user_tree(rose_tree(DSE), adapter(DSE, DSG)) -> DSG.
standard_tree_to_user_tree(Tree, Adapter) ->
User_children = gleam@list:map(
erlang:element(3, Tree),
fun(_capture) -> standard_tree_to_user_tree(_capture, Adapter) end
),
(erlang:element(4, Adapter))(erlang:element(2, Tree), User_children).
-file("src/zipper/rose_tree.gleam", 167).
?DOC(
" Converts a zipper back to a user-defined tree using an adapter.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" // Given a `zipper` and a corresponding `adapter` for a custom tree type:\n"
" let my_tree = to_tree(zipper, adapter)\n"
"\n"
" // `my_tree` is now an instance of the custom tree type.\n"
" ```\n"
).
-spec to_tree(zipper(DRZ), adapter(DRZ, DSB)) -> DSB.
to_tree(Zipper, Adapter) ->
Tree = to_standard_tree(Zipper),
standard_tree_to_user_tree(Tree, Adapter).
-file("src/zipper/rose_tree.gleam", 693).
?DOC(
" Checks if the current focus node is the leftmost among its siblings.\n"
"\n"
" Returns `True` if the node is the root or has no left siblings.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(0, [RoseTree(1, []), RoseTree(2, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper) // focus on 1\n"
"\n"
" is_leftmost(zipper)\n"
" // => True\n"
" ```\n"
).
-spec is_leftmost(zipper(any())) -> boolean().
is_leftmost(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
true;
[{choice, _, [], _} | _] ->
true;
_ ->
false
end.
-file("src/zipper/rose_tree.gleam", 210).
?DOC(
" Moves the focus to the left sibling of the current node.\n"
"\n"
" Returns `Ok(zipper)` focused on the left sibling if it exists,\n"
" or `Error(Nil)` if there is no left sibling (current node is leftmost).\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [RoseTree(2, []), RoseTree(3, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper)\n"
" let assert Ok(zipper) = go_right(zipper)\n"
" get_value(zipper)\n"
" // => 3\n"
"\n"
" let assert Ok(zipper) = go_left(zipper)\n"
" get_value(zipper)\n"
" // => 2\n"
" ```\n"
).
-spec go_left(zipper(DSO)) -> {ok, zipper(DSO)} | {error, nil}.
go_left(Zipper) ->
case is_leftmost(Zipper) of
true ->
{error, nil};
false ->
{Value@1, New_focus@1, Left_siblings@1, Right_siblings@1, Rest@1} = case erlang:element(
2,
Zipper
) of
[{choice, Value, [New_focus | Left_siblings], Right_siblings} |
Rest] -> {
Value,
New_focus,
Left_siblings,
Right_siblings,
Rest};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"safe assert"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"zipper/rose_tree"/utf8>>,
function => <<"go_left"/utf8>>,
line => 214,
value => _assert_fail,
start => 6969,
'end' => 7147,
pattern_start => 6980,
pattern_end => 7131})
end,
Right_siblings@2 = [erlang:element(3, Zipper) | Right_siblings@1],
New_choice = {choice, Value@1, Left_siblings@1, Right_siblings@2},
{ok, {zipper, [New_choice | Rest@1], New_focus@1}}
end.
-file("src/zipper/rose_tree.gleam", 715).
?DOC(
" Checks if the current focus node is the rightmost among its siblings.\n"
"\n"
" Returns `True` if the node is the root or has no right siblings.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(0, [RoseTree(1, []), RoseTree(2, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper)\n"
" let assert Ok(zipper) = go_right(zipper) // focus on 2\n"
"\n"
" is_rightmost(zipper)\n"
" // => True\n"
" ```\n"
).
-spec is_rightmost(zipper(any())) -> boolean().
is_rightmost(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
true;
[{choice, _, _, []} | _] ->
true;
_ ->
false
end.
-file("src/zipper/rose_tree.gleam", 245).
?DOC(
" Moves the focus to the right sibling of the current node.\n"
"\n"
" Returns `Ok(zipper)` focused on the right sibling if it exists,\n"
" or `Error(Nil)` if there is no right sibling (current node is rightmost).\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [RoseTree(2, []), RoseTree(3, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper)\n"
" let assert Ok(zipper) = go_right(zipper)\n"
" get_value(zipper)\n"
" // => 3\n"
" ```\n"
).
-spec go_right(zipper(DST)) -> {ok, zipper(DST)} | {error, nil}.
go_right(Zipper) ->
case is_rightmost(Zipper) of
true ->
{error, nil};
false ->
{Value@1, Left_siblings@1, New_focus@1, Right_siblings@1, Rest@1} = case erlang:element(
2,
Zipper
) of
[{choice, Value, Left_siblings, [New_focus | Right_siblings]} |
Rest] -> {
Value,
Left_siblings,
New_focus,
Right_siblings,
Rest};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"safe assert"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"zipper/rose_tree"/utf8>>,
function => <<"go_right"/utf8>>,
line => 249,
value => _assert_fail,
start => 7986,
'end' => 8165,
pattern_start => 7997,
pattern_end => 8149})
end,
Left_siblings@2 = [erlang:element(3, Zipper) | Left_siblings@1],
New_choice = {choice, Value@1, Left_siblings@2, Right_siblings@1},
{ok, {zipper, [New_choice | Rest@1], New_focus@1}}
end.
-file("src/zipper/rose_tree.gleam", 286).
?DOC(
" Moves the focus to the parent node.\n"
"\n"
" Returns `Ok(zipper)` focused on the parent node if not at the root.\n"
" Returns `Error(Nil)` if already at the root.\n"
"\n"
" This function takes $O(k)$ time,\n"
" where $k$ is the number of left siblings of the current node.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [RoseTree(2, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper)\n"
" get_value(zipper)\n"
" // => 2\n"
"\n"
" let assert Ok(zipper) = go_up(zipper)\n"
" get_value(zipper)\n"
" // => 1\n"
" ```\n"
).
-spec go_up(zipper(DSY)) -> {ok, zipper(DSY)} | {error, nil}.
go_up(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
{error, nil};
[{choice, Value, Left_siblings, Right_siblings} | Rest] ->
Children = lists:append(
lists:reverse(Left_siblings),
[erlang:element(3, Zipper) | Right_siblings]
),
New_focus = {rose_tree, Value, Children},
{ok, {zipper, Rest, New_focus}}
end.
-file("src/zipper/rose_tree.gleam", 324).
?DOC(
" Moves the focus back to the root of the tree.\n"
"\n"
" This operation is infallible: it always succeeds and returns a new zipper\n"
" focused at the root node while preserving the underlying tree structure.\n"
"\n"
" This function's running time depends on the path back to the root. Each\n"
" `go_up` step takes $O(k_i)$ time, where $k_i$ is the number of left siblings\n"
" at that level, so the total cost is $O(\\sum_{i=1}^{d} k_i)$ for a focus at\n"
" depth $d$. In the worst case — for example, a tree where every level is\n"
" extremely wide on the left — this can be $O(n)$, where $n$ is the number of\n"
" nodes in the tree.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [RoseTree(2, [RoseTree(3, [])])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(child_zipper) = go_down(zipper)\n"
" assert get_value(child_zipper) == 2\n"
"\n"
" let root_zipper = go_to_root(child_zipper)\n"
" assert is_root(root_zipper) == True\n"
" assert get_value(root_zipper) == 1\n"
" ```\n"
).
-spec go_to_root(zipper(DTD)) -> zipper(DTD).
go_to_root(Zipper) ->
case go_up(Zipper) of
{ok, Zipper@1} ->
go_to_root(Zipper@1);
{error, nil} ->
Zipper
end.
-file("src/zipper/rose_tree.gleam", 345).
?DOC(
" Moves the focus to the first child of the current node.\n"
"\n"
" Returns `Ok(zipper)` focused on the first child if it exists.\n"
" Returns `Error(Nil)` if the current node is a leaf (has no children).\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(1, [RoseTree(2, [])])\n"
" let zipper = from_standard_tree(tree)\n"
"\n"
" let assert Ok(zipper) = go_down(zipper)\n"
" get_value(zipper)\n"
" // => 2\n"
" ```\n"
).
-spec go_down(zipper(DTG)) -> {ok, zipper(DTG)} | {error, nil}.
go_down(Zipper) ->
case erlang:element(3, erlang:element(3, Zipper)) of
[] ->
{error, nil};
[New_focus | New_right_siblings] ->
Choice = {choice,
erlang:element(2, erlang:element(3, Zipper)),
[],
New_right_siblings},
{ok, {zipper, [Choice | erlang:element(2, Zipper)], New_focus}}
end.
-file("src/zipper/rose_tree.gleam", 365).
?DOC(
" Gets the value of the current focus node.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(42, []))\n"
" get_value(zipper)\n"
" // => 42\n"
" ```\n"
).
-spec get_value(zipper(DTL)) -> DTL.
get_value(Zipper) ->
erlang:element(2, erlang:element(3, Zipper)).
-file("src/zipper/rose_tree.gleam", 380).
?DOC(
" Gets the current focus subtree as a standard rose tree.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let child_tree = RoseTree(2, [])\n"
" let zipper = from_standard_tree(RoseTree(1, [child_tree]))\n"
" let assert Ok(zipper) = go_down(zipper)\n"
"\n"
" get_standard_tree(zipper)\n"
" // => RoseTree(2, [])\n"
" ```\n"
).
-spec get_standard_tree(zipper(DTN)) -> rose_tree(DTN).
get_standard_tree(Zipper) ->
erlang:element(3, Zipper).
-file("src/zipper/rose_tree.gleam", 393).
?DOC(
" Gets the current focus subtree as a user-defined tree using an adapter.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" // Given a `zipper` and a corresponding `adapter` for a custom tree type:\n"
" let focused_subtree = get_tree(zipper, adapter)\n"
"\n"
" // `focused_subtree` is an instance of the custom tree type.\n"
" ```\n"
).
-spec get_tree(zipper(DTQ), adapter(DTQ, DTS)) -> DTS.
get_tree(Zipper, Adapter) ->
Tree = get_standard_tree(Zipper),
standard_tree_to_user_tree(Tree, Adapter).
-file("src/zipper/rose_tree.gleam", 410).
?DOC(
" Sets the value of the current focus node.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(1, []))\n"
" let zipper = set_value(zipper, 42)\n"
" get_value(zipper)\n"
" // => 42\n"
" ```\n"
).
-spec set_value(zipper(DTV), DTV) -> zipper(DTV).
set_value(Zipper, Value) ->
{zipper,
erlang:element(2, Zipper),
begin
_record = erlang:element(3, Zipper),
{rose_tree, Value, erlang:element(3, _record)}
end}.
-file("src/zipper/rose_tree.gleam", 427).
?DOC(
" Sets the current focus subtree to a new standard rose tree.\n"
"\n"
" This replaces the entire focused subtree with the provided tree.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(1, [RoseTree(2, [])]))\n"
" let new_subtree = RoseTree(99, [])\n"
" let zipper = set_standard_tree(zipper, new_subtree)\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(99, [])\n"
" ```\n"
).
-spec set_standard_tree(zipper(DTY), rose_tree(DTY)) -> zipper(DTY).
set_standard_tree(Zipper, Tree) ->
{zipper, erlang:element(2, Zipper), Tree}.
-file("src/zipper/rose_tree.gleam", 441).
?DOC(
" Sets the current focus subtree to a user-defined tree using an adapter.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" // Given a `zipper`, a `my_subtree` of a user-defined type,\n"
" // and a corresponding `adapter`:\n"
" let updated_zipper = set_tree(zipper, my_subtree, adapter)\n"
"\n"
" // The focus of `updated_zipper` is now `my_subtree`.\n"
" ```\n"
).
-spec set_tree(zipper(DUC), DUE, adapter(DUC, DUE)) -> zipper(DUC).
set_tree(Zipper, Tree, Adapter) ->
New_focus = user_tree_to_standard_tree(Tree, Adapter),
set_standard_tree(Zipper, New_focus).
-file("src/zipper/rose_tree.gleam", 459).
?DOC(
" Updates the value of the current focus node using a transformation function.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(10, []))\n"
" let zipper = update(zipper, fn(x) { x * 2 })\n"
" get_value(zipper)\n"
" // => 20\n"
" ```\n"
).
-spec update(zipper(DUI), fun((DUI) -> DUI)) -> zipper(DUI).
update(Zipper, Updater) ->
New_value = Updater(get_value(Zipper)),
set_value(Zipper, New_value).
-file("src/zipper/rose_tree.gleam", 487).
?DOC(
" Maps the current focus of the rose tree using a transformation function.\n"
"\n"
" Unlike [`update`](#update), which only transforms the value of the focus node,\n"
" `map_focus` allows you to transform both the value and the children of the focus\n"
" node by providing a function that receives the current `RoseTree(a)` and returns\n"
" a new `RoseTree(a)`.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(10, []))\n"
" let zipper =\n"
" map_focus(zipper, fn(t) {\n"
" RoseTree(..t, children: [\n"
" RoseTree(value: 1, children: []),\n"
" RoseTree(value: 2, children: []),\n"
" RoseTree(value: 3, children: []),\n"
" ])\n"
" })\n"
" let assert Ok(zipper) = go_down(zipper)\n"
"\n"
" get_value(zipper)\n"
" // => 1\n"
" ```\n"
).
-spec map_focus(zipper(DUL), fun((rose_tree(DUL)) -> rose_tree(DUL))) -> zipper(DUL).
map_focus(Zipper, Updater) ->
{zipper, erlang:element(2, Zipper), Updater(erlang:element(3, Zipper))}.
-file("src/zipper/rose_tree.gleam", 654).
?DOC(
" Checks if the current focus is the root of the tree.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(1, [RoseTree(2, [])]))\n"
" is_root(zipper)\n"
" // => True\n"
"\n"
" let assert Ok(child_zipper) = go_down(zipper)\n"
" is_root(child_zipper)\n"
" // => False\n"
" ```\n"
).
-spec is_root(zipper(any())) -> boolean().
is_root(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
true;
_ ->
false
end.
-file("src/zipper/rose_tree.gleam", 510).
?DOC(
" Inserts a new tree as the immediate left sibling of the current node.\n"
"\n"
" Returns `Ok(zipper)` with the new sibling inserted.\n"
" Returns `Error(Nil)` if the current node is the root.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(0, [RoseTree(2, [])])\n"
" let assert Ok(zipper) = from_standard_tree(tree) |> go_down // focus on 2\n"
"\n"
" let new_sibling = RoseTree(1, [])\n"
" let assert Ok(zipper) = insert_left(zipper, new_sibling)\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(0, [RoseTree(1, []), RoseTree(2, [])])\n"
" ```\n"
).
-spec insert_left(zipper(DUQ), rose_tree(DUQ)) -> {ok, zipper(DUQ)} |
{error, nil}.
insert_left(Zipper, Tree) ->
case is_root(Zipper) of
true ->
{error, nil};
false ->
{C@1, Left_siblings@1, Rest@1} = case erlang:element(2, Zipper) of
[{choice, _, Left_siblings, _} = C | Rest] -> {
C,
Left_siblings,
Rest};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"safe assert"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"zipper/rose_tree"/utf8>>,
function => <<"insert_left"/utf8>>,
line => 517,
value => _assert_fail,
start => 15843,
'end' => 15911,
pattern_start => 15854,
pattern_end => 15895})
end,
New_left_siblings = [Tree | Left_siblings@1],
New_choice = {choice,
erlang:element(2, C@1),
New_left_siblings,
erlang:element(4, C@1)},
{ok, {zipper, [New_choice | Rest@1], erlang:element(3, Zipper)}}
end.
-file("src/zipper/rose_tree.gleam", 543).
?DOC(
" Inserts a new tree as the immediate right sibling of the current node.\n"
"\n"
" Returns `Ok(zipper)` with the new sibling inserted.\n"
" Returns `Error(Nil)` if the current node is the root.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(0, [RoseTree(1, [])])\n"
" let assert Ok(zipper) = from_standard_tree(tree) |> go_down // focus on 1\n"
"\n"
" let new_sibling = RoseTree(2, [])\n"
" let assert Ok(zipper) = insert_right(zipper, new_sibling)\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(0, [RoseTree(1, []), RoseTree(2, [])])\n"
" ```\n"
).
-spec insert_right(zipper(DUW), rose_tree(DUW)) -> {ok, zipper(DUW)} |
{error, nil}.
insert_right(Zipper, Tree) ->
case is_root(Zipper) of
true ->
{error, nil};
false ->
{C@1, Right_siblings@1, Rest@1} = case erlang:element(2, Zipper) of
[{choice, _, _, Right_siblings} = C | Rest] -> {
C,
Right_siblings,
Rest};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"safe assert"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"zipper/rose_tree"/utf8>>,
function => <<"insert_right"/utf8>>,
line => 550,
value => _assert_fail,
start => 16846,
'end' => 16915,
pattern_start => 16857,
pattern_end => 16899})
end,
New_right_siblings = [Tree | Right_siblings@1],
New_choice = {choice,
erlang:element(2, C@1),
erlang:element(3, C@1),
New_right_siblings},
{ok, {zipper, [New_choice | Rest@1], erlang:element(3, Zipper)}}
end.
-file("src/zipper/rose_tree.gleam", 570).
?DOC(
" Inserts a new tree as the first child of the current node.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(1, [RoseTree(3, [])]))\n"
" let zipper = insert_child(zipper, RoseTree(2, []))\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(1, [RoseTree(2, []), RoseTree(3, [])])\n"
" ```\n"
).
-spec insert_child(zipper(DVC), rose_tree(DVC)) -> zipper(DVC).
insert_child(Zipper, Tree) ->
{rose_tree, Value, Children} = erlang:element(3, Zipper),
New_children = [Tree | Children],
New_focus = {rose_tree, Value, New_children},
{zipper, erlang:element(2, Zipper), New_focus}.
-file("src/zipper/rose_tree.gleam", 589).
?DOC(
" Inserts a new tree as the last child of the current node.\n"
"\n"
" NOTE: This operation is $O(d)$ time complexity, where $d$ is the number of children.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let zipper = from_standard_tree(RoseTree(1, [RoseTree(2, [])]))\n"
" let zipper = insert_child_back(zipper, RoseTree(3, []))\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(1, [RoseTree(2, []), RoseTree(3, [])])\n"
" ```\n"
).
-spec insert_child_back(zipper(DVG), rose_tree(DVG)) -> zipper(DVG).
insert_child_back(Zipper, Tree) ->
{rose_tree, Value, Children} = erlang:element(3, Zipper),
New_children = lists:append(Children, [Tree]),
New_focus = {rose_tree, Value, New_children},
{zipper, erlang:element(2, Zipper), New_focus}.
-file("src/zipper/rose_tree.gleam", 618).
?DOC(
" Deletes the current focus node.\n"
"\n"
" The focus moves to the right sibling if it exists, otherwise to the left\n"
" sibling, otherwise to the parent.\n"
"\n"
" Returns `Ok(zipper)` with the focus moved to the new location.\n"
" Returns `Error(Nil)` if the focus is the root node.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let tree = RoseTree(0, [RoseTree(1, []), RoseTree(2, []), RoseTree(3, [])])\n"
" let zipper = from_standard_tree(tree)\n"
" let assert Ok(zipper) = go_down(zipper) // focus on 1\n"
" let assert Ok(zipper) = go_right(zipper) // focus on 2\n"
"\n"
" let assert Ok(zipper) = delete(zipper)\n"
" get_value(zipper) // focus moved to the right sibling\n"
" // => 3\n"
"\n"
" to_standard_tree(zipper)\n"
" // => RoseTree(0, [RoseTree(1, []), RoseTree(3, [])])\n"
" ```\n"
).
-spec delete(zipper(DVK)) -> {ok, zipper(DVK)} | {error, nil}.
delete(Zipper) ->
case erlang:element(2, Zipper) of
[] ->
{error, nil};
[{choice, Value, Left_siblings, Right_siblings} | Rest] ->
case Right_siblings of
[New_focus | Rs] ->
New_choice = {choice, Value, Left_siblings, Rs},
{ok, {zipper, [New_choice | Rest], New_focus}};
[] ->
case Left_siblings of
[New_focus@1 | Ls] ->
New_choice@1 = {choice, Value, Ls, []},
{ok, {zipper, [New_choice@1 | Rest], New_focus@1}};
[] ->
New_focus@2 = {rose_tree, Value, []},
{ok, {zipper, Rest, New_focus@2}}
end
end
end.
-file("src/zipper/rose_tree.gleam", 673).
?DOC(
" Checks if the current focus node is a leaf (has no children).\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let leaf_zipper = from_standard_tree(RoseTree(1, []))\n"
" is_leaf(leaf_zipper)\n"
" // => True\n"
"\n"
" let node_zipper = from_standard_tree(RoseTree(1, [RoseTree(2, [])]))\n"
" is_leaf(node_zipper)\n"
" // => False\n"
" ```\n"
).
-spec is_leaf(zipper(any())) -> boolean().
is_leaf(Zipper) ->
case erlang:element(3, erlang:element(3, Zipper)) of
[] ->
true;
_ ->
false
end.