docs: def normalise_alignments( alignments, data_type=VISLCG3_DATA, **kwargs ):
      ''' Normalises dependency syntactic information in the given list of alignments.
          *) Translates tree node indices from the syntax format (indices starting 
             from 1), to EstNLTK format (indices starting from 0);
          *) Removes redundant information (morphological analyses) and keeps only 
             syntactic information, in the most compact format;
          *) Brings MaltParser and VISLCG3 info into common format;
          
          Expects that the list of alignments contains dicts, where each dict has 
          following attributes (at minimum):
            'start'   -- start index of the word in Text;
            'end'     -- end index of the word in Text;
            'sent_id' -- index of the sentence in Text, starting from 0;
            'parser_out' -- list of analyses from the output of the syntactic parser;
          Assumes that dicts are listed in the order of words appearance in the text;
          ( basically, assumes the output of methods align_CONLL_with_Text() and 
            align_cg3_with_Text() )
          
          Returns the input list (alignments), where old analysis lines ('parser_out') have 
          been replaced with the new compact form of analyses (if keep_old == False), or where 
          old analysis lines ('parser_out') have been replaced the new compact form of analyses,
          and the old analysis lines are preserved under a separate key: 'init_parser_out' (if 
          keep_old == True);
  
          In the compact list of analyses, each item has the following structure:
             [ syntactic_label, index_of_the_head ]
           *) syntactic_label
                  surface syntactic label of the word, e.g. '@SUBJ', '@OBJ', '@ADVL'
           *) index_of_the_head
                  index of the head; -1 if the current token is root;
  
  
          Parameters
          -----------
          alignments : list of items
              A list of dicts, where each item/dict has following attributes:
              'start', 'end', 'sent_id', 'parser_out'
  
          data_type : str
              Type of data in list_of_analysis_lines; Possible types: 'vislcg3'
              (default), and 'conll';
  
          rep_miss_w_dummy : bool
              Optional argument specifying whether missing analyses should be replaced
              with dummy analyses ( in the form ['xxx', link_to_self] ); If False,
              an Exception is raised in case of a missing analysis;
              Default:True
              
          fix_selfrefs : bool
              Optional argument specifying  whether  self-references  in  syntactic 
              dependencies should be fixed;
              A self-reference link is firstly re-oriented as a link to the previous word
              in the sentence, and if the previous word does  not  exist,  the  link  is 
              re-oriented to the next word in the sentence; If the self-linked word is
              the only word in the sentence, it is made the root of the sentence;
              Default:True
          
          fix_out_of_sent : bool
              Optional argument specifying whether references pointing out of the sentence
              (the parent index exceeds the sentence boundaries) should be fixed; 
              The logic used in fixing out-of-sentence links is the same as the logic for 
              fix_selfrefs;
              Default:False
          
          keep_old : bool
              Optional argument specifying  whether the old analysis lines should be 
              preserved after overwriting 'parser_out' with new analysis lines;
              If True, each dict will be augmented with key 'init_parser_out' which
              contains the initial/old analysis lines;
              Default:False
          
          mark_root : bool
              Optional argument specifying whether the root node in the dependency tree 
              (the node pointing to -1) should be assigned the label 'ROOT' (regardless
              its current label).
              This might be required, if one wants to make MaltParser's and VISLCG3 out-
              puts more similar, as MaltParser currently uses 'ROOT' labels, while VISLCG3 
              does not;
              Default:False
  
  
          (Example text: 'Millega pitsat tellida ? Hea küsimus .')
          Example input (VISLC3):
          -----------------------
          {'end': 7, 'sent_id': 0, 'start': 0, 'parser_out': ['\t"mis" Lga P inter rel sg kom @NN> @ADVL #1->3\r']}
          {'end': 14, 'sent_id': 0, 'start': 8, 'parser_out': ['\t"pitsa" Lt S com sg part @OBJ #2->3\r']}
          {'end': 22, 'sent_id': 0, 'start': 15, 'parser_out': ['\t"telli" Lda V main inf @IMV #3->0\r']}
          {'end': 23, 'sent_id': 0, 'start': 22, 'parser_out': ['\t"?" Z Int CLB #4->4\r']}
          {'end': 27, 'sent_id': 1, 'start': 24, 'parser_out': ['\t"hea" L0 A pos sg nom @AN> #1->2\r']}
          {'end': 35, 'sent_id': 1, 'start': 28, 'parser_out': ['\t"küsimus" L0 S com sg nom @SUBJ #2->0\r']}
          {'end': 36, 'sent_id': 1, 'start': 35, 'parser_out': ['\t"." Z Fst CLB #3->3\r']}
  
          Example output:
          ---------------
          {'sent_id': 0, 'start': 0, 'end': 7, 'parser_out': [['@NN>', 2], ['@ADVL', 2]]}
          {'sent_id': 0, 'start': 8, 'end': 14, 'parser_out': [['@OBJ', 2]]}
          {'sent_id': 0, 'start': 15, 'end': 22, 'parser_out': [['@IMV', -1]]}
          {'sent_id': 0, 'start': 22, 'end': 23, 'parser_out': [['xxx', 2]]}
          {'sent_id': 1, 'start': 24, 'end': 27, 'parser_out': [['@AN>', 1]]}
          {'sent_id': 1, 'start': 28, 'end': 35, 'parser_out': [['@SUBJ', -1]]}
          {'sent_id': 1, 'start': 35, 'end': 36, 'parser_out': [['xxx', 1]]}
  
      '''
      if not isinstance( alignments, list ):
          raise Exception('(!) Unexpected type of input argument! Expected a list of strings.')
      if data_type.lower() == VISLCG3_DATA:
         data_type = VISLCG3_DATA
      elif data_type.lower() == CONLL_DATA:
         data_type = CONLL_DATA
      else: 
         raise Exception('(!) Unexpected type of data: ', data_type)
      keep_old         = False
      rep_miss_w_dummy = True
      mark_root        = False
      fix_selfrefs     = True
      fix_out_of_sent  = False
      for argName, argVal in kwargs.items():
          if argName in ['selfrefs', 'fix_selfrefs'] and argVal in [True, False]:
             #  Fix self-references
             fix_selfrefs = argVal
          if argName in ['keep_old'] and argVal in [True, False]:
             #  After the normalisation, keep also the original analyses;
             keep_old = argVal
          if argName in ['rep_miss_w_dummy', 'rep_miss'] and argVal in [True, False]:
             #  Replace missing analyses with dummy analyses;
             rep_miss_w_dummy = argVal
          if argName in ['mark_root', 'root'] and argVal in [True, False]:
             #  Mark the root node in the syntactic tree with the label ROOT;
             mark_root = argVal
          if argName in ['fix_out_of_sent']:
             #  Fix links pointing out of the sentence;
             fix_out_of_sent = bool(argVal)
      # Iterate over the alignments and normalise information
      prev_sent_id = -1
      wordID       = 0
      sentStart    = -1
      for i in range(len(alignments)):
          alignment = alignments[i]
          if prev_sent_id != alignment[SENT_ID]:
              # Detect and fix out-of-the-sentence links in the previous sentence (if required)
              if fix_out_of_sent and sentStart > -1:
                  _fix_out_of_sentence_links( alignments, sentStart, i )
              # Start of a new sentence: reset word id
              wordID = 0
              sentStart = i
          # 1) Extract syntactic information
          foundRelations = []
          if data_type == VISLCG3_DATA:
              # *****************  VISLCG3 format
              for line in alignment[PARSER_OUT]:
                  # Extract info from VISLCG3 format analysis:
                  sfuncs  = pat_cg3_surface_rel.findall( line )
                  deprels = pat_cg3_dep_rel.findall( line )
                  # If sfuncs is empty, generate an empty syntactic function (e.g. for 
                  # punctuation)
                  sfuncs = ['xxx'] if not sfuncs else sfuncs
                  # Generate all pairs of labels vs dependency
                  for func in sfuncs:
                      for (relS,relT) in deprels:
                          relS = int(relS)-1
                          relT = int(relT)-1
                          foundRelations.append( [func, relT] )
          elif data_type == CONLL_DATA:
              # *****************  CONLL format
              for line in alignment[PARSER_OUT]:
                  parts = line.split('\t')
                  if len(parts) != 10:
                      raise Exception('(!) Unexpected line format for CONLL data:', line)
                  relT = int( parts[6] ) - 1
                  func = parts[7]
                  foundRelations.append( [func, relT] )
          # Handle missing relations (VISLCG3 specific problem)
          if not foundRelations:
              # If no alignments were found (probably due to an error in analysis)
              if rep_miss_w_dummy:
                  # Replace missing analysis with a dummy analysis, with dep link 
                  # pointing to self;
                  foundRelations.append( ['xxx', wordID] )
              else:
                  raise Exception('(!) Analysis missing for the word nr.', alignment[0])
          # Fix self references ( if requested )
          if fix_selfrefs:
              for r in range(len(foundRelations)):
                  if foundRelations[r][1] == wordID:
                      # Make it to point to the previous word in the sentence,
                      # and if the previous one does not exist, make it to point
                      # to the next word;
                      foundRelations[r][1] = \
                          wordID-1 if wordID-1 > -1 else wordID+1
                      # If the self-linked token is the only token in the sentence, 
                      # mark it as the root of the sentence:
                      if wordID-1 == -1 and (i+1 == len(alignments) or \
                         alignments[i][SENT_ID] != alignments[i+1][SENT_ID]):
                          foundRelations[r][1] = -1
          # Mark the root node in the syntactic tree with the label ROOT ( if requested )
          if mark_root:
              for r in range(len(foundRelations)):
                  if foundRelations[r][1] == -1:
                      foundRelations[r][0] = 'ROOT'
          # 2) Replace existing syntactic info with more compact info
          if not keep_old:
              # Overwrite old info
              alignment[PARSER_OUT] = foundRelations
          else: 
              # or preserve the initial information, and add new compact information
              alignment[INIT_PARSER_OUT] = alignment[PARSER_OUT]
              alignment[PARSER_OUT]      = foundRelations
          alignments[i] = alignment
          prev_sent_id = alignment[SENT_ID]
          # Increase word id 
          wordID += 1
      # Detect and fix out-of-the-sentence links in the last sentence (if required)
      if fix_out_of_sent and sentStart > -1:
          _fix_out_of_sentence_links( alignments, sentStart, len(alignments) )
      return alignments