(********************************************************************)
(*                                                                  *)
(*  fixarray.s7i  Support for arrays with a fixed size              *)
(*  Copyright (C) 2024  Thomas Mertes                               *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser 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.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  Abstract data type, describing fixed size arrays with [[integer]] index.
 *  A fixed size array type defines the minimum and maximum allowed indices.
 *  All arrays declared with this type have the same length and the same
 *  minimum and maximum indices. All abstract fixed size array types are
 *  incompatible to each other. A type declaration is needed to use the
 *  same fixed size array type at several places. E.g.:
 *    const type: dataBlockType is array [1 .. 64] integer;
 *  afterwards ''dataBlockType'' can be used in declarations. E.g.:
 *    var dataBlockType: aDataBlock is dataBlockType.value;
 *  @param minIdx Minimum index of the array type.
 *  @param maxIdx Maximum index of the array type.
 *  @param baseType Type of the array elements.
 *)
const func type: array [ (in integer: minIdx) .. (in integer: maxIdx) ] (in type: baseType) is func
  result
    var type: arrayType is void;
  local
    var type: tupleType is void;
    var type: sliceType is void;
  begin
    global
      arrayType := newtype;
      IN_PARAM_IS_REFERENCE(arrayType);
      tupleType := tuple(baseType);
      sliceType := newtype;

      const creator: (ref arrayType: dest) ::= (in arrayType: source)         is action "ARR_CREATE";
      const destroyer: destroy (ref arrayType: aValue)                        is action "ARR_DESTR";
      const proc: (inout arrayType: dest) := (in arrayType: source)           is action "ARR_CPY";

      const func arrayType: ARRLIT3 (in integer: minIdx, in integer: maxIdx,
                                     in tupleType: aTuple, attr arrayType)    is action "ARR_ARRLIT3";
      const func arrayType: BASELIT3 (in integer: minIdx, in integer: maxIdx,
                                      in baseType: anElement, attr arrayType) is action "ARR_BASELIT3";
      const func arrayType: SET_MIN_IDX (paren1, in integer: start, paren2,
                                         in arrayType: arr)                   is action "ARR_ARRLIT2";
      const func arrayType: TIMES (in integer: factor, times,
                                   in baseType: element, attr arrayType)      is action "ARR_TIMES";

      const arrayType: (attr arrayType) . value is
          SET_MIN_IDX(paren1, minIdx, paren2,
                      TIMES(succ(maxIdx - minIdx), times, baseType.value, arrayType));

      const func arrayType: (attr arrayType) [] (in tupleType: aTuple) is
          return ARRLIT3(minIdx, maxIdx, aTuple, arrayType);

      const func arrayType: (attr arrayType) [] (in baseType: anElement) is
          return BASELIT3(minIdx, maxIdx, anElement, arrayType);

      (**
       *  Generate an ''arrayType'' array filled with ''elements''.
       *  @return a fixed size array filled with ''elements''.
       *  @exception MEMORY_ERROR Not enough memory to represent the result.
       *)
      const func arrayType: (attr arrayType) times (in baseType: element) is
          return SET_MIN_IDX(paren1, minIdx, paren2,
                             TIMES(succ(maxIdx - minIdx), times, element, arrayType));

      (**
       *  Length of all arrays declared with ''arrayType''.
       *  All arrays declared with ''arrayType'' have this length.
       *  @return the length of the array.
       *)
      const integer: length (attr arrayType) is succ(maxIdx - minIdx);

      (**
       *  Minimum index of all arrays declared with ''arrayType''.
       *  All arrays declared with ''arrayType'' have this minimum index.
       *  @return the length of the array.
       *)
      const integer: minIdx (attr arrayType) is minIdx;

      (**
       *  Maximum index of all arrays declared with ''arrayType''.
       *  All arrays declared with ''arrayType'' have this maximum index.
       *  @return the length of the array.
       *)
      const integer: maxIdx (attr arrayType) is maxIdx;

      (**
       *  Length of the array ''arr''.
       *  The length of a fixed size array does not depend on the value.
       *  @return the length of the array.
       *)
      const integer: length (in arrayType: arr) is succ(maxIdx - minIdx);

      (**
       *  Minimum index of array ''arr''.
       *  The minimum index of a fixed size array does not depend on the value.
       *  @return the minimum index of the array.
       *)
      const integer: minIdx (in arrayType: arr) is minIdx;

      (**
       *  Maximum index of array ''arr''.
       *  The maximum index of a fixed size array does not depend on the value.
       *  @return the maximum index of the array.
       *)
      const integer: maxIdx (in arrayType: arr) is maxIdx;

      (**
       *  Access one element from the fixed size array ''arr''.
       *  @return the element with the specified ''index'' from ''arr''.
       *  @exception INDEX_ERROR If ''index'' is less than [[#minIdx(attr_arrayType)|minIdx]](arrayType) or
       *                         greater than [[#maxIdx(attr_arrayType)|maxIdx]](arrayType)
       *)
      const func baseType: (in arrayType: arr) [ (in integer: index) ]        is action "ARR_IDX";

      const varfunc baseType: (inout arrayType: arr) [ (in integer: index) ]  is action "ARR_IDX";

      const creator: (ref sliceType: dest) ::= (ref sliceType: source)        is action "ARR_CREATE";
      const destroyer: destroy (ref sliceType: aValue)                        is action "ARR_DESTR";
      const proc: (inout sliceType: dest) := (ref sliceType: source)          is action "ARR_CPY";

      (**
       *  Assign array 'source' to the 'position' of the 'destination'.
       *   A @:= [B] C;
       *  is equivalent to
       *   A := A[..pred(B)] & C & A[B+length(C)..];
       *  @exception INDEX_ERROR If 'position' is less than [[#minIdx(in_arrayType)|minIdx]](destination) or
       *                         elements beyond 'destination' would be overwritten.
       *)
      const proc: (inout arrayType: destination) @:=
                   [ (in integer: position) ] (ref sliceType: source)         is action "ARR_POSCPY";

      const func sliceType: (in arrayType: arr) [ (in integer: start) .. ]    is action "ARR_TAIL";

      const func sliceType: (in arrayType: arr) [ .. (in integer: stop) ]     is action "ARR_HEAD";

      const func sliceType: (in arrayType: arr) [ (in integer: start) ..
                                                  (in integer: length) ]      is action "ARR_RANGE";

      const func sliceType: (in arrayType: arr) [ (in integer: start) len
                                                  (in integer: length) ]      is action "ARR_SUBARR";

      const func sliceType: (in arrayType: arr) [ (in integer: start) fixLen
                                                  (in integer: length) ]      is action "ARR_SUBARR_FIXLEN";

      (**
       *  For-loop where ''forVar'' loops over the elements of the array ''arr''.
       *)
      const proc: for (inout baseType: forVar) range (in arrayType: arr) do
                    (in proc: statements)
                  end for is func
        local
          var integer: number is 0;
        begin
          for number range minIdx to maxIdx do
            forVar := arr[number];
            statements;
          end for;
        end func;

      (**
       *  For-loop where ''keyVar'' loops over the indices of the array ''arr''.
       *)
      const proc: for key (inout integer: keyVar) range (in arrayType: arr) do
                    (in proc: statements)
                  end for is func
        begin
          for keyVar range minIdx to maxIdx do
            statements;
          end for;
        end func;

      (**
       *  For-loop where ''forVar'' and ''keyVar'' loop over the array ''arr''.
       *  The variable ''forVar'' loops over the elements of ''arr''
       *  and ''keyVar'' loops over the indices of ''arr''.
       *)
      const proc: for (inout baseType: forVar) key (inout integer: keyVar) range (in arrayType: arr) do
                    (in proc: statements)
                  end for is func
        begin
          for keyVar range minIdx to maxIdx do
            forVar := arr[keyVar];
            statements;
          end for;
        end func;

      (**
       *  For-loop where ''forVar'' loops over the elements of the array ''arr''.
       *  Additionally a ''condition'' is checked before the statements in
       *  the loop body are executed.
       *)
      const proc: for (inout baseType: forVar)
                  range (in arrayType: arr)
                  until (ref func boolean: condition) do
                    (in proc: statements)
                  end for is func
        local
          var integer: index is 0;
        begin
          index := minIdx;
          if index <= maxIdx then
            forVar := arr[index];
            while index <= maxIdx and not condition do
              statements;
              incr(index);
              if index <= maxIdx then
                forVar := arr[index];
              end if;
            end while;
          end if;
        end func;

      const proc: for (inout baseType: forVar)
                  range (in arrayType: arr)
                  until (ref boolean: condition) do
                    (in proc: statements)
                  end for is func
        local
          var integer: index is 0;
        begin
          index := minIdx;
          if index <= maxIdx then
            forVar := arr[index];
            while index <= maxIdx and not condition do
              statements;
              incr(index);
              if index <= maxIdx then
                forVar := arr[index];
              end if;
            end while;
          end if;
        end func;

      (**
       *  For-loop where ''keyVar'' loops over the indices of the array ''arr''.
       *  Additionally a ''condition'' is checked before the statements in
       *  the loop body are executed.
       *)
      const proc: for key (inout integer: keyVar)
                  range (in arrayType: arr)
                  until (ref func boolean: condition) do
                    (in proc: statements)
                  end for is func
        begin
          for keyVar range minIdx to maxIdx until condition do
            statements;
          end for;
        end func;

      const proc: for key (inout integer: keyVar)
                  range (in arrayType: arr)
                  until (ref boolean: condition) do
                    (in proc: statements)
                  end for is func
        begin
          for keyVar range minIdx to maxIdx until condition do
            statements;
          end for;
        end func;

      (**
       *  For-loop where ''forVar'' and ''keyVar'' loop over the array ''arr''.
       *  The variable ''forVar'' loops over the elements of ''arr''
       *  and ''keyVar'' loops over the indices of ''arr''.
       *  Additionally a ''condition'' is checked before the statements in
       *  the loop body are executed.
       *)
      const proc: for (inout baseType: forVar)
                  key (inout integer: keyVar)
                  range (in arrayType: arr)
                  until (ref func boolean: condition) do
                    (in proc: statements)
                  end for is func
        begin
          keyVar := minIdx;
          if keyVar <= maxIdx then
            forVar := arr[keyVar];
            while keyVar <= maxIdx and not condition do
              statements;
              incr(keyVar);
              if keyVar <= maxIdx then
                forVar := arr[keyVar];
              end if;
            end while;
          end if;
        end func;

      const proc: for (inout baseType: forVar)
                  key (inout integer: keyVar)
                  range (in arrayType: arr)
                  until (ref boolean: condition) do
                    (in proc: statements)
                  end for is func
        begin
          keyVar := minIdx;
          if keyVar <= maxIdx then
            forVar := arr[keyVar];
            while keyVar <= maxIdx and not condition do
              statements;
              incr(keyVar);
              if keyVar <= maxIdx then
                forVar := arr[keyVar];
              end if;
            end while;
          end if;
        end func;

      if getobj((in baseType: element1) = (in baseType: element2)) <> NIL and
          getobj((in baseType: element1) <> (in baseType: element2)) <> NIL then

        const func boolean: (in arrayType: arr1) = (in arrayType: arr2) is func
          result
            var boolean: isEqual is TRUE;
          local
            var integer: number is 1;
          begin
            number := minIdx;
            while number <= maxIdx and isEqual do
              isEqual := arr1[number] = arr2[number];
              incr(number);
            end while;
          end func;

        const func boolean: (in arrayType: arr1) <> (in arrayType: arr2) is func
          result
            var boolean: isNotEqual is FALSE;
          local
            var integer: number is 1;
          begin
            number := minIdx;
            while number <= maxIdx and not isNotEqual do
              isNotEqual := arr1[number] <> arr2[number];
              incr(number);
            end while;
          end func;

      end if;

    end global;
  end func;


(**
 *  Abstract data type for fixed size arrays with minimum index of 1.
 *  A fixed size array type defines the minimum and maximum allowed indices.
 *  All arrays declared with this type have the same length and the same
 *  minimum and maximum indices. All abstract fixed size array types are
 *  incompatible to each other. A type declaration is needed to use the
 *  same fixed size array type at several places. E.g.:
 *    const type: dataBlockType is array [64] integer;
 *  afterwards ''dataBlockType'' can be used in declarations. E.g.:
 *    var dataBlockType: aDataBlock is dataBlockType.value;
 *  @param maxIdx Maximum index of the array type.
 *  @param baseType Type of the array elements.
 *)
const func type: array [ (in integer: maxIdx) ] (in type: baseType) is
  return array[1 .. maxIdx] baseType;