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

Lecture 16: A Road Trip
*/

/* 
Goals:

 - learn to use classes that represent functional behavior

Introduction:
We came back from a long car trip. We recorded the miles traveled each 
time we refueled, together with the price of gas and the number of
gallons of gas we bought. We would like to look back an analyze our
expenses, gas mileage, distances.

We first design the class to represent the information about each 
segment of the trip, and a list of segments to represent the whole
trip information. The class diagrams are shown here, the code is at 
the end:

             +------+                
             | ALoS |<--------------+
             +------+               |
             +------+               |
               / \                  |
               ---                  |
                |                   |
      ------------------            |
      |                |            |
  +-------+    +---------------+    |
  | MTLoS |    | ConsLoS       |    |
  +-------+    +---------------+    |
  +-------+  +-| Segment first |    |
             | | ALoS rest     |----+
             | +---------------+ 
             |
             v
      +-------------+
      | Segment     |
      +-------------+
      | String from |
      | String to   |
      | int miles   |
      | int gallons |
      | int price   |
      +-------------+


We want to produce a list of cities from which we started each segment
of the trip, a list of information about the miles per gallon consumption
for each segment, and the list of the costs of the trip segments. We plan 
to record this infromation as instances of the classes String, MPG, and 
Cost. The later two are defined as follows:

+-------------+    +-------------+
| MPG         |    | Cost        |
+-------------+    +-------------+
| String from |    | String from |
| String to   |    | String to   |
| int mpg     |    | int cost    |
+-------------+    +-------------+

We make examples of segments, trips, and the desired results:

  Segment b2nyc  = new Segment("Boston", "NYC", 200, 10, 210);
  Segment nyc2ph = new Segment("NYC", "Philly", 180, 9, 220);
  Segment ph2dc  = new Segment("Philly", "DC", 120, 12, 200);
  Segment dc2nor = new Segment("DC", "Norfolk", 150, 10, 200);

  ALoObj mtlist = new MTLoObj();

  ALoObj mytrip = new ConsLoObj(b2nyc, 
                    new ConsLoObj(nyc2ph, 
                      new ConsLoObj(ph2dc, 
                        new ConsLoObj(dc2nor, 
                          mtlist))));

  MPG b2nycmpg  = new MPG("Boston", "NYC", 200/10);
  MPG nyc2phmpg = new MPG("NYC", "Philly", 180/9);
  MPG ph2dcmpg  = new MPG("Philly", "DC", 120/12);
  MPG dc2normpg = new MPG("DC", "Norfolk", 150/10);

  Cost b2nycCost  = new Cost("Boston", "NYC", 10 * 210);
  Cost nyc2phCost = new Cost("NYC", "Philly", 9 * 220);
  Cost ph2dcCost  = new Cost("Philly", "DC", 12 * 200);
  Cost dc2norCost = new Cost("DC", "Norfolk", 10 * 200);

Our plan is to design three methods in the classes that represent
a list of segments as follows:

makeCities: ALoS -> ALoStrings
makeMPGs:   ALoS -> ALoMPGs
makeCosts:  ALoS -> ALoCosts

We already know that it is not necessary to design a separate list
for each type of objects - we can instead build in each case a list
of Object-s.  

Next, we need to define the methods in the class segment that 
produce from the given segment the desired new object: a String,
an MPG object, or a Cost object:

Exercise: Follow the design recipe to design these methods.
(The results are included in the code below.) Here we show just
the purpose statements and the headers for these methods:

  // produce the city of origin for this segment of the trip
  String makeCity(){...}

  // produce the record of gas mileage for this segment of the trip
  MPG makeMPG(){...}

  // produce the record of the cost of this segment of the trip
  Cost makeCost(){...}

as well as some examples:

  b2nyc.makeCity() --> "Boston"
  nyc2ph.makeCity() --> "NYC"

  b2nyc.makeMPG() --> b2nycmpg
  ph2dc.makeMPG() --> ph2dcmpg

  b2nyc.makeCost() --> b2nycCost
  ph2dc.makeCost() --> ph2dcCost

We can now design the methods 

makeCities: ALoS -> ALoStrings
makeMPGs:   ALoS -> ALoMPGs
makeCosts:  ALoS -> ALoCosts

Each of the methods just consumes this list of segments and produces
the appropriate list of objects. Let us start with the methods for 
class ALoS and MTLoS. 

In the class ALoS:

  // produce a list of origins for all segments of this trip
  abstract ALoObj makeCities();

  // produce a list of MPG records for all segments of this trip
  abstract ALoObj makeMPGs();

  // produce a list of cost records for all segments of this trip
  abstract ALoObj makeCosts();

In the class MTLoS:

  // produce a list of origins for all segments of this trip
  ALoObj makeCities(){ return new MTLoObj(); }

  // produce a list of MPG records for all segments of this trip
  ALoObj makeMPGs(){ return new MTLoObj(); }

  // produce a list of cost records for all segments of this trip
  ALoObj makeCosts(){ return new MTLoObj(); }

The template for the ConsLoObj cases is next:

   ... this.first ...
   ... this.first.makeCity() ...
   ... this.first.makeMPG() ...
   ... this.first.makeCost() ...
   ... this.rest ...
   ... this.rest.makeCities() ...
   ... this.rest.makeMPGs() ...
   ... this.rest.makeCosts() ...

The examples are: 

  boolean testMakeCities1 = mtlist.makeCities().same(mtloobj);
  boolean testMakeCities2 = mytrip.makeCities().same(
                               new ConsLoObj("Boston", 
                                 new ConsLoObj("NYC", 
                                   new ConsLoObj("Philly", 
                                     new ConsLoObj("DC", 
                                       mtloobj)))));

  boolean testMakeMPGs1 = mtlist.makeMPGs().same(mtloobj);
  boolean testMakeMPGs2 = mytrip.makeMPGs().same(
                               new ConsLoObj(b2nycmpg, 
                                 new ConsLoObj(nyc2phmpg, 
                                   new ConsLoObj(ph2dcmpg, 
                                     new ConsLoObj(dc2normpg, 
                                       mtloobj)))));

  boolean testMakeCosts1 = mtlist.makeCosts().same(mtloobj);
  boolean testMakeCosts2 = mytrip.makeCosts().same(
                               new ConsLoObj(b2nycCost, 
                                 new ConsLoObj(nyc2phCost, 
                                   new ConsLoObj(ph2dcCost, 
                                     new ConsLoObj(dc2norCost, 
                                       mtloobj)))));

However, we cannot run the first set of tests, because the 'same'
method for the classes that represent a list of Object-s expects
the list elements to implement the ISame interface. We are not
allowed to extend the Java String class. We will omit those tests
for now.

The methods for the class ConsLoS follow easily:

  // produce a list of origins for all segments of this trip
  ALoObj makeCities(){ 
    return new ConsLoObj(this.first.makeCity(), 
                         this.rest.makeCities()); 
  }

  // produce a list of MPG records for all segments of this trip
  ALoObj makeMPGs(){ 
    return new ConsLoObj(this.first.makeMPG(), 
                         this.rest.makeMPGs()); 
  }

  // produce a list of cost records for all segments of this trip
  ALoObj makeCosts(){ 
    return new ConsLoObj(this.first.makeCost(), 
                         this.rest.makeCosts()); 
  }

Again, we see that the methods makeCities, makeMPGs and MakeCosts
differ only in the way in which the list element 'first' is 
used to produce the element of the new list. Furthermore, 
recalling the lecture in which we built the 'ormap', we see
that this, too, is one of our familiar Scheme loops - the map.

Let us recall the purpose, contract and header for 'map':

;; map: (X -> Y) (listof X) -> (Listof Y)
;; to construct a list by applying f to each item on alox
;; that is, (map f (list x-1 ... x-n)) = (list (f x-1) ... (f x-n))
(define (map f alox) ... )

Our methods makeCity, makeMPG, and makeCost in the class Segment
played the role of function f above. We want to abstract over them
to define just one method makeTripRecord. But, again, we can only 
pass an instance of a class as an argument to a method. We define 
an interface:

interface IObj2Obj{
  // produce an object from the given object
  Object makeObject(Object obj);
}

We can now supply an argument of the type IObj2Obj to the method
makeTripRecord. The only significant chane will be in the class
ConsLoS:

  // produce a list of records for all segments of this trip
  ALoObj makeTripRecord(IObj2Obj transform){ 
    return new ConsLoObj(transform.makeObject(this.first), 
                         this.rest.makeTripRecord(transform)); 
  }

To complete the abstraction, we need to define classes that implement
the IObj2Obj interface with the three methods that perform the 
original transformation from Segment to String, MPG, and Cost objects.
(We also add the wrapper class for our resulting String, so we can run
our tests --- see the code.)

// to produce the from city string (wrapper) from the given segment
class MakeCity implements IObj2Obj{
  Object makeObject(Object obj){
    return new StringWrapper(((Segment)obj).from);
  }
}

// to produce the MPG record from the given segment
class MakeMPG implements IObj2Obj{
  Object makeObject(Object obj){
    return new MPG(((Segment)obj).from, 
                   ((Segment)obj).to,
                   ((Segment)obj).miles / ((Segment)obj).gallons);
  }
}

// to produce the Cost record from the given segment
class MakeCost implements IObj2Obj{
  Object makeObject(Object obj){
    return new Cost(((Segment)obj).from, 
                   ((Segment)obj).to,
                   ((Segment)obj).gallons * ((Segment)obj).price);
  }
}

Finally, our tests confirm that the original examples work equally
well when we use the new abstracted method makeTripRecord. The full
tests are shown in the code, here we just show the method invocation
for three test cases:

  mytrip.makeTripRecord(new MakeCity())
  mytrip.makeTripRecord(new MakeMPG())
  mytrip.makeTripRecord(new MakeCost())

Exercise:

Make a concise collection of the Scheme loops 'ormap', 'andmap', 'filter', 
and 'map' for the class that represent lists of Objec-s, that produce 
either a boolean value (for 'ormap' and 'andmap') or a list of objects 
(for 'filter' and 'map'), and use 'generic' interfaces IObj2Bool or 
IObj2Obj for the function object arguments. Make several examples of use, 
for different source and target lists.
*/

