Current section

Files

Jump to
caffeine_lang src caffeine_lang@analysis@dependency_validator.erl
Raw

src/caffeine_lang@analysis@dependency_validator.erl

-module(caffeine_lang@analysis@dependency_validator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/analysis/dependency_validator.gleam").
-export([parse_dependency_path/1, build_expectation_index/1, validate_dependency_relations/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.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 19).
?DOC(" Extracts depends_on from an IR's SloFields.\n").
-spec get_depends_on(caffeine_lang@linker@ir:intermediate_representation(any())) -> gleam@option:option(gleam@dict:dict(caffeine_lang@linker@dependency:dependency_relation_type(), list(binary()))).
get_depends_on(Ir) ->
erlang:element(8, erlang:element(5, Ir)).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 489).
?DOC(
" For each hard dep of a `time_slice` expectation with a `below` bound,\n"
" error if the dep's `below_ms` exceeds the dependent's. Skips deps that\n"
" aren't time_slice or have no `below_ms`.\n"
).
-spec validate_single_ir_hard_latency(
caffeine_lang@linker@ir:intermediate_representation(IAX),
gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(IAX))
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
validate_single_ir_hard_latency(Ir, Expectation_index) ->
Self_path = caffeine_lang@linker@ir:ir_to_identifier(Ir),
Parent_below = erlang:element(10, erlang:element(5, Ir)),
gleam@bool:guard(
gleam@option:is_none(Parent_below),
{ok, nil},
fun() ->
Parent_ms@1 = case Parent_below of
{some, Parent_ms} -> Parent_ms;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"caffeine_lang/analysis/dependency_validator"/utf8>>,
function => <<"validate_single_ir_hard_latency"/utf8>>,
line => 497,
value => _assert_fail,
start => 15198,
'end' => 15246,
pattern_start => 15209,
pattern_end => 15231})
end,
Hard_targets = case erlang:element(8, erlang:element(5, Ir)) of
{some, Relations} ->
_pipe = gleam_stdlib:map_get(Relations, hard),
gleam@result:unwrap(_pipe, []);
none ->
[]
end,
_pipe@1 = Hard_targets,
gleam@list:try_each(
_pipe@1,
fun(Target) ->
case gleam_stdlib:map_get(Expectation_index, Target) of
{error, nil} ->
{ok, nil};
{ok, Target_ir} ->
case erlang:element(
10,
erlang:element(5, Target_ir)
) of
none ->
{ok, nil};
{some, Dep_ms} ->
case gleam@float:compare(
Dep_ms,
Parent_ms@1
) of
gt ->
{error,
caffeine_lang@errors:semantic_analysis_dependency_validation_error(
<<<<<<<<<<<<<<<<"Hard-dep latency monotonicity violation: '"/utf8,
Self_path/binary>>/binary,
"' guarantees below "/utf8>>/binary,
(gleam_stdlib:float_to_string(
Parent_ms@1
))/binary>>/binary,
"ms but hard-depends on '"/utf8>>/binary,
Target/binary>>/binary,
"' which guarantees below "/utf8>>/binary,
(gleam_stdlib:float_to_string(
Dep_ms
))/binary>>/binary,
"ms; child must be no slower than its hard dependencies"/utf8>>
)};
_ ->
{ok, nil}
end
end
end
end
)
end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 477).
-spec expectation_type_to_string(caffeine_lang@frontend@ast:expectation_type()) -> binary().
expectation_type_to_string(T) ->
case T of
success_rate_type ->
<<"success_rate"/utf8>>;
time_slice_type ->
<<"time_slice"/utf8>>
end.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 441).
?DOC(
" For each hard dep with a declared expectation type, error if it doesn't\n"
" match the dependent's declared type. Skips pairs where either side is\n"
" unmeasured or undeclared.\n"
).
-spec validate_single_ir_hard_type_alignment(
caffeine_lang@linker@ir:intermediate_representation(IAQ),
gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(IAQ))
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
validate_single_ir_hard_type_alignment(Ir, Expectation_index) ->
Self_path = caffeine_lang@linker@ir:ir_to_identifier(Ir),
Parent_type = erlang:element(11, erlang:element(5, Ir)),
Hard_targets = case erlang:element(8, erlang:element(5, Ir)) of
{some, Relations} ->
_pipe = gleam_stdlib:map_get(Relations, hard),
gleam@result:unwrap(_pipe, []);
none ->
[]
end,
_pipe@1 = Hard_targets,
gleam@list:try_each(
_pipe@1,
fun(Target) -> case gleam_stdlib:map_get(Expectation_index, Target) of
{error, nil} ->
{ok, nil};
{ok, Target_ir} ->
case {Parent_type,
erlang:element(11, erlang:element(5, Target_ir))} of
{{some, P}, {some, D}} when P =/= D ->
{error,
caffeine_lang@errors:semantic_analysis_dependency_validation_error(
<<<<<<<<<<<<<<<<"Expectation-type mismatch on hard dependency: '"/utf8,
Self_path/binary>>/binary,
"' ("/utf8>>/binary,
(expectation_type_to_string(
P
))/binary>>/binary,
") hard-depends on '"/utf8>>/binary,
Target/binary>>/binary,
"' ("/utf8>>/binary,
(expectation_type_to_string(D))/binary>>/binary,
"); hard dependencies must share an expectation type"/utf8>>
)};
{_, _} ->
{ok, nil}
end
end end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 432).
?DOC(" Rounds a float to 4 decimal places for cleaner display.\n").
-spec round_to_4(float()) -> float().
round_to_4(F) ->
erlang:float(erlang:round(F * 10000.0)) / 10000.0.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 427).
?DOC(
" Computes the composite availability ceiling from a list of thresholds.\n"
" Each threshold is a percentage (e.g. 99.99). The composite ceiling is\n"
" the product of individual availabilities.\n"
).
-spec compute_composite_ceiling(list(float())) -> float().
compute_composite_ceiling(Thresholds) ->
gleam@list:fold(Thresholds, 1.0, fun(Acc, T) -> Acc * (T / 100.0) end) * 100.0.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 411).
?DOC(
" Collects thresholds from hard dependency targets.\n"
" Skips targets that don't exist in the index.\n"
).
-spec collect_hard_dep_thresholds(
list(binary()),
gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(any()))
) -> list({binary(), float()}).
collect_hard_dep_thresholds(Targets, Expectation_index) ->
_pipe = Targets,
gleam@list:filter_map(
_pipe,
fun(Target_path) ->
case gleam_stdlib:map_get(Expectation_index, Target_path) of
{error, nil} ->
{error, nil};
{ok, Target_ir} ->
{ok,
{Target_path,
erlang:element(2, erlang:element(5, Target_ir))}}
end
end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 362).
?DOC(
" Validates hard dependency thresholds for a single IR using composite ceiling.\n"
" The composite ceiling is the product of all hard dependency thresholds,\n"
" representing the maximum achievable availability given those dependencies.\n"
).
-spec validate_single_ir_hard_thresholds(
caffeine_lang@linker@ir:intermediate_representation(IAC),
gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(IAC))
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
validate_single_ir_hard_thresholds(Ir, Expectation_index) ->
Self_path = caffeine_lang@linker@ir:ir_to_identifier(Ir),
Slo = erlang:element(5, Ir),
Source_threshold = erlang:element(2, Slo),
Hard_targets = case erlang:element(8, Slo) of
{some, Relations} ->
_pipe = gleam_stdlib:map_get(Relations, hard),
gleam@result:unwrap(_pipe, []);
none ->
[]
end,
Dep_thresholds = collect_hard_dep_thresholds(
Hard_targets,
Expectation_index
),
gleam@bool:guard(
gleam@list:is_empty(Dep_thresholds),
{ok, nil},
fun() ->
Composite_ceiling = compute_composite_ceiling(
gleam@list:map(
Dep_thresholds,
fun(Pair) -> erlang:element(2, Pair) end
)
),
Display_ceiling = round_to_4(Composite_ceiling),
case gleam@float:compare(Source_threshold, Composite_ceiling) of
gt ->
Deps_description = begin
_pipe@1 = Dep_thresholds,
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Pair@1) ->
<<<<<<<<"'"/utf8,
(erlang:element(1, Pair@1))/binary>>/binary,
"' ("/utf8>>/binary,
(gleam_stdlib:float_to_string(
erlang:element(2, Pair@1)
))/binary>>/binary,
")"/utf8>>
end
),
gleam@string:join(_pipe@2, <<", "/utf8>>)
end,
{error,
caffeine_lang@errors:semantic_analysis_dependency_validation_error(
<<<<<<<<<<<<<<"Composite hard dependency threshold violation: '"/utf8,
Self_path/binary>>/binary,
"' (threshold: "/utf8>>/binary,
(gleam_stdlib:float_to_string(
Source_threshold
))/binary>>/binary,
") exceeds the composite availability ceiling of "/utf8>>/binary,
(gleam_stdlib:float_to_string(
Display_ceiling
))/binary>>/binary,
" from its hard dependencies: "/utf8>>/binary,
Deps_description/binary>>
)};
_ ->
{ok, nil}
end
end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 319).
-spec explore_neighbors(
list(binary()),
gleam@dict:dict(binary(), list(binary())),
gleam@set:set(binary()),
gleam@set:set(binary()),
list(binary())
) -> {ok, {gleam@set:set(binary()), gleam@set:set(binary())}} |
{error, caffeine_lang@errors:compilation_error()}.
explore_neighbors(Neighbors, Adjacency, Visited, In_progress, Path) ->
case Neighbors of
[] ->
{ok, {Visited, In_progress}};
[Neighbor | Rest] ->
gleam@bool:guard(
gleam@set:contains(In_progress, Neighbor),
{error,
caffeine_lang@errors:semantic_analysis_dependency_validation_error(
<<<<<<"Circular dependency detected: "/utf8,
(gleam@string:join(
lists:reverse(Path),
<<" -> "/utf8>>
))/binary>>/binary,
" -> "/utf8>>/binary,
Neighbor/binary>>
)},
fun() ->
gleam@bool:guard(
gleam@set:contains(Visited, Neighbor),
explore_neighbors(
Rest,
Adjacency,
Visited,
In_progress,
Path
),
fun() ->
gleam@result:'try'(
explore_node(
Neighbor,
Adjacency,
Visited,
In_progress,
[Neighbor | Path]
),
fun(_use0) ->
{Visited@1, In_progress@1} = _use0,
explore_neighbors(
Rest,
Adjacency,
Visited@1,
In_progress@1,
Path
)
end
)
end
)
end
)
end.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 295).
-spec explore_node(
binary(),
gleam@dict:dict(binary(), list(binary())),
gleam@set:set(binary()),
gleam@set:set(binary()),
list(binary())
) -> {ok, {gleam@set:set(binary()), gleam@set:set(binary())}} |
{error, caffeine_lang@errors:compilation_error()}.
explore_node(Node, Adjacency, Visited, In_progress, Path) ->
In_progress@1 = gleam@set:insert(In_progress, Node),
Neighbors = begin
_pipe = gleam_stdlib:map_get(Adjacency, Node),
gleam@result:unwrap(_pipe, [])
end,
gleam@result:'try'(
explore_neighbors(Neighbors, Adjacency, Visited, In_progress@1, Path),
fun(_use0) ->
{Visited@1, In_progress@2} = _use0,
Visited@2 = gleam@set:insert(Visited@1, Node),
In_progress@3 = gleam@set:delete(In_progress@2, Node),
{ok, {Visited@2, In_progress@3}}
end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 270).
-spec detect_cycles_loop(
list(binary()),
gleam@dict:dict(binary(), list(binary())),
gleam@set:set(binary()),
gleam@set:set(binary())
) -> {ok, gleam@set:set(binary())} |
{error, caffeine_lang@errors:compilation_error()}.
detect_cycles_loop(Nodes, Adjacency, Visited, In_progress) ->
case Nodes of
[] ->
{ok, Visited};
[Node | Rest] ->
gleam@bool:guard(
gleam@set:contains(Visited, Node),
detect_cycles_loop(Rest, Adjacency, Visited, In_progress),
fun() ->
gleam@result:'try'(
explore_node(
Node,
Adjacency,
Visited,
In_progress,
[Node]
),
fun(_use0) ->
{Visited@1, In_progress@1} = _use0,
detect_cycles_loop(
Rest,
Adjacency,
Visited@1,
In_progress@1
)
end
)
end
)
end.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 130).
-spec get_all_dependency_targets(
gleam@dict:dict(caffeine_lang@linker@dependency:dependency_relation_type(), list(binary()))
) -> list(binary()).
get_all_dependency_targets(Relations) ->
_pipe = Relations,
_pipe@1 = maps:values(_pipe),
lists:append(_pipe@1).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 239).
?DOC(" Builds a directed adjacency list from all IRs with depends_on.\n").
-spec build_adjacency_list(
list(caffeine_lang@linker@ir:intermediate_representation(any()))
) -> gleam@dict:dict(binary(), list(binary())).
build_adjacency_list(Irs) ->
_pipe = Irs,
_pipe@1 = gleam@list:filter_map(_pipe, fun(Ir) -> case get_depends_on(Ir) of
{some, Relations} ->
Path = caffeine_lang@linker@ir:ir_to_identifier(Ir),
Targets = get_all_dependency_targets(Relations),
{ok, {Path, Targets}};
none ->
{error, nil}
end end),
maps:from_list(_pipe@1).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 257).
?DOC(" Detects circular dependencies in the dependency graph.\n").
-spec detect_cycles(
list(caffeine_lang@linker@ir:intermediate_representation(any()))
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
detect_cycles(Irs) ->
Adjacency = build_adjacency_list(Irs),
Nodes = begin
_pipe = Adjacency,
_pipe@1 = maps:keys(_pipe),
gleam@list:sort(_pipe@1, fun gleam@string:compare/2)
end,
_pipe@2 = detect_cycles_loop(
Nodes,
Adjacency,
gleam@set:new(),
gleam@set:new()
),
gleam@result:map(_pipe@2, fun(_) -> nil end).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 221).
?DOC(" Build a dependency reference error with a consistent message format.\n").
-spec dependency_ref_error(binary(), binary(), binary()) -> caffeine_lang@errors:compilation_error().
dependency_ref_error(Target, Self_path, Reason) ->
caffeine_lang@errors:semantic_analysis_dependency_validation_error(
<<<<<<<<<<"Invalid dependency reference '"/utf8, Target/binary>>/binary,
"' in '"/utf8>>/binary,
Self_path/binary>>/binary,
"': "/utf8>>/binary,
Reason/binary>>
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 209).
?DOC(false).
-spec parse_dependency_path(binary()) -> {ok,
{binary(), binary(), binary(), binary()}} |
{error, nil}.
parse_dependency_path(Path) ->
case gleam@string:split(Path, <<"."/utf8>>) of
[Org, Team, Service, Name] when (((Org =/= <<""/utf8>>) andalso (Team =/= <<""/utf8>>)) andalso (Service =/= <<""/utf8>>)) andalso (Name =/= <<""/utf8>>) ->
{ok, {Org, Team, Service, Name}};
_ ->
{error, nil}
end.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 173).
-spec validate_dependency_target(
binary(),
binary(),
gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(any()))
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
validate_dependency_target(Target, Self_path, Expectation_index) ->
case parse_dependency_path(Target) of
{error, nil} ->
{error,
dependency_ref_error(
Target,
Self_path,
<<"expected format 'org.team.service.name'"/utf8>>
)};
{ok, _} ->
gleam@bool:guard(
Target =:= Self_path,
{error,
dependency_ref_error(
Target,
Self_path,
<<"self-reference not allowed"/utf8>>
)},
fun() -> case gleam_stdlib:map_get(Expectation_index, Target) of
{ok, _} ->
{ok, nil};
{error, nil} ->
{error,
dependency_ref_error(
Target,
Self_path,
<<"target does not exist"/utf8>>
)}
end end
)
end.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 150).
-spec do_check_for_duplicates(list(binary()), gleam@set:set(binary()), binary()) -> {ok,
nil} |
{error, caffeine_lang@errors:compilation_error()}.
do_check_for_duplicates(Targets, Seen, Self_path) ->
case Targets of
[] ->
{ok, nil};
[Target | Rest] ->
gleam@bool:guard(
gleam@set:contains(Seen, Target),
{error,
caffeine_lang@errors:semantic_analysis_dependency_validation_error(
<<<<<<<<"Duplicate dependency reference '"/utf8,
Target/binary>>/binary,
"' in '"/utf8>>/binary,
Self_path/binary>>/binary,
"'"/utf8>>
)},
fun() ->
do_check_for_duplicates(
Rest,
gleam@set:insert(Seen, Target),
Self_path
)
end
)
end.
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 138).
-spec check_for_duplicates_per_relation(
gleam@dict:dict(caffeine_lang@linker@dependency:dependency_relation_type(), list(binary())),
binary()
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
check_for_duplicates_per_relation(Relations, Self_path) ->
_pipe = Relations,
_pipe@1 = maps:to_list(_pipe),
gleam@list:try_each(
_pipe@1,
fun(Pair) ->
{_, Targets} = Pair,
do_check_for_duplicates(Targets, gleam@set:new(), Self_path)
end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 104).
-spec validate_ir_dependencies(
caffeine_lang@linker@ir:intermediate_representation(HXL),
gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(HXL))
) -> {ok, nil} | {error, caffeine_lang@errors:compilation_error()}.
validate_ir_dependencies(Ir, Expectation_index) ->
Depends_on = get_depends_on(Ir),
gleam@bool:guard(
gleam@option:is_none(Depends_on),
{ok, nil},
fun() ->
Self_path = caffeine_lang@linker@ir:ir_to_identifier(Ir),
Relations@1 = case Depends_on of
{some, Relations} -> Relations;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"caffeine_lang/analysis/dependency_validator"/utf8>>,
function => <<"validate_ir_dependencies"/utf8>>,
line => 115,
value => _assert_fail,
start => 3478,
'end' => 3524,
pattern_start => 3489,
pattern_end => 3511})
end,
gleam@result:'try'(
check_for_duplicates_per_relation(Relations@1, Self_path),
fun(_) ->
All_targets = get_all_dependency_targets(Relations@1),
_pipe = All_targets,
gleam@list:try_each(
_pipe,
fun(Target) ->
validate_dependency_target(
Target,
Self_path,
Expectation_index
)
end
)
end
)
end
).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 93).
?DOC(false).
-spec build_expectation_index(
list(caffeine_lang@linker@ir:intermediate_representation(HXF))
) -> gleam@dict:dict(binary(), caffeine_lang@linker@ir:intermediate_representation(HXF)).
build_expectation_index(Irs) ->
_pipe = Irs,
_pipe@1 = gleam@list:map(
_pipe,
fun(Ir) ->
Path = caffeine_lang@linker@ir:ir_to_identifier(Ir),
{Path, Ir}
end
),
maps:from_list(_pipe@1).
-file("src/caffeine_lang/analysis/dependency_validator.gleam", 33).
?DOC(false).
-spec validate_dependency_relations(
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:linked()))
) -> {ok,
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:deps_validated()))} |
{error, caffeine_lang@errors:compilation_error()}.
validate_dependency_relations(Irs) ->
Expectation_index = build_expectation_index(Irs),
gleam@result:'try'(
begin
_pipe = Irs,
_pipe@1 = gleam@list:map(
_pipe,
fun(Ir) -> validate_ir_dependencies(Ir, Expectation_index) end
),
_pipe@2 = caffeine_lang@errors:from_results(_pipe@1),
gleam@result:map(_pipe@2, fun(_) -> nil end)
end,
fun(_) ->
gleam@result:'try'(
detect_cycles(Irs),
fun(_) ->
gleam@result:'try'(
begin
_pipe@3 = Irs,
_pipe@4 = gleam@list:filter(
_pipe@3,
fun(Ir@1) ->
gleam@option:is_some(get_depends_on(Ir@1))
end
),
_pipe@5 = gleam@list:map(
_pipe@4,
fun(Ir@2) ->
validate_single_ir_hard_thresholds(
Ir@2,
Expectation_index
)
end
),
_pipe@6 = caffeine_lang@errors:from_results(_pipe@5),
gleam@result:map(_pipe@6, fun(_) -> nil end)
end,
fun(_) ->
gleam@result:'try'(
begin
_pipe@7 = Irs,
_pipe@8 = gleam@list:filter(
_pipe@7,
fun(Ir@3) ->
gleam@option:is_some(
get_depends_on(Ir@3)
)
end
),
_pipe@9 = gleam@list:map(
_pipe@8,
fun(Ir@4) ->
validate_single_ir_hard_type_alignment(
Ir@4,
Expectation_index
)
end
),
_pipe@10 = caffeine_lang@errors:from_results(
_pipe@9
),
gleam@result:map(
_pipe@10,
fun(_) -> nil end
)
end,
fun(_) ->
gleam@result:'try'(
begin
_pipe@11 = Irs,
_pipe@12 = gleam@list:filter(
_pipe@11,
fun(Ir@5) ->
gleam@option:is_some(
get_depends_on(Ir@5)
)
end
),
_pipe@13 = gleam@list:map(
_pipe@12,
fun(Ir@6) ->
validate_single_ir_hard_latency(
Ir@6,
Expectation_index
)
end
),
_pipe@14 = caffeine_lang@errors:from_results(
_pipe@13
),
gleam@result:map(
_pipe@14,
fun(_) -> nil end
)
end,
fun(_) ->
{ok,
gleam@list:map(
Irs,
fun caffeine_lang@linker@ir:promote/1
)}
end
)
end
)
end
)
end
)
end
).