#include <fstream>
#include <coroutines>

struct Fibonacci {
      int fn; // used for communication
      coroutine c;
};

void ?{}(Fibonacci* this) {
      this->fn = 0;
}

coroutine* get_coroutine(Fibonacci* this) {
      return &this->c;
}

void main(Fibonacci* this) {
#ifdef MORE_DEBUG
      sout | "Starting main of coroutine " | this | endl;
      sout | "Started from " | this->c.last | endl;
#endif
      int fn1, fn2; 		// retained between resumes
      this->fn = 0;
      fn1 = this->fn;
      suspend(); 		// return to last resume

      this->fn = 1;
      fn2 = fn1;
      fn1 = this->fn;
      suspend(); 		// return to last resume

      for ( ;; ) {
            this->fn = fn1 + fn2;
            fn2 = fn1;
            fn1 = this->fn;
            suspend(); 	// return to last resume
      }
}

int next(Fibonacci* this) {
      resume(this); // transfer to last suspend
      return this->fn;
}

int main() {
      Fibonacci f1, f2;
#ifdef MORE_DEBUG      
      Fibonacci *pf1 = &f1, *pf2 = &f2;
      coroutine *cf1 = &f1.c, *cf2 = &f2.c;
      covptr_t  *vf1 = vtable(pf1), *vf2 = vtable(pf2);
      coroutine *cv1 = get_coroutine(vf1), *cv2 = get_coroutine(vf2);
      Fibonacci *ov1 = (Fibonacci *)get_object(vf1), *ov2 = (Fibonacci *)get_object(vf2);

      sout | "User coroutines : " | pf1 | ' ' | pf2 | endl;
      sout | "Coroutine data  : " | cf1 | ' ' | cf2 | endl;
      sout | "Vptr address    : " | vf1 | ' ' | vf2 | endl;
      sout | "Vptr obj data   : " | ov1 | ' ' | ov2 | endl;
      sout | "Vptr cor data   : " | cv1 | ' ' | cv2 | endl;
#endif
      for ( int i = 1; i <= 10; i += 1 ) {
            sout | next(&f1) | ' ' | next(&f2) | endl;
      }

      return 0;
}
