import draw.*;
/*
--- CSU213 Spring 2005 Lecture Notes ---------
Copyright 2005 Viera K. Proulx

Lecture 9: All people are equal, but some are more equal than others...
Part 2
*/

/* 
Goals:

 - learn to determine the equality of two self-referential data
 - learn to sort lists

Our remaining task is to sort the list of CDs. We will sort them by 
the number of tracks, though there are obviously better choices.


1. Problem analysis.

The method 'sort' only needs the list to be sorted.

2. Purpose and Header.

// sort this list of CDs in ascending order of the number of tracks
abstract ALoCD sort();

This method needs to be defined in every subclass of the ALoCD class.

3. Examples.

mtcd.sort() --> new MTLoCD()
list1.sort() --> list1
list4.sort() --> new ConsLoCD(pearl, 
                   new ConsLoCD(boston, 
                   new ConsLoCD(help, 
                   new ConsLoCD(tool, 
                   mtcd))))

4. Template.

Again, there is no data in MTLoCD, so we only need a template 
for the class ConsLoCD:

  ... this.first ...                

  ... this.rest ...                 
  ... this.rest.sort() ... 

5. Body.

As usual we try to do the simple case first.

class MTLoCD:
-------------

An empty list is sorted, so this is a trivial case:

  ALoCD sort(){
    return this;
  }

class ConsLoCD:
---------------

  ... this.first ...
  ... this.rest ...
  ... this.rest.sort() ...

Reading aloud the purpose statement for 'this.rest.sort()' we get:

'sort the rest of this list of CDs in ascending order of the number of tracks'

So all that reamins is to insert the first item into a sorted 
rest of the list. We use a helper method:

// insert the given CD into this sorted list
ALoCD insert(CD cd)

and with the help of this method, the body for the 'sort' becomes:

  return this.rest.sort().insert(this.first);

----------------------------------------------------------------------------
We now work on the helper method 'insert'. we already have the purpose
and header.

Examples.

mtcd.insert(tool) --> new ConsLoCD(tool, mtcd)
list1.insert(pearl) --> new ConsLoCD(pearl, list1)
list1.insert(tool) --> new ConsLoCD(help, new ConsLoCD(tool, mtcd))
new ConsLoCD(pearl, new ConsLoCD(boston, new ConsLoCD(tool, mtcd))).insert(help)
  --> new ConsLoCD(pearl, 
        new ConsLoCD(boston, 
          new ConsLoCD(help, 
            new ConsLoCD(tool, mtcd))))


Template and Body.

class MTLoCD:
-------------

The result is always a list with one item:

  return new ConsLoCD(cd, this);

class ConsLoCD:
---------------

We use the earlier template for the class ConsLoCD 

  ... this.first ...                

  ... this.rest ...                 
  ... this.rest.sort() ... 

However, we cannot add ... this.rest.insert(CD ...) to the template,
because it requires that the instance that invokes the method be
already sorted. But we can use this.rest.sort() instead, to invoke
the 'insert' method:

  ... this.rest.sort().insert(CD ...) ...

Reading aloud the combined purpose statement for 'this.rest.insert(CD ...)' 
we get:

'insert the given CD into the sorted rest of this sorted list'

If the given CD has fewer tracks than the first item in the list,
the resulting list is just

  new ConsLoCD(cd, this)

otherwise, the first item of the sorted list is the first item of the
list we are instering into, and the rest consists of the given CD
inserted into the rest of the list:

  new ConsLoCD(this.first, this.rest.insert(cd))

and the body of the method is:

  if (cd.tracks < this.first.tracks)
    return new ConsLoCD(cd, this);
  else
    return new ConsLoCD(this.first, this.rest.insert(cd));

*/

// The final code is:
/*
;                                      
;                                      
;                                      
;     ;;;;               ;             
;    ;    ;              ;             
;   ;                    ;             
;   ;         ;;;     ;; ;    ;;;      
;   ;        ;   ;   ;  ;;   ;   ;     
;   ;       ;     ; ;    ;  ;    ;     
;   ;       ;     ; ;    ;  ;;;;;;     
;   ;       ;     ; ;    ;  ;          
;    ;    ;  ;   ;   ;  ;;   ;         
;     ;;;;    ;;;     ;; ;    ;;;;     
;                                      
;                                      
;                                      
*/
/*
+-----------------------+
| CD                    |
+-----------------------+
| String title          |
| int tracks            |
+-----------------------+
| boolean same(CD that) |
+-----------------------+

*/


// to represent a CD
class CD {
  String title;
  int tracks;

  CD(String title, int tracks) {
    this.title = title;
    this.tracks = tracks;
  }   

  boolean same(CD that){
    return this.title.equals(that.title)
      && this.tracks == that.tracks;
  }
}

