<commit_before>/*
import type { Rule, State } from "./rule";

type Rulebook = {
  rules: Array<Rule>,
  next: (s:Array<State>, i:string) => Array<State>,
  followFreeMoves: (s:Array<State>) => Array<State>,
}

type NFA = {
  isAccepting: () => bool,
  read: (i:string) => any,
  readString: (s:string) => any,
};

*/

//@type Array<Rule> => Rulebook
export function rulebook (rules) {
  let next = (states, character) => flatMap(states, state => followRulesFor(state, character)),
      followRulesFor = (state, character) => flatMap(rulesFor(state, character), rule => rule.follow()),
      rulesFor = (state, character) => rules.filter(rule => rule.applies(state, character)),
      followFreeMoves = (states) => {
        let more = next(states, null);
        if (isSubset(more, states)) {
          return states;
        } else {
          return followFreeMoves(states.concat(more));
        }
      };

  return Object.freeze({
    rules,
    next,
    followFreeMoves,
  });
}

//@type (Array<State>, Array<State>, Rulebook) => NFA
export function nfa (startState, acceptStates, rulebook) {
  let currentStates = rulebook.followFreeMoves(startState),
      isAccepting = () => currentStates.filter((state) => acceptStates.indexOf(state) !== -1).length > 0,<commit_msg>Fix `rulebook` to handle correct `rule` (`next` is *not* `Array<State>`)<commit_after>/*
import type { Rule, State } from "./rule";

type Rulebook = {
  rules: Array<Rule>,
  next: (s:Array<State>, i:string) => Array<State>,
  followFreeMoves: (s:Array<State>) => Array<State>,
}

type NFA = {
  isAccepting: () => bool,
  read: (i:string) => any,
  readString: (s:string) => any,
};

*/

//@type Array<Rule> => Rulebook
export function rulebook (rules) {
  let next = (states, character) => toSet(flatMap(states, state => followRulesFor(state, character))),
      followRulesFor = (state, character) => rulesFor(state, character).map(rule => rule.follow()),
      rulesFor = (state, character) => rules.filter(rule => rule.applies(state, character)),
      followFreeMoves = (states) => {
        let more = next(states, null);
        if (isSubset(more, states)) {
          return states;
        } else {
          return followFreeMoves(states.concat(more));
        }
      };

  return Object.freeze({
    rules,
    next,
    followFreeMoves,
  });
}

//@type (Array<State>, Array<State>, Rulebook) => NFA
export function nfa (startState, acceptStates, rulebook) {
  let currentStates = rulebook.followFreeMoves(startState),
      isAccepting = () => currentStates.filter((state) => acceptStates.indexOf(state) !== -1).length > 0,
{'fraction_non_alphanumeric': 0.1690248565965583, 'fraction_numerical': 0.0015296367112810707, 'mean_word_length': 3.6382978723404253, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3790140', 'n_tokens_mistral': 838, 'n_tokens_neox': 832, 'n_words': 195}