context: using System;
  using li_boost_shared_ptrNamespace;
  
  public class runme
  {
    // Debugging flag
    public static bool debug = false;
  
    private static void WaitForGC()
    {
      System.GC.Collect(); 
      System.GC.WaitForPendingFinalizers();
      System.Threading.Thread.Sleep(10);
    }
  
    static void Main() 
    {
      if (debug)
        Console.WriteLine("Started");
  
      li_boost_shared_ptr.debug_shared=debug;
  
      // Change loop count to run for a long time to monitor memory
      const int loopCount = 1; //50000;
      for (int i=0; i<loopCount; i++) {
        new runme().runtest();
        System.GC.Collect(); 
        System.GC.WaitForPendingFinalizers();
        if (i%100 == 0) {
          System.Threading.Thread.Sleep(1); // give some time to the lower priority finalizer thread
        }
      }
  
      if (debug)
        Console.WriteLine("Nearly finished");
  
      {
        int countdown = 500;
        int expectedCount = 1;
        while (true) {
          WaitForGC();
          if (--countdown == 0)
            break;
          if (Klass.getTotal_count() == expectedCount) // Expect the one global variable (GlobalValue)
            break;
        }
        int actualCount = Klass.getTotal_count();
        if (actualCount != expectedCount)
          Console.Error.WriteLine("Expected count: " + expectedCount + " Actual count: " + actualCount); // Finalizers are not guaranteed to be run and sometimes they just don't
      }
  
      int wrapper_count = li_boost_shared_ptr.shared_ptr_wrapper_count(); 
      if (wrapper_count != li_boost_shared_ptr.NOT_COUNTING)
        if (wrapper_count != 1) // Expect the one global variable (GlobalSmartValue)
          throw new ApplicationException("shared_ptr wrapper count=" + wrapper_count);
  
      if (debug)
        Console.WriteLine("Finished");
    }
  
    private void runtest() {
      // simple shared_ptr usage - created in C++
      {
        Klass k = new Klass("me oh my");
        String val = k.getValue();
        verifyValue("me oh my", val);
        verifyCount(1, k);
      }
  
      // simple shared_ptr usage - not created in C++
      {
        Klass k = li_boost_shared_ptr.factorycreate();
        String val = k.getValue();
        verifyValue("factorycreate", val);
        verifyCount(1, k);
      }
  
      // pass by shared_ptr
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my smartpointertest", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // pass by shared_ptr pointer
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointerpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my smartpointerpointertest", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // pass by shared_ptr reference
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointerreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my smartpointerreftest", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // pass by shared_ptr pointer reference
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointerpointerreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my smartpointerpointerreftest", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // const pass by shared_ptr
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.constsmartpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // const pass by shared_ptr pointer
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.constsmartpointerpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // const pass by shared_ptr reference
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.constsmartpointerreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my", val);
        verifyCount(2, k);
        verifyCount(2, kret);
      }
  
      // pass by value
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.valuetest(k);
        String val = kret.getValue();
        verifyValue("me oh my valuetest", val);
        verifyCount(1, k);
        verifyCount(1, kret);
      }
  
      // pass by pointer
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.pointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my pointertest", val);
        verifyCount(1, k);
        verifyCount(1, kret);
      }
  
      // pass by reference
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.reftest(k);
        String val = kret.getValue();
        verifyValue("me oh my reftest", val);
        verifyCount(1, k);
        verifyCount(1, kret);
      }
  
      // pass by pointer reference
      {
        Klass k = new Klass("me oh my");
        Klass kret = li_boost_shared_ptr.pointerreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my pointerreftest", val);
        verifyCount(1, k);
        verifyCount(1, kret);
      }
  
      // null tests
      {
        Klass k = null;
  
        // TODO: add in const versions too
        if (li_boost_shared_ptr.smartpointertest(k) != null)
          throw new ApplicationException("return was not null");
  
        if (li_boost_shared_ptr.smartpointerpointertest(k) != null)
          throw new ApplicationException("return was not null");
  
        if (li_boost_shared_ptr.smartpointerreftest(k) != null)
          throw new ApplicationException("return was not null");
  
        if (li_boost_shared_ptr.smartpointerpointerreftest(k) != null)
          throw new ApplicationException("return was not null");
  
        if (li_boost_shared_ptr.nullsmartpointerpointertest(null) != "null pointer")
          throw new ApplicationException("not null smartpointer pointer");
  
        try { li_boost_shared_ptr.valuetest(k); throw new ApplicationException("Failed to catch null pointer"); } catch (ArgumentNullException) {}
  
        if (li_boost_shared_ptr.pointertest(k) != null)
          throw new ApplicationException("return was not null");
  
        try { li_boost_shared_ptr.reftest(k); throw new ApplicationException("Failed to catch null pointer"); } catch (ArgumentNullException) {}
      }
  
      // $owner
      {
        Klass k = li_boost_shared_ptr.pointerownertest();
        String val = k.getValue();
        verifyValue("pointerownertest", val);
        verifyCount(1, k);
      }
      {
        Klass k = li_boost_shared_ptr.smartpointerpointerownertest();
        String val = k.getValue();
        verifyValue("smartpointerpointerownertest", val);
        verifyCount(1, k);
      }
  
      ////////////////////////////////// Derived classes ////////////////////////////////////////
      // derived pass by shared_ptr
      {
        KlassDerived k = new KlassDerived("me oh my");
        KlassDerived kret = li_boost_shared_ptr.derivedsmartptrtest(k);
        String val = kret.getValue();
        verifyValue("me oh my derivedsmartptrtest-Derived", val);
        verifyCount(4, k); // includes two extra references for upcasts in the proxy classes
        verifyCount(4, kret);
      }
      // derived pass by shared_ptr pointer
      {
        KlassDerived k = new KlassDerived("me oh my");
        KlassDerived kret = li_boost_shared_ptr.derivedsmartptrpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my derivedsmartptrpointertest-Derived", val);
        verifyCount(4, k); // includes two extra references for upcasts in the proxy classes
        verifyCount(4, kret);
      }
      // derived pass by shared_ptr ref
      {
        KlassDerived k = new KlassDerived("me oh my");
        KlassDerived kret = li_boost_shared_ptr.derivedsmartptrreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my derivedsmartptrreftest-Derived", val);
        verifyCount(4, k); // includes two extra references for upcasts in the proxy classes
        verifyCount(4, kret);
      }
      // derived pass by shared_ptr pointer ref
      {
        KlassDerived k = new KlassDerived("me oh my");
        KlassDerived kret = li_boost_shared_ptr.derivedsmartptrpointerreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my derivedsmartptrpointerreftest-Derived", val);
        verifyCount(4, k); // includes two extra references for upcasts in the proxy classes
        verifyCount(4, kret);
      }
      // derived pass by pointer
      {
        KlassDerived k = new KlassDerived("me oh my");
        KlassDerived kret = li_boost_shared_ptr.derivedpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my derivedpointertest-Derived", val);
        verifyCount(2, k); // includes an extra reference for the upcast in the proxy class
        verifyCount(2, kret);
      }
      // derived pass by ref
      {
        KlassDerived k = new KlassDerived("me oh my");
        KlassDerived kret = li_boost_shared_ptr.derivedreftest(k);
        String val = kret.getValue();
        verifyValue("me oh my derivedreftest-Derived", val);
        verifyCount(2, k); // includes an extra reference for the upcast in the proxy class
        verifyCount(2, kret);
      }
  
      ////////////////////////////////// Derived and base class mixed ////////////////////////////////////////
      // pass by shared_ptr (mixed)
      {
        Klass k = new KlassDerived("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my smartpointertest-Derived", val);
        verifyCount(3, k); // an extra reference for the upcast in the proxy class
        verifyCount(3, kret);
      }
  
      // pass by shared_ptr pointer (mixed)
      {
        Klass k = new KlassDerived("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointerpointertest(k);
        String val = kret.getValue();
        verifyValue("me oh my smartpointerpointertest-Derived", val);
        verifyCount(3, k); // an extra reference for the upcast in the proxy class
        verifyCount(3, kret);
      }
  
      // pass by shared_ptr reference (mixed)
      {
        Klass k = new KlassDerived("me oh my");
        Klass kret = li_boost_shared_ptr.smartpointerreftest(k);
  
gt:       String val = kret.getValue();