/*
;                                           
;                                           
;                                           
;   ;    ;;;;                               
;   ;   ;    ;                              
;   ;   ;                                   
;   ;   ;      ;;;    ; ;;  ;;     ;;;      
;   ;    ;;   ;   ;   ;;  ;;  ;   ;   ;     
;   ;      ;      ;   ;   ;   ;  ;    ;     
;   ;       ;  ;;;;   ;   ;   ;  ;;;;;;     
;   ;       ; ;   ;   ;   ;   ;  ;          
;   ;   ;   ; ;   ;   ;   ;   ;   ;         
;   ;    ;;;   ;;;;;  ;   ;   ;    ;;;;     
;                                           
;                                           
;                                           
*/
interface ISame{
  // determine whether this object is the same as the given object
  boolean same(Object obj);
}

/*
;                                                            
;                                                            
;                                                            
;    ;;;;                                                    
;   ;    ;                                                   
;   ;                                                ;       
;   ;       ;;;    ;; ;   ; ;;  ;;     ;;;   ; ;;   ;;;;     
;    ;;    ;   ;  ;  ;;   ;;  ;;  ;   ;   ;  ;;  ;   ;       
;      ;  ;    ; ;    ;   ;   ;   ;  ;    ;  ;   ;   ;       
;       ; ;;;;;; ;    ;   ;   ;   ;  ;;;;;;  ;   ;   ;       
;       ; ;      ;    ;   ;   ;   ;  ;       ;   ;   ;       
;   ;   ;  ;      ;  ;;   ;   ;   ;   ;      ;   ;   ;       
;    ;;;    ;;;;   ;; ;   ;   ;   ;    ;;;;  ;   ;    ;;     
;                     ;                                      
;                ;    ;                                      
;                 ;;;;                                       
*/
// to represent a segment of a road trip
class Segment implements ISame{
  String from;
  String to;
  int miles;
  int gallons;
  int price;

