code: def evaluate(gold_ud, system_ud):
      class Score:
          def __init__(
              self,
              gold_total,
              system_total,
              correct,
              aligned_total=None,
          ):
              self.correct = correct
              self.gold_total = gold_total
              self.system_total = system_total
              self.aligned_total = aligned_total
              self.precision = (
                  correct / system_total if system_total else 0.0
              )
              self.recall = correct / gold_total if gold_total else 0.0
              self.f1 = (
                  2 * correct / (system_total + gold_total)
                  if system_total + gold_total
                  else 0.0
              )
              self.aligned_accuracy = (
                  correct / aligned_total
                  if aligned_total
                  else aligned_total
              )
  
      class AlignmentWord:
          def __init__(self, gold_word, system_word):
              self.gold_word = gold_word
              self.system_word = system_word
  
      class Alignment:
          def __init__(self, gold_words, system_words):
              self.gold_words = gold_words
              self.system_words = system_words
              self.matched_words = []
              self.matched_words_map = {}
  
          def append_aligned_words(self, gold_word, system_word):
              self.matched_words.append(
                  AlignmentWord(gold_word, system_word)
              )
              self.matched_words_map[system_word] = gold_word
  
      def spans_score(gold_spans, system_spans):
          correct, gi, si = 0, 0, 0
          while gi < len(gold_spans) and si < len(system_spans):
              if system_spans[si].start < gold_spans[gi].start:
                  si += 1
              elif gold_spans[gi].start < system_spans[si].start:
                  gi += 1
              else:
                  correct += gold_spans[gi].end == system_spans[si].end
                  si += 1
                  gi += 1
  
          return Score(len(gold_spans), len(system_spans), correct)
  
      def alignment_score(alignment, key_fn=None, filter_fn=None):
          if filter_fn is not None:
              gold = sum(
                  1 for gold in alignment.gold_words if filter_fn(gold)
              )
              system = sum(
                  1
                  for system in alignment.system_words
                  if filter_fn(system)
              )
              aligned = sum(
                  1
                  for word in alignment.matched_words
                  if filter_fn(word.gold_word)
              )
          else:
              gold = len(alignment.gold_words)
              system = len(alignment.system_words)
              aligned = len(alignment.matched_words)
  
          if key_fn is None:
              # Return score for whole aligned words
              return Score(gold, system, aligned)
  
          def gold_aligned_gold(word):
              return word
  
          def gold_aligned_system(word):
              return (
                  alignment.matched_words_map.get(word, "NotAligned")
                  if word is not None
                  else None
              )
  
          correct = 0
          for words in alignment.matched_words:
              if filter_fn is None or filter_fn(words.gold_word):
                  if key_fn(
                      words.gold_word, gold_aligned_gold
                  ) == key_fn(words.system_word, gold_aligned_system):
                      correct += 1
  
          return Score(gold, system, correct, aligned)
  
      def enhanced_alignment_score(alignment, EULAS):
          # count all matching enhanced deprels in gold, system GB
          # gold and system = sum of gold and predicted deps
          # parents are pointers to word object, make sure to compare system parent with aligned word in gold in cases where
          # tokenization introduces mismatches in number of words per sentence.
          gold = 0
          for gold_word in alignment.gold_words:
              gold += len(gold_word.columns[DEPS])
          system = 0
          for system_word in alignment.system_words:
              system += len(system_word.columns[DEPS])
          correct = 0
          for words in alignment.matched_words:
              gold_deps = words.gold_word.columns[DEPS]
              system_deps = words.system_word.columns[DEPS]
              for parent, dep in gold_deps:
                  eulas_dep = [d.split(":")[0] for d in dep]
                  for sparent, sdep in system_deps:
                      eulas_sdep = [d.split(":")[0] for d in sdep]
                      if dep == sdep or (
                          eulas_dep == eulas_sdep and EULAS
                      ):
                          if parent == alignment.matched_words_map.get(
                              sparent, "NotAligned"
                          ):
                              correct += 1
                          elif (
                              parent == 0 and sparent == 0
                          ):  # cases where parent is root
                              correct += 1
          return Score(gold, system, correct)
  
      def beyond_end(words, i, multiword_span_end):
          if i >= len(words):
              return True
          if words[i].is_multiword:
              return words[i].span.start >= multiword_span_end
          return words[i].span.end > multiword_span_end
  
      def extend_end(word, multiword_span_end):
          if word.is_multiword and word.span.end > multiword_span_end:
              return word.span.end
          return multiword_span_end
  
      def find_multiword_span(gold_words, system_words, gi, si):
          # We know gold_words[gi].is_multiword or system_words[si].is_multiword.
          # Find the start of the multiword span (gs, ss), so the multiword span is minimal.
          # Initialize multiword_span_end characters index.
          if gold_words[gi].is_multiword:
              multiword_span_end = gold_words[gi].span.end
              if (
                  not system_words[si].is_multiword
                  and system_words[si].span.start
                  < gold_words[gi].span.start
              ):
                  si += 1
          else:  # if system_words[si].is_multiword
              multiword_span_end = system_words[si].span.end
              if (
                  not gold_words[gi].is_multiword
                  and gold_words[gi].span.start
                  < system_words[si].span.start
              ):
                  gi += 1
          gs, ss = gi, si
  
          # Find the end of the multiword span
          # (so both gi and si are pointing to the word following the multiword span end).
          while not beyond_end(
              gold_words, gi, multiword_span_end
          ) or not beyond_end(system_words, si, multiword_span_end):
              if gi < len(gold_words) and (
                  si >= len(system_words)
                  or gold_words[gi].span.start
                  <= system_words[si].span.start
              ):
                  multiword_span_end = extend_end(
                      gold_words[gi], multiword_span_end
                  )
                  gi += 1
              else:
                  multiword_span_end = extend_end(
                      system_words[si], multiword_span_end
                  )
                  si += 1
          return gs, ss, gi, si
  
      def compute_lcs(gold_words, system_words, gi, si, gs, ss):
          lcs = [[0] * (si - ss) for i in range(gi - gs)]
          for g in reversed(range(gi - gs)):
              for s in reversed(range(si - ss)):
                  if (
                      gold_words[gs + g].columns[FORM].lower()
                      == system_words[ss + s].columns[FORM].lower()
                  ):
                      lcs[g][s] = 1 + (
                          lcs[g + 1][s + 1]
                          if g + 1 < gi - gs and s + 1 < si - ss
                          else 0
                      )
                  lcs[g][s] = max(
                      lcs[g][s], lcs[g + 1][s] if g + 1 < gi - gs else 0
                  )
                  lcs[g][s] = max(
                      lcs[g][s], lcs[g][s + 1] if s + 1 < si - ss else 0
                  )
          return lcs
  
      def align_words(gold_words, system_words):
          alignment = Alignment(gold_words, system_words)
  
          gi, si = 0, 0
          while gi < len(gold_words) and si < len(system_words):
              if (
                  gold_words[gi].is_multiword
                  or system_words[si].is_multiword
              ):
                  # A: Multi-word tokens => align via LCS within the whole "multiword span".
                  gs, ss, gi, si = find_multiword_span(
                      gold_words, system_words, gi, si
                  )
  
                  if si > ss and gi > gs:
                      lcs = compute_lcs(
                          gold_words, system_words, gi, si, gs, ss
                      )
  
                      # Store aligned words
                      s, g = 0, 0
                      while g < gi - gs and s < si - ss:
                          if (
                              gold_words[gs + g].columns[FORM].lower()
                              == system_words[ss + s]
                              .columns[FORM]
                              .lower()
                          ):
                              alignment.append_aligned_words(
                                  gold_words[gs + g],
                                  system_words[ss + s],
                              )
                              g += 1
                              s += 1
                          elif lcs[g][s] == (
                              lcs[g + 1][s] if g + 1 < gi - gs else 0
                          ):
                              g += 1
                          else:
                              s += 1
              else:
                  # B: No multi-word token => align according to spans.
                  if (
                      gold_words[gi].span.start,
                      gold_words[gi].span.end,
                  ) == (
                      system_words[si].span.start,
                      system_words[si].span.end,
                  ):
                      alignment.append_aligned_words(
                          gold_words[gi], system_words[si]
                      )
                      gi += 1
                      si += 1
                  elif (
                      gold_words[gi].span.start
                      <= system_words[si].span.start
                  ):
                      gi += 1
                  else:
                      si += 1
  
          return alignment
  
      # Check that the underlying character sequences do match.
      if gold_ud.characters != system_ud.characters:
          index = 0
          while (
              index < len(gold_ud.characters)
              and index < len(system_ud.characters)
              and gold_ud.characters[index]
              == system_ud.characters[index]
          ):
              index += 1
  
          raise UDError(
              "The concatenation of tokens in gold file and in system file differ!\n"
              + "First 20 differing characters in gold file: '{}' and system file: '{}'".format(
                  "".join(
                      map(
                          _encode,
                          gold_ud.characters[index : index + 20],
                      )
                  ),
                  "".join(
                      map(
                          _encode,
                          system_ud.characters[index : index + 20],
                      )
                  ),
              )
          )
  
      # Align words
      alignment = align_words(gold_ud.words, system_ud.words)
  
      # Compute the F1-scores
      return {
          "Tokens": spans_score(gold_ud.tokens, system_ud.tokens),
          "Sentences": spans_score(
              gold_ud.sentences, system_ud.sentences
          ),
          "Words": alignment_score(alignment),
          "UPOS": alignment_score(
              alignment, lambda w, _: w.columns[UPOS]
          ),
          "XPOS": alignment_score(
              alignment, lambda w, _: w.columns[XPOS]
          ),
          "UFeats": alignment_score(
              alignment, lambda w, _: w.columns[FEATS]
          ),
          "AllTags": alignment_score(
              alignment,
              lambda w, _: (
                  w.columns[UPOS],
                  w.columns[XPOS],
                  w.columns[FEATS],
              ),
          ),
          "Lemmas": alignment_score(
              alignment,
              lambda w, ga: w.columns[LEMMA]
              if ga(w).columns[LEMMA] != "_"
              else "_",
          ),
          "UAS": alignment_score(alignment, lambda w, ga: ga(w.parent)),
          "LAS": alignment_score(
              alignment, lambda w, ga: (ga(w.parent), w.columns[DEPREL])
          ),
          "ELAS": enhanced_alignment_score(alignment, 0),
          "EULAS": enhanced_alignment_score(alignment, 1),
          "CLAS": alignment_score(
              alignment,
              lambda w, ga: (ga(w.parent), w.columns[DEPREL]),
              filter_fn=lambda w: w.is_content_deprel,
          ),
          "MLAS": alignment_score(
              alignment,
              lambda w, ga: (
                  ga(w.parent),
                  w.columns[DEPREL],
                  w.columns[UPOS],
                  w.columns[FEATS],
                  [
                      (
                          ga(c),
                          c.columns[DEPREL],
                          c.columns[UPOS],
                          c.columns[FEATS],
                      )
                      for c in w.functional_children
                  ],
              ),
              filter_fn=lambda w: w.is_content_deprel,
          ),
          "BLEX": alignment_score(
              alignment,
              lambda w, ga: (
                  ga(w.parent),
                  w.columns[DEPREL],
                  w.columns[LEMMA]
                  if ga(w).columns[LEMMA] != "_"
                  else "_",
              ),
              filter_fn=lambda w: w.is_content_deprel,
          ),
      }
  
imports: from __future__ import division
  from __future__ import print_function
  import argparse
  import io
  import sys
  import unicodedata
  import unittest
label_desc: Use image node_id 5 for calling a global function with example usage: evaluate(gold_ud, system_ud) and returns: dict, Score, Score, Score, unknown, multiword_span_end, gs,  ss,  gi,  si, lcs, alignment, Score, word, unknown, True, unknown, word