text: import numpy as np
  
  
  def preprocess(text):
      text = text.lower()
      text = text.replace(".", " .")
      words = text.split(" ")
  
      word_to_id = {}
      id_to_word = {}
      for word in words:
          if word not in word_to_id:
              new_id = len(word_to_id)
              word_to_id[word] = new_id
              id_to_word[new_id] = word
  
      corpus = np.array([word_to_id[word] for word in words])
      return corpus, word_to_id, id_to_word
  
  
  def create_co_matrix(corpus, vocab_size, window_size=1):
      corpus_size = len(corpus)
      co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32)
  
      for idx, word_id in enumerate(corpus):
          for i in range(1, window_size + 1):
              left_idx = idx - i
              right_idx = idx + i
  
              if left_idx >= 0:
                  left_word_id = corpus[left_idx]
                  co_matrix[word_id, left_word_id] += 1
  
              if right_idx < corpus_size:
                  right_word_id = corpus[right_idx]
                  co_matrix[word_id, right_word_id] += 1
      return co_matrix
  
  
  def cos_similarity(x, y, eps=1e-8):
      nx = x / np.sqrt(np.sum(x**2) + eps)
      ny = y / np.sqrt(np.sum(y**2) + eps)
      return nx @ ny
  
  
  def most_similar(query, word_to_id, id_to_word, word_matrix, top=5):
      if query not in word_to_id:
          print(f"{query} is not found")
          return
  
      print("\n[query]" + query)
      query_id = word_to_id[query]
      query_vec = word_matrix[query_id]
  
      vocab_size = len(id_to_word)
      similarity = np.zeros(vocab_size)
      for i in range(vocab_size):
          similarity[i] = cos_similarity(word_matrix[i], query_vec)
  
      count = 0
      for i in (-1 * similarity).argsort():
          if id_to_word[i] == query:
              continue
          print(f" {id_to_word[i]}: {similarity[i]}")
          count += 1
          if count >= top:
              return
  
  
  def ppmi(C, verbose=False, eps=1e-8):
      M = np.zeros_like(C, dtype=np.float32)
      N = np.sum(C)
      S = np.sum(C, axis=0)
      total = C.shape[0] * C.shape[1]
      cnt = 0
      for i in range(C.shape[0]):
          for j in range(C.shape[1]):
              pmi = np.log2(C[i, j] * N / (S[j] * S[i]) + eps)
              M[i, j] = max(0, pmi)
  
              if verbose:
                  cnt += 1
                  if cnt % (total / 100) == 0:
                      print(f"{100 * cnt / total:.1f}% done")
      return M
  
  
  def create_context_target(corpus, window_size=1):
      target = corpus[window_size:-window_size]
      contexts = []
  
      for idx in range(window_size, len(corpus) - window_size):
          cs = []
          for t in range(-window_size, window_size + 1):
              if t == 0:
                  continue
              cs.append(corpus[idx + t])
          contexts.append(cs)
      return np.array(contexts), np.array(target)
  
  
  def convert_one_hot(target, vocab_size):
      if target.ndim == 1:
          one_hot = np.zeros([target.shape[0], vocab_size], dtype=np.int32)
          for i, idx in enumerate(target):
              one_hot[i, idx] = 1
      elif target.ndim == 2:
          one_hot = np.zeros([target.shape[0], target.shape[1], vocab_size],
                             np.int32)
          for i, v in enumerate(target):
              for j, idx in enumerate(v):
                  one_hot[i, j, idx] = 1
  
      return one_hot
  