(********************************************************************)
(*                                                                  *)
(*  set_act.s7i   Generate code for actions of the type set.        *)
(*  Copyright (C) 1990 - 1994, 2004 - 2015  Thomas Mertes           *)
(*                2019 - 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: SET_ARRLIT           is action "SET_ARRLIT";
const ACTION: SET_BASELIT          is action "SET_BASELIT";
const ACTION: SET_CARD             is action "SET_CARD";
const ACTION: SET_CMP              is action "SET_CMP";
const ACTION: SET_CONV1            is action "SET_CONV1";
const ACTION: SET_CONV3            is action "SET_CONV3";
const ACTION: SET_CPY              is action "SET_CPY";
const ACTION: SET_DIFF             is action "SET_DIFF";
const ACTION: SET_DIFF_ASSIGN      is action "SET_DIFF_ASSIGN";
const ACTION: SET_ELEM             is action "SET_ELEM";
const ACTION: SET_EQ               is action "SET_EQ";
const ACTION: SET_EXCL             is action "SET_EXCL";
const ACTION: SET_GE               is action "SET_GE";
const ACTION: SET_GT               is action "SET_GT";
const ACTION: SET_HASHCODE         is action "SET_HASHCODE";
const ACTION: SET_ICONV1           is action "SET_ICONV1";
const ACTION: SET_ICONV3           is action "SET_ICONV3";
const ACTION: SET_INCL             is action "SET_INCL";
const ACTION: SET_INTERSECT        is action "SET_INTERSECT";
const ACTION: SET_INTERSECT_ASSIGN is action "SET_INTERSECT_ASSIGN";
const ACTION: SET_LE               is action "SET_LE";
const ACTION: SET_LT               is action "SET_LT";
const ACTION: SET_MAX              is action "SET_MAX";
const ACTION: SET_MIN              is action "SET_MIN";
const ACTION: SET_NE               is action "SET_NE";
const ACTION: SET_NEXT             is action "SET_NEXT";
const ACTION: SET_NOT_ELEM         is action "SET_NOT_ELEM";
const ACTION: SET_RAND             is action "SET_RAND";
const ACTION: SET_RANGELIT         is action "SET_RANGELIT";
const ACTION: SET_SCONV1           is action "SET_SCONV1";
const ACTION: SET_SCONV3           is action "SET_SCONV3";
const ACTION: SET_SYMDIFF          is action "SET_SYMDIFF";
const ACTION: SET_UNION            is action "SET_UNION";
const ACTION: SET_UNION_ASSIGN     is action "SET_UNION_ASSIGN";
const ACTION: SET_VALUE            is action "SET_VALUE";


const func integer: bitset_pos (in integer: number) is
  return number >> log2(ccConf.INTTYPE_SIZE);


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

  begin
    declareExtern(c_prog, "setType     setArrlit (const_arrayType);");
    declareExtern(c_prog, "setType     setBaselit (const intType);");
    declareExtern(c_prog, "intType     setCard (const const_setType);");
    declareExtern(c_prog, "intType     setCmp (const const_setType, const const_setType);");
    declareExtern(c_prog, "intType     setCmpGeneric (const genericType, const genericType);");
    declareExtern(c_prog, "void        setCpy (setType *const, const const_setType);");
    declareExtern(c_prog, "void        setCpyGeneric (genericType *const, const genericType);");
    declareExtern(c_prog, "setType     setCreate (const const_setType);");
    declareExtern(c_prog, "genericType setCreateGeneric (const genericType);");
    declareExtern(c_prog, "void        setDestr (const const_setType);");
    declareExtern(c_prog, "void        setDestrGeneric (const genericType);");
    declareExtern(c_prog, "setType     setDiff (const const_setType, const const_setType);");
    declareExtern(c_prog, "void        setDiffAssign (setType *const, const const_setType);");
    declareExtern(c_prog, "boolType    setElem (const intType, const const_setType);");
    declareExtern(c_prog, "boolType    setEq (const const_setType, const const_setType);");
    declareExtern(c_prog, "void        setExcl (setType *const, const intType);");
    declareExtern(c_prog, "intType     setHashCode (const const_setType);");
    declareExtern(c_prog, "intType     setHashCodeGeneric (const genericType);");
    declareExtern(c_prog, "setType     setIConv (intType);");
    declareExtern(c_prog, "void        setIncl (setType *const, const intType);");
    declareExtern(c_prog, "setType     setIntersect (const const_setType, const const_setType);");
    declareExtern(c_prog, "void        setIntersectAssign (setType *const, const const_setType);");
    declareExtern(c_prog, "boolType    setIsEmpty (const const_setType);");
    declareExtern(c_prog, "boolType    setIsProperSubset (const const_setType, const const_setType);");
    declareExtern(c_prog, "boolType    setIsSubset (const const_setType, const const_setType);");
    declareExtern(c_prog, "intType     setMax (const const_setType);");
    declareExtern(c_prog, "intType     setMin (const const_setType);");
    declareExtern(c_prog, "intType     setNext (const const_setType, const intType);");
    declareExtern(c_prog, "intType     setRand (const const_setType);");
    declareExtern(c_prog, "setType     setRangelit (const intType, const intType);");
    declareExtern(c_prog, "intType     setSConv (const const_setType);");
    declareExtern(c_prog, "setType     setSymdiff (const const_setType, const const_setType);");
    declareExtern(c_prog, "setType     setUnion (const const_setType, const const_setType);");
    declareExtern(c_prog, "void        setUnionAssign (setType *const, const const_setType);");
    declareExtern(c_prog, "setType     setValue (const const_objRefType);");
  end func;


const func string: bitsetElemAsHex (in bitset: set1, in integer: start_num) is func

  result
    var string: hex_num is " " mult ccConf.INTTYPE_SIZE div 4 + 2;
  local
    const string: to_hex is "0123456789abcdef";
    var integer: hex_digit_num is 0;
    var integer: bit_num is 0;
    var integer: hex_digit is 0;
    var integer: digit_idx is ccConf.INTTYPE_SIZE div 4 + 2;
  begin
    for hex_digit_num range start_num to start_num + pred(ccConf.INTTYPE_SIZE) step 4 do
      hex_digit := 0;
      for bit_num range 0 to 3 do
        if hex_digit_num + bit_num in set1 then
          hex_digit +:= 2 ** bit_num;
        end if;
      end for;
      hex_num @:= [digit_idx] to_hex[succ(hex_digit)];
      decr(digit_idx);
    end for;
    hex_num @:= [1] "0x";
  end func;


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

  begin
    prepare_set_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "setArrlit(";
    getAnyParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ")";
  end func;


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

  begin
    prepare_set_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "setBaselit(";
    getStdParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: optimize_set_card (in reference: argument,
    inout expr_type: c_expr) is func

  local
    var intRange: argumentRange is intRange.value;
    var string: argumentName is "";
  begin
    incr(countOptimizations);
    argumentRange := getIntRange(argument);
    if argumentRange.maxValue < 0 then
      warning(DOES_RAISE, "RANGE_ERROR", c_expr);
      c_expr.expr &:= intRaiseError("RANGE_ERROR");
    elsif argumentRange.minValue = argumentRange.maxValue then
      c_expr.expr &:= integerLiteral(card(bitset(argumentRange.minValue)));
    elsif function_range_check then
      if argumentRange.minValue >= 0 then
        # This function cannot trigger a range error.
        countRangeOptimizations(c_expr);
        c_expr.expr &:= "/*no_range_check_cardinality*/uintCard((uintType) (";
        process_expr(argument, c_expr);
        c_expr.expr &:= "))";
      else
        incr(countRangeChecks);
        c_expr.expr &:= "(";
        argumentName := getTempVariable("intType", "tmp_", argument, c_expr);
        c_expr.expr &:= "rngChk(";
        c_expr.expr &:= argumentName;
        c_expr.expr &:= " < ";
        c_expr.expr &:= integerLiteral(0);
        c_expr.expr &:= ") ? ";
        c_expr.expr &:= intRaiseError("RANGE_ERROR");
        c_expr.expr &:= " : ";
        c_expr.expr &:= "uintCard((uintType) (";
        c_expr.expr &:= argumentName;
        c_expr.expr &:= ")))";
      end if;
    else
      incr(countNoRangeChecks);
      c_expr.expr &:= "uintCard((uintType) (";
      process_expr(argument, c_expr);
      c_expr.expr &:= "))";
    end if;
  end func;


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

  begin
    if evaluate_const_expr >= 1 and
        isActionExpression(params[1], "SET_ICONV1") then
      optimize_set_card(getActionParameter(params[1], 1), c_expr);
    elsif evaluate_const_expr >= 1 and
        isActionExpression(params[1], "SET_ICONV3") then
      optimize_set_card(getActionParameter(params[1], 3), c_expr);
    else
      c_expr.expr &:= "setCard(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ")";
    end if;
  end func;


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

  begin
    c_expr.expr &:= "setCmp(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ", ";
    getAnyParamToExpr(params[2], c_expr);
    c_expr.expr &:= ")";
  end func;


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

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


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

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


