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

Lecture 19: Which Way to the Magic Mountain? ...
*/

/* 
Goals:

 - to design a program where accumulators are necessary
 - to learn to traverse a circular structure


Introduction:

Again, we seek to find out whether there is a path from one station
in our transit tystem to another station. However, we change the 
constraints on our system and the way we represent it.

The system is represented as a list of segments, where each segment
is just a pair fo String-s that represent the origin and destination 
of that route segment.

The origin and destination are stations in the transit system. Each 
station is the origin of exactly one segment.

The last restriction means that we can go somewhere from each station,
and that we can only go in one direction. It is a draconian constraint,
but it is sufficient to illustrate a serious problem we encounter when
searching for a routing from one station to another.

Here is an example of a transit system:

A -> B
B -> C
D -> E
E -> B
C -> E

The data that represent this transit is

  ALoRt transit = new ConsLoRt(new Segment("A", "B"),
                    new ConsLoRt(new Segment("B", "C"),
                    new ConsLoRt(new Segment("D", "E"),
                    new ConsLoRt(new Segment("E", "B"),
                    new ConsLoRt(new Segment("C", "E"),
                    new MTLoRt())))));



We ask whether there is a routing from D to C.

The method (in the classes that represent the transit) consumes the 
'from' and 'to' String-s and produces a boolean value:

  // is there a path in this transit between the given stations
  boolean hasRouting(String orig, String dest)

Let us make some examples:

  boolean testHasRouting1 = transit.hasRouting("D", "C") == true;
  boolean testHasRouting2 = transit.hasRouting("E", "C") == true;

  boolean testHasRouting3 = transit.hasRouting("C", "D") == false;
  boolean testHasRouting4 = transit.hasRouting("C", "C") == true;

  boolean testHasRouting5 = transit.hasRouting("P", "C") == false;
  boolean testHasRouting6 = (new MTLoS()).hasRouting("C", "C") == false;
  boolean testHasRouting7 = (new MTLoS()).hasRouting("E", "C") == false;


The first and second examples show that to find a routing from "D" to "C",
we first find the segment in which "D" is the origin, check whether
the destination is our desired dest. If it is, we found the routing,
otherwise we go on with the search, now starting from the destination
station in the current segment. Of course, this is the 'general' case.
If we are asked (as the fourth example shows) whether there is a path
from a given point to itself, the answer is true, as long as this 
is a station in our transit system. 

We see that in the classes ALoRt and MTLoRt our work is simple - there 
is no routing between any two stations in an empty list of segments, and 
the method produces 'false'.

Some of the examples refer to stations that are not in our transit system.
We assume that a preliminary screening has been done and we seek routing
only between stations that do appear in our transit system.

Further analysis of the examples one and two indicates that the transit
system used to invoke our method does not change as we proceed with 
our search - only the arguments our method consumes keep changing.

We will use the method findNext that produces the station in the
transit system that has the given origin. Here is the header and the 
purpose statement - we leave it up to you to design the method

  // find the next station in this transit system after the given station
  String findNext(String origin){...}

In the case of MTLoS, we return an empty String "". Based on the constraints
of our system the search should succeed before we exhaust all the segments
in our transit, and so this code should not be used. (Later we will learn 
how to notify the user that an unexpected thing happened.)

Here is the template for the method hasRouting:

  ... this.first ...
  ... this.first.origin ...
  ... this.first.destination ...

  ... this.rest ...
  ... this.rest.hasRouting(String some-orig, String some-dest) ...

  ... orig ...
  ... dest ...

Let us think about the body:

  boolean hasRouting(String orig, String dest){
   if (orig.compareTo(seen) == 0)
     return true;             // this follows from the third example
   else                       
     // see if there is a route from the next station after orig
     // that leads to the given dest - in this same transit !!!
     return this.hasRouting(this.findNext(origin), dest);
  }

The comments explain our reasoning. We can now run our tests.
..................... - we are still waiting ......................

What is the problem?

We stopped the execution and see that there is a problem with the 
second test case. Let us trace what is happening. Maybe we should
have a picture of our transit system:

                       ------------
                      /            \
                     /              v
   A ----> B ----> C        D ----> E
           ^                      /
            \                    /
             --------------------

We are looking to get from C to D.
The sequence of method invocations looks like this:

transit.hasRouting("C", "D")    
                           --- find next after C and continue:
transit.hasRouting("E", "D")    
                           --- find next after E and continue:
transit.hasRouting("B", "D")    
                           --- find next after B and continue:
transit.hasRouting("C", "D")    
                           --- find next after C and continue:

... but we are back where we started from - and will keep running
in circles forever. We lost the knowledge of where we have been
and that information is crucial to solving the problem.

Following the design recipe for methods (or functions) with 
accumulators we decide on the purpose for the accumulator:
                              ---------------------------

"to record all stations we have already traveled through"

The initial value for the accumulator is an empty list.
    ---------------------------------

We use the accumulator to see whether we are again at the station 
   -------------------
we have already traveled through and thus failed to find the routing.

We update the accumulator by adding the origin to the list of 
   ----------------------
stations we have seen as we move to the next station.

Because we know that the transit system we deal with remains
unchanged as we search for the routing, we can leave the methods
in the abstract class and in the class MTLoS unchanged. In the 
class ConsLoS we add a method:

  // is there a path in this transit between the given stations
  // that does not go through the seen stations
  boolean hasNoLoopRouting(String orig, String dest, ALoS seen){
   if (orig.compareTo(dest) == 0)
     return true;             // this follows from the third example
   else if (seen.contains(orig))
     return false;
   else                       
     // see if there is a route from the next station after orig
     // that leads to the given dest - in this same transit !!!
     return this.hasNoLoopRouting(this.findNext(orig), dest,
                                  new ConsLoS(orig, seen));
  }

We added a clause in the middle that checks whether we are repeating
our search (using the accumulator) and construct a new list of stations
with the origin added each time we recur (updating the accumulator).

We conclude with the new vesrsion of our method hasRouting in the class
ConsLoS:

  boolean hasRouting(String orig, String dest){
    return this.hasNoLoopRouting(orig, dest, new MTLoS());
  }

This is where the accumulator is initialized and the method that uses
the accumulator is invoked for the first time.

We can now run our program, including the original examples.
------------------------------------------------------------------------

*/

