% @doc a word is an ordset of ltrs % % This mathematically is a cluster of letters; so in "A + B + AB", this is one % of the summands. % % multiplication is implied in a word % % empty word is 1 % % anything times itself equals itself, so duplication is ignored % % multiplication = inclusive union % % operations assume all inputs are valid -module(wfc_word). -export_type([ word/0 ]). -export([ %% constructors one/0, validate/1, from_list/1, to_list/1, %% ops mul/2, mul/1 ]). -opaque word() :: {w, ordsets:ordset(wfc_ltr:ltr())}. %%---------------------------- %% constructor %%---------------------------- -spec one() -> word(). one() -> {w, []}. -spec validate(Word) -> Result when Word :: word(), Result :: ok | {error, Reason :: string()}. % @doc % check each letter in the word for validity % % also check that word shape is valid % @end validate(W = {w, Letters}) -> case from_list(Letters) of {ok, Result} when Result =:= W -> ok; Error -> Error end; validate(X) -> {error, wfc_utils:str("wfc_word:validate: malformed word: ~tp", [X])}. -spec to_list(word()) -> Result when Result :: {ok, [wfc_ltr:ltr()]} | {error, string()}. to_list({w, Ltrs}) -> {ok, Ltrs}; to_list(Bad) -> {error, wfc_utils:str("wfc_word:to_list: bad letter: ~tp", [Bad])}. -spec from_list(Ltrs) -> Result when Ltrs :: list(wfc_ltr:ltr()), Result :: {ok, word()} | {error, Reason :: string()}. from_list(Chars) -> from_list(ordsets:from_list(Chars), []). %% validate each letter from_list([Ltr | Rest], Acc) -> case wfc_ltr:validate(Ltr) of ok -> from_list(Rest, [Ltr | Acc]); Error -> Error end; % done, all good from_list([], Acc) -> {ok, {w, lists:reverse(Acc)}}. %%---------------------------- %% ops %%---------------------------- -spec mul(word(), word()) -> word(). % @doc product of two words % % assumes the words are valid mul({w, X}, {w, Y}) -> {w, ordsets:union(X, Y)}. -spec mul([word()]) -> word(). % @doc multiply a list of words together mul([Word | Rest]) -> mul(Word, mul(Rest)); mul([]) -> one().