// BEGIN mycpp output

#include "mycpp/runtime.h"

GLOBAL_STR(str0, "TextOutput constructor");
GLOBAL_STR(str1, "num_chars = %d");
GLOBAL_STR(str2, "i = %d");
GLOBAL_STR(str3, "Base(%s)");
GLOBAL_STR(str4, "next");
GLOBAL_STR(str5, "null");
GLOBAL_STR(str6, "DerivedI(%s, %d)");
GLOBAL_STR(str7, "next");
GLOBAL_STR(str8, "null");
GLOBAL_STR(str9, "DerivedSS(%s, %s, %s)");
GLOBAL_STR(str10, "next");
GLOBAL_STR(str11, "null");
GLOBAL_STR(str12, "foo\n");
GLOBAL_STR(str13, "bar\n");
GLOBAL_STR(str14, "Wrote %d bytes");
GLOBAL_STR(str15, "left");
GLOBAL_STR(str16, "right");
GLOBAL_STR(str17, "Integer() = %d");
GLOBAL_STR(str18, "b.TypeString()   %s");
GLOBAL_STR(str19, "di.TypeString()  %s");
GLOBAL_STR(str20, "dss.TypeString() %s");
GLOBAL_STR(str21, "f(b)           %s");
GLOBAL_STR(str22, "f(di)          %s");
GLOBAL_STR(str23, "f(dss)         %s");
GLOBAL_STR(str24, "BenchmarkWriter");
GLOBAL_STR(str25, "");
GLOBAL_STR(str26, "foo\n");
GLOBAL_STR(str27, "  Ran %d iterations");
GLOBAL_STR(str28, "  Wrote %d bytes");
GLOBAL_STR(str29, "");
GLOBAL_STR(str30, "  -> %d");
GLOBAL_STR(str31, "");
GLOBAL_STR(str32, "  linked list len = %d");
GLOBAL_STR(str33, "");
GLOBAL_STR(str34, "BenchmarkSimpleNode");
GLOBAL_STR(str35, "");
GLOBAL_STR(str36, "  -> %s");
GLOBAL_STR(str37, "");
GLOBAL_STR(str38, "  linked list len = %d");
GLOBAL_STR(str39, "");
GLOBAL_STR(str40, "BenchmarkVirtualNodes");
GLOBAL_STR(str41, "");
GLOBAL_STR(str42, "+%d");

namespace classes {  // forward declare

  class ColorOutput;
  class TextOutput;
  class Abstract;
  class Base;
  class DerivedI;
  class DerivedSS;
  class Node;

}  // forward declare namespace classes

namespace classes {  // declare

class ColorOutput {
 public:
  ColorOutput(mylib::Writer* f);
  void write(BigStr* s);
  mylib::Writer* f;
  int num_chars;
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(ColorOutput, f));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ColorOutput));
  }

  DISALLOW_COPY_AND_ASSIGN(ColorOutput)
};

class TextOutput : public ::classes::ColorOutput {
 public:
  TextOutput(mylib::Writer* f);
  void MutateFields();
  void PrintFields();

  int i;
  
  static constexpr uint32_t field_mask() {
    return ::classes::ColorOutput::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(TextOutput));
  }

  DISALLOW_COPY_AND_ASSIGN(TextOutput)
};

class Abstract {
 public:
  Abstract();
  virtual BigStr* TypeString();
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Abstract));
  }

  DISALLOW_COPY_AND_ASSIGN(Abstract)
};

class Base : public ::classes::Abstract {
 public:
  Base(classes::Base* n);
  virtual BigStr* TypeString();

  classes::Base* next;
  
  static constexpr uint32_t field_mask() {
    return ::classes::Abstract::field_mask()
         | maskbit(offsetof(Base, next));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Base));
  }

  DISALLOW_COPY_AND_ASSIGN(Base)
};

class DerivedI : public ::classes::Base {
 public:
  DerivedI(classes::Base* n, int i);
  int Integer();
  virtual BigStr* TypeString();

  int i;
  
  static constexpr uint32_t field_mask() {
    return ::classes::Base::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DerivedI));
  }

  DISALLOW_COPY_AND_ASSIGN(DerivedI)
};