/*
;                                      
;                                      
;                                      
;     ;;;;               ;             
;    ;    ;              ;             
;   ;                    ;             
;   ;         ;;;     ;; ;    ;;;      
;   ;        ;   ;   ;  ;;   ;   ;     
;   ;       ;     ; ;    ;  ;    ;     
;   ;       ;     ; ;    ;  ;;;;;;     
;   ;       ;     ; ;    ;  ;          
;    ;    ;  ;   ;   ;  ;;   ;         
;     ;;;;    ;;;     ;; ;    ;;;;     
;                                      
;                                      
;                                      
*/
// to represent a transit route segment
class Segment {
  String origin;
  String destination;

  Segment(String origin, String destination) {
    this.origin = origin;
    this.destination = destination;
  }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.origin ...
    ... this.destination ...
  }
*/
}

/*
;                                                  
;                                                  
;                                                  
;  ;;;;;;;                             ;           
;     ;                                            
;     ;                                    ;       
;     ;     ; ;  ;;;    ; ;;     ;;;   ;  ;;;;     
;     ;     ;;  ;   ;   ;;  ;   ;      ;   ;       
;     ;     ;       ;   ;   ;   ;;     ;   ;       
;     ;     ;    ;;;;   ;   ;    ;;    ;   ;       
;     ;     ;   ;   ;   ;   ;      ;   ;   ;       
;     ;     ;   ;   ;   ;   ;      ;   ;   ;       
;     ;     ;    ;;;;;  ;   ;   ;;;    ;    ;;     
;                                                  
;                                                  
;                                                  
*/
// to represent a list of transit route segments
abstract class ALoRt {

  // find the next station in this transit system after the given station
  abstract String findNext(String origin);

  // is there a path in this transit between the given stations
  abstract boolean hasRouting(String orig, String dest);

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

// to represent an empty list of transit route segments
class MTLoRt extends ALoRt {

  MTLoRt() {
  }

  // find the next station in this transit system after the given station
  String findNext(String origin){ return ""; }

  boolean hasRouting(String orig, String dest){ return false; }

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

// to represent a nonempty list of transit route segments
class ConsLoRt extends ALoRt {
  Segment first;
  ALoRt rest;

  ConsLoRt(Segment first, ALoRt rest) {
    this.first = first;
    this.rest = rest;
  }

  // find the next station in this transit system after the given station
  String findNext(String origin){
    if (this.first.origin.compareTo(origin) == 0) 
      return this.first.destination;
    else
      return this.rest.findNext(origin);
  }

  boolean hasRouting(String orig, String dest){
    return this.hasNoLoopRouting(orig, dest, new MTLoS());
  }

