/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <stdio.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#include <unordered_map>

#include "se_defines.h"

SE_GLOBALS()

class StateMachine {
  uint32_t state;
  std::unordered_map<uint32_t, std::unordered_map<char, uint32_t>> transitions;
  std::unordered_map<uint32_t, std::string> names;

 public:
  bool valid;

  StateMachine() : state(0), valid(true) {}
  void make_transition(uint32_t state, char input, uint32_t next_state) {
    transitions[state][input] = next_state;
  }

  void make_default(uint32_t state, uint32_t next_state) {
    for (char input : "abcdefghijklmnopqrstuvwxyz") {
      make_transition(state, input, next_state);
    }
  }

  void make_name(uint32_t state, std::string val) {
    names[state] = val;
  }

  std::optional<std::string> step(char x) {
    if (!valid) {
      return std::nullopt;
    }
    auto table = transitions.find(state);
    if (table == transitions.end()) {
      valid = false;
      return std::nullopt;
    }
    auto new_state = table->second.find(x);
    if (new_state == table->second.end()) {
      valid = false;
      return std::nullopt;
    }
    state = new_state->second;
    if (names.find(state) != names.end()) {
      return names[state];
    }
    return std::nullopt;
  }

  std::optional<std::string> run(std::string str) {
    std::optional<std::string> state_str = std::nullopt;
    for (char c : str) {
      state_str = step(c);
      if (!valid) {
        break;
      }
    }
    return state_str;
  }
};

void test_case(std::string str) {
  if (str.size() < 5) {
    return;
  }
  SE_STEP(true);

  StateMachine machine1;

  machine1.make_transition(0, 'g', 0);
  machine1.make_transition(0, 'b', 1);
  machine1.make_transition(1, 'a', 2);
  machine1.make_transition(2, 'r', 0);
  machine1.make_transition(2, 'z', 0);
  machine1.make_name(0, "final");

  auto res = machine1.run(str);
  if (!res.has_value() || *res != "final") {
    return;
  }
  SE_STEP(true);

  StateMachine machine2;

  machine2.make_default(0, 0);
  machine2.make_transition(0, 'r', 1);
  machine2.make_transition(0, 'z', 2);
  machine2.make_transition(0, 'g', 3);
  machine2.make_default(1, 0);
  machine2.make_default(2, 0);
  machine2.make_default(3, 0);
  machine2.make_transition(1, 'b', 4);
  machine2.make_transition(2, 'b', 4);
  machine2.make_transition(3, 'g', 4);

  machine2.make_name(0, "final");
  machine2.make_name(1, "final");
  machine2.make_name(2, "final");
  machine2.make_name(3, "final");

  res = machine2.run(str);

  if (!res.has_value() || *res != "final") {
    return;
  }
  SE_TARGET_STATE(true);
}

int main(int argc, char* argv[]) {
  std::istream* input;
  std::ifstream file;
  if (argc < 2) {
    input = &std::cin; // No file name provided, read from stdin
  } else {
    file.open(argv[1]);
    if (!file) {
      std::cerr << "Could not open the file.\n";
      return 1;
    }
    input = &file; // File opened successfully, read from file
  }
  std::string content(
      (std::istreambuf_iterator<char>(*input)),
      std::istreambuf_iterator<char>());
  test_case(content.c_str());
  return 0;
}
