/* File:    DetectLoopInLinkedList.java
 * Created: 2018-05-09
 * Author:  Sabbir Manandhar
 *
 * Copyright (c) 2018 Hogwart
 */

import java.util.HashSet;
import java.util.Set;

/**
 * Detect Loop in a Linked List
 *
 * @author Sabbir Manandhar
 * @version 1.0
 */
public class DetectLoopInLinkedList {

  public static void main(String[] args) {
    DetectLoopInLinkedList detectLoopInLinkedList = new DetectLoopInLinkedList();
    int i = 1;
    Node head = detectLoopInLinkedList.new Node(i++);
    Node tail = head;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++); // 11
    tail = tail.next;
    Node loopBegin = tail;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);
    tail = tail.next;
    tail.next = detectLoopInLinkedList.new Node(i++);

    // No Loop
    Node loopStart1 = detectLoopInLinkedList.detectLoop(head);
    Node loopStart2 = detectLoopInLinkedList.detectLoopFloydMethod(head);
    System.out.println(loopStart1 == null && loopStart2 == null ? "SUCCESS: Loop Not Found" : "Failure");

    // Crate Loop at 11
    tail = tail.next;
    tail.next = loopBegin;

    loopStart1 = detectLoopInLinkedList.detectLoop(head);
    loopStart2 = detectLoopInLinkedList.detectLoopFloydMethod(head);
    System.out.println(loopStart1 == loopStart2 ? "SUCCESS: Loop Start Found: " + loopStart1.value : "Failure");
  } // main

  //-----------------------------------------------------------------------------------

  /**
   * Detects a loop in a given LinkedList
   * Based upon keeping track of already visited Nodes
   * @param head head of the LinkedList
   * @return start of loop if it exists, otherwise null
   */
  public Node detectLoop(Node head) {
    Set<Node> visitedNodes = new HashSet<Node>();

    Node node = head;
    while (node != null) {
      if (visitedNodes.contains(node)) { // loop detected. node is the start of the loop
        return node;
      } else {
        visitedNodes.add(node);
        node = node.next;
      }
    }

    return null; // no loop
  } // detectLoop

  //------------------------------------------------------------------------------------

  /**
   * Detect Loop in a LinkedList using Floyd's Method
   * @param head Head of the LinkedList
   * @return start of loop if it exists, otherwise null
   */
  public Node detectLoopFloydMethod(Node head) {
    Node faster = head;
    Node slower = head;

    // detect loop
    while (faster != null && faster.next != null) {
      faster = faster.next.next;
      slower = slower.next;

      if (faster == slower) { // loop detected
        break;
      }
    }

    if (faster != slower) return null; // loop not detected

    // detect start of loop
    faster = head;
    while (faster != slower) {
      faster = faster.next;
      slower = slower.next;
    }

    return faster;
  } // detectLoopFloydMethod


  //------------------------------------------------------------------------------------

  /**
   * Class to represent Linked List Node
   */
  public class Node {
    Node next;
    int value;

    public Node(int value) {
      this.value = value;
    } // Node
  } // Node

} // DetectLoopInLinkedList