[Question]
  [
Disclaimer: while I have been on this site for entertainment purposes for a little while now, this is my first question, so please forgive any minor errors.
# Background
When assigning us homework, my teacher is really annoying and writes out *all* the problems we have to do individually. As such, it takes me *forever* to copy down which problems I have to do. I thought to make my life easier, I would send him a program that could make the list of problems take up less space.
While writing down a list of page or problem numbers, we use a dash to denote a range. For example, `19-21` becomes `19, 20, 21`. If there is a gap in between, two comma-separated ranges are used: `19-21, 27-31` becomes `19, 20, 21, 27, 28, 29, 30, 31`.   
Right about now, you're probably thinking: "this seems pretty trivial". In fact, this has already been answered [here](https://codegolf.stackexchange.com/questions/52321/determine-ranges-from-a-list-of-values) and [here](https://codegolf.stackexchange.com/questions/20545/string-of-alphanumeric-characters-to-a-sorted-list-of-comma-separated-ranges).
However, there is a catch. If we have a range with equal consecutive digits, the repeated digits can be left out. For example: `15, 16, 17` becomes `15-7`, and `107, 108, 109` becomes `107-9`. For a bonus, if the last consecutive equal digit is 1 greater and the upper limit's last digit is less than or equal to that of the lower, the following can be omitted (sorry if that sounded confusing; perhaps some examples will clear it up). `109-113` becomes `109-3`, as a lower last digit implies increasing the 10s place. 
# Challenge
Your program should take a list of integers via input (whatever is standard for your language, or a function). You can decide whether this list is comma-separated, space-separated, or as an actual list/array.
Output the **shortest way** (first sorted by number of ranges, then the sum of the characters included in the ranges) to represent that list using this notation. Each dashed range must be on the same line, but the ranges can be separated by commas or newlines (trailing newlines or commas are allowed). These ranges must be in order.
Since our school Wi-Fi is **terrible**, I have to make the file as small as possible in order to send it to him. The shortest code (in bytes) wins.
# Bonuses
My teacher is sloppy, so there are a few things that would help him out. 
Multiple bonuses stack through multiplication, e.g. a -10% bonus (x 90%) and a -25% (x 75%) bonus = 90% \* 75% = x 67.5% (-32.5% bonus).
* Sometimes he puts them in the wrong order (he's not a math teacher). Take a -20% bonus if your program can accept integers that are not sorted least-to-greatest.
* Our book is weird, and each section starts counting the problems at -10. If your program can accept negative numbers, take a -25%.
* If it accepts the bonus of a lower last digit increasing 10's place, e.g. `25-32` reducing to `25-2`, take a -50% bonus.
# Test Cases
```
In:  1, 2, 3, 4, 5
Out: 1-5
In:  3, 4, 5, 9, 10, 11, 12
Out: 3-5, 9-12
In:  149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160
Out: 149-60
In: 1 2 3 4
Out: 1-4
For bonuses:
In: 109, 110, 111, 112, 113
Out: 109-3
In:  19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
Out: 19-9
In: -3, -2, -1, 0, 1, 2
Out: -3-2
In: -3, -2, -1
Out: -3--1
```
An answer will be accepted on Saturday, 19 December 2015.
GLHF!
  
[Answer]
## C++11, 451 \* 80% \* 75% \* 50% = 135.3 bytes
Saved 9 bytes thanks to @kirbyfan64sos.
Saved 19 bytes thanks to @JosephMalle and @cat.
Saved 11 bytes thanks to @pinkfloydx33.
```
#include<vector>
#include<cmath>
#include<algorithm>
#include<string>
#define T string
#define P append
using namespace std;T f(vector<int>v){sort(v.begin(),v.end());T r=to_T(v[0]);int b=1;int m=v[0];for(int i=1;i<=v.size();i++){if(i!=v.size()&&v[i]==v[i-1]+1){if(!b){m=v[i-1];}b=1;}else{if(b){T s=to_T(v[i-1]);r.P("-").P(s.substr(s.size()-(v[i-1]-m==1?1:log10(v[i-1]-m)),s.size()));}if(i!=v.size()){r.P(", ").P(to_T(v[i]));}b=0;}}return r;}
```
This qualifies for all the bonuses.
Sample parameter test and result:
```
In:  [1, 2, 3, 4, 5]
Out: 1-5
In:  [3, 4, 5, 9, 10, 11, 12]
Out: 3-5, 9-12
In:  [149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160]
Out: 149-60
In:  [1, 2, 3, 4]
Out: 1-4
In:  [109, 110, 111, 112, 113]
Out: 109-3
In:  [19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
Out: 19-9
```
[Answer]
## Ruby, ~~120~~ 118 \* 0.8 \* 0.75 \* 0.5 = 35.4 bytes
Takes command-line arguments as input (commas are okay); prints one range per line to standard output.
```
c=(b=(a=$*.map(&:to_i).sort).map &:succ)-a
puts (a-b).map{|m|(m<n=c.shift-1)?"#{m}-#{m<0?n:n%10**"#{n-m-1}".size}":m}
```
With whitespace/comments:
```
c=(
  b=(
    # a is the sorted input
    a=$*.map(&:to_i).sort
  # b is the set of successors of elements of a
  ).map &:succ
# c=b-a is the set of not-quite-least upper bounds of our ranges
)-a
# a-b is the set of greatest lower bounds of our ranges
puts (a-b).map {|m|
  # for each range [m,n], if there are multiple elements
  (m < n = c.shift-1) ?
    # yield the range, abbreviating n appropriately if positive
    "#{m}-#{m<0 ? n : n % 10 ** "#{n-m-1}".size}" :
    # in the one-element case, just yield that
    m
}
```
**Test cases**
```
$ ruby homework.rb 1, 2, 3, 4, 5
1-5
$ ruby homework.rb 3, 4, 5, 9, 10, 11, 12
3-5
9-2
$ ruby homework.rb 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160
149-60
$ ruby homework.rb 1 2 3 4
1-4
$ ruby homework.rb 109, 110, 111, 112, 113
109-3
$ ruby homework.rb 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
19-9
```
**Features not covered by test cases**
Unordered input & single-element ranges:
```
$ ruby homework.rb 2 17 19 22 0 1 8 20 18
0-2
8
17-0
22
```
Negative ranges (not possible to abbreviate the larger number with these):
```
$ ruby homework.rb -17 -18 -19 -20 -21
-21--17
```
Abbreviation of arbitrary numbers of digits (ordinary bash expansion used for the input here):
```
$ ruby homework.rb {1234567..1235467} 2345999 2346000 2346001
1234567-467
2345999-1
```
[Answer]
# LabVIEW, 97\*0.8\*0.75\*0.5 = 29.1 [LabVIEW Primitives](https://codegolf.meta.stackexchange.com/a/7589/39490)
this works by counting upwards if sucessive elemts are 1 apart and then creates a string from the the number and the number - count modulo 10 and some multiplication cause negatives are a bitch.
The gif showcases an input of `8,9,10,11` and outputs `8-1`. For input `-5,-4,-3,1,3,4,5` `-5--3,1,3-5`comes out.
[![](https://i.stack.imgur.com/kelGW.gif)](https://i.stack.imgur.com/kelGW.gif)
[Answer]
# Javascript ES6, 229 \* 80% \* 75% \* 50% = 68.7 bytes
## Test Input
I'm using the following test data:
```
var A1=[
  5,6,7,            // => 5-7     # (a) group pages 
  2,3,              // => 2-3,5-7 # (b) must be properly sorted
  -9,-8,-7,         // => -10--8  # (c) allow negative numbers
  29,30,31,32,      // => 29-2    # (d) lower last digit implies increasing the 10s place
  9,10,11,12,       // => 9-11    # NOT 9-2
  36,37,38,39,40,41,42,43,44,45,46,47, 
                    // => 36-47   # NOT 36-7
  99,100,101,102,   // => 99-102  # NOT 99-2
  109,110,111,112,  // => 109-2   # NOT 109-12
],
// more tests, not specified in the question
A2=[
  120,124,       // => 120,124 # (e) handle single pages
],
A3=[
  135,136,135    // => 135-6   # (f) handle duplicates
];
```
## Basic: 229 bytes
This version satisfies the requirements of the question (a) with all bonuses (c,d,e), but hangs on single pages. It can also handle duplicates(f). It handles negative pages down to -10,000, which can be easily increased with (large) loss of speed.
```
F=(a)=>{var R=[],i=NaN,l,u;a.map(x=>R[1e4+x]=x);R.concat('').map(x=>(i!=i&&(l=x,u=l-1),i=0,u=(x+="")-u-1?l=console.log(l+'-'+(u>0?(l.length-u.length||(z=>{for(;l[i]==u[i];i++);})(),u.length-i-2||u-l>9||i++,u.slice(i)):u))||x:x))}
F(A1.concat(A3)) --> -9--7 2-3 5-7 9-12 29-2 36-47 99-102 109-2 135-136
```
(The output above shows spaces instead of actual newlines for brevity)
## Single pages: 233 bytes
This slightly longer version additionally satisfies (e) and displays single pages as a range with equal lower and upper limits
```
G=(a)=>{var R=[],i=NaN,l,u;a.map(x=>R[1e4+x]=x);R.concat('').map(x=>(i!=i&&(l=x,u=l-1),i=0,u=(x+="")-u-1?l=console.log(l+'-'+(u-l&u>0?(l.length-u.length||(z=>{for(;l[i]==u[i];i++);})(),u.length-i-2||u-l>9||i++,u.slice(i)):u))||x:x))}
G(A1.concat(A2,A3)) --> -9--7 2-3 5-7 9-12 29-2 36-47 99-102 109-2 120-120 124-124
```
[Answer]
# [GAP](http://www.gap-system.org), 355 Bytes \*0.8\*0.75\*0.5 = 106.5
This satisfies all the bonuses. It cost me almost 100 extra bytes to make everything work nicely. This function only omits leading digits if the gap does not overflow the ones place once. For example `9 10 11` outputs `9-1` but `9 10 11 12 .. 20 21` outputs `9-21`. 
If GAP was a little less verbose, I could have gotten this pretty short (also could have saved many bytes if I didn't follow the exact syntax.) I'll probably try golfing this a bit harder tomorrow. See below for test cases.
```
g:=function(l)local n;if not l=[] then Sort(l);n:=1;while not l=[] do;if not IsSubset(l,[l[1]..l[1]+n]) then if not n=1 then if n-1>10-l[1] mod 10 and n-1<11 then Print(l[1],"-",(l[1]+n-1) mod 10);else Print(l[1],"-",l[1]+n-1);fi;else Print(l[1]);fi;Print(", ");SubtractSet(l,[l[1]..l[1]+n-1]);g(l);fi;n:=n+1;od;fi;Print("\b\b  ");end; 
```
ungolfed:
```
g:=function(l)
    local n;
    if not l=[] then
        Sort(l);
        n:=1;
        while not l=[] do;
            if not IsSubset(l,[l[1]..l[1]+n]) then
                if not n=1 then
                    if n-1>10-l[1] mod 10 and n-1<11 then
                        Print(l[1],"-",(l[1]+n-1) mod 10);
                    else
                        Print(l[1],"-",l[1]+n-1);
                    fi;
                else
                    Print(l[1]);
                fi;
                Print(", ");
                SubtractSet(l,[l[1]..l[1]+n-1]);
                g(l);
            fi;
            n:=n+1;
        od; 
    fi;
    Print("\b\b  ");
end;
```
Note that in GAP syntax, `[a..b]` is equivalent to `[a,a+1,...,b]`. I believe that these test cases demonstrate that this program meets all the requirements. If something is wrong, let me know.
```
gap> h([1..5]);
1-5  
gap> h([3,4,5,9,10,11,12]);
3-5, 9-2  
gap> h([149..160]);
149-160  
gap> h([109..113]);
109-3  
gap> h([19..29]);
19-9  
gap> h([-1,-2,-3,-7,-20000,9,10,110101,110102]);
-20000, -7, -3--1, 9-10, 110101-110102  
gap> h([10101,10102,10103,10,11,12,13,14,15,16,234,999,1000,1001,1002]);
10-16, 234, 999-2, 10101-10103  
```
[Answer]
# Lua, 322\*80%\*75%\*50% = 96.6 Bytes
Finally done with the 3 challenges, Scores under 100 bytes :D
### Golfed
```
function f(l)table.sort(l)l[#l+1]=-13 a=l[1]t,s=a,"" for _,v in next,l do if v-t>1 or t-v>1 then s,p,r=s..a.."-",""..t,""..a r:gsub("%d",function(c)p=r:find(c)~=r:len()and p:gsub("^(-?)"..c,"%1")or p r=r:gsub("^"..c,"")end)p=t-a<10 and t%10<a%10 and p:gsub("^(%d)","")or p s,a,t=s..p..",",v,v else t=v end end return s end
```
### Ungolfed
```
function f(l)
    table.sort(l)
    l[#l+1]=-13 
    a=l[1] 
    t,s=a,"" 
    for _,v in next,l 
    do
        if v-t>1 or t-v>1
        then
            s,p,r=s..a.."-",""..t,""..a
            r:gsub("%d",function(c)
                p=r:find(c)~=#r and p:gsub("^(-?)"..c,"%1")or p
                r=r:gsub("^"..c,"")
            end)
            p=t-a<10 and t%10<a%10 and p:gsub("^(%d)","")or p
            s=s..p..","
            a,t=v,v
        else
            t=v
        end
    end
return s
end
```
You can test lua [online](http://www.lua.org/cgi-bin/demo), to see how it perform against the test cases, copy paste the function, followed by this code :
```
a={1,2,3,4,5}
b={3,4,5,9,10,11,12,13,14,15,16,17,18,19,20,21}
c={149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160}
d={-7,8,5,-6,-5,6,7}
print(f(a))
print(f(b))
print(f(c))
print(f(d))
```
[Answer]
# Java, 252 \* 80% \* 75% \* 50% = 75.6 bytes
I've decided to go for a method (is much smaller in Java), here is the golfed version:
### Golfed
```
int p,c,s;String m(int[]a){p=s=c=0;c--;String o="";Arrays.sort(a);for(int n:a){if(s==0)o+=s=n;else if(n-p==1)c++;else{o+=t()+", "+(s=n);c=-1;}p=n;}return o+t();}String t(){return c>=0?"-"+(""+p).substring((""+Math.abs(p)).length()-(""+c).length()):"";}
```
And here is the readable version:
```
int p, c, s;
String m(int[] a) {
    p = s = c = 0;
    c--;
    String o = "";
    Arrays.sort(a);
    for (int n : a) {
        if (s == 0)
            o += s = n;
        else if (n - p == 1)
            c++;
        else {
            o += t() + ", " + (s = n);
            c = -1;
        }
        p = n;
    }
    return o + t();
}
String t() {
    return c >= 0 ? "-" + ("" + p).substring(("" + Math.abs(p)).length() - ("" + c).length()) : "";
}
```
When tested these are the results:
```
import java.util.Arrays;
public class A {
    public static void main(String...s) {
        A a = new A();
        System.out.println(a.m(new int[] {1, 2, 3, 4, 5}));
        System.out.println(a.m(new int[] {3, 4, 5, 9, 10, 11, 12}));
        System.out.println(a.m(new int[] {149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160}));
        System.out.println(a.m(new int[] {109, 110, 111, 112, 113}));
        System.out.println(a.m(new int[] {19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}));
        System.out.println(a.m(new int[] {1,10,11,16}));
        System.out.println(a.m(new int[] {-3,-2,-1,0,1,2,3}));
        System.out.println(a.m(new int[] {-3,-2,-1}));
    }
    int p,c,s;String m(int[]a){p=s=c=0;c--;String o="";Arrays.sort(a);for(int n:a){if(s==0)o+=s=n;else if(n-p==1)c++;else{o+=t()+", "+(s=n);c=-1;}p=n;}return o+t();}String t(){return c>=0?"-"+(""+p).substring((""+Math.abs(p)).length()-(""+c).length()):"";}
}
```
Output:
```
1-5
3-5, 9-2
149-60
109-3
19-9
1, 10-1, 16
-3-3
-3--1
```
### Update:
It can now handle negative numbers as well, adding to the bonus.
[Answer]
# Japt, 127 bytes \* 80% \* 75% \* 50% = 38.1
Wow, that was one heck of a challenge to include all the bonuses. It could probably be made shorter.
```
D=[]N=Nn-;DpNr@Y-1¥Xg1 ?[Xg Y]:DpX ©[YY]}D;Ds1 £[BC]=Xms;B¥C?B:B+'-+CsBg ¦'-©Cl ¥Bl ©C¬r@B¯Z ¥C¯Z ªC-B§ApCl -Z ©ÂBsX <ÂCsX ?Z:X
```
[Try it online!](http://ethproductions.github.io/japt?v=master&code=RD1bXU49Tm4tO0RwTnJAWS0xpVhnMSA/W1hnIFldOkRwWCCpW1lZXX1EO0RzMSCjW0JDXT1YbXM7QqVDP0I6QisnLStDc0OsckBCr1ogpUOvWiCqQy1Cp0FwQ2wgLVogqcJCc1ggPMJDc1ggP1o6WA==&input=MTAwOCwxMDA5LDEwMTAsMTAxMSwxMDEyLDEwMTMsMTAxNCwxMDE1LDEwMTYsMTAxNywxMDE4LDEwMTksMTAyOSwxMDMw)
### How it works
The explanation is very rough; don't hesitate to ask any questions you might have.
```
/*    Setting up basic variables    */
                      // Implicit: A = 10, N = list of input numbers.
D=[],N=Nn-;           // D = empty array, N = N sorted by subtraction.
/*    Finding ranges of page numbers    */    
Dp                    // Push into D the result of
NrXYZ{                // reducing each previous value X and item Y in N by this function,
}[];                  // starting with an empty array:
 Y-1==Xg1 ?           //  If Y is 1 more than the second item of X,
 [Xg Y]:              //   return [X[0], Y].
 DpX &&[YY]           //  Otherwise, push X into D and return [Y, Y].
/*    Formatting result    */
Ds1 mXYZ{             // Take the first item off of D and map each item X by this function:
 [BC]=Xms;            //  Set B and C to the first to items in X as strings.
 B==C?B               //  If B is the same as C, return B.
 :B+'-+Cs             //  Otherwise, return B + a hyphen + C.slice(
  Bg !='-&&           //   If B[0] is not a hyphen (B is not negative), AND
  Cl ==Bl &&          //   B and C are the same length,
  /*    Cutting off unnecessary digits    */
  Cq r                //    then C split into digits, reduced with
  rXYZ{               //    each previous value X, item Y, and index Z mapped by this function:
   Bs0,Z ==Cs0,Z ||   //     If B.slice(0,Z) equals C.slice(0,Z), OR
   C-B<=ApCl -Z       //     C - B <= 10 to the power of (C.length - Z);
   &&~~BsX <~~CsX     //     AND B.slice(X) is a smaller number than C.slice(X),
   ?Z:X               //     then Z; otherwise, X.
                      //   Otherwise, 0.
```
[Answer]
# R, 167 bytes x 80% x 75% x 50% -> 50.1
```
s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];z=tail(x,1);r=c(r,paste0(x[1],"-",ifelse(z-x[1]<=10,z%%10,z%%100)))};cat(r,sep=", ")
```
Indented, with new lines:
```
s=sort(scan(se=","))
r=c()
while(length(s)){
w=s==1:length(s)+s[1]-1
x=s[w]
s=s[!w]
z=tail(x,1)
r=c(r, paste0(x[1],"-", ifelse(z-x[1]<=10, 
                               z%%10,
                               z%%100)))}
cat(r,sep=", ")
```
Test cases:
```
> s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];r=c(r,paste0(x[1],"-",ifelse(tail(x,1)-x[1]<=10,tail(x,1)%%10,tail(x,1)%%100)))};cat(r,sep=", ")
1: 3, 4, 5, 9, 10, 11, 12
8: 
Read 7 items
3-5, 9-2
> s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];r=c(r,paste0(x[1],"-",ifelse(tail(x,1)-x[1]<=10,tail(x,1)%%10,tail(x,1)%%100)))};cat(r,sep=", ")
1: 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160
13: 
Read 12 items
149-60
```
It works for the -50% bonus:
```
> s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];r=c(r,paste0(x[1],"-",ifelse(tail(x,1)-x[1]<=10,tail(x,1)%%10,tail(x,1)%%100)))};cat(r,sep=", ")
1: 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
12: 
Read 11 items
19-9
> s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];r=c(r,paste0(x[1],"-",ifelse(tail(x,1)-x[1]<=10,tail(x,1)%%10,tail(x,1)%%100)))};cat(r,sep=", ")
1: 109, 110, 111, 112, 113
6: 
Read 5 items
109-3
```
It accepts unsorted input:
```
> s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];r=c(r,paste0(x[1],"-",ifelse(tail(x,1)-x[1]<=10,tail(x,1)%%10,tail(x,1)%%100)))};cat(r,sep=", ")
1: 114, 109, 110, 111, 112, 113
7: 
Read 6 items
109-4
```
It accepts negative numbers:
```
> s=sort(scan(se=","));r=c();while(length(s)){w=s==1:length(s)+s[1]-1;x=s[w];s=s[!w];r=c(r,paste0(x[1],"-",ifelse(tail(x,1)-x[1]<=10,tail(x,1)%%10,tail(x,1)%%100)))};cat(r,sep=", ")
1: -1,0,1,2
4: 
Read 3 items
-1-2
```
[Answer]
# sh, 135 \* .8 \* .75 \* .5 = 40.5
```
tr , \\n|sort -n|awk -vORS="" '$1-o>1||!c{print p c$1;s=$1}{o=$1;c=", ";p=""}o>s{p="-"substr(o,length(o)-length(o-s-1)+1)}END{print p}'
```
shell script
```
tr , \\n|           # comma separated -> newline separated
sort -n|            # sort
awk -vORS=""        # suppress automatic newlines in output
```
awk script
```
# on step > 1 or first run, end the current sequence and start a new one.
# on first run, p and c are empty strings.
$1-o>1||!c
    {print p c$1;s=$1}
# old = current, c = ", " except for first run, clear end string.
    {o=$1;c=", ";p=""}
# if the sequence is not a single number, its end is denoted by "-o".
# print only the last n digits of o.
o>s
    {p="-"substr(o,length(o)-length(o-s-1)+1)}
# end the current sequence without starting a new one.
END
    {print p}'
```
where `s` is the start of the current sequence and `o` is the previous input value.
]