  Segment(String from, String to, int miles, int gallons, int price) {
    this.from = from;
    this.to = to;
    this.miles = miles;
    this.gallons = gallons;
    this.price = price;
  }

  // determine whether this segment is the same as the given object
  boolean same(Object obj){
    return this.from.equals(((Segment)obj).from)
        && this.to.equals(((Segment)obj).to)
        && this.miles == ((Segment)obj).miles
        && this.gallons == ((Segment)obj).gallons
        && this.price == ((Segment)obj).price;
  }

  // produce the city of origin for this segment of the trip
  String makeCity(){
    return this.from;
  }

  // produce the record of gas mileage for this segment of the trip
  MPG makeMPG(){
    return new MPG(this.from, this.to, this.miles/this.gallons);
  }

  // produce the record of the cost of this segment of the trip
  Cost makeCost(){
    return new Cost(this.from, this.to, this.gallons * this.price);
  }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.from ...
    ... this.to ...
    ... this.miles ...
    ... this.gallons ...
    ... this.price ...
  }
*/
}

/*
;                                     
;                                     
;                                     
;      ;     ;               ;;;;     
;      ;     ;              ;    ;    
;     ; ;    ;              ;         
;     ; ;    ;       ;;;    ;         
;    ;   ;   ;      ;   ;    ;;       
;    ;   ;   ;     ;     ;     ;      
;   ;;;;;;;  ;     ;     ;      ;     
;   ;     ;  ;     ;     ;      ;     
;   ;     ;  ;      ;   ;   ;   ;     
;  ;       ; ;;;;;;  ;;;     ;;;      
;                                     
;                                     
;                                     
*/
// to represent a list of segments of a road trip
abstract class ALoS {

  // produce a list of origins for all segments of this trip
  abstract ALoObj makeCities();

  // produce a list of MPG records for all segments of this trip
  abstract ALoObj makeMPGs();

  // produce a list of cost records for all segments of this trip
  abstract ALoObj makeCosts();

  // produce a list of records for all segments of this trip
 abstract ALoObj makeTripRecord(IObj2Obj transform);

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  // purpose statement 
  abstract ??? mmm();
*/
}

