(********************************************************************)
(*                                                                  *)
(*  arr_act.s7i   Generate code for actions of the type array.      *)
(*  Copyright (C) 1990 - 1994, 2004 - 2015, 2018  Thomas Mertes     *)
(*  Copyright (C) 2020 - 2022  Thomas Mertes                        *)
(*                                                                  *)
(*  This file is part of the Seed7 compiler.                        *)
(*                                                                  *)
(*  This program is free software; you can redistribute it and/or   *)
(*  modify it under the terms of the GNU General Public License as  *)
(*  published by the Free Software Foundation; either version 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  This program is distributed in the hope that it will be useful, *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of  *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *)
(*  GNU General Public License for more details.                    *)
(*                                                                  *)
(*  You should have received a copy of the GNU General Public       *)
(*  License along with this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


const ACTION: ARR_APPEND       is action "ARR_APPEND";
const ACTION: ARR_ARRLIT       is action "ARR_ARRLIT";
const ACTION: ARR_ARRLIT2      is action "ARR_ARRLIT2";
const ACTION: ARR_BASELIT      is action "ARR_BASELIT";
const ACTION: ARR_BASELIT2     is action "ARR_BASELIT2";
const ACTION: ARR_CAT          is action "ARR_CAT";
const ACTION: ARR_CONV         is action "ARR_CONV";
const ACTION: ARR_CPY          is action "ARR_CPY";
const ACTION: ARR_CREATE       is action "ARR_CREATE";
const ACTION: ARR_DESTR        is action "ARR_DESTR";
const ACTION: ARR_EXTEND       is action "ARR_EXTEND";
const ACTION: ARR_GEN          is action "ARR_GEN";
const ACTION: ARR_HEAD         is action "ARR_HEAD";
const ACTION: ARR_IDX          is action "ARR_IDX";
const ACTION: ARR_INSERT       is action "ARR_INSERT";
const ACTION: ARR_INSERT_ARRAY is action "ARR_INSERT_ARRAY";
const ACTION: ARR_LNG          is action "ARR_LNG";
const ACTION: ARR_MAXIDX       is action "ARR_MAXIDX";
const ACTION: ARR_MINIDX       is action "ARR_MINIDX";
const ACTION: ARR_PUSH         is action "ARR_PUSH";
const ACTION: ARR_RANGE        is action "ARR_RANGE";
const ACTION: ARR_REMOVE       is action "ARR_REMOVE";
const ACTION: ARR_REMOVE_ARRAY is action "ARR_REMOVE_ARRAY";
const ACTION: ARR_SORT         is action "ARR_SORT";
const ACTION: ARR_SORT_REVERSE is action "ARR_SORT_REVERSE";
const ACTION: ARR_SUBARR       is action "ARR_SUBARR";
const ACTION: ARR_TAIL         is action "ARR_TAIL";
const ACTION: ARR_TIMES        is action "ARR_TIMES";

var boolean_type_hash: times_prototype_declared is boolean_type_hash.EMPTY_HASH;


const proc: arr_prototypes (inout file: c_prog) is func

  begin
    declareExtern(c_prog, "void        arrAppend (arrayType *const, const arrayType);");
    declareExtern(c_prog, "arrayType   arrArrlit2 (intType, arrayType);");
    declareExtern(c_prog, "arrayType   arrBaselit (const genericType);");
    declareExtern(c_prog, "arrayType   arrBaselit2 (intType, const genericType);");
    declareExtern(c_prog, "arrayType   arrCat (arrayType, const arrayType);");
    declareExtern(c_prog, "arrayType   arrExtend (arrayType, const genericType);");
    declareExtern(c_prog, "void        arrFree (arrayType);");
    declareExtern(c_prog, "arrayType   arrGen (const genericType, const genericType);");
    declareExtern(c_prog, "arrayType   arrHead (const const_arrayType, intType);");
    declareExtern(c_prog, "arrayType   arrHeadTemp (arrayType *, intType);");
    declareExtern(c_prog, "genericType arrIdxTemp (arrayType *, intType);");
    declareExtern(c_prog, "void        arrInsert (arrayType *, intType, genericType);");
    declareExtern(c_prog, "void        arrInsertArray (arrayType *, intType, arrayType);");
    declareExtern(c_prog, "void        arrInsertArrayTemp (arrayType *, intType, arrayType);");
    declareExtern(c_prog, "arrayType   arrMalloc (intType, intType);");
    declareExtern(c_prog, "void        arrPush (arrayType *const, const genericType);");
    declareExtern(c_prog, "arrayType   arrRange (const const_arrayType, intType, intType);");
    declareExtern(c_prog, "arrayType   arrRangeTemp (arrayType *, intType, intType);");
    declareExtern(c_prog, "arrayType   arrRealloc (arrayType, memSizeType, memSizeType);");
    declareExtern(c_prog, "genericType arrRemove (arrayType *, intType);");
    declareExtern(c_prog, "arrayType   arrRemoveArray (arrayType *, intType, intType);");
    declareExtern(c_prog, "arrayType   arrSort (arrayType, compareType);");
    declareExtern(c_prog, "arrayType   arrSortReverse (arrayType, compareType);");
    declareExtern(c_prog, "arrayType   arrSubarr (const const_arrayType, intType, intType);");
    declareExtern(c_prog, "arrayType   arrSubarrTemp (arrayType *, intType, intType);");
    declareExtern(c_prog, "arrayType   arrTail (const const_arrayType, intType);");
    declareExtern(c_prog, "arrayType   arrTailTemp (arrayType *, intType);");
    declareExtern(c_prog, "arrayType   arrTimes (intType, intType, const genericType);");
  end func;


const proc: declare_times_prototype (in type: arrayType,
    inout expr_type: c_expr) is func

  local
    var type: elementType is void;
  begin
    if arrayType not in times_prototype_declared then
      elementType := array_element[arrayType];
      process_create_declaration(elementType, c_expr);
      # c_expr.expr &:= type_name(arrayType);
      c_expr.expr &:= "static arrayType times_";
      c_expr.expr &:= str(typeNumber(arrayType));
      c_expr.expr &:= " (intType, const ";
      if useConstPrefix(elementType) then
        c_expr.expr &:= "const_";
      end if;
      c_expr.expr &:= type_name(elementType);
      c_expr.expr &:= ");\n\n";
      times_prototype_declared @:= [arrayType] TRUE;
    end if;
  end func;


const proc: process_arr_append (in reference: param1, in reference: param3,
    inout expr_type: c_expr) is func

  local
    var expr_type: c_param1 is expr_type.value;
    var expr_type: c_param3 is expr_type.value;
  begin
    process_expr(param1, c_param1);
    c_param3.temp_num := c_param1.temp_num;
    getTemporaryToResultExpr(param3, c_param3);
    incr(c_param3.temp_num);
    if has_temp_values(c_param3) then
      c_expr.expr &:= "{\n";
      appendWithDiagnostic(c_param1.temp_decls, c_expr);
      appendWithDiagnostic(c_param3.temp_decls, c_expr);
      appendWithDiagnostic(c_param1.temp_assigns, c_expr);
      appendWithDiagnostic(c_param3.temp_assigns, c_expr);
    end if;
    setDiagnosticLine(c_expr);
    c_expr.expr &:= "arrAppend(&(";
    c_expr.expr &:= c_param1.expr;
    c_expr.expr &:= "), ";
    c_expr.expr &:= c_param3.result_expr;
    c_expr.expr &:= ");\n";
    if has_temp_values(c_param3) then
      appendWithDiagnostic(c_param1.temp_frees, c_expr);
      appendWithDiagnostic(c_param3.temp_frees, c_expr);
      c_expr.expr &:= "}\n";
    end if;
  end func;


const proc: process (ARR_APPEND, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    process_arr_append(params[1], params[3], c_expr);
  end func;


const proc: process (ARR_ARRLIT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: anArray is NIL;
    var expr_type: c_param is expr_type.value;
  begin
    if isConstant(params[3]) then
      anArray := evaluate(prog, params[3]);
      if category(anArray) = ARRAYOBJECT then
        if anArray not in const_table then
          const_table @:= [anArray] length(const_table);
        end if;
        c_expr.expr &:= "arr[";
        c_expr.expr &:= str(const_table[anArray]);
        c_expr.expr &:= "]";
      end if;
    else
      prepareAnyParamTemporarys(params[3], c_param, c_expr);
      if c_param.result_expr <> "" then
        prepare_typed_result(getExprResultType(params[3]), c_expr);
        c_expr.result_expr := "/*arrArrlit()*/";
        c_expr.result_expr &:= c_param.result_expr;
      else
        c_expr.expr &:= "/*arrArrlit()*/";
        c_expr.expr &:= c_param.expr;
      end if;
    end if;
  end func;


const proc: process (ARR_ARRLIT2, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(getExprResultType(params[4]), c_expr);
    c_expr.result_expr := "arrArrlit2(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ", ";
    getTemporaryToResultExpr(params[4], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: process (ARR_BASELIT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    if resultType(getType(function)) not in array_element then
      array_element @:= [resultType(getType(function))] getType(params[3]);
    end if;
    prepare_typed_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "arrBaselit((genericType)(";
    getGenericTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_BASELIT2, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    if resultType(getType(function)) not in array_element then
      array_element @:= [resultType(getType(function))] getType(params[4]);
    end if;
    prepare_typed_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "arrBaselit2(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ", (genericType)(";
    getGenericTemporaryToResultExpr(params[4], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_CAT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrCat(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", ";
    getTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: process (ARR_CONV, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var expr_type: c_param is expr_type.value;
  begin
    prepareAnyParamTemporarys(params[3], c_param, c_expr);
    if c_param.result_expr <> "" then
      prepare_typed_result(getExprResultType(params[3]), c_expr);
      c_expr.result_expr := "/*arrConv()*/";
      c_expr.result_expr &:= c_param.result_expr;
    else
      c_expr.expr &:= "/*arrConv()*/";
      c_expr.expr &:= c_param.expr;
    end if;
  end func;


const func boolean: isIntArrayOfZeros (in reference: arr) is func
  result
    var boolean: isIntArrayOfZeros is FALSE;
  local
    var type: arrayType is void;
    var type: elementType is void;
    var ref_list: arrayList is ref_list.EMPTY;
    var reference: element is NIL;
    var integer: elementValue is 0;
  begin
    arrayType := getExprResultType(arr);
    elementType := array_element[arrayType];
    if elementType in typeCategory and typeCategory[elementType] = INTOBJECT then
      arrayList := arrayToList(arr);
      isIntArrayOfZeros := TRUE;
      for element range arrayList until not isIntArrayOfZeros do
        elementValue := getValue(element, integer);
        if elementValue <> 0 then
          isIntArrayOfZeros := FALSE;
        end if;
      end for;
    end if;
  end func;


const proc: process_arr_cpy_times_optimization (in reference: dest,
    in reference: factor, inout expr_type: c_expr) is func

  local
    var expr_type: statement is expr_type.value;
    var expr_type: c_param1 is expr_type.value;
    var intRange: factorRange is intRange.value;
    var string: factorName is "";
  begin
    writeln("process_arr_cpy_times_optimization A " <& c_expr.currentFile <& "(" <& c_expr.currentLine <& ")");
    statement.temp_num := c_expr.temp_num;
    prepareAnyParamTemporarys(dest, c_param1, statement);
    statement.expr &:= "(";
    factorName := getParameterAsVariable("intType", "factor_", factor, statement);
    if function_range_check then
      factorRange := getIntRange(factor);
      if factorRange.minValue < 0 then
        statement.expr &:= "(rngChk((";
        statement.expr &:= factorName;
        statement.expr &:= ") < 0)?";
        statement.expr &:= intRaiseError("RANGE_ERROR");
        statement.expr &:= ":0),\n";
      else
        countRangeOptimizations(statement);
      end if;
    else
      incr(countNoRangeChecks);
    end if;
    if isNormalVariable(dest) then
      statement.temp_decls &:= "memSizeType size_a = (uintType)((";
      statement.temp_decls &:= c_param1.expr;
      statement.temp_decls &:= ")->max_position - (";
      statement.temp_decls &:= c_param1.expr;
      statement.temp_decls &:= ")->min_position + 1);\n";
      statement.expr &:= "(size_a != (uintType)(";
      statement.expr &:= factorName;
      statement.expr &:= ")?";
      statement.expr &:= c_param1.expr;
      statement.expr &:= "=(arrayType)(arrRealloc(";
      statement.expr &:= c_param1.expr;
      statement.expr &:= ", size_a, ";
      statement.expr &:= factorName;
      statement.expr &:= ")):0),\n";
      statement.expr &:= "memset((";
      statement.expr &:= c_param1.expr;
      statement.expr &:= ")->arr, 0, ";
      statement.expr &:= factorName;
      statement.expr &:= " * sizeof(rtlObjectType)),\n";
      statement.expr &:= "(";
      statement.expr &:= c_param1.expr;
      statement.expr &:= ")->min_position = 1,\n";
      statement.expr &:= "(";
      statement.expr &:= c_param1.expr;
      statement.expr &:= ")->max_position = ";
    else
      statement.temp_decls &:= "arrayType *array_ptr=&(";
      statement.temp_decls &:= c_param1.expr;
      statement.temp_decls &:= ");\n";
      statement.temp_decls &:= "memSizeType size_a = (uintType)((*array_ptr)->max_position - (*array_ptr)->min_position + 1);\n";
      statement.expr &:= "(size_a != (uintType)(";
      statement.expr &:= factorName;
      statement.expr &:= ")?\n";
      statement.expr &:= "*array_ptr=(arrayType)(arrRealloc(*array_ptr, size_a, ";
      statement.expr &:= factorName;
      statement.expr &:= ")):0),\n";
      statement.expr &:= "memset((*array_ptr)->arr, 0, ";
      statement.expr &:= factorName;
      statement.expr &:= " * sizeof(rtlObjectType)),\n";
      statement.expr &:= "(*array_ptr)->min_position = 1,\n";
      statement.expr &:= "(*array_ptr)->max_position = ";
    end if;
    statement.expr &:= factorName;
    statement.expr &:= ");\n";
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process_arr_cpy_times_optimization (in reference: dest,
    in integer: minIdx, in reference: maxIdx, inout expr_type: c_expr) is func

  local
    var type: arrayType is void;
    var expr_type: statement is expr_type.value;
    var expr_type: c_param1 is expr_type.value;
    var intRange: maxIdxRange is intRange.value;
    var string: maxIdxName is "";
    var string: factorName is "";
  begin
    writeln("process_arr_cpy_times_optimization B " <& c_expr.currentFile <& "(" <& c_expr.currentLine <& "): " <& minIdx);
    arrayType := getExprResultType(dest);
    statement.temp_num := c_expr.temp_num;
    prepareAnyParamTemporarys(dest, c_param1, statement);
    statement.expr &:= "(";
    maxIdxName := getParameterAsVariable("intType", "maxIdx_", maxIdx, statement);
    factorName := defineTempVariable("intType", "factor_", statement);
    statement.expr &:= factorName;
    statement.expr &:= "=";
    statement.expr &:= maxIdxName;
    if minIdx > 1 then
      statement.expr &:= "- ";
      statement.expr &:= integerLiteral(pred(minIdx));
    elsif minIdx <= succ(integer.first) then
      statement.expr &:= "- ";
      statement.expr &:= integerLiteral(minIdx);
      statement.expr &:= ";\n";
      statement.expr &:= factorName;
      statement.expr &:= "++";
    elsif minIdx < 1 then
      statement.expr &:= "+ ";
      statement.expr &:= integerLiteral(succ(-minIdx));
    end if;
    statement.expr &:= ", ";
    if function_range_check then
      maxIdxRange := getIntRange(maxIdx);
      if maxIdxRange.maxValue < pred(minIdx) then
        warning(DOES_RAISE, "INDEX_ERROR", c_expr);
        statement.expr &:= intRaiseError("RANGE_ERROR");
        statement.expr &:= ",\n";
      elsif maxIdxRange.minValue < pred(minIdx) then
        statement.expr &:= "(rngChk((";
        statement.expr &:= factorName;
        statement.expr &:= ") < 0)?";
        statement.expr &:= intRaiseError("RANGE_ERROR");
        statement.expr &:= ":0),\n";
      else
        countRangeOptimizations(statement);
      end if;
    else
      incr(countNoRangeChecks);
    end if;
    if isNormalVariable(dest) then
      statement.expr &:= "(";
      statement.expr &:= c_param1.expr;
      statement.expr &:= ")->max_position != ";
      statement.expr &:= maxIdxName;
      statement.expr &:= "?\n(";
      process_destr_declaration(arrayType, global_c_expr);
      statement.expr &:= "destr_";
      statement.expr &:= str(typeNumber(arrayType));
      statement.expr &:= "(";
      statement.expr &:= c_param1.expr;
      statement.expr &:= "),";
      statement.expr &:= c_param1.expr;
      statement.expr &:= "=arrMalloc(";
      statement.expr &:= integerLiteral(minIdx);
      statement.expr &:= ", ";
      statement.expr &:= maxIdxName;
      statement.expr &:= ")):0,\nmemset((";
      statement.expr &:= c_param1.expr;
      statement.expr &:= ")->arr, 0, ";
      statement.expr &:= factorName;
      statement.expr &:= " * sizeof(rtlObjectType)));\n";
    else
      statement.temp_decls &:= "arrayType *array_ptr=&(";
      statement.temp_decls &:= c_param1.expr;
      statement.temp_decls &:= ");\n";
      statement.expr &:= "(*array_ptr)->max_position != ";
      statement.expr &:= maxIdxName;
      statement.expr &:= "?\n(";
      process_destr_declaration(arrayType, global_c_expr);
      statement.expr &:= "destr_";
      statement.expr &:= str(typeNumber(arrayType));
      statement.expr &:= "(*array_ptr),*array_ptr=arrMalloc(";
      statement.expr &:= integerLiteral(minIdx);
      statement.expr &:= ", ";
      statement.expr &:= maxIdxName;
      statement.expr &:= ")):0,\nmemset((*array_ptr)->arr, 0, ";
      statement.expr &:= factorName;
      statement.expr &:= " * sizeof(rtlObjectType)));\n";
    end if;
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process_arr_cpy_array_of_zeros (in reference: dest,
    in integer: minPosition, in integer: maxPosition, inout expr_type: c_expr) is func

  local
    var integer: arraySize is 0;
    var type: arrayType is void;
    var expr_type: statement is expr_type.value;
    var expr_type: c_param1 is expr_type.value;
  begin
    arraySize := succ(maxPosition - minPosition);
    statement.temp_num := c_expr.temp_num;
    prepareAnyParamTemporarys(dest, c_param1, statement);
    statement.expr &:= "(";
    arrayType := getExprResultType(dest);
    if isNormalVariable(dest) then
      if arrayType not in array_minIdx or arrayType not in array_maxIdx or
          minPosition <> array_minIdx[arrayType] and
          maxPosition <> array_maxIdx[arrayType] then
        statement.temp_decls &:= "memSizeType size_a = (uintType)((";
        statement.temp_decls &:= c_param1.expr;
        statement.temp_decls &:= ")->max_position - (";
        statement.temp_decls &:= c_param1.expr;
        statement.temp_decls &:= ")->min_position + 1);\n";
        statement.expr &:= "(size_a != (uintType)(";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= ")?";
        statement.expr &:= c_param1.expr;
        statement.expr &:= "=(arrayType)(arrRealloc(";
        statement.expr &:= c_param1.expr;
        statement.expr &:= ", size_a, ";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= ")):0),\n";
        statement.expr &:= "memset((";
        statement.expr &:= c_param1.expr;
        statement.expr &:= ")->arr, 0, ";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= " * sizeof(rtlObjectType)),\n";
        statement.expr &:= "(";
        statement.expr &:= c_param1.expr;
        statement.expr &:= ")->min_position = ";
        statement.expr &:= integerLiteral(minPosition);
        statement.expr &:= ",\n";
        statement.expr &:= "(";
        statement.expr &:= c_param1.expr;
        statement.expr &:= ")->max_position = ";
        statement.expr &:= integerLiteral(maxPosition);
      else
        statement.expr &:= "memset((";
        statement.expr &:= c_param1.expr;
        statement.expr &:= ")->arr, 0, ";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= " * sizeof(rtlObjectType))";
      end if;
    else
      statement.temp_decls &:= "arrayType *array_ptr=&(";
      statement.temp_decls &:= c_param1.expr;
      statement.temp_decls &:= ");\n";
      if arrayType not in array_minIdx or arrayType not in array_maxIdx or
          minPosition <> array_minIdx[arrayType] and
          maxPosition <> array_maxIdx[arrayType] then
        statement.temp_decls &:= "memSizeType size_a = (uintType)((*array_ptr)->max_position - (*array_ptr)->min_position + 1);\n";
        statement.expr &:= "(size_a != (uintType)(";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= ")?\n";
        statement.expr &:= "*array_ptr=(arrayType)(arrRealloc(*array_ptr, size_a, ";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= ")):0),\n";
        statement.expr &:= "memset((*array_ptr)->arr, 0, ";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= " * sizeof(rtlObjectType)),\n";
        statement.expr &:= "(*array_ptr)->min_position = ";
        statement.expr &:= integerLiteral(minPosition);
        statement.expr &:= ",\n";
        statement.expr &:= "(*array_ptr)->max_position = ";
        statement.expr &:= integerLiteral(maxPosition);
      else
        statement.expr &:= "memset((*array_ptr)->arr, 0, ";
        statement.expr &:= integerLiteral(arraySize);
        statement.expr &:= " * sizeof(rtlObjectType))";
      end if;
    end if;
    statement.expr &:= ");\n";
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const func boolean: isFixedMinIdxTimes (in reference: aParam) is func
  result
    var boolean: isFixedMinIdxTimes is FALSE;
  local
    var type: arrayType is void;
    var reference: timesBody is NIL;
    var reference: timesExpression is NIL;
  begin
    arrayType := getExprResultType(aParam);
    if arrayType in array_minIdx and
        category(aParam) = CALLOBJECT and
        length(getValue(aParam, ref_list)) >= 4 and
        str(getActionParameter(aParam, 2)) = "times" and
        category(getValue(aParam, ref_list)[1]) = BLOCKOBJECT then
      timesBody := body(getValue(aParam, ref_list)[1]);
      if isActionExpression(timesBody, "ARR_ARRLIT2") then
        timesExpression := getActionParameter(timesBody, 4);
        if isActionExpression(timesExpression, "ARR_TIMES") then
          isFixedMinIdxTimes := TRUE;
        end if;
      end if;
    end if;
  end func;


const proc: two_dimensional_times_optimization (in reference: dest,
    in integer: minIdx1, in reference: maxIdx1, in integer: minIdx2, in reference: maxIdx2,
    inout expr_type: c_expr) is func

  local
    var type: arrayType is void;
    var string: maxIdx1Name is "";
    var string: maxIdx2Name is "";
    var string: resultName is "";
    var expr_type: statement is expr_type.value;
    var expr_type: c_param1 is expr_type.value;
  begin
    incr(countOptimizations);
    arrayType := getExprResultType(dest);
    writeln("two_dimensional_times_optimization " <& c_expr.currentFile <& "(" <& c_expr.currentLine <& "):" <& minIdx1 <& " " <& minIdx2);
    statement.temp_num := c_expr.temp_num;
    prepareAnyParamTemporarys(dest, c_param1, statement);
    maxIdx1Name := defineTempVariable("intType", "maxIdx1_", statement);
    if isNormalVariable(maxIdx2) then
      maxIdx2Name := normalVariable(maxIdx2, statement);
    else
      maxIdx2Name := defineTempVariable("intType", "maxIdx2_", statement);
    end if;
    statement.expr &:= maxIdx1Name;
    statement.expr &:= "=(";
    process_expr(maxIdx1, statement);
    statement.expr &:= ");\n";
    if not isNormalVariable(maxIdx2) then
      statement.expr &:= maxIdx2Name;
      statement.expr &:= "=(";
      process_expr(maxIdx2, statement);
      statement.expr &:= ");\n";
    end if;
    if isNormalVariable(dest) then
      resultName := "(" & c_param1.expr & ")";
    else
      statement.temp_decls &:= "arrayType *array_ptr=&(";
      statement.temp_decls &:= c_param1.expr;
      statement.temp_decls &:= ");\n";
      resultName := "(*array_ptr)";
    end if;
    process_destr_declaration(arrayType, global_c_expr);
    process_destr_call(arrayType, resultName, statement.expr);
    statement.expr &:= resultName;
    statement.expr &:= "=arrMalloc(";
    statement.expr &:= integerLiteral(minIdx1);
    statement.expr &:= ", ";
    statement.expr &:= maxIdx1Name;
    statement.expr &:= ");\n";
    if minIdx1 > 1 then
      statement.expr &:= maxIdx1Name;
      statement.expr &:= "-= ";
      statement.expr &:= integerLiteral(pred(minIdx1));
      statement.expr &:= ";\n";
    elsif minIdx1 <= succ(integer.first) then
      statement.expr &:= maxIdx1Name;
      statement.expr &:= "-= ";
      statement.expr &:= integerLiteral(minIdx1);
      statement.expr &:= ";\n";
      statement.expr &:= maxIdx1Name;
      statement.expr &:= "++;\n";
    elsif minIdx1 < 1 then
      statement.expr &:= maxIdx1Name;
      statement.expr &:= "+= ";
      statement.expr &:= integerLiteral(succ(-minIdx1));
      statement.expr &:= ";\n";
    end if;
    statement.expr &:= "while (";
    statement.expr &:= maxIdx1Name;
    statement.expr &:= "!=0) {\n";
    statement.expr &:= maxIdx1Name;
    statement.expr &:= "--;\n";
    statement.expr &:= resultName;
    statement.expr &:= "->arr[";
    statement.expr &:= maxIdx1Name;
    statement.expr &:= "].value.arrayValue=arrTimes(";
    statement.expr &:= integerLiteral(minIdx2);
    statement.expr &:= ", ";
    statement.expr &:= maxIdx2Name;
    statement.expr &:= ", 0);\n";
    statement.expr &:= "}\n";
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process (ARR_CPY, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: arrayType is void;
    var reference: evaluatedParam is NIL;
    var expr_type: statement is expr_type.value;
    var expr_type: c_param1 is expr_type.value;
    var expr_type: c_param3 is expr_type.value;
  begin
    arrayType := getExprResultType(params[1]);
    if isActionExpression(params[3], "ARR_CAT") and
        getActionParameter(params[3], 1) = params[1] then
      process_arr_append(params[1], getActionParameter(params[3], 3), c_expr);
    elsif inlineFunctions and
        isActionExpression(params[3], "ARR_TIMES") and
        getConstant(getActionParameter(params[3], 3), INTOBJECT, evaluatedParam) and
        getValue(evaluatedParam, integer) = 0 then
      process_arr_cpy_times_optimization(params[1],
          getActionParameter(params[3], 1), c_expr);
    elsif inlineFunctions and
        getConstant(params[3], ARRAYOBJECT, evaluatedParam) and
        isIntArrayOfZeros(evaluatedParam) then
      process_arr_cpy_array_of_zeros(params[1],
          arrayMinIdx(evaluatedParam), arrayMaxIdx(evaluatedParam), c_expr);
(*
    elsif inlineFunctions and isFixedMinIdxTimes(params[3]) and
        getConstant(getActionParameter(params[3], 3), INTOBJECT, evaluatedParam) and
        getValue(evaluatedParam, integer) = 0 then
      process_arr_cpy_times_optimization(params[1], array_minIdx[arrayType],
          getActionParameter(params[3], 1), c_expr);
*)

    elsif isFixedMinIdxTimes(params[3]) and
        isFixedMinIdxTimes(getActionParameter(params[3], 3)) and
        getConstant(getActionParameter(getActionParameter(params[3], 3), 3), INTOBJECT, evaluatedParam) and
        getValue(evaluatedParam, integer) = 0 then
      two_dimensional_times_optimization(params[1],
          array_minIdx[arrayType], getActionParameter(params[3], 1),
          array_minIdx[getExprResultType(getActionParameter(params[3], 3))],
          getActionParameter(getActionParameter(params[3], 3), 1), c_expr);
    else
      if isFixedMinIdxTimes(params[3]) and
          getConstant(getActionParameter(params[3], 3), INTOBJECT, evaluatedParam) and
          getValue(evaluatedParam, integer) = 0 then
        writeln("process_arr_cpy_times_optimization " <& c_expr.currentFile <& "(" <& c_expr.currentLine <& "): " <& array_minIdx[arrayType]);
      end if;
      if isFixedMinIdxTimes(params[3]) and
          isFixedMinIdxTimes(getActionParameter(params[3], 3)) and
          getConstant(getActionParameter(getActionParameter(params[3], 3), 3), INTOBJECT, evaluatedParam) and
          getValue(evaluatedParam, integer) = 0 then
        writeln("two_dimensional_arr_cpy_times " <& c_expr.currentFile <& "(" <& c_expr.currentLine <& "):" <& array_minIdx[arrayType] <& " " <& array_minIdx[getExprResultType(getActionParameter(params[3], 3))]);
        # getActionParameter(params[3], 1)
	# getActionParameter(getActionParameter(params[3], 3), 1)
      end if;

      statement.temp_num := c_expr.temp_num;
      prepareAnyParamTemporarys(params[1], c_param1, statement);
      c_param3.demand := ASSIGN_RESULT;
      prepareAnyParamTemporarys(params[3], c_param3, statement);
      if c_param3.result_expr <> "" then
        statement.temp_decls &:= "arrayType new_array;\n";
        statement.expr &:= "new_array=";
        statement.expr &:= c_param3.result_expr;
        statement.expr &:= ";\n";
        process_destr_declaration(arrayType, global_c_expr);
        if isNormalVariable(params[1]) then
          process_destr_call(arrayType, c_param1.expr, statement.expr);
          statement.expr &:= c_param1.expr;
          statement.expr &:= "=new_array;\n";
        else
          statement.temp_decls &:= "arrayType *array_ptr=&(";
          statement.temp_decls &:= c_param1.expr;
          statement.temp_decls &:= ");\n";
          process_destr_call(arrayType, "*array_ptr", statement.expr);
          statement.expr &:= "*array_ptr=new_array;\n";
        end if;
      else
        process_cpy_declaration(arrayType, global_c_expr);
        process_cpy_call(arrayType, c_param1.expr, c_param3.expr,
            statement.expr);
        statement.expr &:= ";\n";
      end if;
      doLocalDeclsOfStatement(statement, c_expr);
    end if;
  end func;


const proc: process (ARR_CREATE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
  begin
    param_type := getExprResultType(params[3]);
    typeCategory @:= [param_type] ARRAYOBJECT;
    process_create_declaration(param_type, global_c_expr);
    process_expr(params[1], c_expr);
    c_expr.expr &:= "=create_";
    c_expr.expr &:= str(typeNumber(param_type));
    c_expr.expr &:= "(";
    process_expr(params[3], c_expr);
    c_expr.expr &:= ");\n";
  end func;


const proc: process (ARR_DESTR, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
  begin
    param_type := getExprResultType(params[1]);
    process_destr_declaration(param_type, global_c_expr);
    c_expr.expr &:= "destr_";
    c_expr.expr &:= str(typeNumber(param_type));
    c_expr.expr &:= "(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ");\n";
  end func;


const proc: process (ARR_EXTEND, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrExtend(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", (genericType)(";
    getGenericTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_GEN, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    prepare_typed_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "arrGen((genericType)(";
    getGenericTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= "), (genericType)(";
    getGenericTemporaryToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_HEAD, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrHeadTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[4], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrHead(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[4], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrHead(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[4], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process_const_arr_idx (in reference: function, in reference: anArray,
    in reference: index, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var integer: index_value is 0;
    var intRange: indexRange is intRange.value;
    var string: index_name is "";
  begin
    incr(countOptimizations);
    if anArray not in const_table then
      const_table @:= [anArray] length(const_table);
    end if;
    if getConstant(index, INTOBJECT, evaluatedParam) then
      index_value := getValue(evaluatedParam, integer);
      if index_value < arrayMinIdx(anArray) or index_value > arrayMaxIdx(anArray) then
        warning(DOES_RAISE, "INDEX_ERROR", c_expr);
        c_expr.expr &:= "(";
        c_expr.expr &:= type_name(resultType(getType(function)));
        c_expr.expr &:= ")(raiseError(INDEX_ERROR), 0)";
      else
        c_expr.expr &:= "arr[";
        c_expr.expr &:= str(const_table[anArray]);
        c_expr.expr &:= "]->arr[";
        c_expr.expr &:= str(index_value - arrayMinIdx(anArray));
        c_expr.expr &:= "]";
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
      end if;
    else
      indexRange := getIntRange(index);
      if indexRange.maxValue < arrayMinIdx(anArray) or
          indexRange.minValue > arrayMaxIdx(anArray) then
        warning(DOES_RAISE, "INDEX_ERROR", c_expr);
        c_expr.expr &:= "(";
        c_expr.expr &:= type_name(resultType(getType(function)));
        c_expr.expr &:= ")(raiseError(INDEX_ERROR), 0)";
      else
        c_expr.expr &:= "arr[";
        c_expr.expr &:= str(const_table[anArray]);
        c_expr.expr &:= "]->arr[";
        if array_index_check then
          if indexRange.minValue < arrayMinIdx(anArray) or
              indexRange.maxValue > arrayMaxIdx(anArray) then
            incr(countIndexChecks);
            incr(c_expr.temp_num);
            index_name := "idx_" & str(c_expr.temp_num);
            if ccConf.TWOS_COMPLEMENT_INTTYPE then
              c_expr.temp_decls &:= "uintType ";
            else
              c_expr.temp_decls &:= "intType ";
            end if;
            c_expr.temp_decls &:= index_name;
            c_expr.temp_decls &:= ";\n";
            c_expr.expr &:= "(";
            c_expr.expr &:= index_name;
            c_expr.expr &:= "=(";
            process_expr(index, c_expr);
            c_expr.expr &:= ")";
            if arrayMinIdx(anArray) <> 0 then
              if ccConf.TWOS_COMPLEMENT_INTTYPE then
                c_expr.expr &:= "- (uintType) ";
              else
                c_expr.expr &:= "- ";
              end if;
              c_expr.expr &:= integerLiteral(arrayMinIdx(anArray));
            end if;
            c_expr.expr &:= ", idxChk(";
            if not ccConf.TWOS_COMPLEMENT_INTTYPE then
              c_expr.expr &:= index_name;
              c_expr.expr &:= "<0 || ";
            end if;
            c_expr.expr &:= index_name;
            c_expr.expr &:= ">=";
            c_expr.expr &:= integerLiteral(arrayLength(anArray));
            c_expr.expr &:= ") ? ";
            c_expr.expr &:= intRaiseError("INDEX_ERROR");
            c_expr.expr &:= " : ";
            c_expr.expr &:= index_name;
            c_expr.expr &:= ")";
          else
            countIndexOptimizations(c_expr);
            c_expr.expr &:= "(";
            process_expr(index, c_expr);
            c_expr.expr &:= ")";
            if arrayMinIdx(anArray) <> 0 then
              c_expr.expr &:= "- ";
              c_expr.expr &:= integerLiteral(arrayMinIdx(anArray));
            end if;
          end if;
        else
          incr(countSuppressedIndexChecks);
          c_expr.expr &:= "(";
          process_expr(index, c_expr);
          c_expr.expr &:= ")";
          if arrayMinIdx(anArray) <> 0 then
            c_expr.expr &:= "- ";
            c_expr.expr &:= integerLiteral(arrayMinIdx(anArray));
          end if;
        end if;
        c_expr.expr &:= "]";
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
      end if;
    end if;
  end func;


const proc: process_const_arr_idx (in reference: function, in reference: param1,
    in integer: index, inout expr_type: c_expr) is func

  local
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
    var type: arrayType is void;
    var string: result_name is "";
  begin
    prepareAnyParamTemporarys(param1, c_param, c_expr);
    if c_param.result_expr <> "" then
      incr(c_expr.temp_num);
      result_name := "help_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "rtlObjectType ";
      c_expr.temp_decls &:= result_name;
      c_expr.temp_decls &:= ";\n";
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      if resultType(getType(function)) in typeCategory and
          typeCategory[resultType(getType(function))] in destrNecessary then
        prepare_typed_result(resultType(getType(function)), c_expr);
        c_expr.result_expr &:= "(";
        c_expr.result_expr &:= c_param.result_intro;
        c_expr.result_expr &:= c_param.result_expr;
        c_expr.result_expr &:= c_param.result_finish;
        c_expr.result_expr &:= ", ";
        c_expr.result_expr &:= result_name;
        c_expr.result_expr &:= ".value.genericValue=arrIdxTemp(&(";
        c_expr.result_expr &:= c_param.result_name;
        c_expr.result_expr &:= "), ";
        c_expr.result_expr &:= integerLiteral(index);
        c_expr.result_expr &:= "), ";
        c_expr.result_expr &:= result_name;
        c_expr.result_expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
        c_expr.result_expr &:= ")";
      else
        c_expr.expr &:= "(";
        c_expr.expr &:= c_param.result_intro;
        c_expr.expr &:= c_param.result_expr;
        c_expr.expr &:= c_param.result_finish;
        c_expr.expr &:= ", ";
        c_expr.expr &:= result_name;
        c_expr.expr &:= ".value.genericValue=arrIdxTemp(&(";
        c_expr.expr &:= c_param.result_name;
        c_expr.expr &:= "), ";
        c_expr.expr &:= integerLiteral(index);
        c_expr.expr &:= "), ";
        c_expr.expr &:= result_name;
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
        c_expr.expr &:= ")";
      end if;
    else
      incr(countOptimizations);
      if isNormalVariable(param1) then
        array_name := "(" & c_param.expr & ")";
      else
        incr(c_expr.temp_num);
        array_name := "tmp_" & str(c_expr.temp_num);
        if not isVarfunc(getType(function)) then
          c_expr.temp_decls &:= "const_";
        end if;
        c_expr.temp_decls &:= "arrayType ";
        c_expr.temp_decls &:= array_name;
        c_expr.temp_decls &:= ";\n";
        c_expr.expr &:= "(*(";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "=";
        c_expr.expr &:= c_param.expr;
        c_expr.expr &:= ", &";
      end if;
      c_expr.expr &:= array_name;
      c_expr.expr &:= "->arr[";
      arrayType := getExprResultType(param1);
      if array_index_check then
        if arrayType in array_minIdx and arrayType in array_maxIdx then
          if index < array_minIdx[arrayType] or
              index > array_maxIdx[arrayType] then
            warning(DOES_RAISE, "INDEX_ERROR", c_expr);
            c_expr.expr &:= intRaiseError("INDEX_ERROR");
          else
            countIndexOptimizations(c_expr);
            c_expr.expr &:= integerLiteral(index - array_minIdx[arrayType]);
          end if;
        elsif arrayType in array_minIdx and index < array_minIdx[arrayType] then
          warning(DOES_RAISE, "INDEX_ERROR", c_expr);
          c_expr.expr &:= intRaiseError("INDEX_ERROR");
        else
          incr(countIndexChecks);
          c_expr.expr &:= "(idxChk(";
          if arrayType not in array_minIdx then
            c_expr.expr &:= integerLiteral(index);
            c_expr.expr &:= "<";
            c_expr.expr &:= array_name;
            c_expr.expr &:= "->min_position || ";
          end if;
          c_expr.expr &:= integerLiteral(index);
          c_expr.expr &:= ">";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "->max_position) ? ";
          c_expr.expr &:= intRaiseError("INDEX_ERROR");
          c_expr.expr &:= " : ";
          if arrayType in array_minIdx then
            c_expr.expr &:= integerLiteral(index - array_minIdx[arrayType]);
          else
            c_expr.expr &:= integerLiteral(index);
            c_expr.expr &:= "-";
            c_expr.expr &:= array_name;
            c_expr.expr &:= "->min_position";
          end if;
          c_expr.expr &:= ")";
        end if;
      else
        incr(countSuppressedIndexChecks);
        if arrayType in array_minIdx then
          c_expr.expr &:= integerLiteral(index - array_minIdx[arrayType]);
        else
          c_expr.expr &:= integerLiteral(index);
          c_expr.expr &:= "-";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "->min_position";
        end if;
      end if;
      c_expr.expr &:= "]";
      c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
      if not isNormalVariable(param1) then
        c_expr.expr &:= "))";
      end if;
    end if;
  end func;


const proc: process_fixlen_array_index (in reference: index,
    in intRange: indexRange, in integer: minIndex, in integer: maxIndex,
    inout expr_type: c_expr) is func

  local
    var string: index_name is "";
  begin
    if indexRange.maxValue < minIndex or
        indexRange.minValue > maxIndex then
      warning(DOES_RAISE, "INDEX_ERROR", c_expr);
      c_expr.expr &:= intRaiseError("INDEX_ERROR");
    elsif indexRange.minValue >= minIndex and
        indexRange.maxValue <= maxIndex then
      countIndexOptimizations(c_expr);
      c_expr.expr &:= "(";
      process_expr(index, c_expr);
      c_expr.expr &:= ")";
      if minIndex <> 0 then
        c_expr.expr &:= "- ";
        c_expr.expr &:= integerLiteral(minIndex);
      end if;
    else
      incr(countIndexChecks);
      if ccConf.TWOS_COMPLEMENT_INTTYPE then
        index_name := getTempVariable("uintType", "idx_", "", c_expr);
        c_expr.expr &:= "(";
        c_expr.expr &:= index_name;
        c_expr.expr &:= " = (uintType)(";
        process_expr(index, c_expr);
        c_expr.expr &:= ")";
        if minIndex <> 0 then
          c_expr.expr &:= "-(uintType)";
          c_expr.expr &:= integerLiteral(minIndex);
        end if;
        c_expr.expr &:= ",idxChk(";
        c_expr.expr &:= index_name;
        c_expr.expr &:= " >= (uintType)";
        c_expr.expr &:= integerLiteral(succ(maxIndex - minIndex));
        c_expr.expr &:= ") ? ";
        c_expr.expr &:= intRaiseError("INDEX_ERROR");
        c_expr.expr &:= " : ";
        c_expr.expr &:= index_name;
        c_expr.expr &:= ")";
      else
        index_name := getParameterAsVariable("intType", "idx_", index, c_expr);
        c_expr.expr &:= "(idxChk(";
        c_expr.expr &:= index_name;
        c_expr.expr &:= " < ";
        c_expr.expr &:= integerLiteral(minIndex);
        c_expr.expr &:= " || ";
        c_expr.expr &:= index_name;
        c_expr.expr &:= " > ";
        c_expr.expr &:= integerLiteral(maxIndex);
        c_expr.expr &:= ") ? ";
        c_expr.expr &:= intRaiseError("INDEX_ERROR");
        c_expr.expr &:= " : ";
        c_expr.expr &:= index_name;
        if minIndex <> 0 then
          c_expr.expr &:= "- ";
          c_expr.expr &:= integerLiteral(minIndex);
        end if;
        c_expr.expr &:= ")";
      end if;
    end if;
  end func;


const proc: process_base_array_index (in string: array_name,
    in reference: index, in intRange: indexRange, in integer: minIndex,
    inout expr_type: c_expr) is func

  local
    var string: index_name is "";
  begin
    if indexRange.maxValue < minIndex then
      warning(DOES_RAISE, "INDEX_ERROR", c_expr);
      c_expr.expr &:= intRaiseError("INDEX_ERROR");
    else
      incr(countIndexChecks);
      index_name := getParameterAsVariable("intType", "idx_", index, c_expr);
      c_expr.expr &:= "(idxChk(";
      if ccConf.TWOS_COMPLEMENT_INTTYPE and
          indexRange.minValue < minIndex and
          minIndex = 0 then
        c_expr.expr &:= "(uintType)";
        c_expr.expr &:= index_name;
        c_expr.expr &:= " > (uintType)(";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->max_position)";
      else
        if indexRange.minValue < minIndex then
          c_expr.expr &:= index_name;
          c_expr.expr &:= " < ";
          c_expr.expr &:= integerLiteral(minIndex);
          c_expr.expr &:= " || ";
        end if;
        c_expr.expr &:= index_name;
        c_expr.expr &:= " > ";
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->max_position";
      end if;
      c_expr.expr &:= ") ? ";
      c_expr.expr &:= intRaiseError("INDEX_ERROR");
      c_expr.expr &:= " : ";
      c_expr.expr &:= index_name;
      if minIndex <> 0 then
        c_expr.expr &:= "- ";
        c_expr.expr &:= integerLiteral(minIndex);
      end if;
      c_expr.expr &:= ")";
    end if;
  end func;


const proc: process (ARR_IDX, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
    var type: arrayType is void;
    var intRange: indexRange is intRange.value;
    var string: index_name is "";
    var string: result_name is "";
  begin
    if getType(params[1]) not in array_element then
      array_element @:= [getType(params[1])] resultType(getType(function));
    end if;
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      process_const_arr_idx(function, evaluatedParam, params[3], c_expr);
    elsif getConstant(params[3], INTOBJECT, evaluatedParam) then
      process_const_arr_idx(function, params[1], getValue(evaluatedParam, integer), c_expr);
    else
      prepareAnyParamTemporarys(params[1], c_param, c_expr);
      if c_param.result_expr <> "" then
        incr(c_expr.temp_num);
        result_name := "help_" & str(c_expr.temp_num);
        c_expr.temp_decls &:= "rtlObjectType ";
        c_expr.temp_decls &:= result_name;
        c_expr.temp_decls &:= ";\n";
        c_expr.temp_decls &:= c_param.result_decl;
        c_expr.temp_frees &:= c_param.result_free;
        c_expr.temp_to_null &:= c_param.result_to_null;
        if resultType(getType(function)) in typeCategory and
            typeCategory[resultType(getType(function))] in destrNecessary then
          prepare_typed_result(resultType(getType(function)), c_expr);
          c_expr.result_expr &:= "(";
          c_expr.result_expr &:= c_param.result_intro;
          c_expr.result_expr &:= c_param.result_expr;
          c_expr.result_expr &:= c_param.result_finish;
          c_expr.result_expr &:= ", ";
          c_expr.result_expr &:= result_name;
          c_expr.result_expr &:= ".value.genericValue=arrIdxTemp(&(";
          c_expr.result_expr &:= c_param.result_name;
          c_expr.result_expr &:= "), ";
          getAnyParamToResultExpr(params[3], c_expr);
          c_expr.result_expr &:= "), ";
          c_expr.result_expr &:= result_name;
          c_expr.result_expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
          c_expr.result_expr &:= ")";
        else
          c_expr.expr &:= "(";
          c_expr.expr &:= c_param.result_intro;
          c_expr.expr &:= c_param.result_expr;
          c_expr.expr &:= c_param.result_finish;
          c_expr.expr &:= ", ";
          c_expr.expr &:= result_name;
          c_expr.expr &:= ".value.genericValue=arrIdxTemp(&(";
          c_expr.expr &:= c_param.result_name;
          c_expr.expr &:= "), ";
          process_expr(params[3], c_expr);
          c_expr.expr &:= "), ";
          c_expr.expr &:= result_name;
          c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
          c_expr.expr &:= ")";
        end if;
      else
        if isNormalVariable(params[1]) then
          array_name := "(" & c_param.expr & ")";
        else
          incr(c_expr.temp_num);
          array_name := "tmp_" & str(c_expr.temp_num);
          if not isVarfunc(getType(function)) then
            c_expr.temp_decls &:= "const_";
          end if;
          c_expr.temp_decls &:= "arrayType ";
          c_expr.temp_decls &:= array_name;
          c_expr.temp_decls &:= ";\n";
          c_expr.expr &:= "(*(";
          c_expr.expr &:= array_name;
          c_expr.expr &:= "=";
          c_expr.expr &:= c_param.expr;
          c_expr.expr &:= ", &";
        end if;
        c_expr.expr &:= array_name;
        c_expr.expr &:= "->arr[";
        arrayType := getExprResultType(params[1]);
        if array_index_check then
          indexRange := getIntRange(params[3]);
          if arrayType in array_minIdx and arrayType in array_maxIdx then
            process_fixlen_array_index(params[3], indexRange,
                array_minIdx[arrayType], array_maxIdx[arrayType], c_expr);
          elsif arrayType in array_minIdx then
            process_base_array_index(array_name, params[3], indexRange,
                array_minIdx[arrayType], c_expr);
          else
            incr(countIndexChecks);
            if ccConf.TWOS_COMPLEMENT_INTTYPE then
              index_name := getTempVariable("uintType", "idx_", "", c_expr);
              c_expr.expr &:= "(";
              c_expr.expr &:= index_name;
              c_expr.expr &:= " = (uintType)(";
              process_expr(params[3], c_expr);
              c_expr.expr &:= ")-(uintType)(";
              c_expr.expr &:= array_name;
              c_expr.expr &:= "->min_position),idxChk(";
              c_expr.expr &:= index_name;
              c_expr.expr &:= " >= (uintType)(";
              c_expr.expr &:= array_name;
              c_expr.expr &:= "->max_position) - (uintType)(";
              c_expr.expr &:= array_name;
              c_expr.expr &:= "->min_position) + (uintType)1) ? ";
              c_expr.expr &:= intRaiseError("INDEX_ERROR");
              c_expr.expr &:= " : ";
              c_expr.expr &:= index_name;
              c_expr.expr &:= ")";
            else
              index_name := getParameterAsVariable("intType", "idx_", params[3], c_expr);
              c_expr.expr &:= "(idxChk(";
              c_expr.expr &:= index_name;
              c_expr.expr &:= " < ";
              c_expr.expr &:= array_name;
              c_expr.expr &:= "->min_position || ";
              c_expr.expr &:= index_name;
              c_expr.expr &:= " > ";
              c_expr.expr &:= array_name;
              c_expr.expr &:= "->max_position) ? ";
              c_expr.expr &:= intRaiseError("INDEX_ERROR");
              c_expr.expr &:= " : ";
              c_expr.expr &:= index_name;
              c_expr.expr &:= "-";
              c_expr.expr &:= array_name;
              c_expr.expr &:= "->min_position)";
            end if;
          end if;
        else
          incr(countSuppressedIndexChecks);
          c_expr.expr &:= "(";
          process_expr(params[3], c_expr);
          c_expr.expr &:= ")- ";
          if arrayType in array_minIdx then
            c_expr.expr &:= integerLiteral(array_minIdx[arrayType]);
          else
            c_expr.expr &:= array_name;
            c_expr.expr &:= "->min_position";
          end if;
        end if;
        c_expr.expr &:= "]";
        c_expr.expr &:= select_value_from_rtlObjectStruct(resultType(getType(function)));
        if not isNormalVariable(params[1]) then
          c_expr.expr &:= "))";
        end if;
      end if;
    end if;
  end func;


const proc: process (ARR_INSERT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: array_type is void;
    var type: element_type is void;
    var expr_type: statement is expr_type.value;
    var reference: evaluatedParam is NIL;
    var string: array_name is "";
    var string: position_name is "";
  begin
    array_type := getExprResultType(params[1]);
    element_type := getExprResultType(params[3]);
    if array_type not in array_element then
      array_element @:= [array_type] element_type;
    end if;
    if valueIsAtHeap(element_type) then
      if array_index_check then
        incr(countIndexChecks);
        incr(statement.temp_num);
        if getConstant(params[2], INTOBJECT, evaluatedParam) then
          position_name := integerLiteral(getValue(evaluatedParam, integer));
        else
          position_name := "pos_" & str(statement.temp_num);
          statement.temp_decls &:= "intType ";
          statement.temp_decls &:= position_name;
          statement.temp_decls &:= ";";
          statement.expr &:= position_name;
          statement.expr &:= " = ";
          process_expr(params[2], statement);
          statement.expr &:= ";\n";
        end if;
        array_name := getParameterAsReference(type_name(array_type),
                                              "arr_", params[1], statement);
        statement.expr &:= "if (idxChk(";
        statement.expr &:= position_name;
        statement.expr &:= " < ";
        statement.expr &:= array_name;
        statement.expr &:= "->min_position || ";
        statement.expr &:= position_name;
        statement.expr &:= " > ";
        statement.expr &:= array_name;
        statement.expr &:= "->max_position + 1)) {\n";
        statement.expr &:= raiseError("INDEX_ERROR");
        statement.expr &:= "} else {\n";
        statement.expr &:= "arrInsert(&(";
        statement.expr &:= array_name;
        statement.expr &:= "), ";
        statement.expr &:= position_name;
        statement.expr &:= ", (genericType)(";
        getCreatedValueAsGeneric(params[3], statement);
        statement.expr &:= "));\n";
        statement.expr &:= "}\n";
        doLocalDeclsOfStatement(statement, c_expr);
      else
        setDiagnosticLine(c_expr);
        c_expr.expr &:= "arrInsert(&(";
        process_expr(params[1], c_expr);
        c_expr.expr &:= "), ";
        process_expr(params[2], c_expr);
        c_expr.expr &:= ", (genericType)(";
        getCreatedValueAsGeneric(params[3], c_expr);
        c_expr.expr &:= "));\n";
      end if;
    else
      setDiagnosticLine(c_expr);
      c_expr.expr &:= "arrInsert(&(";
      process_expr(params[1], c_expr);
      c_expr.expr &:= "), ";
      process_expr(params[2], c_expr);
      c_expr.expr &:= ", ";
      getGenericValue(params[3], c_expr);
      c_expr.expr &:= ");\n";
    end if;
  end func;


const proc: process (ARR_INSERT_ARRAY, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param3_type is void;
    var expr_type: statement is expr_type.value;
  begin
    param3_type := getExprResultType(params[3]);
    if valueIsAtHeap(array_element[param3_type]) then
      statement.expr &:= "arrInsertArrayTemp(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      process_expr(params[2], statement);
      statement.expr &:= ", ";
      getTemporaryToExpr(params[3], statement);
      statement.expr &:= ");\n";
    else
      statement.expr &:= "arrInsertArray(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      process_expr(params[2], statement);
      statement.expr &:= ", ";
      getAnyParamToExpr(params[3], statement);
      statement.expr &:= ");\n";
    end if;
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process (ARR_LNG, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var string: array_name is "";
  begin
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      incr(countOptimizations);
      c_expr.expr &:= str(arrayMaxIdx(evaluatedParam) - arrayMinIdx(evaluatedParam) + 1);
      if evaluatedParam in const_table then
        c_expr.expr &:= " /* length(arr[";
        c_expr.expr &:= str(const_table[evaluatedParam]);
        c_expr.expr &:= "]) */";
      else
        c_expr.expr &:= " /* length(array) */";
      end if;
    else
      c_expr.expr &:= "(";
      array_name := getParameterAsVariable("const_arrayType", "tmp_", params[1], c_expr);
      c_expr.expr &:= array_name;
      c_expr.expr &:= "->max_position - ";
      c_expr.expr &:= array_name;
      c_expr.expr &:= "->min_position + 1)";
    end if;
  end func;


const proc: process (ARR_MAXIDX, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
  begin
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      incr(countOptimizations);
      c_expr.expr &:= str(arrayMaxIdx(evaluatedParam));
      if evaluatedParam in const_table then
        c_expr.expr &:= " /* maxIdx(arr[";
        c_expr.expr &:= str(const_table[evaluatedParam]);
        c_expr.expr &:= "]) */";
      else
        c_expr.expr &:= " /* maxIdx(array) */";
      end if;
    else
      c_expr.expr &:= "(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ")->max_position";
    end if;
  end func;


const proc: process (ARR_MINIDX, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
  begin
    if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
      incr(countOptimizations);
      c_expr.expr &:= str(arrayMinIdx(evaluatedParam));
      if evaluatedParam in const_table then
        c_expr.expr &:= " /* minIdx(arr[";
        c_expr.expr &:= str(const_table[evaluatedParam]);
        c_expr.expr &:= "]) */";
      else
        c_expr.expr &:= " /* minIdx(array) */";
      end if;
    else
      c_expr.expr &:= "(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ")->min_position";
    end if;
  end func;


const proc: process (ARR_PUSH, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var expr_type: c_param1 is expr_type.value;
    var expr_type: c_param3 is expr_type.value;
  begin
    if getType(params[1]) not in array_element then
      array_element @:= [getType(params[1])] getType(params[3]);
    end if;
    process_expr(params[1], c_param1);
    c_param3.temp_num := c_param1.temp_num;
    getGenericTemporaryToResultExpr(params[3], c_param3);
    incr(c_param3.temp_num);
    if has_temp_values(c_param3) then
      c_expr.expr &:= "{\n";
      appendWithDiagnostic(c_param1.temp_decls, c_expr);
      appendWithDiagnostic(c_param3.temp_decls, c_expr);
      appendWithDiagnostic(c_param1.temp_assigns, c_expr);
      appendWithDiagnostic(c_param3.temp_assigns, c_expr);
    end if;
    setDiagnosticLine(c_expr);
    c_expr.expr &:= "arrPush(&(";
    c_expr.expr &:= c_param1.expr;
    c_expr.expr &:= "), (genericType)(";
    c_expr.expr &:= c_param3.result_expr;
    c_expr.expr &:= "));\n";
    if has_temp_values(c_param3) then
      appendWithDiagnostic(c_param1.temp_frees, c_expr);
      appendWithDiagnostic(c_param3.temp_frees, c_expr);
      c_expr.expr &:= "}\n";
    end if;
  end func;


const proc: process (ARR_RANGE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrRangeTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrRange(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrRange(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process (ARR_REMOVE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: proc_type is void;
    var type: result_type is void;
    var string: temp_name is "";
  begin
    proc_type := getType(function);
    result_type := resultType(proc_type);
    if valueIsAtHeap(result_type) then
      prepare_typed_result(result_type, c_expr);
      temp_name := beginCastGenericToResultExpr(result_type, c_expr);
      c_expr.result_expr &:= "arrRemove(&(";
      getStdParamToResultExpr(params[1], c_expr);
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[2], c_expr);
      c_expr.result_expr &:= ")";
      endCastGenericToResultExpr(result_type, temp_name, c_expr);
    else
      temp_name := beginCastGeneric(result_type, c_expr);
      c_expr.expr &:= "arrRemove(&(";
      process_expr(params[1], c_expr);
      c_expr.expr &:= "), ";
      process_expr(params[2], c_expr);
      c_expr.expr &:= ")";
      endCastGeneric(result_type, temp_name, c_expr);
    end if;
  end func;


const proc: process (ARR_REMOVE_ARRAY, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    c_expr.result_expr := "arrRemoveArray(&(";
    getStdParamToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= "), ";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ", ";
    getStdParamToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: process (ARR_SORT, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    declare_func_pointer_if_necessary(params[2], global_c_expr);
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrSort(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", (compareType)(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_SORT_REVERSE, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  begin
    declare_func_pointer_if_necessary(params[2], global_c_expr);
    prepare_typed_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "arrSortReverse(";
    getTemporaryToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", (compareType)(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= "))";
  end func;


const proc: process (ARR_SUBARR, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrSubarrTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrSubarr(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrSubarr(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[5], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: process (ARR_TAIL, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var type: param_type is void;
    var expr_type: c_param is expr_type.value;
    var string: array_name is "";
  begin
    param_type := getExprResultType(params[1]);
    prepare_typed_result(param_type, c_expr);
    prepareAnyParamTemporarys(params[1], c_param, c_expr);
    if c_param.result_expr <> "" then
      c_expr.temp_decls &:= c_param.result_decl;
      c_expr.temp_frees &:= c_param.result_free;
      c_expr.temp_to_null &:= c_param.result_to_null;
      c_expr.result_expr := "(";
      c_expr.result_expr &:= c_param.result_intro;
      c_expr.result_expr &:= c_param.result_expr;
      c_expr.result_expr &:= c_param.result_finish;
      c_expr.result_expr &:= ", arrTailTemp(&(";
      c_expr.result_expr &:= c_param.result_name;
      c_expr.result_expr &:= "), ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= "))";
    elsif valueIsAtHeap(array_element[param_type]) then
      incr(c_expr.temp_num);
      array_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "arrayType ";
      c_expr.temp_decls &:= array_name;
      c_expr.temp_decls &:= "=NULL;\n";
      c_expr.temp_frees &:= "if (";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= " != NULL) {arrFree(";
      c_expr.temp_frees &:= array_name;
      c_expr.temp_frees &:= ");}\n";
      c_expr.temp_to_null &:= array_name;
      c_expr.temp_to_null &:= "=NULL;\n";
      c_expr.result_expr := "(";
      c_expr.result_expr &:= array_name;
      c_expr.result_expr &:= "=arrTail(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= "), ";
      typeCategory @:= [param_type] ARRAYOBJECT;
      process_create_declaration(param_type, global_c_expr);
      process_create_call(param_type, array_name, c_expr.result_expr);
      c_expr.result_expr &:= ")";
    else
      c_expr.result_expr := "arrTail(";
      c_expr.result_expr &:= c_param.expr;
      c_expr.result_expr &:= ", ";
      getStdParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


const proc: two_dimensional_times_optimization (in type: result_type,
    in reference: factor1, in reference: factor2, inout expr_type: c_expr) is func

  local
    var string: factor1Name is "";
    var string: factor2Name is "";
    var string: resultName is "";
  begin
    incr(countOptimizations);
    prepare_typed_result(result_type, c_expr);
    c_expr.result_expr := "({";
    factor1Name := defineTempVariable("intType", "factor1_", c_expr);
    factor2Name := getParameterInResultStatement("intType",
        "factor2_", factor2, c_expr);
    resultName := defineTempVariable("arrayType", "times_res_", c_expr);
    c_expr.result_expr &:= factor1Name;
    c_expr.result_expr &:= "=(";
    getStdParamToResultExpr(factor1, c_expr);
    c_expr.result_expr &:= ");\n";
    c_expr.result_expr &:= resultName;
    c_expr.result_expr &:= "=arrMalloc(1, ";
    c_expr.result_expr &:= factor1Name;
    c_expr.result_expr &:= ");\n";
    c_expr.result_expr &:= "while (";
    c_expr.result_expr &:= factor1Name;
    c_expr.result_expr &:= "!=0) {\n";
    c_expr.result_expr &:= factor1Name;
    c_expr.result_expr &:= "--;\n";
    c_expr.result_expr &:= resultName;
    c_expr.result_expr &:= "->arr[";
    c_expr.result_expr &:= factor1Name;
    c_expr.result_expr &:= "].value.arrayValue=arrTimes(1, ";
    c_expr.result_expr &:= factor2Name;
    c_expr.result_expr &:= ", 0);\n";
    c_expr.result_expr &:= "}\n";
    c_expr.result_expr &:= resultName;
    c_expr.result_expr &:= ";})";
  end func;


const proc: process (ARR_TIMES, in reference: function,
    in ref_list: params, inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var type: result_type is void;
    var type: element_type is void;
  begin
    result_type := resultType(getType(function));
    typeCategory @:= [result_type] ARRAYOBJECT;
    if getConstant(params[1], INTOBJECT, evaluatedParam) and
        getValue(evaluatedParam, integer) = 0 then
      incr(countOptimizations);
      prepare_typed_result(result_type, c_expr);
      c_expr.result_expr := "arrMalloc(1, 0)";
    elsif inlineFunctions and ccConf.STMT_BLOCK_IN_PARENTHESES_OK and
        isActionExpression(params[3], "ARR_TIMES") and
        getConstant(getActionParameter(params[3], 3), INTOBJECT, evaluatedParam) and
        getValue(evaluatedParam, integer) = 0 then
      two_dimensional_times_optimization(result_type, params[1],
          getActionParameter(params[3], 1), c_expr);
    else
      element_type := getType(formalParams(function)[3]);
      if element_type in typeCategory and
          typeCategory[element_type] in simpleValueType then
        incr(countOptimizations);
        prepare_typed_result(result_type, c_expr);
        c_expr.result_expr := "arrTimes(1, ";
        getStdParamToResultExpr(params[1], c_expr);
        c_expr.result_expr &:= ", ";
        getGenericValueToResultExpr(params[3], c_expr);
        c_expr.result_expr &:= ")";
      else
        declare_times_prototype(result_type, global_c_expr);
        prepare_typed_result(result_type, c_expr);
        c_expr.result_expr := "times_";
        c_expr.result_expr &:= str(typeNumber(result_type));
        c_expr.result_expr &:= "(";
        getStdParamToResultExpr(params[1], c_expr);
        c_expr.result_expr &:= ", ";
        getAnyParamToResultExpr(params[3], c_expr);
        c_expr.result_expr &:= ")";
      end if;
    end if;
  end func;