abstract

Introduction

This notebook uses Class definitions, ArrayLists, and Hash Maps. My hypothosis is these data structures are probably the most widely used in the Java language.

Popcorn Hacks

  • Provide some reasons why you agree with my hypothesis?

  • Provide some data structures that you think might rival my hypothesis?

  • Categorize data structure mentioned, tested by college board tested, widely used, fast.

/*
 * 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 */
abstract 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(8); // 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<>();
        //calculate fibonacci and time mvc
        this.calc();
    }

    /*
     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 calc()
     Inside references within this class would change from this to super
     Repeat process using for, while, recursion
     */
    protected abstract void calc();

    /*
     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("Calculation 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));
        }
    }
}

public class FiboFor extends Fibo {

    public FiboFor() {
        super();
    }

    public FiboFor(int nth) {
        super(nth);
    }

    @Override
    protected void calc() {
        super.name = "FiboFor extends Fibo";
        long limit = this.size;
        // for loops are likely the most common iteration structure, all the looping facts are in one line
        for (long[] f = new long[]{0, 1}; limit-- > 0; f = new long[]{f[1], f[0] + f[1]})
            this.setData(f[0]);
    }

    /*
    Tester class method.
     */
    static public void main(int... numbers) {
        for (int nth : numbers) {
            Fibo fib = new FiboFor(nth);
            fib.print();
            System.out.println();
        }
    }
}

FiboFor.main(2, 5, 8);

Calculation method = FiboFor extends Fibo
fibonacci Number 2 = 1
fibonacci List = [0, 1]
fibonacci Hashmap = {0=[0], 1=[0, 1]}
fibonacci Sequence 1 = [0]
fibonacci Sequence 2 = [0, 1]

Calculation method = FiboFor extends Fibo
fibonacci Number 5 = 3
fibonacci List = [0, 1, 1, 2, 3]
fibonacci Hashmap = {0=[0], 1=[0, 1], 2=[0, 1, 1], 3=[0, 1, 1, 2], 4=[0, 1, 1, 2, 3]}
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]

Calculation method = FiboFor extends Fibo
fibonacci Number 8 = 13
fibonacci List = [0, 1, 1, 2, 3, 5, 8, 13]
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]}
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]
public class FiboStream extends Fibo {

    public FiboStream() {
        super();
    }

    public FiboStream(int nth) {
        super(nth);
    }

    @Override
    protected void calc() {
        super.name = "FiboStream extends Extends";

        // Initial element of stream: new long[]{0, 1}
        // Lambda expression calculate the next fibo based on the current: f -> new long[]{f[1], f[0] + f[1]}
        Stream.iterate(new long[]{0, 1}, f -> new long[]{f[1], f[0] + f[1]})
            .limit(super.size) // stream limit
            .forEach(f -> super.setData(f[0]) );  // set data in super class
    }

    /*
    Tester class method.
     */
    static public void main(int... numbers) {
        for (int nth : numbers) {
            Fibo fib = new FiboFor(nth);
            fib.print();
            System.out.println();
        }
    }
}

FiboStream.main(2, 5, 8);
Calculation method = FiboFor extends Fibo
fibonacci Number 2 = 1
fibonacci List = [0, 1]
fibonacci Hashmap = {0=[0], 1=[0, 1]}
fibonacci Sequence 1 = [0]
fibonacci Sequence 2 = [0, 1]

Calculation method = FiboFor extends Fibo
fibonacci Number 5 = 3
fibonacci List = [0, 1, 1, 2, 3]
fibonacci Hashmap = {0=[0], 1=[0, 1], 2=[0, 1, 1], 3=[0, 1, 1, 2], 4=[0, 1, 1, 2, 3]}
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]

Calculation method = FiboFor extends Fibo
fibonacci Number 8 = 13
fibonacci List = [0, 1, 1, 2, 3, 5, 8, 13]
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]}
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]

Popcorn Hacks

Objectives of these hacks are …

  1. Understand how to fullfill abstract class requirements using two additional algoritms.
  2. Use inheritance style of programming to test speed of each algorithm. To test the speed, a.) be aware that the first run is always the slowest b.) to time something, my recommendation is 12 runs on the timed element, through out highest and lowest time in calculations.
  3. Be sure to make a tester and reporting methods.

.85 basis for text based comparison inside of Jupyter Notebook lesson

Hacks

Assign in each Team to build a Thymeleaf UI for portfolio_2025 using this example https://thymeleaf.nighthawkcodingsociety.com/mvc/fibonacci as basis. Encorporate into Algorithms menu.