/*
;                                               
;                                               
;                                               
;   ;       ; ;;;;;;;  ;               ;;;;     
;   ;;     ;;    ;     ;              ;    ;    
;   ;;     ;;    ;     ;              ;         
;   ; ;   ; ;    ;     ;       ;;;    ;         
;   ; ;   ; ;    ;     ;      ;   ;    ;;       
;   ;  ; ;  ;    ;     ;     ;     ;     ;      
;   ;  ; ;  ;    ;     ;     ;     ;      ;     
;   ;   ;   ;    ;     ;     ;     ;      ;     
;   ;   ;   ;    ;     ;      ;   ;   ;   ;     
;   ;       ;    ;     ;;;;;;  ;;;     ;;;      
;                                               
;                                               
;                                               
*/
// to represent an empty list of segments of a road trip
class MTLoS extends ALoS {

  MTLoS() {
  }

  // produce a list of origins for all segments of this trip
  ALoObj makeCities(){ return new MTLoObj(); }

  // produce a list of MPG records for all segments of this trip
  ALoObj makeMPGs(){ return new MTLoObj(); }

  // produce a list of cost records for all segments of this trip
  ALoObj makeCosts(){ return new MTLoObj(); }

  // produce a list of records for all segments of this trip
  ALoObj makeTripRecord(IObj2Obj transform){ return new MTLoObj(); }
/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
  }
*/
}

/*
;                                                            
;                                                            
;                                                            
;     ;;;;                          ;               ;;;;     
;    ;    ;                         ;              ;    ;    
;   ;                               ;              ;         
;   ;         ;;;    ; ;;     ;;;   ;       ;;;    ;         
;   ;        ;   ;   ;;  ;   ;      ;      ;   ;    ;;       
;   ;       ;     ;  ;   ;   ;;     ;     ;     ;     ;      
;   ;       ;     ;  ;   ;    ;;    ;     ;     ;      ;     
;   ;       ;     ;  ;   ;      ;   ;     ;     ;      ;     
;    ;    ;  ;   ;   ;   ;      ;   ;      ;   ;   ;   ;     
;     ;;;;    ;;;    ;   ;   ;;;    ;;;;;;  ;;;     ;;;      
;                                                            
;                                                            
;                                                            
*/
// to represent a nonempty list of segments of a road trip
class ConsLoS extends ALoS {
  Segment first;
  ALoS rest;

  ConsLoS(Segment first, ALoS rest) {
    this.first = first;
    this.rest = rest;
  }

  // produce a list of origins for all segments of this trip
  ALoObj makeCities(){ 
    return new ConsLoObj(this.first.makeCity(), 
                         this.rest.makeCities()); 
  }

  // produce a list of MPG records for all segments of this trip
  ALoObj makeMPGs(){ 
    return new ConsLoObj(this.first.makeMPG(), 
                         this.rest.makeMPGs()); 
  }

  // produce a list of cost records for all segments of this trip
  ALoObj makeCosts(){ 
    return new ConsLoObj(this.first.makeCost(), 
                         this.rest.makeCosts()); 
  }

  // produce a list of records for all segments of this trip
  ALoObj makeTripRecord(IObj2Obj transform){ 
    return new ConsLoObj(transform.makeObject(this.first), 
                         this.rest.makeTripRecord(transform)); 
  }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.first ...
    ... this.rest.mmm() ...
  }
*/
}

/*
;                                 
;                                 
;                                 
;   ;       ;  ;;;;;    ;;;;      
;   ;;     ;;  ;    ;  ;    ;     
;   ;;     ;;  ;    ; ;           
;   ; ;   ; ;  ;    ; ;           
;   ; ;   ; ;  ;    ; ;           
;   ;  ; ;  ;  ;;;;;  ;           
;   ;  ; ;  ;  ;      ;     ;     
;   ;   ;   ;  ;      ;     ;     
;   ;   ;   ;  ;       ;    ;     
;   ;       ;  ;        ;;;;;     
;                                 
;                                 
;                                 
*/
// to represent the mile per gallons consumption per segment
class MPG implements ISame{
  String from;
  String to;
  int mpg;

  MPG(String from, String to, int mpg) {
    this.from = from;
    this.to = to;
    this.mpg = mpg;
  }

  // determine whether this MPG object is the same as the given object
  boolean same(Object obj){
    return this.from.equals(((MPG)obj).from)
        && this.to.equals(((MPG)obj).to)
        && this.mpg == ((MPG)obj).mpg;
  }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.from ...
    ... this.to ...
    ... this.mpg ...
  }
*/
}

