/*
 * Creator: Nighthawk Coding Society
 * Mini Lab Name: Fibonacci sequence, featuring a Stream Algorithm
 * 
 */

import java.util.ArrayList;  
import java.util.HashMap;
import java.util.stream.Stream;

/* Objective will require changing to abstract class with one or more abstract methods below */
public class Fibo {
    String name;  // name or title of method
    int size;  // nth sequence
    int hashID;  // counter for hashIDs in hash map
    ArrayList<Long> list;   // captures current Fibonacci sequence
    HashMap<Integer, Object> hash;  // captures each sequence leading to final result

    /*
     Zero parameter constructor uses Telescoping technique to allow setting of the required value nth
     @param: none
     */
    public Fibo() {
        this(20); // telescope to avoid code duplication, using default as 20
    }

    /*
     Construct the nth fibonacci number
     @param: nth number, the value is constrained to 92 because of overflow in a long
     */
    public Fibo(int nth) {
        this.size = nth;
        this.list = new ArrayList<>();
        this.hashID = 0;
        this.hash = new HashMap<>();
        //initialize fibonacci and time mvc
        this.init();
    }

    /*
     This Method should be "abstract"
     Leave method as protected, as it is only authorized to extender of the class
     Make new class that extends and defines init()
     Inside references within this class would change from this to super
     Repeat process using for, while, recursion
     */
    protected void init() {
        this.name = "Stream";
        Stream.iterate(new long[]{0, 1}, f -> new long[]{f[1], f[0] + f[1]})
            .limit(this.size)
            .forEach(f -> this.setData(f[0]) );
    }

    /*
     Number is added to fibonacci sequence, current state of "list" is added to hash for hashID "num"
     */
    public void setData(long num) {
        list.add(num);
        hash.put(this.hashID++, list.clone());
    }

    /*
     Custom Getter to return last element in fibonacci sequence
     */
    public long getNth() {
        return list.get(this.size - 1);
    }

    /*
     Custom Getter to return last fibonacci sequence in HashMap
     */
    public Object getNthSeq(int i) {
        return hash.get(i);
    }

    /*
     Console/Terminal supported print method
     */
    public void print() {
        System.out.println("Init method = " + this.name);
        System.out.println("fibonacci Number " + this.size + " = " + this.getNth());
        System.out.println("fibonacci List = " + this.list);
        System.out.println("fibonacci Hashmap = " + this.hash);
        for (int i=0 ; i<this.size; i++ ) {
            System.out.println("fibonacci Sequence " + (i+1) + " = " + this.getNthSeq(i));
        }
    }

    /*
    Tester class method.  If this becomes abstract you will not be able to test it directly ...
    Change this method to  call "main" class of each of the extended classes
     */
    static public void main(String[] args) {
        Fibo fib = new Fibo();
        fib.print();
    }
}
Fibo.main(null);
Init method = Stream
fibonacci Number 20 = 4181
fibonacci List = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
fibonacci Hashmap = {0=[0], 1=[0, 1], 2=[0, 1, 1], 3=[0, 1, 1, 2], 4=[0, 1, 1, 2, 3], 5=[0, 1, 1, 2, 3, 5], 6=[0, 1, 1, 2, 3, 5, 8], 7=[0, 1, 1, 2, 3, 5, 8, 13], 8=[0, 1, 1, 2, 3, 5, 8, 13, 21], 9=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34], 10=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55], 11=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89], 12=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], 13=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233], 14=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377], 15=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610], 16=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987], 17=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597], 18=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584], 19=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]}
fibonacci Sequence 1 = [0]
fibonacci Sequence 2 = [0, 1]
fibonacci Sequence 3 = [0, 1, 1]
fibonacci Sequence 4 = [0, 1, 1, 2]
fibonacci Sequence 5 = [0, 1, 1, 2, 3]
fibonacci Sequence 6 = [0, 1, 1, 2, 3, 5]
fibonacci Sequence 7 = [0, 1, 1, 2, 3, 5, 8]
fibonacci Sequence 8 = [0, 1, 1, 2, 3, 5, 8, 13]
fibonacci Sequence 9 = [0, 1, 1, 2, 3, 5, 8, 13, 21]
fibonacci Sequence 10 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fibonacci Sequence 11 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
fibonacci Sequence 12 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
fibonacci Sequence 13 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
fibonacci Sequence 14 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
fibonacci Sequence 15 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
fibonacci Sequence 16 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
fibonacci Sequence 17 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
fibonacci Sequence 18 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597]
fibonacci Sequence 19 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
fibonacci Sequence 20 = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
public class FibonacciWithMemoization {
 
 public static long fibArray[]= new long[93];
 
 public static long fibonacci(long n){
  long fibValue=0;
  if(n==0 ){
   return 0;
  }else if(n==1){
   return 1;
  }else if(fibArray[(int)n]!=0){
   return fibArray[(int)n];
  }else{
   fibValue=fibonacci(n-1)+fibonacci(n-2);
   fibArray[(int) n]=fibValue;
   return fibValue;
  }
 }
 