  // is there a path in this transit between the given stations
  // that does not go through the seen stations
  boolean hasNoLoopRouting(String orig, String dest, ALoS seen){
   if (orig.compareTo(dest) == 0)
     return true;             // this follows from the third example
   else if (seen.contains(orig))
     return false;
   else                       
     // see if there is a route from the next station after orig
     // that leads to the given dest - in this same transit !!!
     return this.hasNoLoopRouting(this.findNext(orig), dest, new ConsLoS(orig, seen));
  }

/*
  // ** DRAFT TEMPLATE ** Edit as needed.
  ??? mmm() {
    ... this.first ...
    ... this.first.origin ...
    ... this.first.destination ...

    ... this.rest ...
    ... this.rest.hasRouting(String some-orig, String some-dest) ...  
    ... this.rest.hasNoLoopRouting(String some-orig, String some-dest, ALoS seen) ...  
  }
*/
}

/*
;                                                         
;                                                         
;                                                         
;    ;;;;                   ;                             
;   ;    ;                                                
;   ;      ;           ;                                  
;   ;     ;;;;  ;;;   ;;;;  ;    ;;;    ; ;;     ;;;      
;    ;;    ;   ;   ;   ;    ;   ;   ;   ;;  ;   ;         
;      ;   ;       ;   ;    ;  ;     ;  ;   ;   ;;        
;       ;  ;    ;;;;   ;    ;  ;     ;  ;   ;    ;;       
;       ;  ;   ;   ;   ;    ;  ;     ;  ;   ;      ;      
;   ;   ;  ;   ;   ;   ;    ;   ;   ;   ;   ;      ;      
;    ;;;    ;;  ;;;;;   ;;  ;    ;;;    ;   ;   ;;;       
;                                                         
;                                                         
;                                                         
*/
// to represent a list of stations 
abstract class ALoS {

  // does this list contain the given station
  abstract boolean contains(String s);

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

// to represent an empty list of stations
class MTLoS extends ALoS {

  MTLoS() {
  }

  // does this list contain the given station
  boolean contains(String s){ return false; }

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

// to represent an nonempty list of stations
class ConsLoS extends ALoS {
  String first;
  ALoS rest;

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

  // does this list contain the given station
  boolean contains(String s){
    return this.first.compareTo(s) == 0
        || this.rest.contains(s);
  }

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


/*
;                                                                  
;                                                                  
;                                                                  
;   ;;;;;;                                    ;                    
;   ;                                         ;                    
;   ;                                         ;                    
;   ;     ;     ;  ;;;    ; ;;  ;;    ; ;;    ;    ;;;    ;;;      
;   ;;;;;  ;   ;  ;   ;   ;;  ;;  ;   ;;  ;   ;   ;   ;  ;         
;   ;       ; ;       ;   ;   ;   ;   ;    ;  ;  ;    ;  ;;        
;   ;        ;     ;;;;   ;   ;   ;   ;    ;  ;  ;;;;;;   ;;       
;   ;       ; ;   ;   ;   ;   ;   ;   ;    ;  ;  ;          ;      
;   ;      ;   ;  ;   ;   ;   ;   ;   ;;  ;   ;   ;         ;      
;   ;;;;;;;     ;  ;;;;;  ;   ;   ;   ; ;;    ;    ;;;;  ;;;       
;                                     ;                            
;                                     ;                            
;                                     ;                            
*/
class Examples{
  Examples(){}

  ALoRt mtransit = new MTLoRt();

  ALoRt transit = new ConsLoRt(new Segment("A", "B"),
                    new ConsLoRt(new Segment("B", "C"),
                    new ConsLoRt(new Segment("D", "E"),
                    new ConsLoRt(new Segment("E", "B"),
                    new ConsLoRt(new Segment("C", "E"),
                    new MTLoRt())))));

  boolean testHasRouting1 = transit.hasRouting("D", "C") == true;
  boolean testHasRouting2 = transit.hasRouting("C", "D") == false;

  boolean testFindNext1 = transit.findNext("D").compareTo("E") == 0;
  boolean testFindNext2 = transit.findNext("A").compareTo("B") == 0;

  boolean testContains1 = new ConsLoS("A", 
                            new ConsLoS("B", 
                              new ConsLoS("C", 
                                new MTLoS()))).contains("B") == true;

  boolean testContains2 = new ConsLoS("A", 
                            new ConsLoS("B", 
                              new ConsLoS("C", 
                                new MTLoS()))).contains("F") == false;
}
 