/*
;                                   
;                                   
;                                   
;     ;;;;                          
;    ;    ;                         
;   ;                       ;       
;   ;         ;;;     ;;;  ;;;;     
;   ;        ;   ;   ;      ;       
;   ;       ;     ;  ;;     ;       
;   ;       ;     ;   ;;    ;       
;   ;       ;     ;     ;   ;       
;    ;    ;  ;   ;      ;   ;       
;     ;;;;    ;;;    ;;;     ;;     
;                                   
;                                   
;                                   
*/
// to represent the cost of travel for one segment of a road trip
class Cost  implements ISame{
  String from;
  String to;
  int cost;

  Cost(String from, String to, int cost) {
    this.from = from;
    this.to = to;
    this.cost = cost;
  }

  // determine whether this MPG object is the same as the given object
  boolean same(Object obj){
    return this.from.equals(((Cost)obj).from)
        && this.to.equals(((Cost)obj).to)
        && this.cost == ((Cost)obj).cost;
  }

  // produce a list of origins for all segments of this trip
  ALoObj makeCities(){ return new MTLoObj(); }

  // produce a list of MPG records for all segments of this trip
  ALoObj makeMPGs(){ return new MTLoObj(); }

  // produce a list of cost records for all segments of this trip
  ALoObj makeCosts(){ return new MTLoObj(); }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.from ...
    ... this.to ...
    ... this.cost ...
  }
*/
}
/*
;                                                    
;                                                    
;                                                    
;      ;     ;                ;;;;    ;       ;      
;      ;     ;               ;    ;   ;              
;     ; ;    ;              ;      ;  ;              
;     ; ;    ;       ;;;    ;      ;  ; ;;    ;      
;    ;   ;   ;      ;   ;   ;      ;  ;;  ;   ;      
;    ;   ;   ;     ;     ;  ;      ;  ;    ;  ;      
;   ;;;;;;;  ;     ;     ;  ;      ;  ;    ;  ;      
;   ;     ;  ;     ;     ;  ;      ;  ;    ;  ;      
;   ;     ;  ;      ;   ;    ;    ;   ;;  ;   ;      
;  ;       ; ;;;;;;  ;;;      ;;;;    ; ;;    ;      
;                                             ;      
;                                             ;      
;                                           ;;       
*/
/*
            +--------+                
            | ALoObj |<--------------+
            +--------+               |
            +--------+               |
                / \                  |
                ---                  |
                 |                   |
       ------------------            |
       |                |            |
  +---------+    +--------------+    |
  | MTLoObj |    | ConsLoObj    |    |
  +---------+    +--------------+    |
  +---------+    | Object first |    |
                 | ALoObj rest  |-+  |
                 +--------------+ |  |
                                  |  |
                                  +--+

*/

// to represent a list of objects
abstract class ALoObj implements ISame{

  // determine whether this list of objects is the same as the given object
  boolean same(Object obj){
    return this.sameALoObj((ALoObj) obj);
  }

  // is this an empty list of objects
  abstract boolean isEmpty();

  // determine whether this list of objects is the same as
  // the given list of objects
  abstract boolean sameALoObj(ALoObj alist);


/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  // purpose statement 
  abstract ??? mmm();
*/
}

/*
;                                                              
;                                                              
;                                                              
;   ;       ; ;;;;;;;  ;                ;;;;    ;       ;      
;   ;;     ;;    ;     ;               ;    ;   ;              
;   ;;     ;;    ;     ;              ;      ;  ;              
;   ; ;   ; ;    ;     ;       ;;;    ;      ;  ; ;;    ;      
;   ; ;   ; ;    ;     ;      ;   ;   ;      ;  ;;  ;   ;      
;   ;  ; ;  ;    ;     ;     ;     ;  ;      ;  ;    ;  ;      
;   ;  ; ;  ;    ;     ;     ;     ;  ;      ;  ;    ;  ;      
;   ;   ;   ;    ;     ;     ;     ;  ;      ;  ;    ;  ;      
;   ;   ;   ;    ;     ;      ;   ;    ;    ;   ;;  ;   ;      
;   ;       ;    ;     ;;;;;;  ;;;      ;;;;    ; ;;    ;      
;                                                       ;      
;                                                       ;      
;                                                     ;;       
*/
// to represent an empty list of objects
class MTLoObj extends ALoObj {

  MTLoObj() {
  }

  boolean isEmpty(){
    return true;
  }

  boolean sameALoObj(ALoObj alist){
    return alist.isEmpty();
  }


/* TEMPLATE ** Edit as needed.
  ??? mmm() {
  }
*/
}


