code: def get_index(
      custom_phrases: List[str],
      vocab: defaultdict,
      ban_ngram_global: Set[str],
      min_log_prob: float = -4.0,
      max_phrases_per_ngram: int = 100,
  ) -> Tuple[List[str], Dict[str, List[Tuple[int, int, int, float]]]]:
      """Given a restricted vocabulary of replacements,
      loops through custom phrases,
      generates all possible conversions and creates index.
  
      Args:
          custom_phrases: list of all custom phrases, characters should be split by space,  real space replaced to underscore.
          vocab: n-gram mappings vocabulary - dict {key=original_ngram, value=dict{key=misspelled_ngram, value=frequency}}
          ban_ngram_global: set of banned misspelled n-grams
          min_log_prob: minimum log probability, after which we stop growing this n-gram.
          max_phrases_per_ngram: maximum phrases that we allow to store per one n-gram. N-grams exceeding that quantity get banned.
  
      Returns:
          phrases - list of phrases. Position in this list is used as phrase_id.
          ngram2phrases - resulting index, i.e. dict where key=ngram, value=list of tuples (phrase_id, begin_pos, size, logprob)
      """
  
      ban_ngram_local = (
          set()
      )  # these ngrams are banned only for given custom_phrases
      ngram_to_phrase_and_position = defaultdict(list)
  
      for custom_phrase in custom_phrases:
          inputs = custom_phrase.split(" ")
          begin = 0
          index_keys = [
              {} for _ in inputs
          ]  # key - letter ngram, index - beginning positions in phrase
  
          for begin in range(len(inputs)):
              for end in range(
                  begin + 1, min(len(inputs) + 1, begin + 5)
              ):
                  inp = " ".join(inputs[begin:end])
                  if inp not in vocab:
                      continue
                  for rep in vocab[inp]:
                      lp = math.log(vocab[inp][rep])
  
                      for b in range(
                          max(0, end - 5), end
                      ):  # try to grow previous ngrams with new replacement
                          new_ngrams = {}
                          for ngram in index_keys[b]:
                              lp_prev = index_keys[b][ngram]
                              if (
                                  len(ngram) + len(rep) <= 10
                                  and b + ngram.count(" ") == begin
                              ):
                                  if lp_prev + lp > min_log_prob:
                                      new_ngrams[ngram + rep + " "] = (
                                          lp_prev + lp
                                      )
                          index_keys[b].update(
                              new_ngrams
                          )  #  join two dictionaries
                      # add current replacement as ngram
                      if lp > min_log_prob:
                          index_keys[begin][rep + " "] = lp
  
          for b in range(len(index_keys)):
              for ngram, lp in sorted(
                  index_keys[b].items(),
                  key=lambda item: item[1],
                  reverse=True,
              ):
                  if (
                      ngram in ban_ngram_global
                  ):  # here ngram ends with a space
                      continue
                  real_length = ngram.count(" ")
                  ngram = ngram.replace("+", " ").replace("=", " ")
                  ngram = " ".join(
                      ngram.split()
                  )  # here ngram doesn't end with a space anymore
                  if (
                      ngram + " " in ban_ngram_global
                  ):  # this can happen after deletion of + and =
                      continue
                  if ngram in ban_ngram_local:
                      continue
                  ngram_to_phrase_and_position[ngram].append(
                      (custom_phrase, b, real_length, lp)
                  )
                  if (
                      len(ngram_to_phrase_and_position[ngram])
                      > max_phrases_per_ngram
                  ):
                      ban_ngram_local.add(ngram)
                      del ngram_to_phrase_and_position[ngram]
                      continue
  
      phrases = []  # id to phrase
      phrase2id = {}  # phrase to id
      ngram2phrases = defaultdict(
          list
      )  # ngram to list of tuples (phrase_id, begin, length, logprob)
  
      for ngram in ngram_to_phrase_and_position:
          for phrase, b, length, lp in ngram_to_phrase_and_position[
              ngram
          ]:
              if phrase not in phrase2id:
                  phrases.append(phrase)
                  phrase2id[phrase] = len(phrases) - 1
              ngram2phrases[ngram].append(
                  (phrase2id[phrase], b, length, lp)
              )
  
      return phrases, ngram2phrases
  
imports: import json
  import math
  import random
  import re
  from collections import defaultdict, namedtuple
  from typing import Dict, List, Set, Tuple, Union
  import numpy
  from numba import jit
label_desc: Use image node_id 5 for calling a global function with example usage: get_index(custom_phrases, vocab, ban_ngram_global, min_log_prob, max_phrases_per_ngram) and returns: phrases,  ngram2phrases