    "A [[List]] which can be efficiently searched for
     occurrences of a given element, or for inclusions of a
     given sublist of elements. This interface provides
     operations for finding:

     - _occurrences_ of a single value in the list, and
     - _inclusions_ of a given sublist of values in the list.

     Occurrences and inclusions are identified by a list index
     at which the value or sublist of values occurs in the list.
     In the case of an inclusion, it is the index of the first
     matching value from the sublist.

     Inclusions may overlap. For example:

         \"yoyoyoyoyo\".inclusions(\"yoy\")

     produces the stream `{ 0, 2, 4, 6 }`.

     An empty list is considered to be included at every index,
     including the index [[size]] at the very end of the
     list. Thus:

         \"hello\".inclusions(\"\")

     produces the stream `{ 0, 1, 2, 3, 4, 5 }`, with 6
     inclusions in a string of length 5.

     In particular:

     - `\"\".inclusions(\"x\")` is the empty stream `{}`, but
     - `\"\".inclusions(\"\")` is the stream `{ 0 }`."
    see (`class String`, `class Array`)
    since("1.2.0")
    shared interface SearchableList<Element>
            satisfies List<Element> {

        "Determines if the given [[value|element]] occurs at the
         given index in this list."
        shared default
        Boolean occursAt(
            "The index at which the value might occur."
            Integer index,
            "The value. If null, it is considered to occur
             at any index in this list with a null element."
            Element element)
                => let (elem = getFromFirst(index))
                if (exists element, exists elem)
                then elem == element
                else element exists == elem exists;

        "The indexes in this list at which the given
         [[value|element]] occurs."
        shared default
        {Integer*} occurrences(
            "The value. If null, it is considered to occur
             at any index in this list with a null element."
            Element element,
            "The smallest index to consider."
            Integer from = 0,
            "The number of indexes to consider."
            Integer length = size-from)
                //performance of comprehension was
                //worse than hand-written iterable
                //=> { for (i in from:length)
                //     if (occursAt(i, element)) i };
                => object satisfies Iterable<Integer> {
            value len = outer.size;
            value max =
                    from+length>len
                        then len else from+length;
            iterator()
                    => object satisfies Iterator<Integer> {
                variable value index = from;
                shared actual Integer|Finished next() {
                    while (index<max) {
                        if (occursAt(index, element)) {
                            return index++;
                        }
                        else {
                            index++;
                        }
                    }
                    return finished;
                }
            };
            shared actual Integer size {
                variable value size = 0;
                for (index in from:max-from) {
                    if (occursAt(index,element)) {
                        size++;
                    }
                }
                return size;
            }
            empty => occurs(element, from, length);
            first => firstOccurrence(element, from, length);
            last => lastOccurrence(element, from, length);
        };

        "Determines if the given [[value|element]] occurs as an
         element of this list, at any index that falls within
         the segment `from:length` defined by the optional
         [[starting index|from]] and [[length]]."
        shared default
        Boolean occurs(
            "The value. If null, it is considered to occur
             at any index in this list with a null element."
            Element element,
            "The smallest index to consider."
            Integer from = 0,
            "The number of indexes to consider."
            Integer length = size-from)
                => firstOccurrence(element, from, length) exists;

        "The first index in this list at which the given
         [[value|element]] occurs, that falls within the segment
         `from:length` defined by the optional
         [[starting index|from]] and [[length]]."
        shared default
        Integer? firstOccurrence(
            "The value. If null, it is considered to occur
             at any index in this list with a null element."
            Element element,
            "The smallest index to consider."
            Integer from = 0,
            "The number of indexes to consider."
            Integer length = size-from) {
            for (index in from:length) {
                if (occursAt(index,element)) {
                    return index;
                }
            }
            else {
                return null;
            }
        }

        "The last index in this list at which the given
         [[value|element]] occurs, that falls within the range
         `size-length-from:length` defined by the optional
         [[starting index|from]], interpreted as a reverse index
         counting from the _end_ of the list, and [[length]]."
        shared default
        Integer? lastOccurrence(
            "The value. If null, it is considered to occur
             at any index in this list with a null element."
            Element element,
            "The smallest index to consider, interpreted as
             a reverse index counting from the _end_ of the
             list, where `0` is the last element of the list,
             and `size-1` is the first element of the list."
            Integer from = 0,
            "The number of indexes to consider."
            Integer length = size-from) {
            //TODO: refine reversed to return a SearchableList
            // => reversed.firstOccurrence(element, from, length)
            for (index in (size-length-from:length).reversed) {
                if (occursAt(index,element)) {
                    return index;
                }
            }
            else {
                return null;
            }
        }

        "Determine if the given [[list|sublist]] occurs as a
         sublist at the given index of this list."
        shared default
        Boolean includesAt(
            "The index at which the [[sublist]] might occur."
            Integer index,
            List<Element> sublist) {
            if (0 <= index <= size-sublist.size) {
                variable value i = index;
                for (element in sublist) {
                    if (exists element) {
                        if (!occursAt(i, element)) {
                            return false;
                        }
                    }
                    else {
                        assert (is Element null);
                        if (!occursAt(i, null)) {
                            return false;
                        }
                    }
                    i++;
                }
                else {
                    return true;
                }
            }
            else {
                return false;
            }
        }

        "The indexes in this list at which the given
         [[list|sublist]] occurs as a sublist, that are greater
         than or equal to the optional [[starting index|from]]."
        shared default
        {Integer*} inclusions(List<Element> sublist,
            "The smallest index to consider."
            Integer from = 0)
                //performance of comprehension was
                //worse than hand-written iterable
                /*=> { for (i in from:size+1-from-sublist.size)
                     if (includesAt(i, sublist)) i };*/
                => object satisfies Iterable<Integer> {
            value len = outer.size;
            iterator()
                    => object satisfies Iterator<Integer> {
                value max = len+1-sublist.size;
                variable value index = from;
                shared actual Integer|Finished next() {
                    while (index<max) {
                        if (includesAt(index, sublist)) {
                            return index++;
                        }
                        else {
                            index++;
                        }
                    }
                    return finished;
                }
            };
            shared actual Integer size {
                variable value size = 0;
                for (index in from:len-from+1-sublist.size) {
                    if (includesAt(index,sublist)) {
                        size++;
                    }
                }
                return size;
            }
            empty => includes(sublist, from);
            first => firstInclusion(sublist, from);
            last => lastInclusion(sublist, from);
        };

        "Determine if the given [[list|sublist]] occurs as a
         sublist at some index in this list, at any index that
         is greater than or equal to the optional
         [[starting index|from]]."
        shared default
        Boolean includes(List<Element> sublist,
            "The smallest index to consider."
            Integer from = 0)
                => firstInclusion(sublist, from) exists;

        "The first index in this list at which the given
         [[list|sublist]] occurs as a sublist, that is greater
         than or equal to the optional [[starting index|from]]."
        shared default
        Integer? firstInclusion(List<Element> sublist,
            "The smallest index to consider."
            Integer from = 0) {
            for (index in from:size-from+1-sublist.size) {
                if (includesAt(index,sublist)) {
                    return index;
                }
            }
            else {
                return null;
            }
        }

        "The last index in this list at which the given
         [[list|sublist]] occurs as a sublist, that falls within
         the range `0:size-from+1-sublist.size` defined by the
         optional [[starting index|from]], interpreted as a
         reverse index counting from the _end_ of the list."
        shared default
        Integer? lastInclusion(List<Element> sublist,
            "The smallest index to consider, interpreted as
             a reverse index counting from the _end_ of the
             list, where `0` is the last element of the list,
             and `size-1` is the first element of the list."
            Integer from = 0) {
            //TODO: refine reversed to return a SearchableList
            // => reversed.firstInclusion(element, from, length)
            for (index in (0:size-from+1-sublist.size).reversed) {
                if (includesAt(index,sublist)) {
                    return index;
                }
            }
            else {
                return null;
            }
        }

    }