/*
;                                                                           
;                                                                           
;                                                                           
;     ;;;;                          ;                ;;;;    ;       ;      
;    ;    ;                         ;               ;    ;   ;              
;   ;                               ;              ;      ;  ;              
;   ;         ;;;    ; ;;     ;;;   ;       ;;;    ;      ;  ; ;;    ;      
;   ;        ;   ;   ;;  ;   ;      ;      ;   ;   ;      ;  ;;  ;   ;      
;   ;       ;     ;  ;   ;   ;;     ;     ;     ;  ;      ;  ;    ;  ;      
;   ;       ;     ;  ;   ;    ;;    ;     ;     ;  ;      ;  ;    ;  ;      
;   ;       ;     ;  ;   ;      ;   ;     ;     ;  ;      ;  ;    ;  ;      
;    ;    ;  ;   ;   ;   ;      ;   ;      ;   ;    ;    ;   ;;  ;   ;      
;     ;;;;    ;;;    ;   ;   ;;;    ;;;;;;  ;;;      ;;;;    ; ;;    ;      
;                                                                    ;      
;                                                                    ;      
;                                                                  ;;       
*/
// to represent a nonempty list of objects
class ConsLoObj extends ALoObj {
  Object first;
  ALoObj rest;

  ConsLoObj(Object first, ALoObj rest) {
    this.first = first;
    this.rest = rest;
  }

  boolean isEmpty(){
    return false;
  }

  boolean sameALoObj(ALoObj alist){
    if (alist.isEmpty())
      return false;
    else 
      return ((ISame)this.first).same(((ConsLoObj)alist).first)
          && this.rest.sameALoObj(((ConsLoObj)alist).rest);
  }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.first ...
    ... this.rest.mmm() ...
  }
*/
}

/*
;                                                              
;                                                              
;                                                              
;   ;     ;;;;    ;       ;    ;;;;     ;;;;    ;       ;      
;   ;    ;    ;   ;           ;    ;   ;    ;   ;              
;   ;   ;      ;  ;                ;  ;      ;  ;              
;   ;   ;      ;  ; ;;    ;        ;  ;      ;  ; ;;    ;      
;   ;   ;      ;  ;;  ;   ;       ;   ;      ;  ;;  ;   ;      
;   ;   ;      ;  ;    ;  ;      ;    ;      ;  ;    ;  ;      
;   ;   ;      ;  ;    ;  ;     ;     ;      ;  ;    ;  ;      
;   ;   ;      ;  ;    ;  ;    ;      ;      ;  ;    ;  ;      
;   ;    ;    ;   ;;  ;   ;   ;        ;    ;   ;;  ;   ;      
;   ;     ;;;;    ; ;;    ;   ;;;;;;    ;;;;    ; ;;    ;      
;                         ;                             ;      
;                         ;                             ;      
;                       ;;                            ;;       
*/
interface IObj2Obj{
  // produce an object from the given object
  Object makeObject(Object obj);
}

/*
;                                                                
;                                                                
;                                                                
;   ;       ;         ;                ;;;;   ;                  
;   ;;     ;;         ;               ;    ;                     
;   ;;     ;;         ;              ;            ;              
;   ; ;   ; ;  ;;;    ;   ;    ;;;   ;        ;  ;;;; ;     ;    
;   ; ;   ; ; ;   ;   ;  ;    ;   ;  ;        ;   ;   ;     ;    
;   ;  ; ;  ;     ;   ; ;    ;    ;  ;        ;   ;    ;   ;     
;   ;  ; ;  ;  ;;;;   ;;;    ;;;;;;  ;        ;   ;    ;   ;     
;   ;   ;   ; ;   ;   ;  ;   ;       ;        ;   ;     ; ;      
;   ;   ;   ; ;   ;   ;   ;   ;       ;    ;  ;   ;     ; ;      
;   ;       ;  ;;;;;  ;    ;   ;;;;    ;;;;   ;    ;;    ;       
;                                                        ;       
;                                                        ;       
;                                                       ;        
*/
// to produce the from city string (wrapper) from the given segment
class MakeCity implements IObj2Obj{
  Object makeObject(Object obj){
    return new StringWrapper(((Segment)obj).from);
  }
}

/*
;                                                                  
;                                                                  
;                                                                  
;   ;       ;         ;              ;       ;  ;;;;;    ;;;;      
;   ;;     ;;         ;              ;;     ;;  ;    ;  ;    ;     
;   ;;     ;;         ;              ;;     ;;  ;    ; ;           
;   ; ;   ; ;  ;;;    ;   ;    ;;;   ; ;   ; ;  ;    ; ;           
;   ; ;   ; ; ;   ;   ;  ;    ;   ;  ; ;   ; ;  ;    ; ;           
;   ;  ; ;  ;     ;   ; ;    ;    ;  ;  ; ;  ;  ;;;;;  ;           
;   ;  ; ;  ;  ;;;;   ;;;    ;;;;;;  ;  ; ;  ;  ;      ;     ;     
;   ;   ;   ; ;   ;   ;  ;   ;       ;   ;   ;  ;      ;     ;     
;   ;   ;   ; ;   ;   ;   ;   ;      ;   ;   ;  ;       ;    ;     
;   ;       ;  ;;;;;  ;    ;   ;;;;  ;       ;  ;        ;;;;;     
;                                                                  
;                                                                  
;                                                                  
*/
// to produce the MPG record from the given segment
class MakeMPG implements IObj2Obj{
  Object makeObject(Object obj){
    return new MPG(((Segment)obj).from, 
                   ((Segment)obj).to,
                   ((Segment)obj).miles / ((Segment)obj).gallons);
  }
}

