(********************************************************************)
(*                                                                  *)
(*  intrange.s7i  Handle ranges of possible values for expressions. *)
(*  Copyright (C) 2020, 2021, 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 type: intRange is new struct
    var integer: minValue is integer.first;
    var integer: maxValue is integer.last;
    var boolean: mayOverflow is FALSE;
    var boolean: mayRaiseException is TRUE;
  end struct;


const func boolean: (ref intRange: range1) = (ref intRange: range2) is
  return range1.minValue = range2.minValue and range1.maxValue = range2.maxValue;


const func boolean: (ref intRange: range1) <> (ref intRange: range2) is
  return range1.minValue <> range2.minValue or range1.maxValue <> range2.maxValue;


const type: intRangeOfVariableHash is hash[reference] intRange;

var intRangeOfVariableHash: intRangeOfVariable is intRangeOfVariableHash.value;


const func intRange: getIntRange (in integer: number) is func
  result
    var intRange: numberRange is intRange.value;
  begin
    numberRange.minValue := number;
    numberRange.maxValue := number;
    numberRange.mayOverflow := FALSE;
    numberRange.mayRaiseException := FALSE;
  end func;


const func intRange: getIntRange (in reference: intExpression) is forward;


const func intRange: getIntAbsRange (in intRange: argument1Range) is func
  result
    var intRange: absRange is intRange.value;
  begin
    if argument1Range.minValue > argument1Range.maxValue then
      absRange.minValue := 0;
      absRange.maxValue := -1;
    else
      if argument1Range.minValue = integer.first then
        if argument1Range.maxValue = integer.first then
          # Will always raise RANGE_ERROR.
          absRange.minValue := 0;
          absRange.maxValue := -1;
          absRange.mayOverflow := TRUE;
        else
          if argument1Range.maxValue >= 0 then
            absRange.minValue := 0;
          else
            absRange.minValue := -argument1Range.maxValue;
          end if;
          absRange.mayOverflow := TRUE;
        end if;
      else
        if argument1Range.minValue <= 0 and argument1Range.maxValue >= 0 then
          absRange.minValue := 0;
        else
          absRange.minValue := min(abs(argument1Range.minValue),
                                   abs(argument1Range.maxValue));
        end if;
        absRange.maxValue := max(abs(argument1Range.minValue),
                               abs(argument1Range.maxValue));
      end if;
      absRange.mayRaiseException := argument1Range.mayRaiseException or
                                    absRange.mayOverflow;
    end if;
  end func;


const func intRange: getIntAddRange (in intRange: summand1Range, in intRange: summand2Range) is func
  result
    var intRange: sumRange is intRange.value;
  local
    var boolean: valueOutOfRange is FALSE;
  begin
    if summand1Range.minValue > summand1Range.maxValue or
        summand2Range.minValue > summand2Range.maxValue then
      sumRange.minValue := 0;
      sumRange.maxValue := -1;
    else
      if summand1Range.minValue < 0 then
        if summand2Range.minValue >= 0 or
            summand1Range.minValue >= integer.first - summand2Range.minValue then
          sumRange.minValue := summand1Range.minValue + summand2Range.minValue;
        else
          sumRange.mayOverflow := TRUE;
        end if;
      else
        if summand2Range.minValue <= 0 or
            summand1Range.minValue <= integer.last - summand2Range.minValue then
          sumRange.minValue := summand1Range.minValue + summand2Range.minValue;
        else
          valueOutOfRange := TRUE;
        end if
      end if;
      if summand1Range.maxValue > 0 then
        if summand2Range.maxValue <= 0 or
            summand1Range.maxValue <= integer.last - summand2Range.maxValue then
          sumRange.maxValue := summand1Range.maxValue + summand2Range.maxValue;
        else
          sumRange.mayOverflow := TRUE;
        end if;
      else
        if summand2Range.maxValue >= 0 or
            summand1Range.maxValue >= integer.first - summand2Range.maxValue then
          sumRange.maxValue := summand1Range.maxValue + summand2Range.maxValue;
        else
          valueOutOfRange := TRUE;
        end if
      end if;
      if sumRange.minValue > sumRange.maxValue or valueOutOfRange then
        sumRange.minValue := 0;
        sumRange.maxValue := -1;
        sumRange.mayOverflow := TRUE;
      end if;
      sumRange.mayRaiseException := summand1Range.mayRaiseException or
                                    summand2Range.mayRaiseException or
                                    sumRange.mayOverflow;
    end if;
  end func;


const func intRange: getIntSbtrRange (in intRange: minuendRange, in intRange: subtrahendRange) is func
  result
    var intRange: differenceRange is intRange.value;
  local
    var boolean: valueOutOfRange is FALSE;
  begin
    if minuendRange.minValue > minuendRange.maxValue or
        subtrahendRange.minValue > subtrahendRange.maxValue then
      differenceRange.minValue := 0;
      differenceRange.maxValue := -1;
    else
      if minuendRange.minValue < 0 then
        if subtrahendRange.maxValue <= 0 or
            minuendRange.minValue >= integer.first + subtrahendRange.maxValue then
          differenceRange.minValue := minuendRange.minValue - subtrahendRange.maxValue;
        else
          differenceRange.mayOverflow := TRUE;
        end if;
      else
        if subtrahendRange.maxValue >= 0 or
            minuendRange.minValue <= integer.last + subtrahendRange.maxValue then
          differenceRange.minValue := minuendRange.minValue - subtrahendRange.maxValue;
        else
          valueOutOfRange := TRUE;
        end if
      end if;
      if minuendRange.maxValue >= 0 then
        if subtrahendRange.minValue >= 0 or
            minuendRange.maxValue <= integer.last + subtrahendRange.minValue then
          differenceRange.maxValue := minuendRange.maxValue - subtrahendRange.minValue;
        else
          differenceRange.mayOverflow := TRUE;
        end if;
      else
        if subtrahendRange.minValue <= 0 or
            minuendRange.maxValue >= integer.first + subtrahendRange.minValue then
          differenceRange.maxValue := minuendRange.maxValue - subtrahendRange.minValue;
        else
          valueOutOfRange := TRUE;
        end if
      end if;
      if differenceRange.minValue > differenceRange.maxValue or valueOutOfRange then
        differenceRange.minValue := 0;
        differenceRange.maxValue := -1;
        differenceRange.mayOverflow := TRUE;
      end if;
      differenceRange.mayRaiseException := minuendRange.mayRaiseException or
                                           subtrahendRange.mayRaiseException or
                                           differenceRange.mayOverflow;
    end if;
  end func;


const func intRange: getIntLshiftRange (in intRange: numberRange, in intRange: lshiftRange) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var boolean: valueOutOfRange is FALSE;
    var integer: lshiftMin is 0;
    var integer: lshiftMax is 0;
    var integer: lshiftFound is 0;
    var integer: lshift is 0;
  begin
    if numberRange.minValue > numberRange.maxValue or
        lshiftRange.minValue > lshiftRange.maxValue then
      valueRange.minValue := 0;
      valueRange.maxValue := -1;
    elsif lshiftRange.maxValue < 0 or
        lshiftRange.minValue > bitLength(integer.last) then
      valueRange.minValue := 0;
      valueRange.maxValue := -1;
      valueRange.mayOverflow := TRUE;
    else
      if numberRange.minValue < 0 then
        if lshiftRange.maxValue <= bitLength(integer.last) and
            numberRange.minValue >= integer.first >> lshiftRange.maxValue then
          valueRange.minValue := numberRange.minValue << lshiftRange.maxValue;
        else
          lshiftMin := max(0, lshiftRange.minValue);
          lshiftMax := min(bitLength(integer.last), lshiftRange.maxValue);
          lshiftFound := integer.last;
          for lshift range lshiftMin to lshiftMax until lshiftFound <> integer.last do
            if integer.first >> lshift >= numberRange.minValue then
              lshiftFound := lshift;
            end if;
          end for;
          if lshiftFound <> integer.last then
            valueRange.minValue := integer.first >> lshiftFound << lshiftFound;
          end if;
          valueRange.mayOverflow := TRUE;
        end if;
      elsif numberRange.minValue > 0 then
        if lshiftRange.minValue >= 0 then
          if numberRange.minValue <= integer.last >> lshiftRange.minValue then
            valueRange.minValue := numberRange.minValue << lshiftRange.minValue;
          else
            # No valid valueRange because minValue > integer.last holds.
            valueOutOfRange := TRUE;
            valueRange.mayOverflow := TRUE;
          end if;
        else
          valueRange.minValue := numberRange.minValue;
          valueRange.mayOverflow := TRUE;
        end if;
      else
        valueRange.minValue := 0;
      end if;
      if numberRange.maxValue < 0 then
        if lshiftRange.minValue >= 0 then
          if numberRange.maxValue >= integer.first >> lshiftRange.minValue then
            valueRange.maxValue := numberRange.maxValue << lshiftRange.minValue;
          else
            # No valid valueRange because maxValue < integer.first holds.
            valueOutOfRange := TRUE;
            valueRange.mayOverflow := TRUE;
          end if;
        else
          valueRange.maxValue := numberRange.maxValue;
          valueRange.mayOverflow := TRUE;
        end if;
      elsif numberRange.maxValue > 0 then
        if lshiftRange.maxValue <= bitLength(integer.last) and
            numberRange.maxValue <= integer.last >> lshiftRange.maxValue then
          valueRange.maxValue := numberRange.maxValue << lshiftRange.maxValue;
        else
          lshiftMin := max(0, lshiftRange.minValue);
          lshiftMax := min(bitLength(integer.last), lshiftRange.maxValue);
          lshiftFound := integer.last;
          for lshift range lshiftMin to lshiftMax until lshiftFound <> integer.last do
            if integer.last >> lshift <= numberRange.maxValue then
              lshiftFound := lshift;
            end if;
          end for;
          if lshiftFound <> integer.last then
            valueRange.maxValue := integer.last >> lshiftFound << lshiftFound;
          end if;
          valueRange.mayOverflow := TRUE;
        end if;
      else
        valueRange.maxValue := 0;
      end if;
      if valueRange.minValue > valueRange.maxValue or valueOutOfRange then
        valueRange.minValue := 0;
        valueRange.maxValue := -1;
      end if;
      valueRange.mayOverflow := valueRange.mayOverflow or
                                lshiftRange.minValue < 0 or
                                lshiftRange.maxValue > bitLength(integer.last);
      valueRange.mayRaiseException := numberRange.mayRaiseException or
                                      lshiftRange.mayRaiseException or
                                      valueRange.mayOverflow;
    end if;
  end func;


const func intRange: getIntRshiftRange (in intRange: numberRange, in intRange: rshiftRange) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var boolean: valueOutOfRange is FALSE;
  begin
    if numberRange.minValue > numberRange.maxValue or
        rshiftRange.minValue > rshiftRange.maxValue then
      valueRange.minValue := 0;
      valueRange.maxValue := -1;
    elsif rshiftRange.maxValue < 0 or
        rshiftRange.minValue > bitLength(integer.last) then
      valueRange.minValue := 0;
      valueRange.maxValue := -1;
      valueRange.mayOverflow := TRUE;
    else
      if numberRange.minValue < 0 then
        if rshiftRange.minValue >= 0 then
          valueRange.minValue := numberRange.minValue >> rshiftRange.minValue;
        else
          valueRange.minValue := numberRange.minValue;
        end if;
      elsif numberRange.minValue > 0 then
        if rshiftRange.maxValue <= bitLength(integer.last) then
          valueRange.minValue := numberRange.minValue >> rshiftRange.maxValue;
        else
          valueRange.minValue := 0;
        end if;
      else
        valueRange.minValue := 0;
      end if;
      if numberRange.maxValue < 0 then
        if rshiftRange.maxValue <= bitLength(integer.last) then
          valueRange.maxValue := numberRange.maxValue >> rshiftRange.maxValue;
        else
          valueRange.maxValue := -1;
        end if;
      elsif numberRange.maxValue > 0 then
        if rshiftRange.minValue >= 0 then
          valueRange.maxValue := numberRange.maxValue >> rshiftRange.minValue;
        else
          valueRange.maxValue := numberRange.maxValue;
        end if;
      else
        valueRange.maxValue := 0;
      end if;
      if valueRange.minValue > valueRange.maxValue then
        valueRange.minValue := 0;
        valueRange.maxValue := -1;
      end if;
      valueRange.mayOverflow := rshiftRange.minValue < 0 or
                                rshiftRange.maxValue > bitLength(integer.last);
      valueRange.mayRaiseException := numberRange.mayRaiseException or
                                      rshiftRange.mayRaiseException or
                                      valueRange.mayOverflow;
    end if;
  end func;


const func intRange: getIntModRange (in reference: dividend, in reference: divisor) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var intRange: divisorRange is intRange.value;
    var reference: evaluatedParam is NIL;
    var integer: dividendValue is 0;
    var intRange: valueRange2 is intRange.value;
  begin
    divisorRange := getIntRange(divisor);
    if divisorRange.minValue > 0 then
      valueRange.minValue := 0;
      valueRange.maxValue := pred(divisorRange.maxValue);
    elsif divisorRange.maxValue < 0 then
      valueRange.minValue := succ(divisorRange.minValue);
      valueRange.maxValue := 0;
    end if;
    if getConstant(dividend, INTOBJECT, evaluatedParam) then
      dividendValue := getValue(evaluatedParam, integer);
      if dividendValue = integer.first then
        valueRange2.minValue := succ(integer.first) div 2;
        valueRange2.maxValue := -2 - integer.first;
      elsif dividendValue = integer.last then
        valueRange2.minValue := 2 - integer.last;
        valueRange2.maxValue := pred(integer.last) div 2;
      elsif dividendValue > 0 then
        if integer.first + dividendValue <= 2 - dividendValue then
          valueRange2.minValue := integer.first + dividendValue;
        else
          valueRange2.minValue := 2 - dividendValue;
        end if;
        valueRange2.maxValue := dividendValue;
      elsif dividendValue = 0 then
        valueRange2.minValue := 0;
        valueRange2.maxValue := 0;
      else # dividendValue < 0 then
        valueRange2.minValue := dividendValue;
        if integer.last + dividendValue >= -(dividendValue + 2) then
          valueRange2.maxValue := integer.last + dividendValue;
        else
          valueRange2.maxValue := -(dividendValue + 2);
        end if;
      end if;
      valueRange.minValue := max(valueRange.minValue, valueRange2.minValue);
      valueRange.maxValue := min(valueRange.maxValue, valueRange2.maxValue);
    end if;
    if valueRange.minValue > valueRange.maxValue then
      valueRange.minValue := 0;
      valueRange.maxValue := -1;
    end if;
  end func;


const func intRange: getIntNegateRange (in intRange: argument1Range) is func
  result
    var intRange: negatedRange is intRange.value;
  begin
    if argument1Range.maxValue < argument1Range.minValue then
      negatedRange.minValue := 0;
      negatedRange.maxValue := -1;
    elsif argument1Range.maxValue < -integer.last then
      negatedRange.minValue := 0;
      negatedRange.maxValue := -1;
      negatedRange.mayOverflow := TRUE;
    else
      negatedRange.minValue := -argument1Range.maxValue;
      if argument1Range.minValue < -integer.last then
        negatedRange.maxValue := integer.last;
        negatedRange.mayOverflow := TRUE;
      else
        negatedRange.maxValue := -argument1Range.minValue;
      end if;
      negatedRange.mayRaiseException := argument1Range.mayRaiseException or
                                        negatedRange.mayOverflow;
    end if;
  end func;


const func intRange: getIntMultRange (in integer: factor1, in intRange: factor2Range) is func
  result
    var intRange: productRange is intRange.value;
  local
    var boolean: valueOutOfRange is FALSE;
  begin
    if factor1 >= 2 then
      if factor2Range.minValue <= integer.last div factor1 then
        if factor2Range.minValue >= integer.first div factor1 then
          productRange.minValue := factor2Range.minValue * factor1;
        else
          productRange.minValue := integer.first div factor1 * factor1;
          productRange.mayOverflow := TRUE;
        end if;
      else
        valueOutOfRange := TRUE;
      end if;
      if factor2Range.maxValue >= integer.first div factor1 then
        if factor2Range.maxValue <= integer.last div factor1 then
          productRange.maxValue := factor2Range.maxValue * factor1;
        else
          productRange.maxValue := integer.last div factor1 * factor1;
          productRange.mayOverflow := TRUE;
        end if;
      else
        valueOutOfRange := TRUE;
      end if;
    elsif factor1 = 1 then
      productRange := factor2Range;
      productRange.mayOverflow := FALSE;
    elsif factor1 = 0 then
      productRange.minValue := 0;
      productRange.maxValue := 0;
    elsif factor1 = -1 then
      productRange := getIntNegateRange(factor2Range);
    else # factor1 <= -2 then
      if factor2Range.minValue <= integer.first div factor1 then
        if factor2Range.minValue >= integer.last div factor1 then
          productRange.maxValue := factor2Range.minValue * factor1;
        else
          productRange.maxValue := integer.last div factor1 * factor1;
          productRange.mayOverflow := TRUE;
        end if;
      else
        valueOutOfRange := TRUE;
      end if;
      if factor2Range.maxValue >= integer.last div factor1 then
        if factor2Range.maxValue <= integer.first div factor1 then
          productRange.minValue := factor2Range.maxValue * factor1;
        else
          productRange.minValue := integer.first div factor1 * factor1;
          productRange.mayOverflow := TRUE;
        end if;
      else
        valueOutOfRange := TRUE;
      end if;
    end if;
    if valueOutOfRange then
      productRange.minValue := 0;
      productRange.maxValue := -1;
      productRange.mayOverflow := TRUE;
    end if;
    productRange.mayRaiseException := factor2Range.mayRaiseException or
                                      productRange.mayOverflow;
  end func;


const func intRange: getIntMultRange (in intRange: factor1Range, in intRange: factor2Range) is func
  result
    var intRange: productRange is intRange.value;
  local
    var boolean: valueOutOfRange is FALSE;
    var integer: minProduct is integer.last;
    var integer: maxProduct is integer.first;
    var boolean: maxFound is FALSE;
  begin
    if factor1Range.minValue > factor1Range.maxValue or
        factor2Range.minValue > factor2Range.maxValue then
      productRange.minValue := 0;
      productRange.maxValue := -1;
    elsif factor1Range.minValue = factor1Range.maxValue then
      productRange := getIntMultRange(factor1Range.minValue, factor2Range)
    elsif factor2Range.minValue = factor2Range.maxValue then
      productRange := getIntMultRange(factor2Range.minValue, factor1Range)
    else
      if factor1Range.maxValue > 0 then
        if factor2Range.minValue < 0 then
          if factor2Range.minValue >= integer.first div factor1Range.maxValue then
            minProduct := factor1Range.maxValue * factor2Range.minValue;
          else
            minProduct := integer.first;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.minValue = 0 then
          minProduct := 0;
        end if;
        if factor2Range.maxValue > 0 then
          if factor1Range.maxValue <= integer.last div factor2Range.maxValue then
            maxProduct := factor1Range.maxValue * factor2Range.maxValue;
          else
            maxProduct := integer.last;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.maxValue = 0 then
          maxProduct := 0;
        end if;
      elsif factor1Range.maxValue = 0 then
        if factor2Range.minValue <= 0 then
          minProduct := 0;
        end if;
        if factor2Range.maxValue >= 0 then
          maxProduct := 0;
        end if;
      else # factor1Range.maxValue < 0
        if factor2Range.maxValue < 0 then
          if (factor1Range.maxValue <> integer.first or factor2Range.maxValue <> -1) and
              (factor1Range.maxValue <> -1 or factor2Range.maxValue <> integer.first) and
              factor1Range.maxValue >= integer.last div factor2Range.maxValue then
            minProduct := factor1Range.maxValue * factor2Range.maxValue;
          else
            # No valid productRange because minProduct > integer.last holds.
            valueOutOfRange := TRUE;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.maxValue = 0 then
          minProduct := 0;
        end if;
        if factor2Range.minValue > 0 then
          if factor1Range.maxValue >= integer.first div factor2Range.minValue then
            maxProduct := factor1Range.maxValue * factor2Range.minValue;
          else
            # No valid productRange because maxProduct < integer.first holds.
            valueOutOfRange := TRUE;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.minValue = 0 then
          maxProduct := 0;
        end if;
      end if;
      if factor1Range.minValue > 0 then
        if factor2Range.minValue > 0 then
          if factor1Range.minValue <= integer.last div factor2Range.minValue then
            minProduct := min(minProduct,
                              factor1Range.minValue * factor2Range.minValue);
          else
            # No valid productRange because minProduct > integer.last holds.
            valueOutOfRange := TRUE;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.minValue = 0 then
          minProduct := min(minProduct, 0);
        end if;
        if factor2Range.maxValue < 0 then
          if factor2Range.maxValue >= integer.first div factor1Range.minValue then
            maxProduct := max(maxProduct,
                              factor1Range.minValue * factor2Range.maxValue);
          else
            # No valid productRange because maxProduct < integer.first holds.
            valueOutOfRange := TRUE;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.maxValue = 0 then
          maxProduct := max(maxProduct, 0);
        end if;
      elsif factor1Range.minValue = 0 then
        if factor2Range.minValue >= 0 then
          minProduct := min(minProduct, 0);
        end if;
        if factor2Range.minValue <= 0 then
          maxProduct := max(maxProduct, 0);
        end if;
      else # factor1Range.minValue < 0
        if factor2Range.maxValue > 0 then
          if factor1Range.minValue >= integer.first div factor2Range.maxValue then
            minProduct := min(minProduct,
                              factor1Range.minValue * factor2Range.maxValue);
          else
            minProduct := integer.first;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.maxValue = 0 then
          minProduct := min(minProduct, 0);
        end if;
        if factor2Range.minValue < 0 then
          if (factor1Range.minValue <> integer.first or factor2Range.minValue <> -1) and
              (factor1Range.minValue <> -1 or factor2Range.minValue <> integer.first) and
              factor1Range.minValue >= integer.last div factor2Range.minValue then
            maxProduct := max(maxProduct,
                              factor1Range.minValue * factor2Range.minValue);
          else
            maxProduct := integer.last;
            productRange.mayOverflow := TRUE;
          end if;
        elsif factor2Range.minValue = 0 then
          maxProduct := max(maxProduct, 0);
        end if;
      end if;
      productRange.minValue := minProduct;
      productRange.maxValue := maxProduct;
      if productRange.minValue > productRange.maxValue or valueOutOfRange then
        productRange.minValue := 0;
        productRange.maxValue := -1;
      end if;
      productRange.mayRaiseException := factor1Range.mayRaiseException or
                                        factor2Range.mayRaiseException or
                                        productRange.mayOverflow;
    end if;
  end func;


const func intRange: getArrValueRange (in reference: arr) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var integer: arraySize is 0;
    var ref_list: arrayList is ref_list.EMPTY;
    var reference: element is NIL;
    var integer: elementValue is 0;
  begin
    arraySize := arrayLength(arr);
    if arraySize < 10000 then
      valueRange.minValue := integer.last;
      valueRange.maxValue := integer.first;
      arrayList := arrayToList(arr);
      for element range arrayList do
        elementValue := getValue(element, integer);
        if elementValue < valueRange.minValue then
          valueRange.minValue := elementValue;
        end if;
        if elementValue > valueRange.maxValue then
          valueRange.maxValue := elementValue;
        end if;
      end for;
    end if;
  end func;


const func intRange: getArrLenRange (in reference: arr) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var reference: evaluatedParam is NIL;
  begin
    if getConstant(arr, ARRAYOBJECT, evaluatedParam) then
      valueRange.minValue := arrayLength(evaluatedParam);
      valueRange.maxValue := valueRange.minValue
    elsif isActionExpression(arr, "ARR_SUBARR") and
              getConstant(getActionParameter(arr, 5), INTOBJECT, evaluatedParam) or
          isActionExpression(arr, "ARR_HEAD") and
              getConstant(getActionParameter(arr, 4), INTOBJECT, evaluatedParam) then
      valueRange.minValue := 0;
      valueRange.maxValue := getValue(evaluatedParam, integer);
    elsif ccConf.POINTER_SIZE > ccConf.GENERIC_SIZE then
      valueRange.minValue := 0;
      valueRange.maxValue := integer.last;
    else
      valueRange.minValue := 0;
      valueRange.maxValue :=
          ord(2_ ** ccConf.POINTER_SIZE div bigInteger(ccConf.GENERIC_SIZE div 8));
    end if;
  end func;


const func intRange: getStrLenRange (in reference: stri) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var reference: evaluatedParam is NIL;
    var intRange: argumentRange is intRange.value;
    var integer: base is 0;
    var integer: length is 0;
  begin
    if getConstant(stri, STRIOBJECT, evaluatedParam) then
      length := length(getValue(evaluatedParam, string));
      valueRange.minValue := length;
      valueRange.maxValue := length;
      valueRange.mayRaiseException := FALSE;
    elsif isActionExpression(stri, "STR_SUBSTR_FIXLEN") and
        getConstant(getActionParameter(stri, 5), INTOBJECT, evaluatedParam) then
      length := getValue(evaluatedParam, integer);
      valueRange.minValue := length;
      valueRange.maxValue := length;
    elsif isActionExpression(stri, "STR_SUBSTR") and
              getConstant(getActionParameter(stri, 5), INTOBJECT, evaluatedParam) or
          isActionExpression(stri, "STR_HEAD") and
              getConstant(getActionParameter(stri, 4), INTOBJECT, evaluatedParam) then
      valueRange.minValue := 0;
      valueRange.maxValue := getValue(evaluatedParam, integer);
    elsif isActionExpression(stri, "CHR_STR") then
      valueRange.minValue := 1;
      valueRange.maxValue := 1;
      valueRange.mayRaiseException := FALSE;
    elsif isActionExpression(stri, "INT_STR") then
      argumentRange := getIntRange(getActionParameter(stri, 1));
      valueRange.minValue := 1;
      valueRange.maxValue := max(length(str(argumentRange.minValue)),
                                 length(str(argumentRange.maxValue)));
    elsif (isActionExpression(stri, "INT_RADIX") or
           isActionExpression(stri, "INT_radix")) and
               getConstant(getActionParameter(stri, 3), INTOBJECT, evaluatedParam) then
      argumentRange := getIntRange(getActionParameter(stri, 1));
      base := getValue(evaluatedParam, integer);
      valueRange.minValue := 1;
      valueRange.maxValue := max(length(argumentRange.minValue radix base),
                                 length(argumentRange.maxValue radix base));
    elsif isActionExpression(stri, "INT_N_BYTES_BE_SIGNED") or
          isActionExpression(stri, "INT_N_BYTES_BE_UNSIGNED") or
          isActionExpression(stri, "INT_N_BYTES_LE_SIGNED") or
          isActionExpression(stri, "INT_N_BYTES_LE_UNSIGNED") then
      argumentRange := getIntRange(getActionParameter(stri, 4));
      valueRange.minValue := max(1, argumentRange.minValue);
      valueRange.maxValue := max(1, argumentRange.maxValue);
    elsif isActionExpression(stri, "INT_BYTES_BE_SIGNED") or
          isActionExpression(stri, "INT_BYTES_LE_SIGNED") then
      argumentRange := getIntRange(getActionParameter(stri, 1));
      valueRange.minValue := 1;
      valueRange.maxValue := max(length(bytes(argumentRange.minValue, SIGNED, LE)),
                                 length(bytes(argumentRange.maxValue, SIGNED, LE)));
    elsif isActionExpression(stri, "INT_BYTES_BE_UNSIGNED") or
          isActionExpression(stri, "INT_BYTES_LE_UNSIGNED") then
      valueRange.minValue := 1;
      valueRange.maxValue := max(length(bytes(max(0, argumentRange.minValue), UNSIGNED, LE)),
                                 length(bytes(max(0, argumentRange.maxValue), UNSIGNED, LE)));
    elsif isActionExpression(stri, "BIN_N_BYTES_BE") or
          isActionExpression(stri, "BIN_N_BYTES_LE") then
      argumentRange := getIntRange(getActionParameter(stri, 3));
      valueRange.minValue := max(1, argumentRange.minValue);
      valueRange.maxValue := max(1, argumentRange.maxValue);
    elsif ccConf.POINTER_SIZE > ccConf.INTTYPE_SIZE then
      valueRange.minValue := 0;
      valueRange.maxValue := integer.last;
    else
      valueRange.minValue := 0;
      # Because of UTF-32 the available bytes are divided by four.
      valueRange.maxValue := pred(2 ** (ccConf.POINTER_SIZE - 2));
    end if;
  end func;


const func intRange: getIntBytes2IntRange (in reference: stri) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var intRange: strLenRange is intRange.value;
  begin
    strLenRange := getStrLenRange(stri);
    if strLenRange.maxValue >= 1 and strLenRange.maxValue < 8 then
      if ccConf.TWOS_COMPLEMENT_INTTYPE then
        valueRange.minValue := -2 ** pred(strLenRange.maxValue * 8);
      else
        valueRange.minValue := -pred(2 ** pred(strLenRange.maxValue * 8));
      end if;
      valueRange.maxValue := pred(2 ** pred(strLenRange.maxValue * 8));
    end if;
  end func;


const func intRange: getIntBytes2UIntRange (in reference: stri) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var intRange: strLenRange is intRange.value;
  begin
    valueRange.minValue := 0;
    strLenRange := getStrLenRange(stri);
    if strLenRange.maxValue >= 1 and strLenRange.maxValue < 8 then
      valueRange.maxValue := pred(2 ** (strLenRange.maxValue * 8));
    end if;
  end func;


const func intRange: getIntParse1Range (in reference: stri) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var intRange: strLenRange is intRange.value;
  begin
    strLenRange := getStrLenRange(stri);
    if strLenRange.maxValue >= 1 then
      if strLenRange.maxValue < length(str(integer.first)) then
        valueRange.minValue := -pred(10 ** pred(strLenRange.maxValue));
      end if;
      if strLenRange.maxValue < length(str(integer.last)) then
        valueRange.maxValue := pred(10 ** strLenRange.maxValue);
      end if;
    end if;
  end func;


const func intRange: getSetRandRange (in reference: aSet) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var reference: evaluatedParam is NIL;
    var bitset: setValue is {};
  begin
    if getConstant(aSet, SETOBJECT, evaluatedParam) then
      setValue := getValue(evaluatedParam, bitset);
      if setValue = {} then
        valueRange.minValue := 0;
        valueRange.maxValue := -1;
      else
        valueRange.minValue := min(setValue);
        valueRange.maxValue := max(setValue);
      end if;
    end if;
  end func;


const func intRange: getBlnTernaryRange (in reference: condition,
    in reference: thenParam, in reference: elseParam,
    inout reference: limitedVariable) is forward;


const func intRange: lessEqualLimit (in integer: limit, in reference: conditionParam,
    in reference: thenOrElseParam, inout reference: limitedVariable) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var intRange: subExprValueRange is intRange.value;
    var ref_list: params is ref_list.EMPTY;
    var reference: subExprLimitedVariable is NIL;
  begin
    if conditionParam = thenOrElseParam then
      # a > 255 ? 255 : a
      subExprValueRange := getIntRange(thenOrElseParam);
      valueRange.minValue := min(subExprValueRange.minValue, limit);
      valueRange.maxValue := min(subExprValueRange.maxValue, limit);
      limitedVariable := conditionParam;
    elsif category(thenOrElseParam) = MATCHOBJECT then
      params := getValue(thenOrElseParam, ref_list);
      if category(params[1]) = ACTOBJECT and
          str(getValue(params[1], ACTION)) = "BLN_TERNARY" then
        subExprValueRange :=
            getBlnTernaryRange(params[2], params[4], params[6], subExprLimitedVariable);
        if subExprLimitedVariable = conditionParam then
          # a > 255 ? 255 : (a < 0 ? 0 : a)
          valueRange.minValue := min(subExprValueRange.minValue, limit);
          valueRange.maxValue := min(subExprValueRange.maxValue, limit);
          limitedVariable := conditionParam;
        end if;
      end if;
    end if;
  end func;


const func intRange: greaterEqualLimit (in integer: limit, in reference: conditionParam,
    in reference: thenOrElseParam, inout reference: limitedVariable) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var intRange: subExprValueRange is intRange.value;
    var ref_list: params is ref_list.EMPTY;
    var reference: subExprLimitedVariable is NIL;
  begin
    if conditionParam = thenOrElseParam then
      # a < 0 ? 0 : a
      subExprValueRange := getIntRange(thenOrElseParam);
      valueRange.minValue := max(subExprValueRange.minValue, limit);
      valueRange.maxValue := max(subExprValueRange.maxValue, limit);
      limitedVariable := conditionParam;
    elsif category(thenOrElseParam) = MATCHOBJECT then
      params := getValue(thenOrElseParam, ref_list);
      if category(params[1]) = ACTOBJECT and
          str(getValue(params[1], ACTION)) = "BLN_TERNARY" then
        subExprValueRange :=
            getBlnTernaryRange(params[2], params[4], params[6], subExprLimitedVariable);
        if subExprLimitedVariable = conditionParam then
          # a < 0 ? 0 : (a > 255 ? 255 : a)
          valueRange.minValue := max(subExprValueRange.minValue, limit);
          valueRange.maxValue := max(subExprValueRange.maxValue, limit);
          limitedVariable := conditionParam;
        end if;
      end if;
    end if;
  end func;


const func intRange: getBlnTernaryRange (in reference: condition,
    in reference: thenParam, in reference: elseParam,
    inout reference: limitedVariable) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var reference: evaluatedParam is NIL;
    var integer: limit is 0;
    var reference: evaluatedThenParam is NIL;
    var reference: evaluatedElseParam is NIL;
    var integer: number is 0;
  begin
    if isActionExpression(condition, "INT_GT") or
        isActionExpression(condition, "INT_GE") then
      if getConstant(getActionParameter(condition, 3), INTOBJECT, evaluatedParam) then
        limit := getValue(evaluatedParam, integer);
        if getConstant(thenParam, INTOBJECT, evaluatedThenParam) then
          number := getValue(evaluatedThenParam, integer);
          if limit = number then
            valueRange := lessEqualLimit(limit, getActionParameter(condition, 1),
                                         elseParam, limitedVariable);
          end if;
        elsif getConstant(elseParam, INTOBJECT, evaluatedElseParam) then
          number := getValue(evaluatedElseParam, integer);
          if limit = number then
            valueRange := greaterEqualLimit(limit, getActionParameter(condition, 1),
                                            thenParam, limitedVariable);
          end if;
        end if;
      elsif getConstant(getActionParameter(condition, 1), INTOBJECT, evaluatedParam) then
        limit := getValue(evaluatedParam, integer);
        if getConstant(thenParam, INTOBJECT, evaluatedThenParam) then
          number := getValue(evaluatedThenParam, integer);
          if limit = number then
            valueRange := greaterEqualLimit(limit, getActionParameter(condition, 3),
                                            elseParam, limitedVariable);
          end if;
        elsif getConstant(elseParam, INTOBJECT, evaluatedElseParam) then
          number := getValue(evaluatedElseParam, integer);
          if limit = number then
            valueRange := lessEqualLimit(limit, getActionParameter(condition, 3),
                                         thenParam, limitedVariable);
          end if;
        end if;
      end if;
    elsif isActionExpression(condition, "INT_LT") or
        isActionExpression(condition, "INT_LE") then
      if getConstant(getActionParameter(condition, 3), INTOBJECT, evaluatedParam) then
        limit := getValue(evaluatedParam, integer);
        if getConstant(thenParam, INTOBJECT, evaluatedThenParam) then
          number := getValue(evaluatedThenParam, integer);
          if limit = number then
            valueRange := greaterEqualLimit(limit, getActionParameter(condition, 1),
                                            elseParam, limitedVariable);
          end if;
        elsif getConstant(elseParam, INTOBJECT, evaluatedElseParam) then
          number := getValue(evaluatedElseParam, integer);
          if limit = number then
            valueRange := lessEqualLimit(limit, getActionParameter(condition, 1),
                                         thenParam, limitedVariable);
          end if;
        end if;
      elsif getConstant(getActionParameter(condition, 1), INTOBJECT, evaluatedParam) then
        limit := getValue(evaluatedParam, integer);
        if getConstant(thenParam, INTOBJECT, evaluatedThenParam) then
          number := getValue(evaluatedThenParam, integer);
          if limit = number then
            valueRange := lessEqualLimit(limit, getActionParameter(condition, 3),
                                         elseParam, limitedVariable);
          end if;
        elsif getConstant(elseParam, INTOBJECT, evaluatedElseParam) then
          number := getValue(evaluatedElseParam, integer);
          if limit = number then
            valueRange := greaterEqualLimit(limit, getActionParameter(condition, 3),
                                            thenParam, limitedVariable);
          end if;
        end if;
      end if;
    end if;
  end func;


const func intRange: getBlnTernaryRange (in reference: condition,
    in reference: thenParam, in reference: elseParam) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var reference: evaluatedParam is NIL;
    var reference: limitedVariable is NIL;
    var intRange: thenValueRange is intRange.value;
    var intRange: elseValueRange is intRange.value;
    var boolean: done is FALSE;
  begin
    if getConstant(condition, ENUMLITERALOBJECT, evaluatedParam) then
      if getValue(evaluatedParam, boolean) then
        valueRange := getIntRange(thenParam);
      else
        valueRange := getIntRange(elseParam);
      end if;
      done := TRUE;
    else
      valueRange := getBlnTernaryRange(condition, thenParam, elseParam, limitedVariable);
      done := limitedVariable <> NIL;
    end if;
    if not done then
      thenValueRange := getIntRange(thenParam);
      elseValueRange := getIntRange(elseParam);
      valueRange.minValue := min(thenValueRange.minValue,
                                 elseValueRange.minValue);
      valueRange.maxValue := max(thenValueRange.maxValue,
                                 elseValueRange.maxValue);
    end if;
  end func;


const func intRange: getBigOrdOfBigModRange (in bigInteger: divisor) is func
  result
    var intRange: valueRange is intRange.value;
  begin
    if divisor > 0_ and divisor <= 2_ ** 63 then
      valueRange.minValue := 0;
      valueRange.maxValue := ord(pred(divisor));
    elsif divisor < 0_ and divisor >= pred(-2_ ** 63) then
      valueRange.minValue := ord(succ(divisor));
      valueRange.maxValue := 0;
    end if;
  end func;


const func intRange: getIntRange (in reference: intExpression) is func
  result
    var intRange: valueRange is intRange.value;
  local
    var reference: function is NIL;
    var ref_list: params is ref_list.EMPTY;
    var string: actionName is "";
    var reference: evaluatedParam is NIL;
    var integer: number is 0;
    var intRange: argument1Range is intRange.value;
    var intRange: argument2Range is intRange.value;
  begin
    if reduceOverflowChecks then
      if getConstant(intExpression, INTOBJECT, evaluatedParam) then
        number := getValue(evaluatedParam, integer);
        valueRange.minValue := number;
        valueRange.maxValue := number;
        valueRange.mayRaiseException := FALSE;
        # writeln("intRange of constant: " <& valueRange.minValue <& ".." <& valueRange.maxValue);
      elsif intExpression in intRangeOfVariable then
        valueRange := intRangeOfVariable[intExpression];
        valueRange.mayRaiseException := FALSE;
        # writeln("intRange of variable: " <& valueRange.minValue <& ".." <& valueRange.maxValue);
      elsif category(intExpression) = CALLOBJECT then
        params := getValue(intExpression, ref_list);
        function := params[1];
        params := params[2 ..];
        if category(function) = ACTOBJECT then
          actionName := str(getValue(function, ACTION));
          case actionName of
            when {"BIG_BIT_LENGTH", "BST_LNG", "CMD_FILESIZE", "CON_HEIGHT",
                  "CON_WIDTH", "DRW_HEIGHT", "DRW_WIDTH", "DRW_SCREEN_HEIGHT",
                  "DRW_SCREEN_WIDTH", "FIL_LNG", "HSH_LNG", "RFL_LNG",
                  "SET_CARD", "SQL_STMT_COLUMN_COUNT"}:
              valueRange.minValue := 0;
              valueRange.maxValue := integer.last;
            when {"BIG_CMP", "BIN_CMP", "BST_CMP", "CHR_CMP", "DRW_CMP",
                  "FLT_CMP", "INT_CMP", "ITF_CMP", "PCS_CMP", "REF_CMP",
                  "SET_CMP", "SQL_CMP_DB", "SQL_CMP_STMT", "STR_CMP",
                  "TYP_CMP"}:
              valueRange.minValue := -1;
              valueRange.maxValue := 1;
            when {"ARR_IDX"}:
              if getConstant(params[1], ARRAYOBJECT, evaluatedParam) then
                valueRange := getArrValueRange(evaluatedParam);
              end if;
            when {"ARR_LNG"}:
              valueRange := getArrLenRange(params[1]);
            when {"BIG_LOWEST_SET_BIT"}:
              valueRange.minValue := -1;
              valueRange.maxValue := integer.last;
            when {"BIG_ORD"}:
              if isActionExpression(params[1], "BIG_MOD") and
                  getConstant(getActionParameter(params[1], 3),
                              BIGINTOBJECT, evaluatedParam) then
                valueRange := getBigOrdOfBigModRange(
                    getValue(evaluatedParam, bigInteger));
              end if;
            when {"BIN_AND"}:
              if  getConstant(params[1], INTOBJECT, evaluatedParam) or
                  getConstant(params[3], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                if number >= 0 then
                  valueRange.minValue := 0;
                  valueRange.maxValue := number;
                else
                  valueRange.minValue := integer.first;
                  valueRange.maxValue := number - integer.first;
                end if;
              end if;
            when {"BIN_CARD"}:
              valueRange.minValue := 0;
              valueRange.maxValue := ccConf.INTTYPE_SIZE;
            when {"BLN_ORD"}:
              valueRange.minValue := 0;
              valueRange.maxValue := 1;
            when {"BLN_TERNARY"}:
              valueRange := getBlnTernaryRange(params[1], params[3], params[5]);
            when {"CHR_ORD"}:
              if ccConf.TWOS_COMPLEMENT_INTTYPE then
                valueRange.minValue := -2147483648;
              else
                valueRange.minValue := -2147483647;
              end if;
              valueRange.maxValue := 2147483647;
            when {"ENU_ORD2"}:
              valueRange.minValue := 0;
              valueRange.maxValue :=
                  pred(length(getValue(evaluate(prog, params[2]), ref_list)));
            when {"FIL_TELL"}:
              valueRange.minValue := 1;
              valueRange.maxValue := integer.last;
            when {"INT_ABS"}:
              valueRange := getIntAbsRange(getIntRange(params[1]));
            when {"INT_ADD"}:
              valueRange := getIntAddRange(getIntRange(params[1]),
                                           getIntRange(params[3]));
            when {"INT_BIT_LENGTH"}:
              argument1Range := getIntRange(params[1]);
              if argument1Range.minValue <= 0 and argument1Range.maxValue >= 0 then
                valueRange.minValue := 0;
              else
                valueRange.minValue := min(bitLength(argument1Range.minValue),
                                           bitLength(argument1Range.maxValue));
              end if;
              valueRange.maxValue := max(bitLength(argument1Range.minValue),
                                         bitLength(argument1Range.maxValue));
            when {"INT_BYTES_BE_2_INT",
                  "INT_BYTES_LE_2_INT"}:
              valueRange := getIntBytes2IntRange(params[1]);
            when {"INT_BYTES_BE_2_UINT",
                  "INT_BYTES_LE_2_UINT"}:
              valueRange := getIntBytes2UIntRange(params[1]);
            when {"INT_DIV"}:
              if getConstant(params[3], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                argument1Range := getIntRange(params[1]);
                if number > 0 then
                  valueRange.minValue := argument1Range.minValue div number;
                  valueRange.maxValue := argument1Range.maxValue div number;
                elsif number = -1 then
                  valueRange := getIntNegateRange(argument1Range);
                elsif number < -1 then
                  valueRange.minValue := argument1Range.maxValue div number;
                  valueRange.maxValue := argument1Range.minValue div number;
                end if;
              elsif getConstant(params[1], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                if number >= 0 then
                  valueRange.minValue := -number;
                  valueRange.maxValue := number;
                elsif number <> integer.first then
                  valueRange.minValue := number;
                  valueRange.maxValue := -number;
                end if;
              end if;
            when {"INT_FACT"}:
              valueRange.minValue := 1;
              valueRange.maxValue := 2432902008176640000;
            when {"INT_ICONV1"}:
              valueRange := getIntRange(params[1]);
            when {"INT_ICONV3"}:
              valueRange := getIntRange(params[3]);
            when {"INT_LOG10"}:
              argument1Range := getIntRange(params[1]);
              if argument1Range.maxValue >= 0 then
                if argument1Range.minValue >= 0 then
                  valueRange.minValue := log10(argument1Range.minValue);
                else
                  valueRange.minValue := -1;
                end if;
                valueRange.maxValue := log10(argument1Range.maxValue);
              else
                valueRange.minValue := 0;
                valueRange.maxValue := -1;
              end if;
            when {"INT_LOG2"}:
              argument1Range := getIntRange(params[1]);
              if argument1Range.maxValue >= 0 then
                if argument1Range.minValue >= 0 then
                  valueRange.minValue := log2(argument1Range.minValue);
                else
                  valueRange.minValue := -1;
                end if;
                valueRange.maxValue := log2(argument1Range.maxValue);
              else
                valueRange.minValue := 0;
                valueRange.maxValue := -1;
              end if;
            when {"INT_LOWEST_SET_BIT"}:
              valueRange.minValue := -1;
              valueRange.maxValue := pred(ccConf.INTTYPE_SIZE);
            when {"INT_LSHIFT"}:
              valueRange := getIntLshiftRange(getIntRange(params[1]),
                                              getIntRange(params[3]));
            when {"INT_MDIV"}:
              if getConstant(params[3], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                argument1Range := getIntRange(params[1]);
                if number > 0 then
                  valueRange.minValue := argument1Range.minValue mdiv number;
                  valueRange.maxValue := argument1Range.maxValue mdiv number;
                elsif number = -1 then
                  valueRange := getIntNegateRange(argument1Range);
                elsif number < -1 then
                  valueRange.minValue := argument1Range.maxValue mdiv number;
                  valueRange.maxValue := argument1Range.minValue mdiv number;
                end if;
              elsif getConstant(params[1], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                if number >= 0 then
                  valueRange.minValue := -number;
                  valueRange.maxValue := number;
                elsif number <> integer.first then
                  valueRange.minValue := number;
                  valueRange.maxValue := -number;
                end if;
              end if;
            when {"INT_MOD"}:
              valueRange := getIntModRange(params[1], params[3]);
            when {"INT_MULT"}:
              valueRange := getIntMultRange(getIntRange(params[1]),
                                            getIntRange(params[3]));
            when {"INT_NEGATE"}:
              valueRange := getIntNegateRange(getIntRange(params[2]));
            when {"INT_PARSE1"}:
              valueRange := getIntParse1Range(params[1]);
            when {"INT_PLUS"}:
              valueRange := getIntRange(params[2]);
            when {"INT_PRED"}:
              argument1Range := getIntRange(params[1]);
              if argument1Range.minValue <> integer.first then
                valueRange.minValue := pred(argument1Range.minValue);
              else
                valueRange.minValue := integer.first;
              end if;
              if argument1Range.maxValue <> integer.first then
                valueRange.maxValue := pred(argument1Range.maxValue);
              else
                valueRange.maxValue := integer.first;
              end if;
              if not argument1Range.mayRaiseException and
                  argument1Range.minValue <> integer.first then
                valueRange.mayRaiseException := FALSE;
              end if;
            when {"INT_RAND"}:
              argument1Range := getIntRange(params[1]);
              argument2Range := getIntRange(params[2]);
              valueRange.minValue := argument1Range.minValue;
              valueRange.maxValue := argument2Range.maxValue;
              if not (argument1Range.mayRaiseException or
                      argument2Range.mayRaiseException) and
                  argument1Range.maxValue <= argument2Range.minValue then
                valueRange.mayRaiseException := FALSE;
              end if;
            when {"INT_REM"}:
              if getConstant(params[3], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                if number > 0 then
                  valueRange.minValue := -pred(number);
                  valueRange.maxValue := pred(number);
                elsif number < 0 then
                  valueRange.minValue := succ(number);
                  valueRange.maxValue := -succ(number);
                end if;
              elsif getConstant(params[1], INTOBJECT, evaluatedParam) then
                number := getValue(evaluatedParam, integer);
                if number >= 0 then
                  valueRange.minValue := 0;
                  valueRange.maxValue := number;
                elsif number = 0 then
                  valueRange.minValue := 0;
                  valueRange.maxValue := 0;
                else # number < 0 then
                  valueRange.minValue := number;
                  valueRange.maxValue := 0;
                end if;
              end if;
            when {"INT_RSHIFT"}:
              valueRange := getIntRshiftRange(getIntRange(params[1]),
                                              getIntRange(params[3]));
            when {"INT_SBTR"}:
              valueRange := getIntSbtrRange(getIntRange(params[1]),
                                            getIntRange(params[3]));
            when {"INT_SQRT"}:
              argument1Range := getIntRange(params[1]);
              if argument1Range.maxValue >= 0 then
                if argument1Range.minValue >= 0 then
                  valueRange.minValue := sqrt(argument1Range.minValue);
                else
                  valueRange.minValue := 0;
                end if;
                valueRange.maxValue := sqrt(argument1Range.maxValue);
              else
                valueRange.minValue := 0;
                valueRange.maxValue := -1;
              end if;
            when {"INT_SUCC"}:
              argument1Range := getIntRange(params[1]);
              if argument1Range.minValue <> integer.last then
                valueRange.minValue := succ(argument1Range.minValue);
              else
                valueRange.minValue := integer.last;
              end if;
              if argument1Range.maxValue <> integer.last then
                valueRange.maxValue := succ(argument1Range.maxValue);
              else
                valueRange.maxValue := integer.last;
              end if;
              if not argument1Range.mayRaiseException and
                  argument1Range.maxValue <> integer.last then
                valueRange.mayRaiseException := FALSE;
              end if;
            when {"SET_RAND"}:
              valueRange := getSetRandRange(params[1]);
            when {"STR_CHIPOS", "STR_CHPOS", "STR_IPOS", "STR_POS",
                  "STR_RCHIPOS", "STR_RCHPOS", "STR_RIPOS", "STR_RPOS"}:
              valueRange.minValue := 0;
              valueRange.maxValue := getStrLenRange(params[1]).maxValue;
            when {"STR_LNG"}:
              valueRange := getStrLenRange(params[1]);
          end case;
          # writeln(actionName <& ": " <& valueRange.minValue <& " " <& valueRange.maxValue);
        elsif category(function) = BLOCKOBJECT then
          if resultVar(function) = NIL and
              resultInitValue(function) = NIL and
              localConsts(function) = ref_list.EMPTY and
              localVars(function) = ref_list.EMPTY then
            valueRange := getIntRange(body(function));
          end if;
          # writeln("BLOCKOBJECT: " <& valueRange.minValue <& " " <& valueRange.maxValue);
        end if;
      elsif category(intExpression) = VALUEPARAMOBJECT then
        if intExpression in inlineParam and
            inlineParam[intExpression][1].actualParam <> NIL then
          if category(inlineParam[intExpression][1].actualParam) <> CALLOBJECT or
              category(getValue(inlineParam[intExpression][1].actualParam, ref_list)[1]) <> BLOCKOBJECT then
            valueRange := getIntRange(inlineParam[intExpression][1].actualParam);
          end if;
        end if;
      end if;
    end if;
  end func;