class DerivedSS : public ::classes::Base {
 public:
  DerivedSS(classes::Base* n, BigStr* t, BigStr* u);
  virtual BigStr* TypeString();

  BigStr* t;
  BigStr* u;
  
  static constexpr uint32_t field_mask() {
    return ::classes::Base::field_mask()
         | maskbit(offsetof(DerivedSS, t))
         | maskbit(offsetof(DerivedSS, u));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DerivedSS));
  }

  DISALLOW_COPY_AND_ASSIGN(DerivedSS)
};

class Node {
 public:
  Node(classes::Node* n, int i);
  classes::Node* next;
  int i;

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Node));
  }

  DISALLOW_COPY_AND_ASSIGN(Node)
};

void TestMethods();
BigStr* f(classes::Base* obj);
void TestInheritance();
void run_tests();
void BenchmarkWriter(int n);
void PrintLength(classes::Node* node);
void BenchmarkSimpleNode(int n);
void PrintLengthBase(classes::Base* current);
void BenchmarkVirtualNodes(int n);
void run_benchmarks();

}  // declare namespace classes

namespace classes {  // define


ColorOutput::ColorOutput(mylib::Writer* f) {
  this->f = f;
  this->num_chars = 0;
}

void ColorOutput::write(BigStr* s) {
  StackRoot _root0(&s);

  this->f->write(s);
  this->num_chars += len(s);
}

TextOutput::TextOutput(mylib::Writer* f) : ::classes::ColorOutput(f) {
  print(str0);
  this->i = 0;
}

void TextOutput::MutateFields() {
  this->num_chars = 42;
  this->i = 43;
}

void TextOutput::PrintFields() {
  print(StrFormat("num_chars = %d", this->num_chars));
  print(StrFormat("i = %d", this->i));
}

Abstract::Abstract() {
  ;  // pass
}

BigStr* Abstract::TypeString() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

Base::Base(classes::Base* n) : ::classes::Abstract() {
  this->next = n;
}

BigStr* Base::TypeString() {
  return StrFormat("Base(%s)", this->next ? str4 : str5);
}

DerivedI::DerivedI(classes::Base* n, int i) : ::classes::Base(n) {
  this->i = i;
}

int DerivedI::Integer() {
  return this->i;
}

BigStr* DerivedI::TypeString() {
  return StrFormat("DerivedI(%s, %d)", this->next ? str7 : str8, this->i);
}

DerivedSS::DerivedSS(classes::Base* n, BigStr* t, BigStr* u) : ::classes::Base(n) {
  this->t = t;
  this->u = u;
}

BigStr* DerivedSS::TypeString() {
  return StrFormat("DerivedSS(%s, %s, %s)", this->next ? str10 : str11, this->t, this->u);
}

Node::Node(classes::Node* n, int i) {
  this->next = n;
  this->i = i;
}

void TestMethods() {
  mylib::Writer* stdout_ = nullptr;
  classes::TextOutput* out = nullptr;
  StackRoot _root0(&stdout_);
  StackRoot _root1(&out);

  stdout_ = mylib::Stdout();
  out = Alloc<TextOutput>(stdout_);
  out->write(str12);
  out->write(str13);
  mylib::print_stderr(StrFormat("Wrote %d bytes", out->num_chars));
  out->MutateFields();
  out->PrintFields();
}

BigStr* f(classes::Base* obj) {
  StackRoot _root0(&obj);

  return obj->TypeString();
}

void TestInheritance() {
  classes::Base* b = nullptr;
  classes::DerivedI* di = nullptr;
  classes::DerivedSS* dss = nullptr;
  StackRoot _root0(&b);
  StackRoot _root1(&di);
  StackRoot _root2(&dss);

  b = Alloc<Base>(nullptr);
  di = Alloc<DerivedI>(nullptr, 1);
  dss = Alloc<DerivedSS>(nullptr, str15, str16);
  mylib::print_stderr(StrFormat("Integer() = %d", di->Integer()));
  mylib::print_stderr(StrFormat("b.TypeString()   %s", b->TypeString()));
  mylib::print_stderr(StrFormat("di.TypeString()  %s", di->TypeString()));
  mylib::print_stderr(StrFormat("dss.TypeString() %s", dss->TypeString()));
  mylib::print_stderr(StrFormat("f(b)           %s", f(b)));
  mylib::print_stderr(StrFormat("f(di)          %s", f(di)));
  mylib::print_stderr(StrFormat("f(dss)         %s", f(dss)));
}

void run_tests() {
  TestMethods();
  TestInheritance();
}

void BenchmarkWriter(int n) {
  mylib::BufWriter* f = nullptr;
  classes::TextOutput* out = nullptr;
  int i;
  StackRoot _root0(&f);
  StackRoot _root1(&out);

  mylib::print_stderr(str24);
  mylib::print_stderr(str25);
  f = Alloc<mylib::BufWriter>();
  out = Alloc<TextOutput>(f);
  i = 0;
  while (i < n) {
    out->write(str26);
    i += 1;
  }
  mylib::print_stderr(StrFormat("  Ran %d iterations", n));
  mylib::print_stderr(StrFormat("  Wrote %d bytes", out->num_chars));
  mylib::print_stderr(str29);
}

void PrintLength(classes::Node* node) {
  classes::Node* current = nullptr;
  int linked_list_len;
  StackRoot _root0(&node);
  StackRoot _root1(&current);

  current = node;
  linked_list_len = 0;
  while (true) {
    if (linked_list_len < 10) {
      mylib::print_stderr(StrFormat("  -> %d", current->i));
    }
    current = current->next;
    if (current == nullptr) {
      break;
    }
    linked_list_len += 1;
  }
  mylib::print_stderr(str31);
  mylib::print_stderr(StrFormat("  linked list len = %d", linked_list_len));
  mylib::print_stderr(str33);
}

void BenchmarkSimpleNode(int n) {
  classes::Node* next_ = nullptr;
  classes::Node* node = nullptr;
  StackRoot _root0(&next_);
  StackRoot _root1(&node);

  mylib::print_stderr(str34);
  mylib::print_stderr(str35);
  next_ = Alloc<Node>(nullptr, -1);
  for (int i = 0; i < n; ++i) {
    node = Alloc<Node>(next_, i);
    next_ = node;
  }
  PrintLength(node);
}

void PrintLengthBase(classes::Base* current) {
  int linked_list_len;
  StackRoot _root0(&current);

  linked_list_len = 0;
  while (true) {
    if (linked_list_len < 10) {
      mylib::print_stderr(StrFormat("  -> %s", current->TypeString()));
    }
    current = current->next;
    if (current == nullptr) {
      break;
    }
    linked_list_len += 1;
  }
  mylib::print_stderr(str37);
  mylib::print_stderr(StrFormat("  linked list len = %d", linked_list_len));
  mylib::print_stderr(str39);
}

void BenchmarkVirtualNodes(int n) {
  classes::Base* next_ = nullptr;
  classes::DerivedI* node1 = nullptr;
  BigStr* s1 = nullptr;
  BigStr* s2 = nullptr;
  classes::DerivedSS* node2 = nullptr;
  classes::Base* node3 = nullptr;
  classes::Base* current = nullptr;
  StackRoot _root0(&next_);
  StackRoot _root1(&node1);
  StackRoot _root2(&s1);
  StackRoot _root3(&s2);
  StackRoot _root4(&node2);
  StackRoot _root5(&node3);
  StackRoot _root6(&current);

  mylib::print_stderr(str40);
  mylib::print_stderr(str41);
  next_ = Alloc<Base>(nullptr);
  for (int i = 0; i < n; ++i) {
    node1 = Alloc<DerivedI>(next_, i);
    s1 = str(i);
    s2 = StrFormat("+%d", i);
    node2 = Alloc<DerivedSS>(node1, s1, s2);
    node3 = Alloc<Base>(node2);
    next_ = node3;
  }
  current = nullptr;
  current = node3;
  PrintLengthBase(current);
}

void run_benchmarks() {
  if (1) {
    BenchmarkWriter(30000);
  }
  if (1) {
    BenchmarkSimpleNode(10000);
  }
  if (1) {
    BenchmarkVirtualNodes(1000);
  }
}

}  // define namespace classes