% Програма моделювання одновимірних клітинних автоматів
%% encoding: utf8
get_bit(Index, Bits) when is_bitstring(Bits), is_integer(Index), Index >= 0, Index < bit_size(Bits) ->
  <<_:Index, Val:1, _/bitstring>> = Bits,
  Val.
get_state(Index, Bits) when is_bitstring(Bits), is_integer(Index), Index >= 0, Index < bit_size(Bits) ->
  get_bit(Index, Bits);
get_state(Index, Bits) when is_bitstring(Bits), is_integer(Index), (Index =:= -1) orelse (Index =:= bit_size(Bits)) -> 0.
get_state_wide(Index, Bits) when is_bitstring(Bits), is_integer(Index), bit_size(Bits) > 0, Index >= 0, Index < bit_size(Bits) -> 
  get_state_wide(Index, Bits, 0, 0).
get_state_wide(Index, Bits, 3, Acc) when is_bitstring(Bits), is_integer(Index), bit_size(Bits) > 0, Index >= 0, Index < bit_size(Bits) -> Acc;
get_state_wide(Index, Bits, Rel, Acc) when is_bitstring(Bits), is_integer(Index), bit_size(Bits) > 0, Index >= 0, Index < bit_size(Bits) -> 
  get_state_wide(Index, Bits, Rel + 1, Acc + get_state(Index + Rel - 1, Bits) * trunc(math:pow(2, (2 - Rel)))).
next_state(WideState, Rule) when is_integer(WideState), is_integer(Rule), WideState >= 0, WideState < 8, Rule < 256, Rule >= 0 -> 
  get_bit(7-WideState, <<Rule:8>>);
next_state(Bits, Rule) when is_bitstring(Bits), is_integer(Rule), Rule < 256, Rule >= 0, bit_size(Bits) > 0 ->
  << << (next_state(get_state_wide(X, Bits), Rule)):1 >> || X <- lists:seq(0, bit_size(Bits)-1) >>.
gen_state(Bits, Rule, Count) when is_bitstring(Bits), is_integer(Rule), is_integer(Count), Rule < 256, Rule >= 0, bit_size(Bits) > 0, Count > 0 ->
  gen_state_loop(Rule, Count-1, [Bits]).
gen_state_loop(Rule, 0, Acc) when is_integer(Rule), is_list(Acc), Rule < 256, Rule >= 0 -> lists:reverse(Acc);
gen_state_loop(Rule, Reminder, Acc)
  when is_integer(Rule), is_list(Acc), is_integer(Reminder), Rule < 256, Rule >= 0, Reminder > 0 ->
  [Prev|_] = Acc,
  gen_state_loop(Rule, Reminder-1, [next_state(Prev, Rule)|Acc]).
write_state(Bits) when is_bitstring(Bits), bit_size(Bits) > 0 ->
  Symbols = ".O",
  io:fwrite([lists:nth(get_bit(X, Bits) + 1, Symbols) || X <- lists:seq(0, bit_size(Bits) - 1)] ++ "~n");
write_state([]) -> ok;
write_state([H|T]) -> write_state(H), write_state(T).
read_rule() ->
  Input = io:fread("Введіть правило (ціле число від 0 до 255 включно): ", "~d"),
  case Input of
      {ok, [Rule]} when Rule < 256, Rule >= 0 -> Rule;
      _ -> io:fwrite("Помилка вводу. Спробуйте ще раз.~n~n"), read_rule()
  end.
read_gens() ->
  Input = io:fread("Введіть кількість поколінь (ціле число від 1 до 80 включно): ", "~d"),
  case Input of
      {ok, [Gens]} when Gens =< 80, Gens > 0 -> Gens;
      _ -> io:fwrite("Помилка вводу. Спробуйте ще раз.~n~n"), read_gens()
  end.
  
read_state() ->
  io:fwrite("Введіть початковий стан (крапка означає порожню кілтинку, а латинська велика літера Х означає заповнену клітинку).~n"),
  Input = lists:droplast(io:get_line("")),
  case Input of
      "" -> io:fwrite("Ввід порожній. Спробуйте ще раз.~n~n"), read_state();
      _ ->
        Valid = lists:all(fun(X) -> X =:= $. orelse X =:= $X end, Input),
        if Valid -> << << (if I =:= $X -> 1; true -> 0 end):1 >> || I <- Input >>; true -> io:fwrite("Стрічка містить зайві символи. Спробуйте ще раз.~n~n"), read_state() end
  end.
    
main(_) -> 
  io:setopts([{encoding, unicode}]),
  io:fwrite("Програма моделювання одновимірних клітинних автоматів~n~n"),
  State = read_state(),
  Gens = read_gens(),
  Rule = read_rule(),
  Picture = gen_state(State, Rule, Gens),
  write_state(Picture),
  io:nl().