% @doc % sentence-fun <-> truth table logic -module(wfc_sftt). -vsn("0.2.0"). -export_type([ sf/0, tt/0 ]). -export([ ttfun_to_tt/1, ttfun_to_sf/1, arity/1, tt/1, sf/1, sf_to_tt/1, tt_to_sf/1, appl_ttf/2, appl/2, bfls/1, bfl/2 ]). -type bit() :: 0 | 1. -opaque sf() :: {sf, wfc_bm:bm()}. -opaque tt() :: {tt, wfc_bm:bm()}. -spec ttfun_to_tt(fun()) -> tt(). ttfun_to_tt(Fun) -> {arity, Arity} = erlang:fun_info(Fun, arity), Argses = bfls(Arity), OutputCol = [erlang:apply(Fun, Args) || Args <- Argses], {tt, wfc_bm:col(OutputCol)}. -spec ttfun_to_sf(fun()) -> sf(). ttfun_to_sf(Fun) -> tt_to_sf(ttfun_to_tt(Fun)). -spec arity(sf() | tt()) -> pos_integer(). arity({sf, BM}) -> matrix_arity(BM); arity({tt, BM}) -> matrix_arity(BM). matrix_arity(Matrix) -> {rc, Height, _} = wfc_bm:shape(Matrix), log2(Height). log2(N) when N > 1 -> 1 + log2(N div 2); log2(1) -> 0. -spec sf_to_tt(sf()) -> tt(). sf_to_tt({sf, SFBM}) -> {tt, apply_bmt(SFBM)}. -spec tt_to_sf(tt()) -> sf(). tt_to_sf({tt, SFBM}) -> {sf, apply_bmt(SFBM)}. apply_bmt(Matrix) -> BMT = wfc_bm:bmt(matrix_arity(Matrix)), wfc_bm:mul(BMT, Matrix). tt(List) -> {tt, wfc_bm:col(List)}. sf(List) -> {sf, wfc_bm:col(List)}. -spec appl_ttf(fun(), [wfc:sentence()]) -> wfc:sentence(). appl_ttf(Fun, Sentences) -> SF = ttfun_to_sf(Fun), appl(SF, Sentences). -spec appl(sf() | tt(), [wfc:sentence()]) -> wfc:sentence(). appl(TT = {tt, _}, Sentences) -> appl(tt_to_sf(TT), Sentences); appl(SF = {sf, SFBM}, Sentences) -> Arity = arity(SF), Arity = length(Sentences), % [0, 0, 0], [1, 0, 0], ... BitFlagLists = bfls(Arity), io:format("bfls: ~p~n", [BitFlagLists]), % an SFBM is a Nx1 matrix where N = two_to_the(Arity) % hacky but works [Row] = wfc_bm:to_list(wfc_bm:transpose(SFBM)), io:format("row: ~p~n", [Row]), % filter by whether or not we're including Included = filter_included(BitFlagLists, Row), io:format("included: ~p~n", [Included]), AlmostSummands = inseminate(Included, Sentences), io:format("Almost: ~p~n", [AlmostSummands]), collapse(AlmostSummands). filter_included([_ | Rest], [0 | Rest2]) -> filter_included(Rest, Rest2); filter_included([Item | Rest], [1 | Rest2]) -> [Item | filter_included(Rest, Rest2)]; filter_included([], []) -> []. -spec collapse([[wfc:sentence()]]) -> wfc:sentence(). collapse(AlmostSummands) -> Summands = lists:map(fun(Args) -> {ok,X} = wfc:mul(Args), X end, AlmostSummands), {ok, Y} = wfc:add(Summands), Y. -spec inseminate(BFLs :: [[bit()]], Sentences :: [wfc:sentence()]) -> [[wfc:sentence()]]. inseminate(BFLs, Sentences) -> lists:map(fun(BFL) -> inseminate2(BFL, Sentences) end, BFLs). -spec inseminate2(BFL :: [bit()], Sentences :: [wfc:sentence()]) -> [wfc:sentence()]. inseminate2(BFL, Sentences) -> lists:zipwith(fun pair/2, BFL, Sentences). pair(0, _) -> wfc_sentence:one(); pair(1, S) -> S. -spec bfls(Arity :: non_neg_integer()) -> [[bit()]]. % @doc get all bit-flag lists for 0..(2^Arity - 1) bfls(Arity) when Arity >= 0 -> [bfl(Arity, N) || N <- lists:seq(0, two_to_the(Arity) - 1)]. two_to_the(N) -> 1 bsl N. -spec bfl(Length :: pos_integer(), N :: non_neg_integer()) -> [bit()]. % @doc % get the little-endian representation of N as binary, as a % list of bits. bfl(Length, N) when Length > 0, N >= 0 -> %% hacky but logically straightforward bfl2(Length, lists:reverse(integer_to_list(N, 2))). %% we're chopping digits off the end here bfl2(Length, Bits_Str) when Length =< length(Bits_Str) -> bfl3(take(Length, Bits_Str)); bfl2(Length, Bits_Str) when Length > length(Bits_Str) -> % make more zeros than needed because about to circumcise % them. Zeros = [$0 || _ <- lists:seq(1, Length)], bfl2(Length, Bits_Str ++ Zeros). take(0, _) -> []; take(N, [Item | Rest]) when N >= 1 -> [Item | take(N-1, Rest)]. bfl3(BitsText) -> lists:map(fun unfuck_char/1, BitsText). unfuck_char($0) -> 0; unfuck_char($1) -> 1.