/*
;                                                                    
;                                                                    
;                                                                    
;   ;       ;         ;                ;;;;                          
;   ;;     ;;         ;               ;    ;                         
;   ;;     ;;         ;              ;                       ;       
;   ; ;   ; ;  ;;;    ;   ;    ;;;   ;         ;;;     ;;;  ;;;;     
;   ; ;   ; ; ;   ;   ;  ;    ;   ;  ;        ;   ;   ;      ;       
;   ;  ; ;  ;     ;   ; ;    ;    ;  ;       ;     ;  ;;     ;       
;   ;  ; ;  ;  ;;;;   ;;;    ;;;;;;  ;       ;     ;   ;;    ;       
;   ;   ;   ; ;   ;   ;  ;   ;       ;       ;     ;     ;   ;       
;   ;   ;   ; ;   ;   ;   ;   ;       ;    ;  ;   ;      ;   ;       
;   ;       ;  ;;;;;  ;    ;   ;;;;    ;;;;    ;;;    ;;;     ;;     
;                                                                    
;                                                                    
;                                                                    
*/
// to produce the Cost record from the given segment
class MakeCost implements IObj2Obj{
  Object makeObject(Object obj){
    return new Cost(((Segment)obj).from, 
                   ((Segment)obj).to,
                   ((Segment)obj).gallons * ((Segment)obj).price);
  }
}

/*
;                                                                                              
;                                                                                              
;                                                                                              
;    ;;;;            ;                  ;    ;    ;                                            
;   ;    ;                              ;    ;    ;                                            
;   ;      ;                            ;   ; ;   ;                                            
;   ;     ;;;;  ; ;  ;   ; ;;     ;; ;   ;  ; ;  ;  ; ;  ;;;    ; ;;    ; ;;     ;;;   ; ;     
;    ;;    ;    ;;   ;   ;;  ;   ;  ;;   ;  ; ;  ;  ;;  ;   ;   ;;  ;   ;;  ;   ;   ;  ;;      
;      ;   ;    ;    ;   ;   ;  ;    ;   ; ;   ; ;  ;       ;   ;    ;  ;    ; ;    ;  ;       
;       ;  ;    ;    ;   ;   ;  ;    ;   ; ;   ; ;  ;    ;;;;   ;    ;  ;    ; ;;;;;;  ;       
;       ;  ;    ;    ;   ;   ;  ;    ;   ; ;   ; ;  ;   ;   ;   ;    ;  ;    ; ;       ;       
;   ;   ;  ;    ;    ;   ;   ;   ;  ;;    ;     ;   ;   ;   ;   ;;  ;   ;;  ;   ;      ;       
;    ;;;    ;;  ;    ;   ;   ;    ;; ;    ;     ;   ;    ;;;;;  ; ;;    ; ;;     ;;;;  ;       
;                                    ;                          ;       ;                      
;                               ;    ;                          ;       ;                      
;                                ;;;;                           ;       ;                      
*/
// to allow String object comparison with 'same' method
class StringWrapper implements ISame{
  String s;
 
  StringWrapper(String s){
    this.s = s;
  }

  boolean same(Object obj){
    return s.equals(((StringWrapper)obj).s);
  } 
}
/*
;                                                                  
;                                                                  
;                                                                  
;   ;;;;;;                                    ;                    
;   ;                                         ;                    
;   ;                                         ;                    
;   ;     ;     ;  ;;;    ; ;;  ;;    ; ;;    ;    ;;;    ;;;      
;   ;;;;;  ;   ;  ;   ;   ;;  ;;  ;   ;;  ;   ;   ;   ;  ;         
;   ;       ; ;       ;   ;   ;   ;   ;    ;  ;  ;    ;  ;;        
;   ;        ;     ;;;;   ;   ;   ;   ;    ;  ;  ;;;;;;   ;;       
;   ;       ; ;   ;   ;   ;   ;   ;   ;    ;  ;  ;          ;      
;   ;      ;   ;  ;   ;   ;   ;   ;   ;;  ;   ;   ;         ;      
;   ;;;;;;;     ;  ;;;;;  ;   ;   ;   ; ;;    ;    ;;;;  ;;;       
;                                     ;                            
;                                     ;                            
;                                     ;                            
*/
class Examples{
  Examples() {}

  Segment b2nyc  = new Segment("Boston", "NYC", 200, 10, 210);
  Segment nyc2ph = new Segment("NYC", "Philly", 180, 9, 220);
  Segment ph2dc  = new Segment("Philly", "DC", 120, 12, 200);
  Segment dc2nor = new Segment("DC", "Norfolk", 150, 10, 200);

