docs: def group(TableName,DestinationTableName=QUERY_BUFFER,ParameterNames=None,GroupParameterNames=None,Output=True):
      """
      INPUT PARAMETERS: 
          TableName:                name of source table          (required)
          DestinationTableName:     name of resulting table       (optional)
          ParameterNames:       list of parameters or expressions to take       (optional)
          GroupParameterNames:  list of parameters or expressions to group by   (optional)
          Accending:       sort in ascending (True) or descending (False) order (optional)
          Output:   enable (True) or suppress (False) text output (optional)
      OUTPUT PARAMETERS: 
          none
      ---
      DESCRIPTION:
          none
      ---
      EXAMPLE OF USAGE:
          group('sampletab',ParameterNames=('p1',('sum','p2')),GroupParameterNames=('p1'))
          ... makes grouping by p1,p2. For each group it calculates sum of p2 values.
      ---
      """
      # Implements such functions as:
      # count,sum,avg,min,max,ssq etc...
      # 1) ParameterNames can contain group functions
      # 2) GroupParameterNames can't contain group functions
      # 3) If ParameterNames contains parameters defined by LET directive,
      #    it IS visible in the sub-context of GroupParameterNames
      # 4) Parameters defined in GroupParameterNames are NOT visible in ParameterNames
      # 5) ParameterNames variable represents the structure of the resulting table/collection
      # 6) GroupParameterNames can contain either par_names or expressions with par_names
      # Clear old GROUP_INDEX value
      clearGroupIndex()
      # Consistency check
      if TableName == DestinationTableName:
         raise Exception('TableName and DestinationTableName must be different')
      #if not ParameterNames: ParameterNames=LOCAL_TABLE_CACHE[TableName]['header']['order']
      # Prepare the new DestinationTable
      RowObjectDefault = getDefaultRowObject(TableName)
      VarDictionary = getVarDictionary(RowObjectDefault)
      ContextFormat = getContextFormat(RowObjectDefault)
      RowObjectDefaultNew = newRowObject(ParameterNames,RowObjectDefault,VarDictionary,ContextFormat)
      dropTable(DestinationTableName) # redundant
      createTable(DestinationTableName,RowObjectDefaultNew)
      # Loop through rows of source Table
      # On each iteration group functions update GROUP_INDEX (see description above)
      number_of_rows = LOCAL_TABLE_CACHE[TableName]['header']['number_of_rows']   
      # STAGE 1: CREATE GROUPS
      print('LOOP:')
      for RowID in range(0,number_of_rows):
          print('--------------------------------')
          print('RowID='+str(RowID))
          RowObject = getRowObject(RowID,TableName) # RowObject from source table
          VarDictionary = getVarDictionary(RowObject)
          print('VarDictionary='+str(VarDictionary))
          # This is a trick which makes evaluateExpression function
          #   not consider first expression as an operation
          GroupParameterNames_ = ['LIST'] + list(GroupParameterNames)
          GroupIndexKey = evaluateExpression(GroupParameterNames_,VarDictionary)
          # List is an unhashable type in Python!
          GroupIndexKey = tuple(GroupIndexKey)       
          initializeGroup(GroupIndexKey)
          print('GROUP_INDEX='+str(GROUP_INDEX))
          ContextFormat = getContextFormat(RowObject)
          RowObjectNew = newRowObject(ParameterNames,RowObject,VarDictionary,ContextFormat,GroupIndexKey)
          RowIDGroup = GROUP_INDEX[GroupIndexKey]['ROWID']
          setRowObject(RowIDGroup,RowObjectNew,DestinationTableName)
      # Output result if required
      if Output and DestinationTableName==QUERY_BUFFER:
         outputTable(DestinationTableName,File=File)