// File TryDelegate3.java * Last update 2006-03-23

import java.lang.reflect.*;                             // Method

public class TryDelegate3 {
  private static int k = 1;

  public static void main(String[] args) 
    throws NoSuchMethodException, IllegalAccessException, 
           InvocationTargetException {
    int count = Integer.parseInt(args[0]);
    Class ty = TryDelegate3.class;                      // Get class
    // Get m0() method
    Method m0 = ty.getMethod("m0", new Class[] {});     
    {
      Object[] margs = new Object[] { };
      Timer t = new Timer();
      for (int i=count; i>0; i--)
        m0.invoke(null, margs); 
      System.out.println("Reflective call to m0(): " + t.Check());
      System.out.println(k);
    }
    {
      Timer t = new Timer();
      for (int i=count; i>0; i--)
        m0(); 
      System.out.println("Direct call to m0(): " + t.Check());
      System.out.println(k);
    }
    {
      Void2Void dlg0 = (Void2Void)Delegate.createDelegate(Void2Void.class, m0);
      Timer t = new Timer();
      for (int i=count; i>0; i--)
        dlg0.call(); 
      System.out.println("Delegate call to m0(): " + t.Check());
      System.out.println(k);
    }
    // Get m1() method
    Method m1 = ty.getMethod("m1", new Class[] { int.class }); 
    {
      Object[] margs = new Object[] { };
      Timer t = new Timer();
      int res = 0;
      for (int i=count; i>0; i--) {
        Object obj = m1.invoke(null, new Object[] { new Integer(i) });
        res = ((Integer)obj).intValue();        
      }
      System.out.println("Reflective call to m1(i): " + t.Check());
      System.out.println(res);
      System.out.println(k);
    }
    {
      Timer t = new Timer();
      int res = 0;
      for (int i=count; i>0; i--)
        res = m1(i); 
      System.out.println("Direct call to m1(i): " + t.Check());
      System.out.println(res);
      System.out.println(k);
    }
    {
      Int2Int dlg1 = (Int2Int)Delegate.createDelegate(Int2Int.class, m1);
      Timer t = new Timer();
      int res = 0;
      for (int i=count; i>0; i--)
        res = dlg1.call(i); 
      System.out.println("Delegate call to m1(i): " + t.Check());
      System.out.println(res);
      System.out.println(k);
    }
  }

  // k is always > 0 when calling m0
  public static void m0() {
    if (k > 0)
      k++;
    else { // Some dummy code with a recursive call to prevent inlining
      for (int j=k; j<=0; j++) {
        k++;
        m0();
      }
    }
  }

  // k is always > 0 when calling m1
  public static int m1(int x) {
    if (k > 0) {
      k++;
      return k + x;
    } else { // Some dummy code with a recursive call to prevent inlining
      for (int j=k; j<=0; j++) {
        k++;
        m1(x);
      }
      return k + x;
    }
  }

  public interface Int2Int {
    int call(int x);
  }

  public interface Void2Void {
    void call();
  }
}

// Crude timing utility ----------------------------------------
   
class Timer {
  private long start;

  public Timer() {
    start = System.currentTimeMillis();
  }

  public double Check() {
    return (System.currentTimeMillis()-start)/1000.0;
  }
}