const proc: process (SET_CPY, in reference: function,
    in ref_list: params, 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 expr_type: c_param3 is expr_type.value;
  begin
    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 &:= "setType new_set;\n";
      statement.expr &:= "new_set=";
      statement.expr &:= c_param3.result_expr;
      statement.expr &:= ";\n";
      if isNormalVariable(params[1]) then
        statement.expr &:= "setDestr(";
        statement.expr &:= c_param1.expr;
        statement.expr &:= ");\n";
        statement.expr &:= c_param1.expr;
        statement.expr &:= "=new_set;\n";
      else
        statement.temp_decls &:= "setType *set_ptr=&(";
        statement.temp_decls &:= c_param1.expr;
        statement.temp_decls &:= ");\n";
        statement.expr &:= "setDestr(*set_ptr);\n";
        statement.expr &:= "*set_ptr=new_set;\n";
      end if;
    else
      statement.expr &:= "setCpy(&(";
      statement.expr &:= c_param1.expr;
      statement.expr &:= "), ";
      statement.expr &:= c_param3.expr;
      statement.expr &:= ");\n";
    end if;
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process (SET_DIFF, 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], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      c_expr.expr &:= bitsetLiteral(EMPTY_SET);
    elsif getConstant(params[3], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      process_expr(params[1], c_expr);
    else
      prepare_set_result(getExprResultType(params[1]), c_expr);
      c_expr.result_expr := "setDiff(";
      getAnyParamToResultExpr(params[1], c_expr);
      c_expr.result_expr &:= ", ";
      getAnyParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


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

  local
    var reference: evaluatedParam is NIL;
    var expr_type: statement is expr_type.value;
  begin
    if getConstant(params[3], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      incr(countOptimizations);
      c_expr.expr &:= "/* ignore: set -:= EMPTY_SET */\n";
    else
      statement.expr := "setDiffAssign(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      getAnyParamToExpr(params[3], statement);
      statement.expr &:= ");\n";
    end if;
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process_const_set_elem (in reference: param1, in var bitset: aBitset,
    inout expr_type: c_expr) is func

  local
    var intRange: numberRange is intRange.value;
    var reference: evaluatedParam is NIL;
    var string: number_name is "";
    var integer: number is 0;
    var boolean: first_element is TRUE;
    var boolean: isElement is FALSE;
  begin
    numberRange := getIntRange(param1);
    if aBitset <> EMPTY_SET then
      aBitset &:= {max(numberRange.minValue, min(aBitset)) ..
                   min(numberRange.maxValue, max(aBitset))};
    end if;
    if getConstant(param1, INTOBJECT, evaluatedParam) then
      incr(countOptimizations);
      isElement := getValue(evaluatedParam, integer) in aBitset;
      c_expr.expr &:= str(ord(isElement));
      c_expr.expr &:= "/*";
      c_expr.expr &:= str(isElement);
      c_expr.expr &:= "*/";
    elsif aBitset = EMPTY_SET then
      incr(countOptimizations);
      c_expr.expr &:= "0/*FALSE*/";
    elsif numberRange.minValue >= min(aBitset) and
          numberRange.maxValue <= max(aBitset) and
          {numberRange.minValue .. numberRange.maxValue} <= aBitset and
          not numberRange.mayRaiseException then
      incr(countOptimizations);
      c_expr.expr &:= "1/*TRUE*/";
    elsif card(aBitset) = 1 then
      incr(countOptimizations);
      c_expr.expr &:= "(";
      process_expr(param1, c_expr);
      c_expr.expr &:= ") == ";
      c_expr.expr &:= integerLiteral(min(aBitset));
    elsif card(aBitset) >= 3 and max(aBitset) - min(aBitset) = pred(card(aBitset)) then
      incr(countOptimizations);
      c_expr.expr &:= "(";
      number_name := getParameterAsVariable("intType", "tmp_", param1, c_expr);
      if ccConf.TWOS_COMPLEMENT_INTTYPE and min(aBitset) = 0 then
        c_expr.expr &:= "(uintType)(";
        c_expr.expr &:= number_name;
        c_expr.expr &:= ")";
      else
        c_expr.expr &:= number_name;
        c_expr.expr &:= ">=";
        c_expr.expr &:= integerLiteral(min(aBitset));
        c_expr.expr &:= " && ";
        c_expr.expr &:= number_name;
      end if;
      c_expr.expr &:= "<=";
      c_expr.expr &:= integerLiteral(max(aBitset));
      c_expr.expr &:= ")";
    elsif card(aBitset) = 2 or
        card(aBitset) = 3 and (min(aBitset) < 0 or max(aBitset) >= ccConf.INTTYPE_SIZE) then
      incr(countOptimizations);
      c_expr.expr &:= "(";
      number_name := getParameterAsVariable("intType", "tmp_", param1, c_expr);
      for number range aBitset do
        if first_element then
          first_element := FALSE;
        else
          c_expr.expr &:= "||";
        end if;
        c_expr.expr &:= number_name;
        c_expr.expr &:= "==";
        c_expr.expr &:= integerLiteral(number);
      end for;
      c_expr.expr &:= ")";
    elsif min(aBitset) >= 0 and max(aBitset) < ccConf.INTTYPE_SIZE then
      incr(countOptimizations);
      if numberRange.minValue >= 0 and numberRange.maxValue < ccConf.INTTYPE_SIZE then
        c_expr.expr &:= bitsetElemAsHex(aBitset, 0);
        c_expr.expr &:= ">>(";
        process_expr(param1, c_expr);
        c_expr.expr &:= ")&1";
      else
        c_expr.expr &:= "(";
        number_name := getParameterAsVariable("intType", "tmp_", param1, c_expr);
        if ccConf.TWOS_COMPLEMENT_INTTYPE then
          c_expr.expr &:= "(uintType)(";
          c_expr.expr &:= number_name;
          c_expr.expr &:= ")";
        else
          c_expr.expr &:= number_name;
          c_expr.expr &:= ">=0&&";
          c_expr.expr &:= number_name;
        end if;
        c_expr.expr &:= "<=";
        c_expr.expr &:= str(max(aBitset));
        c_expr.expr &:= "&&";
        c_expr.expr &:= bitsetElemAsHex(aBitset, 0);
        c_expr.expr &:= ">>";
        c_expr.expr &:= number_name;
        c_expr.expr &:= "&1)";
      end if;
    elsif max(aBitset) - min(aBitset) < ccConf.INTTYPE_SIZE then
      incr(countOptimizations);
      c_expr.expr &:= "(";
      number_name := getParameterAsVariable("intType", "tmp_", param1, c_expr);
      if max(aBitset) >= integer.first + ccConf.INTTYPE_SIZE and
          max(aBitset) - ccConf.INTTYPE_SIZE < numberRange.minValue then
        if numberRange.minValue <= integer.last - ccConf.INTTYPE_SIZE and
            numberRange.maxValue >= numberRange.minValue + ccConf.INTTYPE_SIZE then
          c_expr.expr &:= number_name;
          c_expr.expr &:= "<=";
          c_expr.expr &:= integerLiteral(max(aBitset));
          c_expr.expr &:= "&&";
        end if;
        c_expr.expr &:= bitsetElemAsHex(aBitset, numberRange.minValue);
        c_expr.expr &:= ">>(";
        c_expr.expr &:= number_name;
        c_expr.expr &:= "- ";
        c_expr.expr &:= integerLiteral(numberRange.minValue);
      else
        c_expr.expr &:= number_name;
        c_expr.expr &:= ">=";
        c_expr.expr &:= integerLiteral(min(aBitset));
        c_expr.expr &:= "&&";
        c_expr.expr &:= number_name;
        c_expr.expr &:= "<=";
        c_expr.expr &:= integerLiteral(max(aBitset));
        c_expr.expr &:= "&&";
        c_expr.expr &:= bitsetElemAsHex(aBitset, min(aBitset));
        c_expr.expr &:= ">>(";
        c_expr.expr &:= number_name;
        c_expr.expr &:= "- ";
        c_expr.expr &:= integerLiteral(min(aBitset));
      end if;
      c_expr.expr &:= ")&1)";
    elsif inlineFunctions then
      incr(countOptimizations);
      incr(countInlinedFunctions);
      c_expr.expr &:= "(";
      number_name := getParameterAsVariable("intType", "tmp_", param1, c_expr);
      if ccConf.TWOS_COMPLEMENT_INTTYPE and bitset_pos(min(aBitset)) = 0 then
        c_expr.expr &:= "(uintType)(";
        c_expr.expr &:= number_name;
        c_expr.expr &:= ")";
      else
        c_expr.expr &:= number_name;
        c_expr.expr &:= ">=";
        c_expr.expr &:= integerLiteral(min(aBitset));
        c_expr.expr &:= "&&";
        c_expr.expr &:= number_name;
      end if;
      c_expr.expr &:= "<=";
      c_expr.expr &:= integerLiteral(max(aBitset));
      c_expr.expr &:= "&&";
      c_expr.expr &:= bitsetLiteral(aBitset);
      c_expr.expr &:= "->bitset[(uintType)(bitset_pos(";
      c_expr.expr &:= number_name;
      c_expr.expr &:= ")";
      if bitset_pos(min(aBitset)) <> 0 then
        c_expr.expr &:= "- ";
        c_expr.expr &:= integerLiteral(bitset_pos(min(aBitset)));
      end if;
      c_expr.expr &:= ")] >> (";
      c_expr.expr &:= number_name;
      c_expr.expr &:= " & bitset_mask)&1)";
    else
      c_expr.expr &:= "setElem(";
      process_expr(param1, c_expr);
      c_expr.expr &:= ", ";
      c_expr.expr &:= bitsetLiteral(aBitset);
      c_expr.expr &:= ")";
    end if;
  end func;


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

  local
    var reference: evaluatedParam is NIL;
    var string: number_name is "";
    var string: position_name is "";
    var string: set_name is "";
  begin
    if getConstant(param3, SETOBJECT, evaluatedParam) then
      process_const_set_elem(param1, getValue(evaluatedParam, bitset), c_expr);
    elsif inlineFunctions then
      incr(countOptimizations);
      incr(countInlinedFunctions);
      c_expr.expr &:= "(";
      number_name := getParameterAsVariable("intType", "tmp_", param1, c_expr);
      set_name := getParameterAsVariable("const_setType", "tmp_", param3, c_expr);
      incr(c_expr.temp_num);
      position_name := "tmp_" & str(c_expr.temp_num);
      c_expr.temp_decls &:= "intType ";
      c_expr.temp_decls &:= position_name;
      c_expr.temp_decls &:= ";\n";
      c_expr.expr &:= position_name;
      c_expr.expr &:= "=bitset_pos(";
      c_expr.expr &:= number_name;
      c_expr.expr &:= "),";
      c_expr.expr &:= position_name;
      c_expr.expr &:= ">=";
      c_expr.expr &:= set_name;
      c_expr.expr &:= "->min_position&&";
      c_expr.expr &:= position_name;
      c_expr.expr &:= "<=";
      c_expr.expr &:= set_name;
      c_expr.expr &:= "->max_position&&";
      c_expr.expr &:= set_name;
      c_expr.expr &:= "->bitset[(uintType)(";
      c_expr.expr &:= position_name;
      c_expr.expr &:= "-";
      c_expr.expr &:= set_name;
      c_expr.expr &:= "->min_position)] >> (";
      c_expr.expr &:= number_name;
      c_expr.expr &:= " & bitset_mask)&1)";
    else
      c_expr.expr &:= "setElem(";
      process_expr(param1, c_expr);
      c_expr.expr &:= ", ";
      getAnyParamToExpr(param3, c_expr);
      c_expr.expr &:= ")";
    end if;
  end func;


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

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


const proc: process_const_set_eq (in reference: param1, in bitset: aBitset,
    inout expr_type: c_expr) is func

  local
    var reference: evaluatedParam is NIL;
    var boolean: isEqual is FALSE;
  begin
    if getConstant(param1, SETOBJECT, evaluatedParam) then
      incr(countOptimizations);
      isEqual := getValue(evaluatedParam, bitset) = aBitset;
      c_expr.expr &:= str(ord(isEqual));
      c_expr.expr &:= "/*";
      c_expr.expr &:= str(isEqual);
      c_expr.expr &:= "*/";
    elsif aBitset = EMPTY_SET then
      incr(countOptimizations);
      c_expr.expr &:= "setIsEmpty(";
      getAnyParamToExpr(param1, c_expr);
      c_expr.expr &:= ")";
    else
      c_expr.expr &:= "setEq(";
      getAnyParamToExpr(param1, c_expr);
      c_expr.expr &:= ", ";
      c_expr.expr &:= bitsetLiteral(aBitset);
      c_expr.expr &:= ")";
    end if;
  end func;


const proc: process (SET_EQ, 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], SETOBJECT, evaluatedParam) then
      process_const_set_eq(params[3], getValue(evaluatedParam, bitset), c_expr);
    elsif getConstant(params[3], SETOBJECT, evaluatedParam) then
      process_const_set_eq(params[1], getValue(evaluatedParam, bitset), c_expr);
    else
      c_expr.expr &:= "setEq(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ", ";
      getAnyParamToExpr(params[3], c_expr);
      c_expr.expr &:= ")";
    end if;
  end func;


const proc: process_inline_set_excl (in ref_list: params,
    inout expr_type: c_expr) is func

  local
    var expr_type: statement is expr_type.value;
    var string: number_name is "";
    var string: position_name is "";
    var string: set_name is "";
  begin
    if isNormalVariable(params[1]) then
      set_name := normalVariable(params[1], statement);
    else
      incr(statement.temp_num);
      set_name := "tmp_" & str(statement.temp_num);
      statement.temp_decls &:= "setType ";
      statement.temp_decls &:= set_name;
      statement.temp_decls &:= ";\n";
      statement.expr &:= set_name;
      statement.expr &:= "=";
      process_expr(params[1], statement);
      statement.expr &:= ";\n";
    end if;
    if isNormalVariable(params[2]) then
      number_name := normalVariable(params[2], statement);
    else
      incr(statement.temp_num);
      number_name := "tmp_" & str(statement.temp_num);
      statement.temp_decls &:= "intType ";
      statement.temp_decls &:= number_name;
      statement.temp_decls &:= ";\n";
      statement.expr &:= number_name;
      statement.expr &:= "=";
      process_expr(params[2], statement);
      statement.expr &:= ";\n";
    end if;
    incr(statement.temp_num);
    position_name := "tmp_" & str(statement.temp_num);
    statement.temp_decls &:= "intType ";
    statement.temp_decls &:= position_name;
    statement.temp_decls &:= ";\n";
    statement.expr &:= position_name;
    statement.expr &:= "=bitset_pos(";
    statement.expr &:= number_name;
    statement.expr &:= ");\n";
    statement.expr &:= "if (";
    statement.expr &:= position_name;
    statement.expr &:= ">=";
    statement.expr &:= set_name;
    statement.expr &:= "->min_position&&";
    statement.expr &:= position_name;
    statement.expr &:= "<=";
    statement.expr &:= set_name;
    statement.expr &:= "->max_position) {\n";
    statement.expr &:= set_name;
    statement.expr &:= "->bitset[(uintType)(";
    statement.expr &:= position_name;
    statement.expr &:= "-";
    statement.expr &:= set_name;
    statement.expr &:= "->min_position)] &= ~((bitSetType) 1 << (((unsigned int) ";
    statement.expr &:= number_name;
    statement.expr &:= ") & bitset_mask));\n";
    statement.expr &:= "}\n";
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


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

  local
    var expr_type: statement is expr_type.value;
  begin
    if inlineFunctions then
      incr(countOptimizations);
      incr(countInlinedFunctions);
      process_inline_set_excl(params, c_expr);
    else
      statement.expr := "setExcl(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      process_expr(params[2], statement);
      statement.expr &:= ");\n";
      doLocalDeclsOfStatement(statement, c_expr);
    end if;
  end func;


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

  begin
    c_expr.expr &:= "setIsSubset(";
    getAnyParamToExpr(params[3], c_expr);
    c_expr.expr &:= ", ";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setIsProperSubset(";
    getAnyParamToExpr(params[3], c_expr);
    c_expr.expr &:= ", ";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setHashCode(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setIConv(";
    process_expr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setIConv(";
    process_expr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  local
    var expr_type: statement is expr_type.value;
  begin
    statement.expr := "setIncl(&(";
    process_expr(params[1], statement);
    statement.expr &:= "), ";
    process_expr(params[2], statement);
    statement.expr &:= ");\n";
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


const proc: process (SET_INTERSECT, 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], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET or
        getConstant(params[3], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      c_expr.expr &:= bitsetLiteral(EMPTY_SET);
    else
      prepare_set_result(getExprResultType(params[1]), c_expr);
      c_expr.result_expr := "setIntersect(";
      getAnyParamToResultExpr(params[1], c_expr);
      c_expr.result_expr &:= ", ";
      getAnyParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


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

  local
    var reference: evaluatedParam is NIL;
    var expr_type: statement is expr_type.value;
  begin
    if getConstant(params[3], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      incr(countOptimizations);
      statement.expr &:= "setCpy(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      statement.expr &:= bitsetLiteral(EMPTY_SET);
      statement.expr &:= ");\n";
    else
      statement.expr := "setIntersectAssign(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      getAnyParamToExpr(params[3], statement);
      statement.expr &:= ");\n";
    end if;
    doLocalDeclsOfStatement(statement, c_expr);
  end func;


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

  begin
    c_expr.expr &:= "setIsSubset(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ", ";
    getAnyParamToExpr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setIsProperSubset(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ", ";
    getAnyParamToExpr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setMax(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setMin(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "!";
    process(SET_EQ, function, params, c_expr);
  end func;


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

  begin
    c_expr.expr &:= "setNext(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ", ";
    process_expr(params[2], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "!(";
    process_set_elem(params[1], params[4], c_expr);
    c_expr.expr &:= ")";
  end func;


const proc: process_const_set_rand (in bitset: aBitset,
    inout expr_type: c_expr) is func

  begin
    if aBitset = EMPTY_SET then
      incr(countOptimizations);
      warning(DOES_RAISE, "RANGE_ERROR", c_expr);
      c_expr.expr &:= intRaiseError("RANGE_ERROR");
    elsif card(aBitset) = 1 then
      incr(countOptimizations);
      c_expr.expr &:= integerLiteral(min(aBitset));
    elsif min(aBitset) + pred(card(aBitset)) = max(aBitset) then
      process_const_int_rand(min(aBitset), max(aBitset), c_expr);
    else
      c_expr.expr &:= "setRand(";
      c_expr.expr &:= bitsetLiteral(aBitset);
      c_expr.expr &:= ")";
    end if;
  end func;


const proc: process (SET_RAND, 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], SETOBJECT, evaluatedParam) then
      process_const_set_rand(getValue(evaluatedParam, bitset), c_expr);
    else
      c_expr.expr &:= "setRand(";
      getAnyParamToExpr(params[1], c_expr);
      c_expr.expr &:= ")";
    end if;
  end func;


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

  begin
    prepare_set_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "setRangelit(";
    getAnyParamToResultExpr(params[2], c_expr);
    c_expr.result_expr &:= ", ";
    getAnyParamToResultExpr(params[4], c_expr);
    c_expr.result_expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setSConv(";
    getAnyParamToExpr(params[1], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    c_expr.expr &:= "setSConv(";
    getAnyParamToExpr(params[3], c_expr);
    c_expr.expr &:= ")";
  end func;


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

  begin
    prepare_set_result(getExprResultType(params[1]), c_expr);
    c_expr.result_expr := "setSymdiff(";
    getAnyParamToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ", ";
    getAnyParamToResultExpr(params[3], c_expr);
    c_expr.result_expr &:= ")";
  end func;


const proc: process (SET_UNION, 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], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      process_expr(params[3], c_expr);
    elsif getConstant(params[3], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      process_expr(params[1], c_expr);
    else
      prepare_set_result(getExprResultType(params[1]), c_expr);
      c_expr.result_expr := "setUnion(";
      getAnyParamToResultExpr(params[1], c_expr);
      c_expr.result_expr &:= ", ";
      getAnyParamToResultExpr(params[3], c_expr);
      c_expr.result_expr &:= ")";
    end if;
  end func;


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

  local
    var reference: evaluatedParam is NIL;
    var expr_type: statement is expr_type.value;
  begin
    if getConstant(params[3], SETOBJECT, evaluatedParam) and
        getValue(evaluatedParam, bitset) = EMPTY_SET then
      incr(countOptimizations);
      c_expr.expr &:= "/* ignore: set |:= EMPTY_SET */\n";
    else
      statement.expr := "setUnionAssign(&(";
      process_expr(params[1], statement);
      statement.expr &:= "), ";
      getAnyParamToExpr(params[3], statement);
      statement.expr &:= ");\n";
      doLocalDeclsOfStatement(statement, c_expr);
    end if;
  end func;


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

  begin
    prepare_set_result(resultType(getType(function)), c_expr);
    c_expr.result_expr := "setValue(";
    getStdParamToResultExpr(params[1], c_expr);
    c_expr.result_expr &:= ")";
  end func;