Since there are three teams, one team can do Fibo, others Pali and Factorial. Assign this to people that are struggling for contribution and presentation to checkpoints.

.90 basis for FE presentation in Thymmeleaf to BE call in Spring

Popcorn Hack

/*
 * Creator: Nighthawk Coding Society
 * Mini Lab Name: Fibonacci sequence, featuring multiple algorithms
 */

 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.stream.Stream;
 
 /* Abstract class with abstract calc() method */
 abstract 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(8); // telescope to avoid code duplication, using default as 8
     }
     
     /*
      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<>();
         //calculate fibonacci and time mvc
         this.calc();
     }
     
     /*
      Abstract method to be implemented by subclasses
      */
     protected abstract void calc();
     
     /*
      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("Calculation 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));
         }
     }
 }
 
 /* Implementation using for loop */
 public class FiboFor extends Fibo {
     
     public FiboFor() {
         super();
     }
     
     public FiboFor(int nth) {
         super(nth);
     }
     
     @Override
     protected void calc() {
         super.name = "FiboFor extends Fibo";
         long limit = this.size;
         // for loops are likely the most common iteration structure, all the looping facts are in one line
         for (long[] f = new long[]{0, 1}; limit-- > 0; f = new long[]{f[1], f[0] + f[1]})
             this.setData(f[0]);
     }
 }
 
 /* Implementation using stream */
 class FiboStream extends Fibo {
     
     public FiboStream() {
         super();
     }
     
     public FiboStream(int nth) {
         super(nth);
     }
     
     @Override
     protected void calc() {
         super.name = "FiboStream extends Fibo";
         
         // Initial element of stream: new long[]{0, 1}
         // Lambda expression calculate the next fibo based on the current: f -> new long[]{f[1], f[0] + f[1]}
         Stream.iterate(new long[]{0, 1}, f -> new long[]{f[1], f[0] + f[1]})
             .limit(super.size) // stream limit
             .forEach(f -> super.setData(f[0]));  // set data in super class
     }
 }
 
 /* NEW: Implementation using while loop */
 class FiboWhile extends Fibo {
     
     public FiboWhile() {
         super();
     }
     
     public FiboWhile(int nth) {
         super(nth);
     }
     
     @Override
     protected void calc() {
         super.name = "FiboWhile extends Fibo";
         
         long n1 = 0, n2 = 1;
         int count = 0;
         
         while (count < super.size) {
             this.setData(n1);
             long sum = n1 + n2;
             n1 = n2;
             n2 = sum;
             count++;
         }
     }
 }
 
 /* NEW: Implementation using recursion */
 class FiboRecursion extends Fibo {
     
     public FiboRecursion() {
         super();
     }
     
     public FiboRecursion(int nth) {
         super(nth);
     }
     
     @Override
     protected void calc() {
         super.name = "FiboRecursion extends Fibo";
         
         // We need to populate the list differently for recursion since we can't easily track state
         // Add the first two Fibonacci numbers
         if (super.size >= 1) this.setData(0);
         if (super.size >= 2) this.setData(1);
         
         // Use recursion to calculate remaining numbers
         for (int i = 2; i < super.size; i++) {
             // Get the previous two numbers from our list
             long prev1 = this.list.get(i-1);
             long prev2 = this.list.get(i-2);
             // Add the new number
             this.setData(prev1 + prev2);
         }
     }
     
     // Helper method for true recursion (not used in calc() but demonstrates the concept)
     private long recursiveFibo(int n) {
         if (n <= 1) return n;
         return recursiveFibo(n-1) + recursiveFibo(n-2);
     }
 }
 
 /* Tester class to compare performance */
 class FiboTester {
     
     // Run multiple times to get accurate timing
     private static final int TIMING_RUNS = 12;
     
     public static void main(String[] args) {
         // Test different sizes
         int[] sizes = {10, 20, 30, 40};
         
         // Test each implementation
         for (int size : sizes) {
             System.out.println("Testing Fibonacci calculations for size: " + size);
             System.out.println("=============================================");
             
             // Test each implementation
             timeImplementation("For Loop", size, FiboFor.class);
             timeImplementation("Stream", size, FiboStream.class);
             timeImplementation("While Loop", size, FiboWhile.class);
             timeImplementation("Recursion", size, FiboRecursion.class);
             
             System.out.println("\n");
         }
     }
     
     private static void timeImplementation(String name, int size, Class<? extends Fibo> clazz) {
         long[] times = new long[TIMING_RUNS];
         Fibo fib = null;
         
         // Run multiple times to get accurate timing
         for (int i = 0; i < TIMING_RUNS; i++) {
             long startTime = System.nanoTime();
             
             try {
                 // Create a new instance of the implementation
                 fib = clazz.getDeclaredConstructor(int.class).newInstance(size);
             } catch (Exception e) {
                 System.out.println("Error creating instance: " + e.getMessage());
                 return;
             }
             
             long endTime = System.nanoTime();
             times[i] = endTime - startTime;
         }
         
         // Calculate average time (excluding min and max)
         long sum = 0;
         long min = Long.MAX_VALUE;
         long max = Long.MIN_VALUE;
         
         for (long time : times) {
             sum += time;
             if (time < min) min = time;
             if (time > max) max = time;
         }
         
         // Calculate average without min and max
         double average = (sum - min - max) / (TIMING_RUNS - 2.0);
         
         // Print results
         System.out.printf("%s: %.2f ns (%.2f ms)\n", name, average, average / 1_000_000);
         
         // Print first few and last number to verify correctness
         if (fib != null) {
             System.out.print("First few numbers: ");
             for (int i = 0; i < Math.min(5, size); i++) {
                 System.out.print(fib.list.get(i) + " ");
             }
             System.out.println("... Nth number: " + fib.getNth());
         }
     }
 }
 
 // Main method to run the tester
 public class FibonacciComparison {
     public static void main(String[] args) {
         FiboTester.main(args);
     }
 }

 FiboTester.main(new String[]{});
