-module(integer_complexity). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([a000792/1, get_complexities_up_to/2, get_complexity/2, get_expressions_up_to/2, get_expression/2]). -export_type([complexity_data/0, derived_expression/0]). -opaque complexity_data() :: {complexity_data, integer(), derived_expression()}. -type derived_expression() :: {derived_add, derived_expression(), derived_expression()} | {derived_multiply, derived_expression(), derived_expression()} | {derived, integer()} | derived_one. -spec a000792_rec(integer(), integer()) -> integer(). a000792_rec(N, Result) -> case (N >= 5) orelse (N =:= 3) of true -> a000792_rec(N - 3, Result * 3); false -> erlang:'bsl'(Result, N div 2) end. -spec a000792(integer()) -> integer(). a000792(N) -> a000792_rec(N, 1). -spec calc_t(integer(), integer(), integer()) -> integer(). calc_t(T, Target, Index) -> case (a000792(T) + a000792(Target - T)) < Index of true -> calc_t(T - 1, Target, Index); false -> T end. -spec products( integer(), integer(), integer(), gary:erlang_array(complexity_data()) ) -> gary:erlang_array(complexity_data()). products(K, Max, N, Complexity) -> gleam@bool:guard( K > Max, Complexity, fun() -> Complexity_k = begin _pipe = integer_complexity@internal@array:get(Complexity, K), _pipe@1 = gleam@result:map( _pipe, fun(X) -> erlang:element(2, X) end ), gleam@result:unwrap(_pipe@1, 2147483647) end, Complexity_n = begin _pipe@2 = integer_complexity@internal@array:get(Complexity, N), _pipe@3 = gleam@result:map( _pipe@2, fun(X@1) -> erlang:element(2, X@1) end ), gleam@result:unwrap(_pipe@3, 2147483647) end, Complexity_k_n = integer_complexity@internal@array:get( Complexity, K * N ), Prod_value = Complexity_k + Complexity_n, _assert_subject = case Complexity_k_n of {error, _} -> integer_complexity@internal@array:set( Complexity, K * N, {complexity_data, Prod_value, {derived_multiply, {derived, K}, {derived, N}}} ); {ok, {complexity_data, K_n_value, _}} when Prod_value < K_n_value -> integer_complexity@internal@array:set( Complexity, K * N, {complexity_data, Prod_value, {derived_multiply, {derived, K}, {derived, N}}} ); _ -> {ok, Complexity} end, {ok, Updated_complexity} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"integer_complexity"/utf8>>, function => <<"products"/utf8>>, line => 222}) end, products(K + 1, Max, N, Updated_complexity) end ). -spec sums( carpenter@table:set(integer(), complexity_data()), integer(), integer() ) -> gleam@option:option(complexity_data()). sums(Cache, N, Max) -> gleam@bool:guard(Max < 6, none, fun() -> _pipe = gleam@list:range(6, Max), gleam@list:fold( _pipe, none, fun(Acc, M) -> Complexity_m = erlang:element(2, complexity_rec(Cache, M)), Complexity_n_m = erlang:element( 2, complexity_rec(Cache, N - M) ), Sum_value = Complexity_m + Complexity_n_m, case Acc of {some, {complexity_data, Complexity, _}} when Sum_value < Complexity -> {some, {complexity_data, Sum_value, {derived_add, {derived, M}, {derived, N - M}}}}; none -> {some, {complexity_data, Sum_value, {derived_add, {derived, M}, {derived, N - M}}}}; _ -> Acc end end ) end). -spec complexity_rec( carpenter@table:set(integer(), complexity_data()), integer() ) -> complexity_data(). complexity_rec(Cache, N) -> fun rememo@ets@memo:memoize/3( Cache, N, fun() -> gleam@bool:guard( N =:= 1, {complexity_data, N, derived_one}, fun() -> Base_complexity = {complexity_data, erlang:element(2, complexity_rec(Cache, N - 1)) + 1, {derived_add, {derived, N - 1}, derived_one}}, _assert_subject = erlang:element( 2, complexity_rec(Cache, N - 1) ), Target = case _assert_subject of _ -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"integer_complexity"/utf8>>, function => <<"complexity_rec"/utf8>>, line => 122}) end, T = calc_t(Target div 2, Target, N), K_max = a000792(T), Sum_test_result = begin _pipe = sums(Cache, N, K_max), gleam@option:unwrap(_pipe, Base_complexity) end, Divisor_test_result = begin _pipe@1 = divisors(Cache, N), gleam@option:unwrap(_pipe@1, Base_complexity) end, _assert_subject@1 = gleam_community@maths@piecewise:list_minimum( [Base_complexity, Sum_test_result, Divisor_test_result], fun(A, B) -> gleam@int:compare( erlang:element(2, A), erlang:element(2, B) ) end ), {ok, Result} = case _assert_subject@1 of {ok, _} -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"integer_complexity"/utf8>>, function => <<"complexity_rec"/utf8>>, line => 134}) end, Result end ) end ). -spec get_complexity_data_up_to( carpenter@table:set(integer(), complexity_data()), integer() ) -> list(complexity_data()). get_complexity_data_up_to(Cache, Integer) -> _pipe = gleam@list:range(1, Integer), gleam@list:map(_pipe, fun(_capture) -> complexity_rec(Cache, _capture) end). -spec get_complexities_up_to( carpenter@table:set(integer(), complexity_data()), integer() ) -> {ok, list(integer())} | {error, nil}. get_complexities_up_to(Cache, Integer) -> gleam@bool:guard( Integer =< 0, {error, nil}, fun() -> _pipe = gleam@list:map( get_complexity_data_up_to(Cache, Integer), fun(X) -> erlang:element(2, X) end ), {ok, _pipe} end ). -spec get_complexity_data( carpenter@table:set(integer(), complexity_data()), integer() ) -> complexity_data(). get_complexity_data(Cache, Integer) -> complexity_rec(Cache, Integer). -spec get_complexity( carpenter@table:set(integer(), complexity_data()), integer() ) -> integer(). get_complexity(Cache, Integer) -> gleam@bool:guard( Integer =:= 0, 0, fun() -> erlang:element( 2, get_complexity_data(Cache, gleam@int:absolute_value(Integer)) ) end ). -spec divisors(carpenter@table:set(integer(), complexity_data()), integer()) -> gleam@option:option(complexity_data()). divisors(Cache, N) -> Divisors = gleam_community@maths@arithmetics:divisors(N), Smaller_divisors = begin _pipe = gleam@list:take( Divisors, gleam@float:round(gleam@int:to_float(erlang:length(Divisors)) / 2.0) ), gleam@list:drop(_pipe, 1) end, Sum_complexity = gleam@list:fold( Smaller_divisors, none, fun(Acc, A) -> Complexity_a = erlang:element(2, complexity_rec(Cache, A)), Complexity_b = erlang:element(2, complexity_rec(Cache, case A of 0 -> 0; Gleam@denominator -> N div Gleam@denominator end)), Prod_complexity = Complexity_a + Complexity_b, case Acc of none -> {some, {complexity_data, Prod_complexity, {derived_multiply, {derived, A}, {derived, case A of 0 -> 0; Gleam@denominator@1 -> N div Gleam@denominator@1 end}}}}; {some, {complexity_data, Complexity, _}} when Prod_complexity < Complexity -> {some, {complexity_data, Prod_complexity, {derived_multiply, {derived, A}, {derived, case A of 0 -> 0; Gleam@denominator@2 -> N div Gleam@denominator@2 end}}}}; _ -> Acc end end ), Sum_complexity. -spec construct_expression( carpenter@table:set(integer(), complexity_data()), derived_expression() ) -> integer_complexity@expression:expression(). construct_expression(Cache, Derived_expression) -> case Derived_expression of derived_one -> one; {derived_add, Lhs, Rhs} -> {add, construct_expression(Cache, Lhs), construct_expression(Cache, Rhs)}; {derived_multiply, Lhs@1, Rhs@1} -> {multiply, construct_expression(Cache, Lhs@1), construct_expression(Cache, Rhs@1)}; {derived, N} -> Data = complexity_rec(Cache, N), construct_expression(Cache, erlang:element(3, Data)) end. -spec get_expressions_up_to( carpenter@table:set(integer(), complexity_data()), integer() ) -> {ok, list(integer_complexity@expression:expression())} | {error, nil}. get_expressions_up_to(Cache, Integer) -> gleam@bool:guard( Integer =< 0, {error, nil}, fun() -> _pipe = gleam@list:map( get_complexity_data_up_to(Cache, Integer), fun(X) -> construct_expression(Cache, erlang:element(3, X)) end ), {ok, _pipe} end ). -spec get_expression( carpenter@table:set(integer(), complexity_data()), integer() ) -> {ok, integer_complexity@expression:expression()} | {error, nil}. get_expression(Cache, Integer) -> gleam@bool:guard( Integer =:= 0, {error, nil}, fun() -> _pipe = erlang:element( 3, get_complexity_data(Cache, gleam@int:absolute_value(Integer)) ), _pipe@1 = construct_expression(Cache, _pipe), {ok, _pipe@1} end ).