(defun expand-ranges (string)
  (loop
     with prevnum = nil
     for idx = 0 then (1+ nextidx)
     for (number nextidx) = (multiple-value-list
                             (parse-integer string
                                            :start idx :junk-allowed t))
     append (cond
              (prevnum
               (prog1
                   (loop for i from prevnum to number
                      collect i)
                 (setf prevnum nil)))
              ((and (< nextidx (length string))
                    (char= (aref string nextidx) #\-))
               (setf prevnum number)
               nil)
              (t
               (list number)))
     while (< nextidx (length string))))

CL-USER> (expand-ranges "-6,-3--1,3-5,7-11,14,15,17-20")
(-6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20)


include "cowgol.coh";

# Callback interface
interface RangeCb(n: int32);

# This will call `cb' for each number in the range, in ascending order.
# It will return NULL on success, or the location of an error if
# there is one.
sub Expand(ranges: [uint8], cb: RangeCb): (err: [uint8]) is
    err := 0 as [uint8];
    loop
        # Grab first number
        var n1: int32;
        var next: [uint8];
        (n1, next) := AToI(ranges);

        if next == ranges then
            # No number here!
            err := ranges;
            break;
        elseif [next] == ',' or [next] == 0 then
            # Only one number, not a range
            cb(n1);
        elseif [next] != '-' then
            # No dash!
            err := ranges;
            break;
        else
            # Grab second number
            ranges := @next next;
            var n2: int32;
            (n2, next) := AToI(ranges);
            if next == ranges or n1 >= n2 then
                # No second number, or first not before second
                err := ranges;
                break;
            end if;

            # We need all numbers from n1 to n2 inclusive
            while n1 <= n2 loop
                cb(n1);
                n1 := n1 + 1;
            end loop;
        end if;

        # stop if end reached
        if [next] == 0 then break;
        elseif [next] != ',' then
            err := ranges;
            break;
        end if;
        ranges := @next next;
    end loop;
end sub;

# This function will use `Expand' above to expand a comma-separated
# range list, and reformat it as a comma-separated list of integers.
sub ExpandFmt(ranges: [uint8], buf: [uint8]): (err: [uint8]) is
    # Format and add number to buffer
    sub AddNum implements RangeCb is
        buf := IToA(n, 10, buf);
        [buf] := ',';
        buf := @next buf;
    end sub;

    # Expand range, adding numbers to buffer
    err := Expand(ranges, AddNum);
    [@prev buf] := 0;
end sub;

# Expand and print, and/or give error
sub PrintExpansion(ranges: [uint8]) is
    var buf: uint8[256];
    var err := ExpandFmt(ranges, &buf[0]);
    print(ranges);
    print_nl();
    print("  >> ");
    if err == 0 as [uint8] then
        # everything is OK
        print(&buf[0]);
    else
        # error
        print("error at: ");
        print(err);
    end if;
    print_nl();
end sub;

# Try it on the given input
PrintExpansion("-6,-3--1,3-5,7-11,14,15,17-20");

# Try it on a couple of wrong ones
PrintExpansion("-6-3--1,3-5,7-11,14,15,17-20"); # misformatted range
PrintExpansion("-6,-3--1,5-3,7-11,14,15,17-20"); # numbers not in order


def range_expand(range)
  range.split(',').flat_map do |part|
    match = /^(-?\d+)-(-?\d+)$/.match(part)
    if match
      (match[1].to_i .. match[2].to_i).to_a
    else
      part.to_i
    end
  end
end

puts range_expand("-6,-3--1,3-5,7-11,14,15,17-20")


import std.stdio, std.regex, std.conv, std.range, std.algorithm;

enum rangeEx = (string s) /*pure*/ => s.matchAll(`(-?\d+)-?(-?\d+)?,?`)
    .map!q{ a[1].to!int.iota(a[1 + !a[2].empty].to!int + 1) }.join;

void main() {
    "-6,-3--1,3-5,7-11,14,15,17-20".rangeEx.writeln;
}


program Range_expansion;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

const
  input = '-6,-3--1,3-5,7-11,14,15,17-20';

var
  r: TArray<Integer>;
  last, i, n: Integer;

begin
  for var part in input.Split([',']) do
  begin
    i := part.Substring(1).IndexOf('-');
    if i = -1 then
    begin
      if not TryStrToInt(part, n) then
        raise Exception.Create('Error: value invalid ' + part);
      if Length(r) > 0 then
        if last = n then
        begin
          raise Exception.Create('Error: Duplicate value:' + n.ToString);
        end
        else
        begin
          if last > n then
            raise Exception.CreateFmt('Error: values not ordered: %s > %s', [last, n]);
        end;
      SetLength(r, Length(r) + 1);
      r[High(r)] := n;
      last := n;
    end
    else
    begin
      var n1 := 0;
      var n2 := 0;
      if not TryStrToInt(part.Substring(0, i + 1), n1) then
        raise Exception.Create('Error: value invalid ' + part);
      if not TryStrToInt(part.Substring(i + 2), n2) then
        raise Exception.Create('Error: value invalid ' + part);
      if n2 < n1 + 2 then
        raise Exception.Create('Error: Invalid range' + part);
      if Length(r) > 0 then
      begin
        if last = n1 then
          raise Exception.Create('Error: Duplicate value: ' + n1.ToString);
        if last > n1 then
          raise Exception.CreateFmt('Error: Values not ordened: %d > %d', [last, n1]);
      end;

      for i := n1 to n2 do
      begin
        SetLength(r, Length(r) + 1);
        r[High(r)] := i;
      end;

      last := n2;
    end;
  end;
  write('expanded: ');
  for var rr in r do
  begin
    write(rr, ',');
  end;
  readln;
end.


function ExpandRanges(ranges : String) : array of Integer;
begin
   for var range in ranges.Split(',') do begin
      var separator = range.IndexOf('-', 2);
      if separator > 0 then begin
         for var i := range.Left(separator-1).ToInteger to range.Copy(separator+1).ToInteger do
            Result.Add(i);
      end else begin
         Result.Add(range.ToInteger)
      end;
   end;
end;

var expanded := ExpandRanges('-6,-3--1,3-5,7-11,14,15,17-20');
PrintLn(JSON.Stringify(expanded));


func main() {
    let input = "-6,-3--1,3-5,7-11,14,15,17-20"
    print("range: \(input)")
    var r = []
    var last = 0
    for part in input.Split(',') {
        var i = part[1..].IndexOf('-')
        if  i == -1 {
            var n = Integer(part)
            if r.Length() > 0 {
                return print("duplicate value: \(n)") when last == n
                return print("values not ordered: \(last) > \(n)") when last > n
            }
            r.Add(n)
            last = n
        } else {
            var n1 = Integer(part[..i])
            var n2 = Integer(part[(i + 2)..])
            return print("invalid range: \(part)") when n2 < n1 + 2

            if r.Length() > 0 {
                return print("duplicate value: \(n1)") when last == n1
                return print("values not ordered: \(last) > \(n1)") when last > n1
            }

            for i in n1..n2 {
                r.Add(i)
            }
            last = n2
        }
    }

    print("expanded: \(r)")
}

main()


;; parsing [spaces][-]digit(s)-[-]digit(s)[spaces]
(define R (make-regexp "^ *(\-?\\d+)\-(\-?\\d+) *$" ))

;; the native (range a b) is [a ... b[
;; (range+ a b) is [a ... b]
(define (range+ a b)
	(if (< a b) (range a (1+ b))
	(if (> a b) (range a (1- b) -1)
	(list a))))

;; in : string : "number" or "number-number"
;; out : a range = list of integer(s)
(define (do-range str)
(define from-to (regexp-exec R str)) ;; "1-3" --> ("1" "3")
(if from-to
    (range+ (string->number (first from-to)) (string->number (second from-to)))
    (list (string->number str))))

(define (ranges str)
    (apply append (map do-range (string-split str ","))))

(define task "-6,-3--1,3-5,7-11,14,15,17-20")
(ranges task)
    → (-6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20)


defmodule RC do
  def expansion(range) do
    Enum.flat_map(String.split(range, ","), fn part ->
      case Regex.scan(~r/^(-?\d+)-(-?\d+)$/, part) do
        [[_,a,b]] -> Enum.to_list(String.to_integer(a) .. String.to_integer(b))
        [] -> [String.to_integer(part)]
      end
    end)
  end
end

IO.inspect RC.expansion("-6,-3--1,3-5,7-11,14,15,17-20")


-module( range ).

-export( [expansion/1, task/0] ).

expansion( String ) ->
        lists:flatten( [expansion_individual(io_lib:fread("~d", X)) || X <- string:tokens(String, ",")] ).

task() ->
    io:fwrite( "~p~n", [expansion("-6,-3--1,3-5,7-11,14,15,17-20")] ).

expansion_individual( {ok, [N], []} ) -> N;
expansion_individual( {ok, [Start], "-" ++ Stop_string} ) -> lists:seq( Start, erlang:list_to_integer(Stop_string) ).


open System.Text.RegularExpressions

// simplify regex matching with an active pattern
let (|Regexp|_|) pattern txt =
    match Regex.Match(txt, pattern) with
    | m when m.Success -> [for g in m.Groups -> g.Value] |> List.tail |> Some
    | _                -> None

// Parse and expand a single range description.
// string -> int list
let parseRange r =
  match r with
  | Regexp @"^(-?\d+)-(-?\d+)$" [first; last] -> [int first..int last]
  | Regexp @"^(-?\d+)$"         [single]      -> [int single]
  | _ -> failwithf "illegal range format: %s" r

let expand (desc:string) =
  desc.Split(',')
  |> List.ofArray
  |> List.collect parseRange

printfn "%A" (expand "-6,-3--1,3-5,7-11,14,15,17-20")


USING: kernel math.parser math.ranges prettyprint regexp
sequences sequences.extras splitting ;

: expand ( str -- seq )
    "," split [
        R/ (?<=\d)-/ re-split [ string>number ] map
        dup length 2 = [ first2 [a,b] ] when
    ] map-concat ;

"-6,-3--1,3-5,7-11,14,15,17-20" expand .


: >snumber ( str len -- 'str 'len n )
  0. 2swap
  over [email protected] [char] - = if
    1 /string
    >number 2swap drop
    negate
  else
    >number 2swap drop
  then ;

: expand ( str len -- )
  begin dup while
    >snumber >r
    dup if over [email protected] [char] - = if
      1 /string
      >snumber r> over >r
      do i . loop
    then then
    dup if over [email protected] [char] , = if
      1 /string
    then then
    r> .
  repeat 2drop ;

s" -6,-3--1,3-5,7-11,14,15,17-20" expand


      MODULE HOMEONTHERANGE
       CONTAINS	!The key function.
        CHARACTER*200 FUNCTION ERANGE(TEXT)	!Expands integer ranges in a list.
Can't return a character value of variable size.
         CHARACTER*(*) TEXT	!The list on input.
         CHARACTER*200 ALINE	!Scratchpad for output.
         INTEGER N,N1,N2	!Numbers in a range.
         INTEGER I,I1		!Steppers.
          ALINE = ""		!Scrub the scratchpad.
          L = 0			!No text has been placed.
          I = 1			!Start at the start.
          CALL FORASIGN		!Find something to look at.
Chug through another number or number - number range.
        R:DO WHILE(EATINT(N1))	!If I can grab a first number, a term has begun.
            N2 = N1			!Make the far end the same.
            IF (PASSBY("-")) CALL EATINT(N2)	!A hyphen here is not a minus sign.
            IF (L.GT.0) CALL EMIT(",")		!Another, after what went before?
            DO N = N1,N2,SIGN(+1,N2 - N1)	!Step through the range, possibly backwards.
              CALL SPLOT(N)				!Roll a number.
              IF (N.NE.N2) CALL EMIT(",")		!Perhaps another follows.
            END DO				!On to the next number.
            IF (.NOT.PASSBY(",")) EXIT R	!More to come?
          END DO R		!So much for a range.
Completed the scan. Just return the result.
          ERANGE = ALINE(1:L)	!Present the result. Fiddling ERANGE is bungled by some compilers.
         CONTAINS	!Some assistants for the scan to save on repetition and show intent.
          SUBROUTINE FORASIGN	!Look for one.
    1       IF (I.LE.LEN(TEXT)) THEN	!After a thingy,
              IF (TEXT(I:I).LE." ") THEN	!There may follow spaces.
                I = I + 1				!So,
                GO TO 1					!Speed past any.
              END IF			!So that the caller can see
            END IF			!Whatever substantive character follows.
          END SUBROUTINE FORASIGN	!Simple enough.

          LOGICAL FUNCTION PASSBY(C)	!Advances the scan if a certain character is seen.
Could consider or ignore case for letters, but this is really for single symbols.
           CHARACTER*1 C	!The character.
            PASSBY = .FALSE.	!Pessimism.
            IF (I.LE.LEN(TEXT)) THEN	!Can't rely on I.LE.LEN(TEXT) .AND. TEXT(I:I)...
              IF (TEXT(I:I).EQ.C) THEN	!Curse possible full evaluation.
                PASSBY = .TRUE.		!Righto, C is seen.
                I = I + 1		!So advance the scan.
                CALL FORASIGN		!And see what follows.
              END IF		!So much for a match.
            END IF		!If there is something to be uinspected.
          END FUNCTION PASSBY	!Can't rely on testing PASSBY within PASSBY either.

          LOGICAL FUNCTION EATINT(N)	!Convert text into an integer.
           INTEGER N	!The value to be ascertained.
           INTEGER D	!A digit.
           LOGICAL NEG	!In case of a minus sign.
            EATINT = .FALSE.	!Pessimism.
            IF (I.GT.LEN(TEXT)) RETURN	!Anything to look at?
            N = 0			!Scrub to start with.
            IF (PASSBY("+")) THEN	!A plus sign here can be ignored.
              NEG = .FALSE.		!So, there's no minus sign.
             ELSE			!And if there wasn't a plus,
              NEG = PASSBY("-")		!A hyphen here is a minus sign.
            END IF			!One way or another, NEG is initialised.
            IF (I.GT.LEN(TEXT)) RETURN	!Nothing further! We wuz misled!
Chug through digits. Can develop -2147483648, thanks to the workings of two's complement.
   10       D = ICHAR(TEXT(I:I)) - ICHAR("0")	!Hope for a digit.
            IF (0.LE.D .AND. D.LE.9) THEN	!Is it one?
              N = N*10 + D			!Yes! Assimilate it, negatively.
              I = I + 1				!Advance one.
              IF (I.LE.LEN(TEXT)) GO TO 10	!And see what comes next.
            END IF			!So much for a sequence of digits.
            IF (NEG) N = -N		!Apply the minus sign.
            EATINT = .TRUE.		!Should really check for at least one digit.
            CALL FORASIGN		!Ram into whatever follows.
          END FUNCTION EATINT	!Integers are easy. Could check for no digits seen.

          SUBROUTINE EMIT(C)	!Rolls forth one character.
           CHARACTER*1 C	!The character.
            L = L + 1		!Advance the finger.
            IF (L.GT.LEN(ALINE)) STOP "Ran out of ALINE!"	!Maybe not.
            ALINE(L:L) = C	!And place the character.
          END SUBROUTINE EMIT	!That was simple.

          SUBROUTINE SPLOT(N)	!Rolls forth a signed number.
           INTEGER N		!The number.
           CHARACTER*12 FIELD	!Sufficient for 32-bit integers.
           INTEGER I		!A stepper.
            WRITE (FIELD,"(I0)") N	!Roll the number, with trailing spaces.
            DO I = 1,12		!Now transfer the ALINE of the number.
              IF (FIELD(I:I).LE." ") EXIT	!Up to the first space.
              CALL EMIT(FIELD(I:I))	!One by one.
            END DO		!On to the end.
          END SUBROUTINE SPLOT	!Not so difficult either.
        END FUNCTION ERANGE	!A bit tricky.
      END MODULE HOMEONTHERANGE

      PROGRAM POKE
      USE HOMEONTHERANGE
      CHARACTER*(200) SOME
      SOME = "-6,-3--1,3-5,7-11,14,15,17-20"
      SOME = ERANGE(SOME)
      WRITE (6,*) SOME	!If ERANGE(SOME) then the function usually can't write output also.
      END


' FB 1.05.0 Win64

Sub split (s As Const String, sepList As Const String, result() As String)
  If s = "" OrElse sepList = "" Then
     Redim result(0)
     result(0) = s
     Return
  End If
  Dim As Integer i, j, count = 0, empty = 0, length
  Dim As Integer position(Len(s) + 1)
  position(0) = 0

  For i = 0 To len(s) - 1
    For j = 0 to Len(sepList) - 1
      If s[i] = sepList[j] Then
        count += 1
        position(count) = i + 1
      End If
    Next j
  Next i

  Redim result(count)
  If count  = 0 Then
    result(0) = s
    Return
  End If

  position(count + 1) = len(s) + 1

  For i = 1 To count + 1
    length = position(i) - position(i - 1) - 1
    result(i - 1) = Mid(s, position(i - 1) + 1, length)
  Next
End Sub

Function expandRange(s As Const String) As String
  If s = "" Then Return ""
  Dim b() As String
  Dim c() As String
  Dim result As String = ""
  Dim As Integer start = 0, finish = 0, length
  split s, ",", b()
  For i As Integer = LBound(b) To UBound(b)
    split b(i), "-", c()
    length = UBound(c) - LBound(c) + 1
    If length = 1 Then
      start  = ValLng(c(LBound(c)))
      finish = start
    ElseIf length = 2 Then
       If Left(b(i), 1) = "-" Then
         start  = -ValLng(c(UBound(c)))
         finish = start
       Else
         start  = ValLng(c(LBound(c)))
         finish = ValLng(c(UBound(c)))
       End If
    ElseIf length = 3 Then
      start  = -ValLng(c(LBound(c) + 1))
      finish = ValLng(c(UBound(c)))
    Else
      start  = -ValLng(c(LBound(c) + 1))
      finish = -ValLng(c(UBound(c)))
    End If
    For j As Integer = start To finish
      result += Str(j) + ", "
    Next j
  Next i
  Return Left(result, Len(result) - 2) '' get rid of final ", "
End Function

Dim s As String = "-6,-3--1,3-5,7-11,14,15,17-20"
Print expandRange(s)
Print
Print "Press any key to quit"
Sleep


package main

import (
    "fmt"
    "strconv"
    "strings"
)

const input = "-6,-3--1,3-5,7-11,14,15,17-20"

func main() {
    fmt.Println("range:", input)
    var r []int
    var last int
    for _, part := range strings.Split(input, ",") {
        if i := strings.Index(part[1:], "-"); i == -1 {
            n, err := strconv.Atoi(part)
            if err != nil {
                fmt.Println(err)
                return
            }
            if len(r) > 0 {
                if last == n {
                    fmt.Println("duplicate value:", n)
                    return
                } else if last > n {
                    fmt.Println("values not ordered:", last, ">", n)
                    return
                }
            }
            r = append(r, n)
            last = n
        } else {
            n1, err := strconv.Atoi(part[:i+1])
            if err != nil {
                fmt.Println(err)
                return
            }
            n2, err := strconv.Atoi(part[i+2:])
            if err != nil {
                fmt.Println(err)
                return
            }
            if n2 < n1+2 {
                fmt.Println("invalid range:", part)
                return
            }
            if len(r) > 0 {
                if last == n1 {
                    fmt.Println("duplicate value:", n1)
                    return
                } else if last > n1 {
                    fmt.Println("values not ordered:", last, ">", n1)
                    return
                }
            }
            for i = n1; i <= n2; i++ {
                r = append(r, i)
            }
            last = n2
        }
    }
    fmt.Println("expanded:", r)
}


def expandRanges = { compressed ->
    Eval.me('['+compressed.replaceAll(~/(\d)-/, '$1..')+']').flatten().toString()[1..-2]
}


procedure main()
s := "-6,-3--1,3-5,7-11,14,15,17-20"
write("Input string      := ",s)
write("Expanded list   := ", list2string(range_expand(s)) | "FAILED")
end

procedure range_expand(s)          #: return list of integers extracted from an ordered string representation
local R,low,high
R := []

s ? until pos(0) do {
   put(R,low := integer(tab(upto(',-')|0))| fail)           # get lower bound
   if ="-" || (high := integer(tab(find(",")|0))|fail) then
      until low = high do put(R,low +:= 1)                  # find range
   =","
   }
return R
end

procedure list2string(L)        #: helper function to convert a list to a string
local s

   every (s := "[ ") ||:= !L || " "
   return s || "]"
end


require'strings'
thru=: <. + [email protected](+*)@-~
num=: _&".
normaliz=: rplc&(',-';',_';'--';'-_')@,~&','
subranges=:<@(thru/)@(num;._2)@,&'-';._1
rngexp=: ;@[email protected]


import java.util.*;

class RangeExpander implements Iterator<Integer>, Iterable<Integer> {

    private static final Pattern TOKEN_PATTERN = Pattern.compile("([+-]?\\d+)-([+-]?\\d+)");

    private final Iterator<String> tokensIterator;

    private boolean inRange;
    private int upperRangeEndpoint;
    private int nextRangeValue;

    public RangeExpander(String range) {
        String[] tokens = range.split("\\s*,\\s*");
        this.tokensIterator = Arrays.asList(tokens).iterator();
    }

    @Override
    public boolean hasNext() {
        return hasNextRangeValue() || this.tokensIterator.hasNext();
    }

    private boolean hasNextRangeValue() {
        return this.inRange && this.nextRangeValue <= this.upperRangeEndpoint;
    }

    @Override
    public Integer next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        if (hasNextRangeValue()) {
            return this.nextRangeValue++;
        }

        String token = this.tokensIterator.next();

        Matcher matcher = TOKEN_PATTERN.matcher(token);
        if (matcher.find()) {
            this.inRange = true;
            this.upperRangeEndpoint = Integer.valueOf(matcher.group(2));
            this.nextRangeValue = Integer.valueOf(matcher.group(1));
            return this.nextRangeValue++;
        }

        this.inRange = false;
        return Integer.valueOf(token);
    }

    @Override
    public Iterator<Integer> iterator() {
        return this;
    }

}

class RangeExpanderTest {
    public static void main(String[] args) {
        RangeExpander re = new RangeExpander("-6,-3--1,3-5,7-11,14,15,17-20");
        for (int i : re) {
            System.out.print(i + " ");
        }
    }
}


#!/usr/bin/env js

function main() {
    print(rangeExpand('-6,-3--1,3-5,7-11,14,15,17-20'));
}

function rangeExpand(rangeExpr) {

    function getFactors(term) {
        var matches = term.match(/(-?[0-9]+)-(-?[0-9]+)/);
        if (!matches) return {first:Number(term)};
        return {first:Number(matches[1]), last:Number(matches[2])};
    }

    function expandTerm(term) {
        var factors = getFactors(term);
        if (factors.length < 2) return [factors.first];
        var range = [];
        for (var n = factors.first; n <= factors.last;  n++) {
            range.push(n);
        }
        return range;
    }

    var result = [];
    var terms = rangeExpr.split(/,/);
    for (var t in terms) {
        result = result.concat(expandTerm(terms[t]));
    }

    return result;
}

main();


def expand_range:
  def number: "-?[0-9]+";
  def expand: [range(.[0]; .[1] + 1)];

  split(",")
  | reduce .[] as $r
      ( []; . +
            ($r | if test("^\(number)$") then [tonumber]
                  else sub( "(?<x>\(number))-(?<y>\(number))"; "\(.x):\(.y)")
                  | split(":") | map(tonumber) | expand
	          end));


#!/usr/bin/env jsish
"use strict";

/* Range expansion, in Jsish */
function rangeExpand(rangeExpr) {

    function getFactors(term) {
        var matches = term.match(/(-?[0-9]+)-(-?[0-9]+)/);
        if (!matches) return {first:Number(term)};
        return {first:Number(matches[1]), last:Number(matches[2])};
    }

    function expandTerm(term) {
        var factors = getFactors(term);
        if (factors.length < 2) return [factors.first];
        var range = [];
        for (var n = factors.first; n <= factors.last;  n++) {
            range.push(n);
        }
        return range;
    }

    var result = [];
    var terms = rangeExpr.split(",");
    for (var t in terms) {
        result = result.concat(expandTerm(terms[t]));
    }

    return result;
}

if (Interp.conf('unitTest')) {
;    rangeExpand('-6,-3--1,3-5,7-11,14,15,17-20');
}

/*
=!EXPECTSTART!=
rangeExpand('-6,-3--1,3-5,7-11,14,15,17-20') ==> [ -6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20 ]
=!EXPECTEND!=
*/


slurp(s) = readcsv(IOBuffer(s))

conv(s)= colon(map(x->parse(Int,x),match(r"^(-?\d+)-(-?\d+)$", s).captures)...)

expand(s) = mapreduce(x -> isa(x,Number)? Int(x) : conv(x), vcat, slurp(s))


// version 1.0.6

fun expandRange(s: String): MutableList<Int> {
    val list = mutableListOf<Int>()
    val items = s.split(',')
    var first: Int
    var last:  Int
    for (item in items) {
        val count = item.count { it == '-' }
        if (count == 0 || (count == 1 && item[0] == '-'))
            list.add(item.toInt())
        else {
            val items2 = item.split('-')
            if (count == 1) {
                first = items2[0].toInt()
                last  = items2[1].toInt()
            }
            else if (count == 2) {
                first = items2[1].toInt() * -1
                last  = items2[2].toInt()
            }
            else {
                first = items2[1].toInt() * -1
                last  = items2[3].toInt() * -1
            }
            for (i in first..last) list.add(i)
        }
    }
    return list
}

fun main(args: Array<String>) {
    val s = "-6,-3--1,3-5,7-11,14,15,17-20"
    println(expandRange(s))
}


define range_expand(expression::string) => {
    local(parts) = regexp(`^(-?\d+)-(-?\d+)$`)

    return (
        with elm in #expression->split(`,`)
        let isRange = #parts->setInput(#elm)&matches
        select #isRange
            ? (integer(#parts->matchString(1)) to integer(#parts->matchString(2)))->asString
            | integer(#elm)->asString
    )->join(', ')
}

range_expand(`-6,-3--1,3-5,7-11,14,15,17-20`)


print ExpandRange$( "-6,-3--1,3-5,7-11,14,15,17-20")
end

function ExpandRange$( compressed$)
    for i = 1 to ItemCount( compressed$, ",")
        item$ = word$( compressed$, i, ",")
        dash  = instr( item$, "-", 2) 'dash that is not the first character, is a separator
        if dash then
            for k = val( left$( item$, dash - 1)) to val( mid$( item$, dash + 1))
                ExpandRange$ = ExpandRange$ + str$( k) + ","
            next k
        else
            ExpandRange$ = ExpandRange$ + item$ + ","
        end if
    next i
    ExpandRange$ = left$( ExpandRange$, len( ExpandRange$) - 1)
end function

function ItemCount( list$, separator$)
    while word$(list$, ItemCount + 1, separator$) <> ""
        ItemCount = ItemCount + 1
    wend
end function


-- Note: currently does not support extra white space in input string
on expandRange (str)
  res = ""
  _player.itemDelimiter = ","
  cnt = str.item.count
  repeat with i = 1 to cnt
    part = str.item[i]
    pos = offset("-", part.char[2..part.length])
    if pos>0 then
      a = integer(part.char[1..pos])
      b = integer(part.char[pos+2..part.length])
      repeat with j = a to b
        put j&"," after res
      end repeat
    else
      put part&"," after res
    end if
  end repeat
  delete the last char of res
  return res
end


function range beginning ending stepping
    local tRange, tBegin, tEnd, tstep
    if stepping is empty or stepping is 0 then
        put 1 into tstep
    else
        put abs(stepping) into tstep
    end if

    if ending is empty or isNumber(ending) is not true then
        put 0 into tEnd
    else
        put ending into tEnd
    end if

    if beginning is empty or isNumber(beginning) is not true then
        put 0 into tBegin
    else
        put beginning into tBegin
    end if

    repeat with r = tBegin to tEnd step tstep
        put space & r after tRange
    end repeat
    return word 1 to -1 of tRange
end range

function expandRange rangeExpr
    put rangeExpr into tRange
    split tRange by comma
    repeat with n = 1 to the number of elements of tRange
        if matchText(tRange[n],"^(\-*\d+)\-(\-*\d+)",beginning, ending) then
            put range(beginning, ending, 1) & space after z
        else
            put tRange[n] & space after z
        end if
    end repeat
    return z
end expandRange


function range(i, j)
    local t = {}
    for n = i, j, i<j and 1 or -1 do
        t[#t+1] = n
    end
    return t
end

function expand_ranges(rspec)
    local ptn = "([-+]?%d+)%s?-%s?([-+]?%d+)"
    local t = {}

    for v in string.gmatch(rspec, '[^,]+') do
        local s, e = v:match(ptn)

        if s == nil then
            t[#t+1] = tonumber(v)
        else
            for i, n in ipairs(range(tonumber(s), tonumber(e))) do
                t[#t+1] = n
            end
        end
    end
    return t
end

local ranges = "-6,-3--1,3-5,7-11,14,15,17-20"
print(table.concat(expand_ranges(ranges), ', '))


ExpandRanges := proc( s :: string )
  uses  StringTools;
  local DoOne := proc( input )
    uses StringTools;
    local lo, hi, pos;
    if IsDigit( input ) or input[ 1 ] = "-"
     and IsDigit( input[ 2 .. -1 ] ) then
      parse( input )
    else
      pos := Search( "--", input );
      if pos > 0 then
        lo := input[ 1 .. pos - 1 ];
        hi := input[ 1 + pos .. -1 ];
      elif input[ 1 ] = "-" then
        pos := FirstFromLeft( "-", input[ 2 .. -1 ] );
        if pos = 0 then
          lo := input;
          hi := lo
        else
          lo := input[ 1 .. pos ];
          hi := input[ 2 + pos .. -1 ];
        end if;
      else
        pos := FirstFromLeft( "-", input );
        if pos = 0 then
          error "incorrect syntax"
        end if;
        lo := input[ 1 .. pos - 1 ];
        hi := input[ 1 + pos .. -1 ];
      end if;
      lo := parse( lo );
      hi := parse( hi );
      seq( lo .. hi )
    end if
  end proc:
  map( DoOne, map( Trim, Split( s, "," ) ) )
end proc:


rangeexpand[ rng_ ] := Module[ { step1 },
step1 = StringSplit[StringReplacePart[rng,"S",StringPosition[ rng,DigitCharacter~~"-"] /. {x_,y_} -> {y,y}],","];
[email protected]/@[email protected][step1,x__~~"S"~~y__->"Range["<>x<>","<>y<>"]"] ]


function L=range_expansion(S)
% Range expansion
if nargin < 1;
	S='[]';
end

if ~all(isdigit(S) | (S=='-')  | (S==',') | isspace(S))
	error 'invalid input';
end
ixr = find(isdigit(S(1:end-1)) & S(2:end) == '-')+1;
S(ixr)=':';
S=['[',S,']'];
L=eval(S);


pullInt = function(chars)
    numstr = chars.pull
    while chars and chars[0] != "," and chars[0] != "-"
        numstr = numstr + chars.pull
    end while
    return val(numstr)
end function

expandRange = function(s)
    result = []
    chars = s.split("")
    while chars
        num = pullInt(chars)
        if not chars or chars.pull == "," then
            result.push num
        else
            result = result + range(num, pullInt(chars))
            chars.pull  // skip "," after range
        end if
    end while
    return result
end function

print expandRange("-6,-3--1,3-5,7-11,14,15,17-20")


RANGEXP(X) ;Integer range expansion
 NEW Y,I,J,X1,H SET Y=""
 FOR I=1:1:$LENGTH(X,",") DO
 .S X1=$PIECE(X,",",I) FOR  Q:$EXTRACT(X1)'=" "  S X1=$EXTRACT(X1,2,$LENGTH(X1)) ;clean up leading spaces
 .SET H=$FIND(X1,"-")-1
 .IF H=1 SET H=$FIND(X1,"-",(H+1))-1 ;If the first value is negative ignore that "-"
 .IF H<0 SET Y=$SELECT($LENGTH(Y)=0:Y_X1,1:Y_","_X1)
 .IF '(H<0) FOR J=+$EXTRACT(X1,1,(H-1)):1:+$EXTRACT(X1,(H+1),$LENGTH(X1)) SET Y=$SELECT($LENGTH(Y)=0:J,1:Y_","_J)
 KILL I,J,X1,H
 QUIT Y


/*NetRexx program to expand a range of integers into a list. *************
* 09.08.2012 Walter Pachl derived from my Rexx version
* Changes: translate(old,' ',',') -> old.translate(' ',',')
*          dashpos=pos('-',x,2)   -> dashpos=x.pos('-',2)
*          Do                     -> Loop
*          Parse Var a x a        -> Parse a x a
*          Parse Var x ...        -> Parse x ...
**********************************************************************/

parse arg old
if old = '' then
  old='-6,-3--1,3-5,7-11,14,15,17-20' /*original list of nums/ranges */

  Say 'old='old                     /*show old list of nums/ranges.  */
  a=old.translate(' ',',')          /*translate commas to blanks     */
  new=''                            /*new list of numbers (so far).  */

  comma=''
  Loop While a<>''                  /* as long as there is input     */
    Parse a x a                     /* get one element               */
    dashpos=x.pos('-',2)            /* find position of dash, if any */
    If dashpos>0 Then Do            /* element is low-high           */
      Parse x low =(dashpos) +1 high /* split the element        */
      Loop j=low To high            /* output all numbers in range   */
        new=new||comma||j           /* with separating commas        */
        comma=','                   /* from now on use comma         */
        End
      End
    Else Do                         /* element is a number           */
      new=new||comma||x             /* append (with comma)           */
      comma=','                     /* from now on use comma         */
      End
    End
  Say 'new='new                     /*show the expanded list         */


import parseutils, strutils

proc expandRange(input: string): string =
  var output: seq[string]
  for range in input.split(','):
    var sep = range.find('-', 1)
    if sep > 0: # parse range
      var first = -1
      if range.substr(0, sep-1).parseInt(first) == 0:
        break
      var last = -1
      if range.substr(sep+1).parseInt(last) == 0:
        break
      for i in first..last:
        output.add($i)
    else: # parse single number
      var n = -1
      if range.parseInt(n) > 0:
        output.add($n)
      else:
        break
  return output.join(",")

echo("-6,-3--1,3-5,7-11,14,15,17-20".expandRange)


MODULE LIVector;
IMPORT SYSTEM;
TYPE
	LIPool = POINTER TO ARRAY OF LONGINT;
	LIVector*= POINTER TO LIVectorDesc;
	LIVectorDesc = RECORD
		cap-: INTEGER;
		len-: INTEGER;
		LIPool: LIPool;
	END;

	PROCEDURE (v: LIVector) Init*(cap: INTEGER);
	BEGIN
		v.cap := cap;
		v.len := 0;
		NEW(v.LIPool,cap);
	END Init;

	PROCEDURE (v: LIVector) Add*(x: LONGINT);
	VAR
		newLIPool: LIPool;
	BEGIN
		IF v.len = LEN(v.LIPool^) THEN
			(* run out of space *)
			v.cap := v.cap + (v.cap DIV 2);
			NEW(newLIPool,v.cap);
			SYSTEM.MOVE(SYSTEM.ADR(v.LIPool^),SYSTEM.ADR(newLIPool^),v.cap * SIZE(LONGINT));
			v.LIPool := newLIPool
		END;
		v.LIPool[v.len] := x;
		INC(v.len)
	END Add;

	PROCEDURE (v: LIVector) At*(idx: INTEGER): LONGINT;
	BEGIN
		RETURN v.LIPool[idx];
	END At;
END LIVector.

MODULE LIRange;
IMPORT Out, LIV := LIVector;

TYPE
	Range* = POINTER TO RangeDesc;
	RangeDesc = RECORD
		l,r: POINTER TO ARRAY 1 OF LONGINT;
	END;

	PROCEDURE (r: Range) Init*();
	BEGIN
		r.l := NIL;
		r.r := NIL;
	END Init;

	PROCEDURE (r: Range) IsEmpty*(): BOOLEAN;
	BEGIN
		RETURN (r.l = NIL) & (r.l = NIL);
	END IsEmpty;

	PROCEDURE (r: Range) SetLeft*(v: LONGINT);
	BEGIN
		IF r.l = NIL THEN NEW(r.l) END;
		r.l[0] := v;
	END SetLeft;

	PROCEDURE (r: Range) SetRight*(v : LONGINT);
	BEGIN
		IF r.r = NIL THEN NEW(r.r) END;
		r.r[0] := v;
	END SetRight;

	PROCEDURE (r: Range) LeftPart*(): BOOLEAN;
	BEGIN
		RETURN r.l # NIL;
	END LeftPart;

	PROCEDURE (r: Range) GetLeft(): LONGINT;
	BEGIN
		RETURN r.l[0];
	END	GetLeft;

	PROCEDURE (r: Range) RightPart*(): BOOLEAN;
	BEGIN
		RETURN r.l # NIL;
	END RightPart;

	PROCEDURE (r: Range) GetRight*(): LONGINT;
	BEGIN
		RETURN r.r[0];
	END	GetRight;

	PROCEDURE (r: Range) Show*();
	BEGIN
		Out.Char('(');
		IF r.l # NIL THEN Out.LongInt(r.l[0],10) END;
		Out.String(" - ");
		IF r.r # NIL THEN Out.LongInt(r.r[0],10); END;
		Out.Char(')');Out.Ln
	END Show;

	PROCEDURE (r: Range) Expand*(VAR liv: LIV.LIVector);
	VAR
		from, to : LONGINT;
	BEGIN
		IF r.l # NIL THEN from := r.l[0] ELSE from := 0 END;
		IF r.r # NIL THEN to := r.r[0] ELSE to := from END;
		WHILE (from <= to) DO
			liv.Add(from);INC(from)
		END
	END Expand;
END LIRange.

MODULE Splitter;
TYPE
	Splitter* = POINTER TO SplitterDesc;
	SplitterDesc = RECORD
		from: INTEGER;
		c: CHAR;
		s: POINTER TO ARRAY OF CHAR;
	END;

	PROCEDURE (s: Splitter) Init*;
	BEGIN
		s.c := ',';
		s.from := 0;
		s.s := NIL;
	END Init;

	PROCEDURE (s: Splitter) On*(str: ARRAY OF CHAR);
	BEGIN
		s.from := 0;
		NEW(s.s,LEN(str));
		COPY(str,s.s^)
	END On;

	PROCEDURE (s: Splitter) OnWithChar*(str: ARRAY OF CHAR;c: CHAR);
	BEGIN
		s.from := 0;
		s.c := c;
		NEW(s.s,LEN(str));
		COPY(str,s.s^)
	END OnWithChar;

	PROCEDURE (s: Splitter) Next*(VAR str: ARRAY OF CHAR);
	VAR
		k : INTEGER;
	BEGIN
		k := 0;
		IF (s.from < LEN(s.s^) - 1) & (s.s[s.from] = 0X) THEN str[0] := 0X END;
		WHILE (k < LEN(str) - 1) & (s.from < LEN(s.s^) - 1) & (s.s[s.from] # s.c) DO
			str[k] := s.s[s.from];
			INC(k);INC(s.from)
		END;
		IF k < LEN(str) - 1 THEN str[k] := 0X ELSE str[LEN(str) - 1] := 0X END;
		WHILE (s.from < LEN(s.s^) - 1) & (s.s[s.from] # s.c) DO INC(s.from) END;
		INC(s.from)
	END Next;
END Splitter.

MODULE ExpandRange;
IMPORT Out, LIV := LIVector, LIR := LIRange, S := Splitter;

PROCEDURE GetNumberFrom(s: ARRAY OF CHAR; VAR from: INTEGER; VAR done: BOOLEAN): LONGINT;
VAR
	d,i: INTEGER;
	num,sign: LONGINT;
BEGIN
	i := from; num := 0;sign := 1;
	CASE s[i] OF
		 '-': sign := -1;INC(i)
		|'+': INC(i);
		ELSE
	END;
	WHILE (i < LEN(s) - 1) & (s[i] >= '0') & (s[i] <= '9') DO
		d := ORD(s[i]) - ORD('0');
		num := d + num * 10;
		INC(i);
	END;
	IF i = from THEN done := FALSE ELSE done := TRUE; from := i END;
	RETURN sign * num
END GetNumberFrom;

PROCEDURE GetRange(s: ARRAY OF CHAR): LIR.Range;
VAR
	r: LIR.Range;
	i: INTEGER;
	num: LONGINT;
	done: BOOLEAN;
BEGIN
	i := 0;NEW(r);r.Init();
	WHILE (i < LEN(s) - 1) & (s[i] = 20X) DO INC(i) END;
	(* Left value *)
	done := FALSE;
	num := GetNumberFrom(s,i,done);
	IF ~done THEN RETURN r END;
	r.SetLeft(num);

	WHILE (i < LEN(s) - 1) & (s[i] = 20X) DO INC(i) END;
	CASE s[i] OF
		 '-' : INC(i);
		| 0X : RETURN r;
		ELSE
	END;
	WHILE (i < LEN(s) - 1) & (s[i] = 20X) DO INC(i) END;

	(* Right Value *)
	done := FALSE;
	num := GetNumberFrom(s,i,done);
	IF ~done THEN RETURN r END;
	r.SetRight(num);
	RETURN r;
END GetRange;

VAR
	i: INTEGER;
	r: LIR.Range;
	sp: S.Splitter;
	p : ARRAY 128 OF CHAR;
	liv: LIV.LIVector;
BEGIN
	NEW(sp);sp.Init();
	NEW(liv);liv.Init(128);

	sp.On("-6,-3--1,3-5,7-11,14,15,17-20");
	sp.Next(p);
	WHILE (p[0] # 0X) DO
		r := GetRange(p);
		r.Expand(liv);
		sp.Next(p);
	END;
	FOR i := 0 TO liv.len - 2 DO
		Out.LongInt(liv.At(i),3);Out.Char(',');
	END;
	Out.LongInt(liv.At(liv.len - 1),3);Out.Ln;
END ExpandRange.


#load "str.cma"

let range a b =
  if b < a then invalid_arg "range";
  let rec aux i acc =
    if i = b then List.rev (i::acc)
    else aux (succ i) (i::acc)
  in
  aux a []

let parse_piece s =
  try Scanf.sscanf s "%d-%d" (fun a b -> range a b)
  with _ -> [int_of_string s]

let range_expand rng =
  let ps = Str.split (Str.regexp_string ",") rng in
  List.flatten (List.map parse_piece ps)

let () =
  let rng = "-6,-3--1,3-5,7-11,14,15,17-20" in
  let exp = range_expand rng in
  List.iter (Printf.printf " %d") exp;
  print_newline ()


: addRange( s res -- )
| i n |
    s asInteger dup ifNotNull: [ res add return ] drop
    s indexOfFrom('-', 2) ->i
    s left( i 1- ) asInteger  s right( s size i - ) asInteger
    for: n [ n res add ]
;

: rangeExpand ( s -- [ n ] )
    ArrayBuffer new  s wordsWith( ',' ) apply( #[ over addRange ] ) ;


list = '-6,-3--1,3-5,7-11,14,15,17-20'
expanded = expandRanges(list)

say "Original list: ["list"]"
say "Expanded list: ["expanded~tostring("l", ",")"]"

-- expand a string expression a range of numbers into a list
-- of values for the range.  This returns an array
::routine expandRanges
  use strict arg list
  values = list~makearray(',')
  -- build this up using an array first.  Make this at least the
  -- size of the original value set.
  expanded = .array~new(values~items)

  -- now process each element in the range
  loop element over values
      -- if this is a valid number, it's not a range, so add it directly
      if element~datatype('whole') then expanded~append(element)
      else do
          -- search for the divider, starting from the second position
          -- to allow for the starting value to be a minus sign.
          split = element~pos('-', 2)
          parse var element start =(split) +1 finish
          loop i = start to finish
              expanded~append(i)
          end
      end
  end
  return expanded


declare
  fun {Expand RangeDesc}
     {Flatten
      {Map {ParseDesc RangeDesc}
       ExpandRange}}
  end

  fun {ParseDesc Txt}
     {Map {String.tokens Txt &,} ParseRange}
  end

  fun {ParseRange R}
     if {Member &- R.2} then
        First Second
     in
        {String.token R.2 &- ?First ?Second}
        {String.toInt R.1|First}#{String.toInt Second}
     else
        Singleton = {String.toInt R}
     in
        Singleton#Singleton
     end
  end

  fun {ExpandRange From#To}
     {List.number From To 1}
  end
in
  {System.showInfo
   {Value.toVirtualString {Expand "-6,-3--1,3-5,7-11,14,15,17-20"} 100 100}}


with javascript_semantics
function range_expansion(string range)
    sequence s = split(range,','),
             res = {}
    for i=1 to length(s) do
        string si = s[i]
        integer k = find('-',si,2)
        if k=0 then
            res = append(res,to_number(si))
        else
            integer startrange = to_number(si[1..k-1]),
                    endofrange = to_number(si[k+1..$])
            for l=startrange to endofrange do
                res = append(res,l)
            end for
        end if
    end for
    return res
end function

?range_expansion("-6,-3-1,3-5,7-11,14,15,17-20")
?range_expansion("-6,-3--1,3-5,7-11,14,15,17-20")


0 tolist var r

def append
    r swap 0 put var r
enddef

"-6,-3--1,3-5,7-11,14,15,17-20" "," " " subst split

len for
    get dup tonum dup
    nan == if
        drop
        dup len 1 - 2 swap slice
        "-" find dup 2 + rot drop
        rot rot 1 swap slice tonum
        rot rot len rot swap over - 1 + slice tonum
        nip rot drop
        2 tolist for append endfor
    else
        append drop
    endif
endfor
r
pstack


function rangex($str) {
    $lst = array();
    foreach (explode(',', $str) as $e) {
        if (strpos($e, '-', 1) !== FALSE) {
            list($a, $b) = explode('-', substr($e, 1), 2);
            $lst = array_merge($lst, range($e[0] . $a, $b));
        } else {
            $lst[] = (int) $e;
        }
    }
    return $lst;
}


(de rangeexpand (Str)
   (make
      (for S (split (chop Str) ",")
         (if (index "-" (cdr S))
            (chain
               (range
                  (format (head @ S))
                  (format (tail (- -1 @) S)) ) )
            (link (format S)) ) ) ) )


range_expansion:
   procedure options (main);

get_number:
   procedure (Number, c, eof);
   declare number fixed binary (31), c character (1), eof bit (1) aligned;
   declare neg fixed binary (1);

   number = 0; eof = false;
   do until (c ^= ' ');
      get edit (c) (a(1));
   end;
   if c = '-' then do; get edit (c) (a(1)); neg = -1; end; else neg = 1;
   do forever;
      select (c);
         when ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
                    number = number*10 + c;
         when (',', '-') do; number = neg*number; return; end;
         otherwise signal error;
      end;
      on endfile (sysin) go to exit;
      get edit (c) (a(1));
   end;
exit:
   number = neg*number;
   eof = true;
end get_Number;

   declare c character, (i, range_start, range_end) fixed binary (31);
   declare eof bit (1) aligned;
   declare true bit (1) value ('1'b), false bit (1) value ('0'b);
   declare delimiter character (1) initial (' ');
   declare out file output;

   open file (out) output title ('/out, type(text),recsize(80)');
   do while (^eof);
      call get_number(range_start, c, eof);
      if c = '-' then /* we have a range */
         do;
            call get_number (range_end, c, eof);
            do i = range_start to range_end;
               put file (out) edit (delimiter, i) (a, f(3));
            end;
         end;
      else
         do;
            put file (out) edit (delimiter, range_start) (a, f(3));
         end;
      delimiter = ',';
   end;
end range_expansion;


Procedure rangeexpand(txt.s, List outputList())
  Protected rangesCount = CountString(txt, ",") + 1
  Protected subTxt.s, r, rangeMarker, rangeStart, rangeFinish, rangeIncrement, i

  LastElement(outputList())
  For r = 1 To rangesCount
    subTxt = StringField(txt, r, ",")
    rangeMarker = FindString(subTxt, "-", 2)
    If rangeMarker
      rangeStart = Val(Mid(subTxt, 1, rangeMarker - 1))
      rangeFinish = Val(Mid(subTxt, rangeMarker + 1))

      If rangeStart > rangeFinish
        rangeIncrement = -1
      Else
        rangeIncrement = 1
      EndIf

      i = rangeStart - rangeIncrement
      Repeat
        i + rangeIncrement
        AddElement(outputList()): outputList() = i
      Until i = rangeFinish
    Else
      AddElement(outputList()): outputList() = Val(subTxt)
    EndIf
  Next
EndProcedure

Procedure outputListValues(List values())
  Print("[ ")
  ForEach values()
    Print(Str(values()) + " ")
  Next
  PrintN("]")
EndProcedure

If OpenConsole()
  NewList values()
  rangeexpand("-6,-3--1,3-5,7-11,14,15,17-20", values())
  outputListValues(values())

  Print(#CRLF$ + #CRLF$ + "Press ENTER to exit")
  Input()
  CloseConsole()
EndIf


def rangeexpand(txt):
    lst = []
    for r in txt.split(','):
        if '-' in r[1:]:
            r0, r1 = r[1:].split('-', 1)
            lst += range(int(r[0] + r0), int(r1) + 1)
        else:
            lst.append(int(r))
    return lst

print(rangeexpand('-6,-3--1,3-5,7-11,14,15,17-20'))


#lang racket

(define (range-expand s)
  (append*
   (for/list ([r (regexp-split "," s)])
     (match (regexp-match* "(-?[0-9]+)-(-?[0-9]+)" r
                           #:match-select cdr)
       [(list (list f t))
        (range (string->number f) (+ (string->number t) 1))]
       [(list)
        (list (string->number r))]))))

(range-expand "-6,-3--1,3-5,7-11,14,15,17-20")


sub range-expand (Str $range-description) {
    my token number { '-'? \d+ }
    my token range  { (<&number>) '-' (<&number>) }

    $range-description
        .split(',')
        .map({ .match(&range) ?? $0..$1 !! +$_ })
        .flat
}

say range-expand('-6,-3--1,3-5,7-11,14,15,17-20').join(', ');


define get_num use $lst
    # "-22" split by "-" is [ "", "22" ]  so check if
    # first list item is "" -> a negative number
    $lst 0 get "" = if
        # negative number
        #
        # convert str to integer and multiply by -1
        -1 $lst 1 get 0 prefer *
        $lst shift $lst shift drop drop
    else
        # positive number
        $lst 0 get 0 prefer
        $lst shift drop

define range_expand use $rng
    [ ] as $res
    $rng "," split each as $r
        $r m/^(-?\d+)-(-?\d+)$/ TRUE = if
            $r s/-/g as $parts
            $parts get_num as $from
            $parts get_num as $to
            # int list to str list, then joined by ","
            group
                $from $to 1 range each "" prefer
            list "," join $res push
            # range doesn't include the $to, so add to end of generated range
            $to "%d" $res push
        else
            $r $res push
    $res "," join print
    "\n" print

'-6,-3--1,3-5,7-11,14,15,17-20' range_expand


/*REXX program expands an  ordered list  of  integers  into  an expanded list.          */
old= '-6,-3--1,   3-5,  7-11,       14,15,17-20';       a=translate(old,,',')
new=                                             /*translate [↑]  commas (,) ───► blanks*/
      do until a=='';   parse var a X a          /*obtain the next integer ──or── range.*/
      p=pos('-', X, 2)                           /*find the location of a dash (maybe). */
      if p==0 then  new=new   X                  /*append integer   X   to the new list.*/
              else  do j=left(X,p-1)  to substr(X,p+1);     new=new j
                    end   /*j*/                  /*append a single [↑] integer at a time*/
      end                 /*until*/
                                                 /*stick a fork in it,  we're all done. */
new=translate( strip(new),  ',',  " ")           /*remove the first blank,  add commas. */
say 'old list: '   old                           /*show the  old list of numbers/ranges.*/
say 'new list: '   new                           /*  "   "   new   "   " numbers.       */


# Project : Range expansion

int = "-6,-3--1,3-5,7-11,14,15,17-20"
int = str2list(substr(int, ",", nl))
newint = []
for n=1 to len(int)
     nrint = substr(int[n], "-")
     nrint2 = substr(int[n], "--")
     if nrint2 > 0
        temp1 = left(int[n], nrint2 -1)
        temp2 = right(int[n], len(int[n]) - nrint2)
        add(newint, [temp1,temp2])
     else
        if len(int[n]) <= 2
           add(newint, [int[n], ""])
        else
           if nrint > 0 and nrint2 = 0
              temp1 = left(int[n], nrint - 1)
              temp2 = right(int[n], len(int[n]) - nrint)
              add(newint, [temp1,temp2])
           ok
        ok
     ok
next
showarray(newint)

func showarray(vect)
       see "["
       svect = ""
       for n = 1 to len(vect)
           if newint[n][2] != ""
              for nr = newint[n][1] to newint[n][2]
                  svect = svect +"" + nr + ", "
              next
           else
              svect = svect +"" + newint[n][1] + ", "
           ok
       next
       svect = left(svect, len(svect) - 2)
       see svect
       see "]" + nl


def range_expand(rng)
  rng.split(',').flat_map do |part|
    if part =~ /^(-?\d+)-(-?\d+)$/
      ($1.to_i .. $2.to_i).to_a
    else
      Integer(part)
    end
  end
end

p range_expand('-6,-3--1,3-5,7-11,14,15,17-20')


PRINT rangeExpand$("-6,-3--1,3-5,7-11,14,15,17-20")
end

function rangeExpand$(range$)
[loop]
i	= INSTR(range$, "-", i+1)
IF i THEN
  j = i
  WHILE MID$(range$,j-1,1) <> "," AND j <> 1
    j = j - 1
  wend
  IF i > j then
    IF MID$(range$,j,i-j) <> str$(i-j)+" " THEN
      t$ = ""
      FOR k = VAL(MID$(range$,j)) TO VAL(MID$(range$,i+1))-1
        t$ = t$ + str$(k) + ","
      NEXT k
      range$ = LEFT$(range$,j-1) + t$ + MID$(range$,i+1)
      i = j + LEN(t$) + 2
    end if
  end if
end if
if i <> 0 then goto [loop]
rangeExpand$ = range$
end function


use std::str::FromStr;

// Precondition: range doesn't contain multibyte UTF-8 characters
fn range_expand(range : &str) -> Vec<i32> {
   range.split(',').flat_map(|item| {
        match i32::from_str(item) {
            Ok(n) => n..n+1,
            _ => {
                let dashpos=
                    match item.rfind("--") {
                        Some(p) => p,
                        None => item.rfind('-').unwrap(),
                    };
                let rstart=i32::from_str(
                    unsafe{ item.slice_unchecked(0,dashpos)} ).unwrap();
                let rend=i32::from_str(
                    unsafe{ item.slice_unchecked(dashpos+1,item.len()) } ).unwrap();
                rstart..rend+1
            },
        }
    }).collect()
}

fn main() {
    println!("{:?}", range_expand("-6,-3--1,3-5,7-11,14,15,17-20"));
}


variable r_expres = "-6,-3--1,3-5,7-11,14,15,17-20", s, r_expan = {}, dpos, i;

foreach s (strchop(r_expres, ',', 0))
{
  % S-Lang built-in RE's are fairly limited, and have a quirk:
  %   grouping is done with \\( and \\), not ( and )
  % [PCRE and Oniguruma RE's are available via standard libraries]
  if (string_match(s, "-?[0-9]+\\(-\\)-?[0-9]+", 1)) {

    (dpos, ) = string_match_nth(1);

    % Create/loop-over a "range array": from num before - to num after it:
    foreach i ( [integer(substr(s, 1, dpos)) : integer(substr(s, dpos+2, -1))] )
      list_append(r_expan, string(i));
  }
  else
    list_append(r_expan, s);
}
print(strjoin(list_to_array(r_expan), ", "));


def rangex(str: String): Seq[Int] =
  str split "," flatMap { (s) =>
    val r = """(-?\d+)(?:-(-?\d+))?""".r
    val r(a,b) = s
    if (b == null) Seq(a.toInt) else a.toInt to b.toInt
  }


(define split
  (lambda (str char skip count)
    (let ((len (string-length str)))
      (let loop ((index skip)
                 (last-index 0)
                 (result '()))
        (if (= index len)
            (reverse (cons (substring str last-index) result))
            (if (eq? char (string-ref str index))
                (loop (if (= count (+ 2 (length result)))
                          len
                          (+ index 1))
                      (+ index 1)
                      (cons char (cons (substring str last-index index)
                                       result)))
                (loop (+ index 1)
                      last-index
                      result)))))))

(define range-expand
  (lambda (str)
    (for-each
     (lambda (token)
       (if (char? token)
           (display token)
           (let ((range (split token #\- 1 2)))
             (if (null? (cdr range))
                 (display (car range))
                 (do ((count (string->number (list-ref range 0)) (+ 1 count))
                      (high (string->number (list-ref range 2))))
                     ((= count high) (display high))
                   (display count)
                   (display ","))))))
     (split str #\, 0 0))
    (newline)))


$ include "seed7_05.s7i";
  include "scanstri.s7i";

const func array integer: rangeExpansion (in var string: rangeStri) is func
  result
    var array integer: numbers is 0 times 0;
  local
    var integer: number is 0;
  begin
    while rangeStri <> "" do
      number := integer parse getInteger(rangeStri);
      numbers &:= number;
      if startsWith(rangeStri, "-") then
        rangeStri := rangeStri[2 ..];
        for number range succ(number) to integer parse getInteger(rangeStri) do
          numbers &:= number;
        end for;
      end if;
      if startsWith(rangeStri, ",") then
        rangeStri := rangeStri[2 ..];
      elsif rangeStri <> "" then
        raise RANGE_ERROR;
      end if;
    end while;
  end func;

const proc: main is func
  local
    var integer: number is 0;
  begin
    for number range rangeExpansion("-6,-3--1,3-5,7-11,14,15,17-20") do
      write(number <& " ");
    end for;
    writeln;
  end func;


*       # Return range n1 .. n2
        define('range(n1,n2)') :(range_end)
range   range = range n1 ','; n1 = lt(n1,n2) n1 + 1 :s(range)
        range rtab(1) . range :(return)
range_end

        define('rangex(range)d1,d2')
        num = ('-' | '') span('0123456789') :(rangex_end)
rangex  range num . d1 '-' num . d2 = range(d1,d2) :s(rangex)
        rangex = range :(return)
rangex_end

*       # Test and display
        output = rangex('-6,-3--1,3-5,7-11,14,15,17-20')
end


composer expand
  [<element>*]
  rule element: <range|INT> (<','>?)
  rule range: (def start: <INT>; <'-'>) <INT> -> $start..$
end expand

'-6,-3--1,3-5,7-11,14,15,17-20' -> expand -> !OUT::write


proc rangeExpand desc {
    set result {}
    foreach term [split $desc ","] {
	set count [scan $term %d-%d from to]
	if {$count == 1} {
	    lappend result $from
	} elseif {$count == 2} {
	    for {set i $from} {$i <= $to} {incr i} {lappend result $i}
	}
    }
    return $result
}

puts [rangeExpand "-6,-3--1,3-5,7-11,14,15,17-20"]


$$ MODE TUSCRIPT
rangednrs="-6,-3--1,3-5,7-11,14,15,17-20"
expandnrs=SPLIT (rangednrs,":,:")

LOOP/CLEAR r=expandnrs
 test=STRINGS (r,":><-><<>>/:")
 sz_test=SIZE (test)
 IF (sz_test==1) THEN
  expandnrs=APPEND (expandnrs,r)
 ELSE
  r=SPLIT (r,"::<|->/::-:",beg,end)
  expandnrs=APPEND (expandnrs,beg)
  LOOP/CLEAR next=beg,end
   next=next+1
   expandnrs=APPEND (expandnrs,next)
   IF (next==end) EXIT
  ENDLOOP
 ENDIF
ENDLOOP
expandnrs= JOIN (expandnrs,",")

PRINT expandnrs


num := [ + | - ] { digit } +

entry := num [ ws ] - [ ws ] num
      |  num

rangelist := entry [ ws ] , [ ws ] rangelist
          |  entry
          |  /* empty */


#!/usr/bin/bash

range_expand () (
    IFS=,
    set -- $1
    n=$#
    for element; do
        if [[ $element =~ ^(-?[0-9]+)-(-?[0-9]+)$ ]]; then
            set -- "[email protected]" $(eval echo "{${BASH_REMATCH[1]}..${BASH_REMATCH[2]}}")
        else
            set -- "[email protected]" $element
        fi
    done
    shift $n
    echo "[email protected]"
    # to return a comma-separated value: echo "${*// /,}"
)

range_expand "-6,-3--1,3-5,7-11,14,15,17-20"


PROC ℒ next random = (REF ℒ INT a)ℒ REAL: ( a :=
¢ the next pseudo-random ℒ integral value after 'a' from a
uniformly distributed sequence on the interval [ℒ 0,ℒ maxint] ¢;

¢ the real value corresponding to 'a' according to some mapping of
integral values [ℒ 0, ℒ max int] into real values [ℒ 0, ℒ 1)
i.e. such that -0 <= x < 1 such that the sequence of real
values so produced preserves the properties of pseudo-randomness
and uniform distribution of the sequence of integral values ¢);

INT ℒ last random := # some initial random number #;
PROC ℒ random = ℒ REAL: ℒ next random(ℒ last random);


#import std
#import int

rex = sep`,; zrange+*= %zp~~htttPzztPQhQXbiNC+ rlc ~&r~=`-

#cast %zL

t = rex '-6,-3--1,3-5,7-11,14,15,17-20'


Public Function RangeExpand(AString as string)
' return a list with the numbers expressed in AString
Dim Splits() As String
Dim List() As Integer
Dim count As Integer

count = -1 'to start a zero-based List() array
' first split it using comma as delimiter
Splits = Split(AString, ",")
' process all fragments
For Each fragment In Splits
  'is there a "-" in it (do not consider first character)?
  P = InStr(2, fragment, "-")
  If P > 0 Then 'yes, so it's a range: find start and end numbers
    nstart = Val(left$(fragment, P - 1))
    nend = Val(Mid$(fragment, P + 1))
    j = count
    count = count + (nend - nstart + 1)
    'add numbers in range to List
    ReDim Preserve List(count)
    For i = nstart To nend
      j = j + 1
      List(j) = i
    Next
  Else
    'not a range, add a single number
    count = count + 1
    ReDim Preserve List(count)
    List(count) = Val(fragment)
  End If
Next
RangeExpand = List
End Function

Public Sub RangeExpandTest()
'test function RangeExpand
Dim X As Variant

X = RangeExpand("-6,-3--1,3-5,7-11,14,15,17-20")
'print X
Debug.Print "Result:"
For Each el In X
  Debug.Print el;
Next
Debug.Print
End Sub


var expandRange = Fn.new { |s|
    var list = []
    var items = s.split(",")
    for (item in items) {
        var count = item.count { |c| c == "-" }
        if (count == 0 || (count == 1 && item[0] == "-")) {
            list.add(Num.fromString(item))
        } else {
            var items2 = item.split("-")
            var first
            var last
            if (count == 1) {
                first = Num.fromString(items2[0])
                last  = Num.fromString(items2[1])
            } else if (count == 2) {
                first = Num.fromString(items2[1]) * -1
                last  = Num.fromString(items2[2])
            } else {
                first = Num.fromString(items2[1]) * -1
                last  = Num.fromString(items2[3]) * -1
            }
            for (i in first..last) list.add(i)
        }
    }
    return list
}

var s = "-6,-3--1,3-5,7-11,14,15,17-20"
System.print(expandRange.call(s))


#include <stdio.h>
#include <stdlib.h>

/* Flip a coin, 10 times. */
int
main()
{
	int i;
	srand(time(NULL));
	for (i = 0; i < 10; i++)
		puts((rand() % 2) ? "heads" : "tails");
	return 0;
}


include c:\cxpl\codes;          \intrinsic 'code' declarations
string 0;                       \use zero-terminated strings, instead of MSb
char Str;
int  Char, Inx;

proc GetCh;                     \Get character from Str
[Char:= Str(Inx);
Inx:= Inx+1;
]; \GetCh

func GetNum;                    \Get number from Str and return its value
int Neg, Num;
[Neg:= false;
if Char = ^- then [Neg:= true;  GetCh];
Num:= 0;
while Char>=^0 & Char<=^9 do
        [Num:= Num*10 + Char-^0;
        GetCh;
        ];
return if Neg then -Num else Num;
]; \GetNum

int I, N0, N1;
[Str:= "-6,-3--1,3-5,7-11,14,15,17-20";
Inx:= 0;
GetCh;                          \one character look ahead
loop    [N0:= GetNum;
        IntOut(0,N0);
        case Char of
          ^,:   [GetCh;  ChOut(0,^,)];
          ^-:   [GetCh;
                N1:= GetNum;
                for I:= N0+1 to N1 do   \expand range
                        [ChOut(0,^,);  IntOut(0,I)];
                if Char=^, then [GetCh;  ChOut(0,^,)] else quit]
        other   quit;                   \must be 0 string terminator
        ];
CrLf(0);
]


print RangeExpand$("-6,-3--1,3-5,7-11,14,15,17-20")

sub RangeExpand$(s$)
    local w$(1), n, i, r$, p, a, b

    n = token(s$, w$(), ",")

    for i = 1 to n
        p = instr(w$(i), "-", 2)
        if p then
            a = val(left$(w$(i), p-1))
            b = val(right$(w$(i), len(w$(i)) - p))
            repeat
                r$ = r$ + str$(a) + ","
                a = a + 1
            until(a > b)
        else
            r$ = r$ + w$(i) + ","
        end if
    next
    return left$(r$, len(r$) - 1)
end sub


fcn rangex(s){
   fcn(s,re){
      if (re.search(s)){
	 a,b:=re.matched[1,*].apply("toInt");
	 [a..b].walk();
      }
      else s;
   } :
   s.split(",").pump(List, _.fp1(RegExp(0'|(.*\d+)-(.*\d+)|)))
      .flatten().concat(",");
}


#include <iostream>
#include <string>
#include <random>

int main()
{
    std::random_device rd;
    std::uniform_int_distribution<int> dist(1, 10);
    std::mt19937 mt(rd());

    std::cout << "Random Number (hardware): " << dist(rd) << std::endl;
    std::cout << "Mersenne twister (hardware seeded): " << dist(mt) << std::endl;
}


# Show random integer from 0 to 9999.
string(RANDOM LENGTH 4 ALPHABET 0123456789 number)
math(EXPR number "${number} + 0")  # Remove extra leading 0s.
message(STATUS ${number})


(setf *random-state* (make-random-state t))
(rand 10)


 function Random : Extended;
 function Random ( LimitPlusOne  : Integer ) : Integer;
 procedure Randomize;


!print random-int # prints a 32-bit random integer


import extensions;

public program()
{
    console.printLine(randomGenerator.nextReal());
    console.printLine(randomGenerator.eval(0,100))
}


# Seed the RNG
:random.seed(:erlang.now())

# Integer in the range 1..10
:random.uniform(10)

# Float between 0.0 and 1.0
:random.uniform()


program rosetta_random
   implicit none

   integer, parameter :: rdp = kind(1.d0)
   real(rdp) :: num
   integer, allocatable :: seed(:)
   integer :: un,n, istat

   call random_seed(size = n)
   allocate(seed(n))

   ! Seed with the OS random number generator
   open(newunit=un, file="/dev/urandom", access="stream", &
   form="unformatted", action="read", status="old", iostat=istat)
   if (istat == 0) then
      read(un) seed
      close(un)
   end if
   call random_seed (put=seed)
   call random_number(num)
   write(*,'(E24.16)') num
end program rosetta_random


program RandomNumbers;
// Program to demonstrate the Random and Randomize functions.
var
  RandomInteger: integer;
  RandomFloat: double;
begin
  Randomize; // generate a new sequence every time the program is run
  RandomFloat := Random();       // 0 <= RandomFloat < 1
  Writeln('Random float between 0 and 1: ', RandomFloat: 5: 3);
  RandomFloat := Random() * 10;  // 0 <= RandomFloat < 10
  Writeln('Random float between 0 and 10: ', RandomFloat: 5: 3);
  RandomInteger := Random(10);   //  0 <= RandomInteger < 10
  Writeln('Random integer between 0 and 9: ', RandomInteger);
  // Wait for <enter>
  Readln;
end.


# Creating a random source
rs := RandomSource(IsMersenneTwister);

# Generate a random number between 1 and 10
Random(rs, 1, 10);

# Same with default random source
Random(1, 10);


setrand(3)
random(6)+1
\\ chosen by fair dice roll.
\\ guaranteed to the random.


 function  Random(l: LongInt) : LongInt;
 function  Random : Real;
 procedure Randomize;


DBMS_RANDOM.RANDOM --produces integers in [-2^^31, 2^^31).
DBMS_RANDOM.VALUE  --produces numbers in [0,1) with 38 digits of precision.
DBMS_RANDOM.NORMAL --produces normal distributed numbers with a mean of 0 and a variance of 1


typedef unsigned long long u8;
typedef struct ranctx { u8 a; u8 b; u8 c; u8 d; } ranctx;

#define rot(x,k) (((x)<<(k))|((x)>>(64-(k))))
u8 ranval( ranctx *x ) {
    u8 e = x->a - rot(x->b, 7);
    x->a = x->b ^ rot(x->c, 13);
    x->b = x->c + rot(x->d, 37);
    x->c = x->d + e;
    x->d = e + x->a;
    return x->d;
}

void raninit( ranctx *x, u8 seed ) {
    u8 i;
    x->a = 0xf1ea5eed, x->b = x->c = x->d = seed;
    for (i=0; i<20; ++i) {
        (void)ranval(x);
    }
}


import util::Math;
arbInt(int limit); // generates an arbitrary integer below limit
arbRat(int limit, int limit); // generates an arbitrary rational number between the limits
arbReal(); // generates an arbitrary real value in the interval [0.0, 1.0]
arbSeed(int seed);


     /*(below)  returns a random integer between 100 & 200, inclusive.*/

y = random(100, 200)


nr = 10
for i = 1 to nr
     see random(i) + nl
next


import scala.util.Random

/**
 * Histogram of 200 throws with two dices.
 */
object Throws extends App {
  Stream.continually(Random.nextInt(6) + Random.nextInt(6) + 2)
    .take(200).groupBy(identity).toList.sortBy(_._1)
    .foreach {
      case (a, b) => println(f"$a%2d:" + "X" * b.size)
    }
}


say 1.rand          # random float in the interval [0,1)
say 100.irand       # random integer in the interval [0,100)


include c:\cxpl\codes;  \intrinsic 'code' declarations
int I;
[RanSeed(12345);        \set random number generator seed to 12345
for I:= 1 to 5 do
        [IntOut(0, Ran(1_000_000));  CrLf(0)];
]


INT FUNC FindRange(INT ARRAY a INT len,start)
  INT count

  count=1
  WHILE start<len-1
  DO
    IF a(start)+1#a(start+1) THEN
      EXIT
    FI
    start==+1
    count==+1
  OD
RETURN (count)

PROC Append(CHAR ARRAY text,suffix)
  BYTE POINTER srcPtr,dstPtr
  BYTE len

  len=suffix(0)
  IF text(0)+len>255 THEN
    len=255-text(0)
  FI
  IF len THEN
    srcPtr=suffix+1
    dstPtr=text+text(0)+1
    MoveBlock(dstPtr,srcPtr,len)
    text(0)==+suffix(0)
  FI
RETURN

PROC RangeToStr(INT ARRAY a INT len CHAR ARRAY res)
  INT i,count
  CHAR ARRAY tmp(10)

  i=0
  res(0)=0
  WHILE i<len
  DO
    count=FindRange(a,len,i)
    StrI(a(i),tmp) Append(res,tmp)
    IF count=2 THEN
      Append(res,",")
      StrI(a(i+1),tmp) Append(res,tmp)
    ELSEIF count>2 THEN
      Append(res,"-")
      StrI(a(i+count-1),tmp) Append(res,tmp)
    FI
    i==+count
    IF i<len THEN
      Append(res,",")
    FI
  OD
RETURN

PROC Main()
  INT ARRAY a=[0 1 2 4 6 7 8 11 12 14
    15 16 17 18 19 20 21 22 23 24 25 27
    28 29 30 31 32 33 35 36 37 38 39]
  INT ARRAY b=[65530 65533 65534 65535
    0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20]
  CHAR ARRAY res(256)

  RangeToStr(a,33,res)
  PrintE(res) PutE()
  RangeToStr(b,20,res)
  PrintE(res)
RETURN


with Ada.Text_IO;            use Ada.Text_IO;
with Ada.Strings.Unbounded;  use Ada.Strings.Unbounded;
with Ada.Strings.Fixed;      use Ada.Strings.Fixed;

procedure Range_Extraction is
   type Sequence is array (Positive range <>) of Integer;
   function Image (S : Sequence) return String is
      Result : Unbounded_String;
      From   : Integer;
      procedure Flush (To : Integer) is
      begin
         if Length (Result) > 0 then
            Append (Result, ',');
         end if;
         Append (Result, Trim (Integer'Image (From), Ada.Strings.Left));
         if From < To then
            if From+1 = To then
               Append (Result, ',');
            else
               Append (Result, '-');
            end if;
            Append (Result, Trim (Integer'Image (To), Ada.Strings.Left));
         end if;
      end Flush;
   begin
      if S'Length > 0 then
         From := S (S'First);
         for I in S'First + 1..S'Last loop
            if S (I - 1) + 1 /= S (I) then
               Flush (S (I - 1));
               From := S (I);
            end if;
         end loop;
         Flush (S (S'Last));
      end if;
      return To_String (Result);
   end Image;
begin
   Put_Line
     (  Image
          (  (  0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
                15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
                25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
                37, 38, 39
             )  )  );
end Range_Extraction;


###
  REQUIRES(MODE SCALAR, OP(SCALAR,SCALAR)BOOL =, OP(SCALAR,SCALAR)SCALAR +);
###
MODE SCALARLIST = FLEX[0]SCALAR;
MODE YIELDINT = PROC(SCALAR)VOID;

################################################################
# Declarations for manipulating lists of range pairs [lwb:upb] #
################################################################
MODE RANGE = STRUCT(SCALAR lwb, upb);
MODE RANGELIST = FLEX[0]RANGE;
MODE YIELDRANGE = PROC(RANGE)VOID;

PROC range repr = (RANGE range)STRING: (
  STRING lwb := whole(lwb OF range,0);
  IF lwb OF range = upb OF range THEN
    lwb
  ELSE
    # "["+lwb+":"+whole(upb OF range,0)+"]"  #
    lwb+"-"+whole(upb OF range,0)
  FI
);

# OP REPR = (RANGE range)STRING: range repr(range); # # firmly related to UNIRANGE #

######################################################################
# Declarations for manipulating lists containing pairs AND lone INTs #
######################################################################
MODE UNIRANGE = UNION(SCALAR, RANGE);
MODE UNIRANGELIST = FLEX[0]UNIRANGE;
MODE YIELDUNIRANGE = PROC(UNIRANGE)VOID;

PROC unirange repr = (UNIRANGE unirange)STRING:
  CASE unirange IN
    (RANGE range): range repr(range),
    (SCALAR scalar): whole(scalar,0)
  ESAC;

OP (UNIRANGE)STRING REPR = unirange repr; # alias #

# The closest thing Algol68 has to inheritance is the UNION #
MODE UNIRANGELISTS = UNION(UNIRANGELIST, RANGELIST, SCALARLIST);

PROC unirange list repr = (UNIRANGELIST unirange list)STRING: (
### Produce a STRING representation of a UNIRANGELIST ###
  STRING out # := "("#, sep := "";
  FOR key FROM LWB unirange list TO UPB unirange list DO
    out +:= sep + REPR unirange list[key];
    sep := "," # +" " #
  OD;
  out # +")" #
);

OP (UNIRANGELIST)STRING REPR = unirange list repr; # alias #


--------------------- RANGE EXTRACTION ---------------------

-- rangeFormat :: [Int] -> String
on rangeFormat(xs)
    script segment
        on |λ|(xs)
            if 2 < length of xs then
                intercalate("-", {first item of xs, last item of (xs)})
            else
                intercalate(",", xs)
            end if
        end |λ|
    end script

    script gap
        on |λ|(a, b)
            1 < b - a
        end |λ|
    end script

    intercalate(",", map(segment, splitBy(gap, xs)))
end rangeFormat

--------------------------- TEST ---------------------------
on run
    set xs to {0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, ¬
        17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, ¬
        33, 35, 36, 37, 38, 39}

    rangeFormat(xs)

    --> "0-2,4,6-8,11,12,14-25,27-33,35-39"
end run

-------------------- GENERIC FUNCTIONS ---------------------

-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from 1 to lng
            set v to |λ|(v, item i of xs, i, xs)
        end repeat
        return v
    end tell
end foldl

-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map

-- intercalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
    set {dlm, my text item delimiters} to {my text item delimiters, strText}
    set strJoined to lstText as text
    set my text item delimiters to dlm
    return strJoined
end intercalate

-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- splitBy :: (a -> a -> Bool) -> [a] -> [[a]]
on splitBy(f, xs)
    set mf to mReturn(f)

    if length of xs < 2 then
        {xs}
    else
        script p
            on |λ|(a, x)
                set {acc, active, prev} to a
                if mf's |λ|(prev, x) then
                    {acc & {active}, {x}, x}
                else
                    {acc, active & x, x}
                end if
            end |λ|
        end script

        set h to item 1 of xs
        set lstParts to foldl(p, {{}, {h}, h}, items 2 thru -1 of xs)
        item 1 of lstParts & {item 2 of lstParts}
    end if
end splitBy


#!/usr/bin/awk -f

BEGIN {
    delete sequence
    delete range

    seqStr = "0,1,2,4,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,24,"
    seqStr = seqStr "25,27,28,29,30,31,32,33,35,36,37,38,39"
    print "Sequence: " seqStr
    fillSequence(seqStr)
    rangeExtract()
    showRange()
    exit
}

function rangeExtract(    runStart, runLen) {
    delete range
    runStart = 1
    while(runStart <= length(sequence)) {
        runLen = getSeqRunLen(runStart)
        addRange(runStart, runLen)
        runStart += runLen
    }
}

function getSeqRunLen(startPos,    pos) {
    for (pos = startPos; pos < length(sequence); pos++) {
        if (sequence[pos] + 1 != sequence[pos + 1]) break;
    }
    return pos - startPos + 1;
}

function addRange(startPos, len,    str) {
    if (len == 1) str = sequence[startPos]
    else if (len == 2) str = sequence[startPos] "," sequence[startPos + 1]
    else str = sequence[startPos] "-" sequence[startPos + len - 1]
    range[length(range) + 1] = str
}

function showRange(    r) {
    printf "  Ranges: "
    for (r = 1; r <= length(range); r++) {
        if (r > 1) printf ","
        printf range[r]
    }
    printf "\n"
}

function fillSequence(seqStr,    n, s) {
    n = split(seqStr,a,/[,]+/)
    for (s = 1; s <= n; s++) {
        sequence[s] = a[s]
    }
}


  ( rangeExtract
  =     accumulator firstInRange nextInRange
      , accumulate fasten rangePattern
    .   ( accumulate
        =     !accumulator
              (!accumulator:|?&",")
              !firstInRange
              (   !firstInRange+1:<>!nextInRange
                &   ( !firstInRange+2:!nextInRange&","
                    | "-"
                    )
                    -1+!nextInRange
              |
              )
          : ?accumulator
        )
      & ( fasten
        = [%( !accumulate
            & (!sjt:?firstInRange)+1:?nextInRange
            )
        )
      & ( rangePattern
        =   (
            |   ?
                ( !nextInRange
                & 1+!nextInRange:?nextInRange
                )
            )
            ( &!accumulate
            | (#<>!nextInRange:!fasten) !rangePattern
            )
        )
      & :?accumulator:?firstInRange
      & !arg:(|#!fasten !rangePattern)
      & str$!accumulator
  )
& ( test
  =   L A
    .   put$(!arg " ==>\n",LIN)
      & (   !arg:(?,?)
          & whl'(!arg:(?A,?arg)&(!A,!L):?L)
          & whl'(!L:(?A,?L)&!A !arg:?arg)
        |
        )
      & out$(rangeExtract$!arg)
  )
&   test
  $ (0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
    37, 38, 39)


#include <stdio.h>
#include <stdlib.h>

size_t rprint(char *s, int *x, int len)
{
#define sep (a > s ? "," : "") /* use comma except before first output */
#define ol (s ? 100 : 0)       /* print only if not testing for length */
	int i, j;
	char *a = s;
	for (i = j = 0; i < len; i = ++j) {
		for (; j < len - 1 && x[j + 1] == x[j] + 1; j++);

		if (i + 1 < j)
			a += snprintf(s?a:s, ol, "%s%d-%d", sep, x[i], x[j]);
		else
			while (i <= j)
				a += snprintf(s?a:s, ol, "%s%d", sep, x[i++]);
	}
	return a - s;
#undef sep
#undef ol
}

int main()
{
	int x[] = {	0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
			15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
			25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
			37, 38, 39 };

	char *s = malloc(rprint(0, x, sizeof(x) / sizeof(int)) + 1);
	rprint(s, x, sizeof(x) / sizeof(int));
	printf("%s\n", s);

	return 0;
}


using System;
using System.Collections.Generic;
using System.Linq;

class RangeExtraction
{
    static void Main()
    {
        const string testString = "0,  1,  2,  4,  6,  7,  8, 11, 12, 14,15, 16, 17, 18, 19, 20, 21, 22, 23, 24,25, 27, 28, 29, 30, 31, 32, 33, 35, 36,37, 38, 39";
        var result = String.Join(",", RangesToStrings(GetRanges(testString)));
        Console.Out.WriteLine(result);
    }

    public static IEnumerable<IEnumerable<int>> GetRanges(string testString)
    {
        var numbers = testString.Split(new[] { ',' }).Select(x => Convert.ToInt32(x));
        var current = new List<int>();
        foreach (var n in numbers)
        {
            if (current.Count == 0)
            {
                current.Add(n);
            }
            else
            {
                if (current.Max() + 1 == n)
                {
                    current.Add(n);
                }
                else
                {
                    yield return current;
                    current = new List<int> { n };
                }
            }
        }
        yield return current;
    }

    public static IEnumerable<string> RangesToStrings(IEnumerable<IEnumerable<int>> ranges)
    {
        foreach (var range in ranges)
        {
            if (range.Count() == 1)
            {
                yield return range.Single().ToString();
            }
            else if (range.Count() == 2)
            {
                yield return range.Min() + "," + range.Max();
            }
            else
            {
                yield return range.Min() + "-" + range.Max();
            }
        }
    }
}


#include <iostream>
#include <iterator>
#include <cstddef>

template<typename InIter>
 void extract_ranges(InIter begin, InIter end, std::ostream& os)
{
  if (begin == end)
    return;

  int current = *begin++;
  os << current;
  int count = 1;

  while (begin != end)
  {
    int next = *begin++;
    if (next == current+1)
      ++count;
    else
    {
      if (count > 2)
        os << '-';
      else
        os << ',';
      if (count > 1)
        os << current << ',';
      os << next;
      count = 1;
    }
    current = next;
  }

  if (count > 1)
    os << (count > 2? '-' : ',') << current;
}

template<typename T, std::size_t n>
 T* end(T (&array)[n])
{
  return array+n;
}

int main()
{
  int data[] = { 0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
                 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
                 25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
                 37, 38, 39 };

  extract_ranges(data, end(data), std::cout);
  std::cout << std::endl;
}


shared void run() {

	value numbers = [
		0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
		15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
		25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
		37, 38, 39
	];

	function asRangeFormattedString<Value>([Value*] values)
			given Value satisfies Enumerable<Value> {

		value builder = StringBuilder();

		void append(Range<Value> range) {
			if(!builder.empty) {
				builder.append(",");
			}
			if(1 <= range.size < 3) {
				builder.append(",".join(range));
			} else {
				builder.append("``range.first``-``range.last``");
			}
		}

		if(nonempty values) {
			variable value currentRange = values.first..values.first;
			for(val in values.rest) {
				if(currentRange.last.successor == val) {
					currentRange = currentRange.first..val;
				} else {
					append(currentRange);
					currentRange = val..val;
				}
			}
			append(currentRange);
		}
		return builder.string;
	}

	value rangeString = asRangeFormattedString(numbers);
	assert(rangeString == "0-2,4,6-8,11,12,14-25,27-33,35-39");
	print(rangeString);
}


(use '[flatland.useful.seq :only (partition-between)])

(defn nonconsecutive? [[x y]]
  (not= (inc x) y))

(defn string-ranges [coll]
  (let [left (first coll)
        size (count coll)]
    (cond
      (> size 2) (str left "-" (last coll))
      (= size 2) (str left "," (last coll))
      :else (str left))))

(defn format-with-ranges [coll]
  (println (clojure.string/join ","
    (map string-ranges (partition-between nonconsecutive? coll)))))


       IDENTIFICATION DIVISION.
       PROGRAM-ID. extract-range-task.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  data-str                PIC X(200) VALUE "0,  1,  2,  4,  6,"
           & " 7,  8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, "
           & "24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39".

       01  result                  PIC X(200).

       PROCEDURE DIVISION.
           CALL "extract-range" USING CONTENT data-str, REFERENCE result
           DISPLAY FUNCTION TRIM(result)

           GOBACK
           .
       END PROGRAM extract-range-task.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. extract-range.

       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       COPY "nums-table.cpy".

       01  difference              PIC 999.

       01  rng-begin               PIC S999.
       01  rng-end                 PIC S999.

       01  num-trailing            PIC 999.

       01  trailing-comma-pos      PIC 999.

       LINKAGE SECTION.
       01  nums-str                PIC X(200).
       01  extracted-range         PIC X(200).

       01  extracted-range-len     CONSTANT LENGTH extracted-range.

       PROCEDURE DIVISION USING nums-str, extracted-range.
           CALL "split-nums" USING CONTENT nums-str, ", ",
               REFERENCE nums-table

           *> Process the table
           MOVE nums (1) TO rng-begin
           PERFORM VARYING nums-idx FROM 2 BY 1
                   UNTIL num-nums < nums-idx
               SUBTRACT nums (nums-idx - 1) FROM nums (nums-idx)
                   GIVING difference

               *> If number is more than one away from the previous one
               *> end the range and start a new one.
               IF difference > 1
                   MOVE nums (nums-idx - 1) TO rng-end
                   CALL "add-next-range" USING CONTENT rng-begin,
                       rng-end, REFERENCE extracted-range
                   MOVE nums (nums-idx) TO rng-begin
               END-IF
           END-PERFORM

           *> Process the last number
           MOVE nums (num-nums) TO rng-end
           CALL "add-next-range" USING CONTENT rng-begin,
               rng-end, REFERENCE extracted-range

           *> Remove trailing comma.
           CALL "find-num-trailing-spaces"
               USING CONTENT extracted-range, REFERENCE num-trailing
           COMPUTE trailing-comma-pos =
               extracted-range-len - num-trailing
           MOVE SPACE TO extracted-range (trailing-comma-pos:1)

           GOBACK
           .

       IDENTIFICATION DIVISION.
       PROGRAM-ID. split-nums INITIAL.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  num-len                 PIC 9.
       01  next-num-pos            PIC 999.

       LINKAGE SECTION.
       01  str                     PIC X(200).
       01  delim                   PIC X ANY LENGTH.

       COPY "nums-table.cpy".

       PROCEDURE DIVISION USING str, delim, nums-table.
           INITIALIZE num-nums

           PERFORM UNTIL str = SPACES
               INITIALIZE num-len
               INSPECT str TALLYING num-len FOR CHARACTERS BEFORE delim

               ADD 1 TO num-nums

               *> If there are no more instances of delim in the string,
               *> add the rest of the string to the last element of the
               *> table.
               IF num-len = 0
                   MOVE str TO nums (num-nums)
                   EXIT PERFORM
               ELSE
                   MOVE str (1:num-len) TO nums (num-nums)
                   ADD 3 TO num-len GIVING next-num-pos
                   MOVE str (next-num-pos:) TO str
               END-IF
           END-PERFORM
           .
       END PROGRAM split-nums.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. add-next-range INITIAL.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  num-trailing            PIC 999.
       01  start-pos               PIC 999.

       01  range-len               PIC 999.

       01  begin-edited            PIC -ZZ9.
       01  end-edited              PIC -ZZ9.

       LINKAGE SECTION.
       01  rng-begin               PIC S999.
       01  rng-end                 PIC S999.

       01  extracted-range         PIC X(200).

       01  extracted-range-len     CONSTANT LENGTH extracted-range.

       PROCEDURE DIVISION USING rng-begin, rng-end, extracted-range.
           CALL "find-num-trailing-spaces"
               USING CONTENT extracted-range, REFERENCE num-trailing
           COMPUTE start-pos = extracted-range-len - num-trailing + 1

           SUBTRACT rng-begin FROM rng-end GIVING range-len

           MOVE rng-begin TO begin-edited
           MOVE rng-end TO end-edited

           EVALUATE TRUE
               WHEN rng-begin = rng-end
                   STRING FUNCTION TRIM(begin-edited), ","
                       INTO extracted-range (start-pos:)

               WHEN range-len = 1
                   STRING FUNCTION TRIM(begin-edited), ",",
                       FUNCTION TRIM(end-edited), ","
                       INTO extracted-range (start-pos:)

               WHEN OTHER
                   STRING FUNCTION TRIM(begin-edited), "-",
                         FUNCTION TRIM(end-edited), ","
                         INTO extracted-range (start-pos:)
           END-EVALUATE
           .
       END PROGRAM add-next-range.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. find-num-trailing-spaces.

       DATA DIVISION.
       LINKAGE SECTION.
       01  str                     PIC X(200).
       01  num-trailing            PIC 999.

       PROCEDURE DIVISION USING str, num-trailing.
           INITIALIZE num-trailing
           INSPECT str TALLYING num-trailing FOR TRAILING SPACES
           .
       END PROGRAM find-num-trailing-spaces.

       END PROGRAM extract-range.


(defun format-with-ranges (list)
  (unless list (return ""))
  (with-output-to-string (s)
    (let ((current (first list))
          (list    (rest list))
          (count   1))
      (princ current s)
      (dolist (next list)
        (if (= next (1+ current))
            (incf count)
            (progn (princ (if (> count 2) "-" ",") s)
                   (when (> count 1)
                     (princ current s)
                     (princ "," s))
                   (princ next s)
                   (setf count 1)))
        (setf current next))
      (when (> count 1)
        (princ (if (> count 2) "-" ",") s)
        (princ current s)))))

CL-USER> (format-with-ranges (list 0  1  2  4  6  7  8 11 12 14
                                   15 16 17 18 19 20 21 22 23 24
                                   25 27 28 29 30 31 32 33 35 36
                                   37 38 39))
"0-2,4,6-8,11,12,14-25,27-33,35-39"


import std.stdio, std.conv, std.string, std.algorithm, std.range;

string rangeExtraction(in int[] items)
in {
    assert(items.isSorted);
} body {
    if (items.empty)
        return null;
    auto ranges = [[items[0].text]];

    foreach (immutable x, immutable y; items.zip(items[1 .. $]))
        if (x + 1 == y)
            ranges[$ - 1] ~= y.text;
        else
            ranges ~= [y.text];

    return ranges
           .map!(r => r.length > 2 ? r[0] ~ "-" ~ r.back : r.join(','))
           .join(',');
}

void main() {
    foreach (data; [[-8, -7, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9,
                     10, 11, 14, 15, 17, 18, 19, 20],
                    [0, 0, 0, 1, 1],
                    [0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18,
                     19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31,
                     32, 33, 35, 36, 37, 38, 39]])
        data.rangeExtraction.writeln;
}


procedure ExtractRanges(const values : array of Integer);
begin
   var i:=0;
   while i<values.Length do begin
      if i>0 then
         Print(',');
      Print(values[i]);
      var j:=i+1;
      while (j<values.Length) and (values[j]=values[j-1]+1) do
         Inc(j);
      Dec(j);
      if j>i then begin
         if j=i+1 then
            Print(',')
         else Print('-');
         Print(values[j]);
      end;
      i:=j+1;
   end;
end;

ExtractRanges([ 0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
               15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
               25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
               37, 38, 39]);


def rex(numbers :List[int]) {
    var region := 0..!0
    for n in numbers { region |= n..n }
    var ranges := []
    for interval in region.getSimpleRegions() {
        def a := interval.getOptStart()
        def b := interval.getOptBound() - 1
        ranges with= if (b > a + 1) {
                         `$a-$b`
                     } else if (b <=> a + 1) {
                         `$a,$b`
                     } else { # b <=> a
                         `$a`
                     }
    }
    return ",".rjoin(ranges)
}


(define task '(0 1 2 4 6 7 8 11 12 14 15 16 17 18 19 20 21 22 23 24 25 27 28 29 30 31 32 33 35 36 37 38 39))

;; 1- GROUPING
(define (group-range item acc)
(if
    (or (empty? acc) (!= (caar acc) (1- item)))
      (cons (cons item item) acc)
      (begin  (set-car! (car acc) item) acc)))

;; intermediate result
;; (foldl group-range () task)
;; → ((39 . 35) (33 . 27) (25 . 14) (12 . 11) (8 . 6) (4 . 4) (2 . 0))

;; 2- FORMATTING
(define (range->string range)
(let ((from (rest range)) (to (first range)))
		(cond
		((= from to) (format "%d " from))
		((= to (1+ from))  (format "%d, %d " from to))
		(else (format "%d-%d " from to)))))

;; 3 - FINAL
(string-join (map range->string  (reverse (foldl group-range () task))) ",")
    → "0-2 ,4 ,6-8 ,11, 12 ,14-25 ,27-33 ,35-39 "


class
	RANGE

create
	make

feature
	make
		local
			extended_range: STRING
		do
			extended_range := "0,  1,  2,  4,  6,  7,  8, 11, 12, 14, " +
				"15, 16, 17, 18, 19, 20, 21, 22, 23, 24, " +
				"25, 27, 28, 29, 30, 31, 32, 33, 35, 36, " +
                		"37, 38, 39"
			print("Extended range: " + extended_range + "%N")
			print("Extracted range: " + extracted_range(extended_range) + "%N%N")
		end

feature
	extracted_range(sequence: STRING): STRING
		local
			elements: LIST[STRING]
			first, curr: STRING
			subrange_size, index: INTEGER
		do
			sequence.replace_substring_all (", ", ",")
			elements := sequence.split (',')
			from
				index := 2
				first := elements.at (1)
				subrange_size := 0
				Result := ""
			until
				index > elements.count
			loop
				curr := elements.at (index)
				if curr.to_integer - first.to_integer - subrange_size = 1
				then
					subrange_size := subrange_size + 1
				else
					Result.append(first)
					if (subrange_size <= 1)
					then
						Result.append (", ")
					else
						Result.append (" - ")
					end
					if (subrange_size >= 1)
					then
						Result.append ((first.to_integer + subrange_size).out)
						Result.append (", ")
					end

					first := curr
					subrange_size := 0
				end
				index := index + 1
			end
			Result.append(first)
			if (subrange_size <= 1)
			then
				Result.append (", ")
			else
				Result.append (" - ")
			end
			if (subrange_size >= 1)
			then
				Result.append ((first.to_integer + subrange_size).out)
			end
		end
end


defmodule RC do
  def range_extract(list) do
    max = Enum.max(list) + 2
    sorted = Enum.sort([max|list])
    candidate_number = hd(sorted)
    current_number = hd(sorted)
    extract(tl(sorted), candidate_number, current_number, [])
  end

  defp extract([], _, _, range), do: Enum.reverse(range) |> Enum.join(",")
  defp extract([next|rest], candidate, current, range) when current+1 >= next do
    extract(rest, candidate, next, range)
  end
  defp extract([next|rest], candidate, current, range) when candidate == current do
    extract(rest, next, next, [to_string(current)|range])
  end
  defp extract([next|rest], candidate, current, range) do
    separator = if candidate+1 == current, do: ",", else: "-"
    str = "#{candidate}#{separator}#{current}"
    extract(rest, next, next, [str|range])
  end
end

list = [
   0,  1,  2,  4,  6,  7,  8, 11, 12, 14,
  15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  25, 27, 28, 29, 30, 31, 32, 33, 35, 36,
  37, 38, 39
]
IO.inspect RC.range_extract(list)


-module( range ).

-export( [extraction/1, task/0] ).

extraction( [H | T] ) when is_integer(H) ->
        Reversed_extracts = extraction_acc( lists:foldl(fun extraction/2, {H, []}, T) ),
        string:join( lists:reverse(Reversed_extracts), "," ).

task() ->
    io:fwrite( "~p~n", [extraction([0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39])] ).

extraction( N, {Start, Acc} ) when N =:= Start + 1 -> {Start, N, Acc};
extraction( N, {Start, Acc} )  -> {N, extraction_acc( {Start, Acc} )};
extraction( N, {Start, Stop, Acc} ) when N =:= Stop + 1 -> {Start, N, Acc};
extraction( N, {Start, Stop, Acc} ) -> {N, extraction_acc( {Start, Stop, Acc} )}.

extraction_acc( {N, Acc} ) -> [erlang:integer_to_list(N) | Acc];
extraction_acc( {Start, Stop, Acc} ) when Stop > Start + 1 -> [erlang:integer_to_list(Start) ++ "-" ++ erlang:integer_to_list(Stop) | Acc];
extraction_acc( {Start, Stop, Acc} ) -> [erlang:integer_to_list(Stop), erlang:integer_to_list(Start) | Acc]. % Reversed


function extract_ranges(sequence s)
    integer first
    sequence out
    out = ""
    if length(s) = 0 then
        return out
    end if
    first = 1
    for i = 2 to length(s) do
        if s[i] != s[i-1]+1 then
            if first = i-1 then
                out &= sprintf("%d,", s[first])
            elsif first = i-2 then
                out &= sprintf("%d,%d,", {s[first],s[i-1]})
            else
                out &= sprintf("%d-%d,", {s[first],s[i-1]})
            end if
            first = i
        end if
    end for
    if first = length(s) then
        out &= sprintf("%d", s[first])
    elsif first = length(s)-1 then
        out &= sprintf("%d,%d", {s[first],s[$]})
    else
        out &= sprintf("%d-%d", {s[first],s[$]})
    end if
    return out
end function

puts(1, extract_ranges({0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39}))


T Pt
   Float x, y

   F (x, y)
      .x = x
      .y = y

   F String()
      R ‘Pt(x=#., y=#.)’.format(.x, .y)

T Edge
   Pt a, b

   F (a, b)
      .a = a
      .b = b

   F String()
      R ‘Edge(a=#., b=#.)’.format(.a, .b)

T Poly
   String name
   [Edge] edges

   F (name, edges)
      .name = name
      .edges = edges

V _eps = 0.00001
V _huge = 1e+100
V _tiny = 1e-100

F rayintersectseg(=p, edge)
   V a = edge.a
   V b = edge.b
   I a.y > b.y
      swap(&a, &b)
   I p.y == a.y | p.y == b.y
      p = Pt(p.x, p.y + :_eps)

   V intersect = 0B

   I (p.y > b.y | p.y < a.y) | (p.x > max(a.x, b.x))
      R 0B

   I p.x < min(a.x, b.x)
      intersect = 1B
   E
      Float m_red, m_blue
      I abs(a.x - b.x) > :_tiny
         m_red = (b.y - a.y) / Float(b.x - a.x)
      E
         m_red = :_huge
      I abs(a.x - p.x) > :_tiny
         m_blue = (p.y - a.y) / Float(p.x - a.x)
      E
         m_blue = :_huge
      intersect = m_blue >= m_red
   R intersect

F ispointinside(p, poly)
   R sum(poly.edges.map(edge -> Int(rayintersectseg(@p, edge)))) % 2 == 1

F polypp(poly)
   print("\n  Polygon(name='#.', edges=(".format(poly.name))
   print(‘    ’(poly.edges.map(e -> String(e)).join(",\n    ")"\n    ))"))

V polys = [
  Poly(name' ‘square’, edges' [Edge(Pt(0, 0), Pt(10, 0)), Edge(Pt(10, 0), Pt(10, 10)), Edge(Pt(10, 10), Pt(0, 10)), Edge(Pt(0, 10), Pt(0, 0))]),
  Poly(name' ‘square_hole’, edges' [Edge(Pt(0, 0), Pt(10, 0)), Edge(Pt(10, 0), Pt(10, 10)), Edge(Pt(10, 10), Pt(0, 10)), Edge(Pt(0, 10), Pt(0, 0)), Edge(Pt(2.5, 2.5), Pt(7.5, 2.5)), Edge(Pt(7.5, 2.5), Pt(7.5, 7.5)), Edge(Pt(7.5, 7.5), Pt(2.5, 7.5)), Edge(Pt(2.5, 7.5), Pt(2.5, 2.5))]),
  Poly(name' ‘strange’, edges' [Edge(Pt(0, 0), Pt(2.5, 2.5)), Edge(Pt(2.5, 2.5), Pt(0, 10)), Edge(Pt(0, 10), Pt(2.5, 7.5)), Edge(Pt(2.5, 7.5), Pt(7.5, 7.5)), Edge(Pt(7.5, 7.5), Pt(10, 10)), Edge(Pt(10, 10), Pt(10, 0)), Edge(Pt(10, 0), Pt(2.5, 2.5))]),
  Poly(name' ‘exagon’, edges' [Edge(Pt(3, 0), Pt(7, 0)), Edge(Pt(7, 0), Pt(10, 5)), Edge(Pt(10, 5), Pt(7, 10)), Edge(Pt(7, 10), Pt(3, 10)), Edge(Pt(3, 10), Pt(0, 5)), Edge(Pt(0, 5), Pt(3, 0))])]

V testpoints = [Pt(5, 5), Pt(5, 8),
                Pt(-10, 5), Pt(0, 5),
                Pt(10, 5), Pt(8, 5),
                Pt(10, 10)]

print("\n TESTING WHETHER POINTS ARE WITHIN POLYGONS")
L(poly) polys
   polypp(poly)
   print(‘    ’testpoints[0.<3].map(p -> ‘#.: #.’.format(p, I ispointinside(p, @poly) {‘True’} E ‘False’)).join("\t"))
   print(‘    ’testpoints[3.<6].map(p -> ‘#.: #.’.format(p, I ispointinside(p, @poly) {‘True’} E ‘False’)).join("\t"))
   print(‘    ’testpoints[6.. ].map(p -> ‘#.: #.’.format(p, I ispointinside(p, @poly) {‘True’} E ‘False’)).join("\t"))


let extractRanges = function
  | []    -> Seq.empty
  | x::xr ->
      let rec loop ys first last = seq {
        match ys with
        | y::yr when y = last + 1 -> yield! loop yr first y  // add to current range
        | y::yr                   -> yield (first, last)     // finish current range
                                     yield! loop yr y y      //  and start next
        | []                      -> yield (first, last) }   // finish final range
      loop xr x x

let rangeToString (s,e) =
  match e-s with
  | 0 -> sprintf "%d" s
  | 1 -> sprintf "%d,%d" s e
  | _ -> sprintf "%d-%d" s e

let extract = extractRanges >> Seq.map rangeToString >> String.concat ","

printfn "%s" (extract [ 0; 1; 2; 4; 6; 7; 8; 11; 12; 14; 15; 16; 17; 18; 19; 20; 21;
                        22; 23; 24; 25; 27; 28; 29; 30; 31; 32; 33; 35; 36; 37; 38; 39 ])


package Polygons is

   type Point is record
      X, Y : Float;
   end record;
   type Point_List is array (Positive range <>) of Point;
   subtype Segment is Point_List (1 .. 2);
   type Polygon is array (Positive range <>) of Segment;

   function Create_Polygon (List : Point_List) return Polygon;

   function Is_Inside (Who : Point; Where : Polygon) return Boolean;

end Polygons;