/*
           +-------+            
           | ALoCD |            
           +-------+            
           +-------+            
              / \               
              ---               
               |                
      -----------------         
      |               |         
  +--------+    +------------+  
  | MTLoCD |    | ConsLoCD   |  
  +--------+    +------------+  
  +--------+    | CD first   |  
                | ALoCd rest |  
                +------------+  
                                
*/                                

// to represent a list of CDs
abstract class ALoCD {

  // determine whether this list of CDs is the same as the given one
  abstract boolean same(ALoCD that);

  // determine whether this list is the same as given MTLoCD list
  abstract boolean sameMTLoCD(MTLoCD other);
  
  // determine whether this list is the same as given ConsLoCD list 
  abstract boolean sameConsLoCD(ConsLoCD other);

  // sort this list of CDs in ascending order of the number of tracks
  abstract ALoCD sort();

  // insert the given CD into this sorted list
  abstract ALoCD insert(CD cd);

  // produce a list of all long CDs (> t10 tracks) in this list
  abstract ALoCD longCDs();
}

// to represent an empty llist of CDs
class MTLoCD extends ALoCD {

  MTLoCD() {}

  // determine whether this list of CDs is the same as the given one
  boolean same(ALoCD that){
    return that.sameMTLoCD(this);
  }

  // determine whether this list is the same as given MTLoCD list
  boolean sameMTLoCD(MTLoCD other){
    return true;
  }
  
  // determine whether this list is the same as given ConsLoCD list
  boolean sameConsLoCD(ConsLoCD other){
    return false;
  }

  ALoCD sort(){
    return this;
  }

  ALoCD insert(CD cd){
    return new ConsLoCD(cd, this);
  }

  ALoCD longCDs(){
    return this; }

}

// to represnet a non-empty list of CDs
class ConsLoCD extends ALoCD {
  CD first;
  ALoCD rest;

  ConsLoCD(CD first, ALoCD rest) {
    this.first = first;
    this.rest = rest;
  }

  // determine whether this list of CDs is the same as the given one
  boolean same(ALoCD that){
    return that.sameConsLoCD(this);
  }

  // determine whether this list is the same as given MTLoCD list
  boolean sameMTLoCD(MTLoCD other){
    return false;
  }

  // determine whether this list is the same as given ConsLoCD list
  boolean sameConsLoCD(ConsLoCD other){
    return this.first.same(other.first) && this.rest.same(other.rest);
  }

  ALoCD sort(){
    return this.rest.sort().insert(this.first);
  }

  ALoCD insert(CD cd){
    if (cd.tracks < this.first.tracks)
      return new ConsLoCD(cd, this);
    else
      return new ConsLoCD(this.first, this.rest.insert(cd));
  }

  ALoCD longCDs(){
    if (this.first.tracks > 10)
      return new ConsLoCD(this.first, this.rest.longCDs());
    else 
      return this.rest.longCDs();}

}


class Examples {
  Examples(){}
 
  CD help = new CD("Help", 12);
  CD pearl = new CD("Pearl", 8);
  CD tool = new CD("Tool", 13);
  CD boston = new CD("Boston", 10);

  ALoCD mtcd = new MTLoCD();
  ALoCD list1 = new ConsLoCD(this.help, this.mtcd);
  ALoCD list2 = new ConsLoCD(this.pearl,this.list1);
  ALoCD list4 = new ConsLoCD(this.tool, new ConsLoCD(this.boston, this.list2));

  // test the method totalTracks
  boolean testLongCD(){
    return this.mtcd.longCDs().same(this.mtcd)
        && this.list1.longCDs().same(this.list1)
        && this.list2.longCDs().same(this.list1)
        && this.list4.longCDs().same(new ConsLoCD(this.tool, 
                                                  new ConsLoCD(this.help, 
                                                               this.mtcd)));
  }

  // test the method insert
boolean testInsert(){
  return this.mtcd.insert(this.tool).same(new ConsLoCD(this.tool, this.mtcd))
      && this.list1.insert(this.pearl).same(new ConsLoCD(this.pearl, this.list1))
      && this.list1.insert(this.tool).same(new ConsLoCD(this.help, 
                                             new ConsLoCD(this.tool, this.mtcd)))
      && new ConsLoCD(this.pearl, 
           new ConsLoCD(this.boston, 
             new ConsLoCD(this.tool, this.mtcd))).insert(this.help).same
        (new ConsLoCD(this.pearl, 
           new ConsLoCD(this.boston, 
             new ConsLoCD(this.help, 
               new ConsLoCD(this.tool,this.mtcd)))));
  }

  boolean testSort(){
    return this.mtcd.sort().same(new MTLoCD())
      && this.list1.sort().same(this.list1)
      && this.list4.sort().same(new ConsLoCD(this.pearl, 
                                  new ConsLoCD(this.boston, 
                                    new ConsLoCD(this.help, 
                                      new ConsLoCD(this.tool, 
                                        this.mtcd)))));
  }

  boolean testAll(){
    return this.testLongCD()
        && this.testInsert()
        && this.testSort();
  }
}