 public static void main(String args[]){
  fibArray[0]=1;
  fibArray[1]=1;
  for(int i = 0; i < 93; i++) {

    long ranth = i;//(long) Math.floor(Math.random() * 92);
    System.out.println(ranth + "th Fibonacci number: "+fibonacci(ranth));

  }
 }
}
FibonacciWithMemoization.main(null);
0th Fibonacci number: 0
1th Fibonacci number: 1
2th Fibonacci number: 1
3th Fibonacci number: 2
4th Fibonacci number: 3
5th Fibonacci number: 5
6th Fibonacci number: 8
7th Fibonacci number: 13
8th Fibonacci number: 21
9th Fibonacci number: 34
10th Fibonacci number: 55
11th Fibonacci number: 89
12th Fibonacci number: 144
13th Fibonacci number: 233
14th Fibonacci number: 377
15th Fibonacci number: 610
16th Fibonacci number: 987
17th Fibonacci number: 1597
18th Fibonacci number: 2584
19th Fibonacci number: 4181
20th Fibonacci number: 6765
21th Fibonacci number: 10946
22th Fibonacci number: 17711
23th Fibonacci number: 28657
24th Fibonacci number: 46368
25th Fibonacci number: 75025
26th Fibonacci number: 121393
27th Fibonacci number: 196418
28th Fibonacci number: 317811
29th Fibonacci number: 514229
30th Fibonacci number: 832040
31th Fibonacci number: 1346269
32th Fibonacci number: 2178309
33th Fibonacci number: 3524578
34th Fibonacci number: 5702887
35th Fibonacci number: 9227465
36th Fibonacci number: 14930352
37th Fibonacci number: 24157817
38th Fibonacci number: 39088169
39th Fibonacci number: 63245986
40th Fibonacci number: 102334155
41th Fibonacci number: 165580141
42th Fibonacci number: 267914296
43th Fibonacci number: 433494437
44th Fibonacci number: 701408733
45th Fibonacci number: 1134903170
46th Fibonacci number: 1836311903
47th Fibonacci number: 2971215073
48th Fibonacci number: 4807526976
49th Fibonacci number: 7778742049
50th Fibonacci number: 12586269025
51th Fibonacci number: 20365011074
52th Fibonacci number: 32951280099
53th Fibonacci number: 53316291173
54th Fibonacci number: 86267571272
55th Fibonacci number: 139583862445
56th Fibonacci number: 225851433717
57th Fibonacci number: 365435296162
58th Fibonacci number: 591286729879
59th Fibonacci number: 956722026041
60th Fibonacci number: 1548008755920
61th Fibonacci number: 2504730781961
62th Fibonacci number: 4052739537881
63th Fibonacci number: 6557470319842
64th Fibonacci number: 10610209857723
65th Fibonacci number: 17167680177565
66th Fibonacci number: 27777890035288
67th Fibonacci number: 44945570212853
68th Fibonacci number: 72723460248141
69th Fibonacci number: 117669030460994
70th Fibonacci number: 190392490709135
71th Fibonacci number: 308061521170129
72th Fibonacci number: 498454011879264
73th Fibonacci number: 806515533049393
74th Fibonacci number: 1304969544928657
75th Fibonacci number: 2111485077978050
76th Fibonacci number: 3416454622906707
77th Fibonacci number: 5527939700884757
78th Fibonacci number: 8944394323791464
79th Fibonacci number: 14472334024676221
80th Fibonacci number: 23416728348467685
81th Fibonacci number: 37889062373143906
82th Fibonacci number: 61305790721611591
83th Fibonacci number: 99194853094755497
84th Fibonacci number: 160500643816367088
85th Fibonacci number: 259695496911122585
86th Fibonacci number: 420196140727489673
87th Fibonacci number: 679891637638612258
88th Fibonacci number: 1100087778366101931
89th Fibonacci number: 1779979416004714189
90th Fibonacci number: 2880067194370816120
91th Fibonacci number: 4660046610375530309
92th Fibonacci number: 7540113804746346429
public class Fibonacci extends Fibo {

    public Fibonacci() {

        super();

    }

    static public void loopPrint(int num) {
        Fibonacci fib = new Fibonacci();
        for (int i=0 ; i<num; i++ ) {
            System.out.println("fibonacci Sequence " + (i+1) + " = " + fib.getNthSeq(i));
        }
    }

    static public void hashPrint(int num) {
        Fibonacci fib = new Fibonacci();
        System.out.println("fibonacci Hashmap = " + fib.hash.get(num));
    }

    static public void initPrint(int num) {
        Fibonacci fib = new Fibonacci();
        System.out.println("Init method = " + fib.name);
    }

    static public void listPrint(int num) {
        Fibonacci fib = new Fibonacci();
        System.out.println("fibonacci List = " + fib.list);
    }

    static public void numberPrint(int num) {
        Fibonacci fib = new Fibonacci();
        System.out.println("fibonacci Number " + fib.size + " = " + fib.getNth());
    }

}
Fibonacci.numberPrint(2);
fibonacci Number 20 = 4181