  ALoS mtlist = new MTLoS();

  ALoS mytrip = new ConsLoS(b2nyc, 
                    new ConsLoS(nyc2ph, 
                      new ConsLoS(ph2dc, 
                        new ConsLoS(dc2nor, 
                          mtlist))));


  // testing the method same in the class segment
  boolean testSameSegment1 = b2nyc.same(ph2dc) == false;
  boolean testSameSegment2 = ph2dc.same(
                             new Segment("Philly", "DC", 120, 12, 200)) == true;

  MPG b2nycmpg  = new MPG("Boston", "NYC", 200/10);
  MPG nyc2phmpg = new MPG("NYC", "Philly", 180/9);
  MPG ph2dcmpg  = new MPG("Philly", "DC", 120/12);
  MPG dc2normpg = new MPG("DC", "Norfolk", 150/10);

  Cost b2nycCost  = new Cost("Boston", "NYC", 10 * 210);
  Cost nyc2phCost = new Cost("NYC", "Philly", 9 * 220);
  Cost ph2dcCost  = new Cost("Philly", "DC", 12 * 200);
  Cost dc2norCost = new Cost("DC", "Norfolk", 10 * 200);

  boolean testMakeCity1 = b2nyc.makeCity().equals("Boston");
  boolean testMakeCity2 = nyc2ph.makeCity().equals("NYC");

  boolean testMakeMPG1 = b2nyc.makeMPG().same(b2nycmpg);
  boolean testMakeMPG2 = ph2dc.makeMPG().same(ph2dcmpg);

  boolean testMakeCost1 = b2nyc.makeCost().same(b2nycCost);
  boolean testMakeCost2 = ph2dc.makeCost().same(ph2dcCost);

  ALoObj mtloobj = new MTLoObj();

/* the following tests cannot be easily performed, because
   we cannot compare two String-s using the 'same' method
   that works for all classes we defined. We could instead
   define a class StringWrapper that contains one String field
   and implements ISame and use that as the target class for
   the makeCity method in the class Segment.

  boolean testMakeCities1 = mtlist.makeCities().same(mtloobj);
  boolean testMakeCities2 = mytrip.makeCities().same(
                               new ConsLoObj("Boston", 
                                 new ConsLoObj("NYC", 
                                   new ConsLoObj("Philly", 
                                     new ConsLoObj("DC", 
                                       mtloobj)))));
*/
  boolean testMakeMPGs1 = mtlist.makeMPGs().same(mtloobj);
  boolean testMakeMPGs2 = mytrip.makeMPGs().same(
                               new ConsLoObj(b2nycmpg, 
                                 new ConsLoObj(nyc2phmpg, 
                                   new ConsLoObj(ph2dcmpg, 
                                     new ConsLoObj(dc2normpg, 
                                       mtloobj)))));

  boolean testMakeCosts1 = mtlist.makeCosts().same(mtloobj);
  boolean testMakeCosts2 = mytrip.makeCosts().same(
                               new ConsLoObj(b2nycCost, 
                                 new ConsLoObj(nyc2phCost, 
                                   new ConsLoObj(ph2dcCost, 
                                     new ConsLoObj(dc2norCost, 
                                       mtloobj)))));

  // test the final abstraction through the method makeTripRecord
  boolean testMakeRecordCities1 = mtlist.makeTripRecord(new MakeCity()).same(mtloobj);
  boolean testMakeRecordCities2 = mytrip.makeTripRecord(new MakeCity()).same(
                               new ConsLoObj(new StringWrapper("Boston"), 
                                 new ConsLoObj(new StringWrapper("NYC"), 
                                   new ConsLoObj(new StringWrapper("Philly"), 
                                     new ConsLoObj(new StringWrapper("DC"), 
                                       mtloobj)))));

  boolean testMakeRecordMPGs1 = mtlist.makeTripRecord(new MakeMPG()).same(mtloobj);

  boolean testMakeRecordMPGs2 = mytrip.makeTripRecord(new MakeMPG()).same(
                               new ConsLoObj(b2nycmpg, 
                                 new ConsLoObj(nyc2phmpg, 
                                   new ConsLoObj(ph2dcmpg, 
                                     new ConsLoObj(dc2normpg, 
                                       mtloobj)))));

  boolean testMakeRecordCosts1 = mtlist.makeTripRecord(new MakeCost()).same(mtloobj);
  boolean testMakeRecordCosts2 = mytrip.makeTripRecord(new MakeCost()).same(
                               new ConsLoObj(b2nycCost, 
                                 new ConsLoObj(nyc2phCost, 
                                   new ConsLoObj(ph2dcCost, 
                                     new ConsLoObj(dc2norCost, 
                                       mtloobj)))));

}