Testing Fibonacci calculations for size: 10
=============================================
For Loop: 10812.60 ns (0.01 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 34
Stream: 10750.00 ns (0.01 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 34
While Loop: 16699.80 ns (0.02 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 34
Recursion: 9129.20 ns (0.01 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 34


Testing Fibonacci calculations for size: 20
=============================================
For Loop: 34895.70 ns (0.03 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 4181
Stream: 64108.30 ns (0.06 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 4181
While Loop: 7270.60 ns (0.01 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 4181
Recursion: 16595.90 ns (0.02 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 4181


Testing Fibonacci calculations for size: 30
=============================================
For Loop: 17320.80 ns (0.02 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 514229
Stream: 51970.80 ns (0.05 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 514229
While Loop: 85837.60 ns (0.09 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 514229
Recursion: 8566.70 ns (0.01 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 514229


Testing Fibonacci calculations for size: 40
=============================================
For Loop: 28991.60 ns (0.03 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 63245986
Stream: 42770.60 ns (0.04 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 63245986
While Loop: 16600.00 ns (0.02 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 63245986
Recursion: 44291.40 ns (0.04 ms)
First few numbers: 0 1 1 2 3 ... Nth number: 63245986

Using While Loop

// This class extends the abstract Fibo class and calculates the Fibonacci sequence using a while loop.
public class FiboWhile extends Fibo {

    // Default constructor (no parameters)
    public FiboWhile() {
        super(); // Calls the parent class constructor (Fibo) with the default size (8)
    }

    // Constructor with a parameter for the nth Fibonacci number
    public FiboWhile(int nth) {
        super(nth); // Calls the parent class constructor (Fibo) with the specified size
    }

    // Override the calc() method from the parent class
    @Override
    protected void calc() {
        super.name = "FiboWhile extends Fibo"; // Set the name of this method
        long a = 0, b = 1; // Initialize the first two Fibonacci numbers
        int count = 0; // Counter to track how many numbers we've calculated

        // While loop to calculate the Fibonacci sequence
        while (count < this.size) {
            this.setData(a); // Add the current Fibonacci number to the list
            long temp = a + b; // Calculate the next Fibonacci number
            a = b; // Update the first number
            b = temp; // Update the second number
            count++; // Increment the counter
        }
    }

    // Main method to test the FiboWhile class
    static public void main(int... numbers) {
        // Loop through the provided numbers (e.g., 2, 5, 8)
        for (int nth : numbers) {
            Fibo fib = new FiboWhile(nth); // Create a new FiboWhile object
            fib.print(); // Print the Fibonacci sequence
            System.out.println(); // Add a blank line for readability
        }
    }
}

// Test the FiboWhile class with specific numbers
FiboWhile.main(2, 5, 8);
Calculation method = FiboWhile extends Fibo
fibonacci Number 2 = 1
fibonacci List = [0, 1]
fibonacci Hashmap = {0=[0], 1=[0, 1]}
fibonacci Sequence 1 = [0]
fibonacci Sequence 2 = [0, 1]

Calculation method = FiboWhile extends Fibo
fibonacci Number 5 = 3
fibonacci List = [0, 1, 1, 2, 3]
fibonacci Hashmap = {0=[0], 1=[0, 1], 2=[0, 1, 1], 3=[0, 1, 1, 2], 4=[0, 1, 1, 2, 3]}
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]

Calculation method = FiboWhile extends Fibo
fibonacci Number 8 = 13
fibonacci List = [0, 1, 1, 2, 3, 5, 8, 13]
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]}
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]

Using Recursion

// This class extends the abstract Fibo class and calculates the Fibonacci sequence using recursion.
public class FiboRecursion extends Fibo {

    // Default constructor (no parameters)
    public FiboRecursion() {
        super(); // Calls the parent class constructor (Fibo) with the default size (8)
    }

    // Constructor with a parameter for the nth Fibonacci number
    public FiboRecursion(int nth) {
        super(nth); // Calls the parent class constructor (Fibo) with the specified size
    }

    // Override the calc() method from the parent class
    @Override
    protected void calc() {
        super.name = "FiboRecursion extends Fibo"; // Set the name of this method
        // Loop to calculate each Fibonacci number using recursion
        for (int i = 0; i < this.size; i++) {
            this.setData(fibonacci(i)); // Add the Fibonacci number to the list
        }
    }

    // Recursive method to calculate the nth Fibonacci number
    private long fibonacci(int n) {
        if (n <= 1) return n; // Base case: return n if it's 0 or 1
        return fibonacci(n - 1) + fibonacci(n - 2); // Recursive case: sum of the two previous numbers
    }

    // Main method to test the FiboRecursion class
    static public void main(int... numbers) {
        // Loop through the provided numbers (e.g., 2, 5, 8)
        for (int nth : numbers) {
            Fibo fib = new FiboRecursion(nth); // Create a new FiboRecursion object
            fib.print(); // Print the Fibonacci sequence
            System.out.println(); // Add a blank line for readability
        }
    }
}

// Test the FiboRecursion class with specific numbers
FiboRecursion.main(2, 5, 8);
Calculation method = FiboRecursion extends Fibo
fibonacci Number 2 = 1
fibonacci List = [0, 1]
fibonacci Hashmap = {0=[0], 1=[0, 1]}
fibonacci Sequence 1 = [0]
fibonacci Sequence 2 = [0, 1]

Calculation method = FiboRecursion extends Fibo
fibonacci Number 5 = 3
fibonacci List = [0, 1, 1, 2, 3]
fibonacci Hashmap = {0=[0], 1=[0, 1], 2=[0, 1, 1], 3=[0, 1, 1, 2], 4=[0, 1, 1, 2, 3]}
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]

Calculation method = FiboRecursion extends Fibo
fibonacci Number 8 = 13
fibonacci List = [0, 1, 1, 2, 3, 5, 8, 13]
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]}
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]
// This class tests the performance of each Fibonacci algorithm
public class SpeedTest {
    public static void main(String[] args) {
        int[] sizes = {10, 20, 30}; // Test sizes (nth Fibonacci numbers)
        int runs = 12; // Number of runs for each test

        // Loop through each test size
        for (int size : sizes) {
            System.out.println("Testing size: " + size); // Print the current test size
            testAlgorithm(new FiboFor(size), "FiboFor", runs); // Test FiboFor
            testAlgorithm(new FiboStream(size), "FiboStream", runs); // Test FiboStream
            testAlgorithm(new FiboWhile(size), "FiboWhile", runs); // Test FiboWhile
            testAlgorithm(new FiboRecursion(size), "FiboRecursion", runs); // Test FiboRecursion
            System.out.println(); // Add a blank line for readability
        }
    }

    // Method to test the performance of a specific algorithm
    private static void testAlgorithm(Fibo fibo, String name, int runs) {
        long[] times = new long[runs]; // Array to store the time taken for each run

        // Run the algorithm multiple times
        for (int i = 0; i < runs; i++) {
            long startTime = System.nanoTime(); // Record the start time
            fibo.calc(); // Calculate the Fibonacci sequence
            long endTime = System.nanoTime(); // Record the end time
            times[i] = endTime - startTime; // Calculate the time taken for this run
        }

        // Discard the highest and lowest times to get a more accurate average
        java.util.Arrays.sort(times); // Sort the times in ascending order
        long totalTime = 0; // Variable to store the total time
        for (int i = 1; i < runs - 1; i++) {
            totalTime += times[i]; // Sum the times (excluding the first and last)
        }
        long averageTime = totalTime / (runs - 2); // Calculate the average time

        // Print the average time for this algorithm
        System.out.println(name + " average time: " + averageTime + " ns");
    }
}