#include <iostream>

#include <ir/local-graph.h>
#include <wasm-builder.h>
#include <wasm.h>

using namespace wasm;

int main() {
  Module wasm;
  Builder builder(wasm);

  {
    Function foo;
    foo.type = Signature(Type::none, Type::none);
    foo.vars = {Type::i32};
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(0, Type::i32);
    foo.body = builder.makeBlock({
      builder.makeLocalSet(0, builder.makeConst(Literal(int32_t(0)))),
      // two equivalent gets, as both are preceded by the same single set
      builder.makeDrop(get1),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(graph.equivalent(get1, get2));
  }

  {
    Function foo;
    foo.type = Signature(Type::none, Type::none);
    foo.vars = {Type::i32};
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(0, Type::i32);
    foo.body = builder.makeBlock({
      // two non-equivalent gets, as there is a set in between them
      builder.makeDrop(get1),
      builder.makeLocalSet(0, builder.makeConst(Literal(int32_t(0)))),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(!graph.equivalent(get1, get2));
  }

  {
    Function foo;
    foo.type = Signature({Type::i32}, Type::none);
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(0, Type::i32);
    foo.body = builder.makeBlock({
      // two equivalent gets of the same parameter
      builder.makeDrop(get1),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(graph.equivalent(get1, get2));
  }

  {
    Function foo;
    foo.type = Signature({Type::i32}, Type::none);
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(0, Type::i32);
    foo.body = builder.makeBlock({
      // two non-equivalent gets of the same parameter, as there is a set
      builder.makeDrop(get1),
      builder.makeLocalSet(0, builder.makeConst(Literal(int32_t(0)))),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(!graph.equivalent(get1, get2));
  }

  {
    Function foo;
    foo.type = Signature({Type::i32, Type::i32}, Type::none);
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(1, Type::i32);
    foo.body = builder.makeBlock({
      // two non-equivalent gets as they are of different parameters
      builder.makeDrop(get1),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(!graph.equivalent(get1, get2));
  }

  {
    Function foo;
    foo.type = Signature(Type::none, Type::none);
    foo.vars = {Type::i32, Type::i32};
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(1, Type::i32);
    foo.body = builder.makeBlock({
      // two equivalent gets, even though they have a different index, as both
      // use the zero initialized value
      builder.makeDrop(get1),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(graph.equivalent(get1, get2));
  }

  {
    Function foo;
    foo.type = Signature(Type::none, Type::none);
    foo.vars = {Type::i32, Type::f64};
    auto* get1 = builder.makeLocalGet(0, Type::i32);
    auto* get2 = builder.makeLocalGet(1, Type::f64);
    foo.body = builder.makeBlock({
      // two non-equivalent gets as their zero-init value is different
      builder.makeDrop(get1),
      builder.makeDrop(get2),
    });
    LocalGraph graph(&foo);
    assert(!graph.equivalent(get1, get2));
  }

  std::cout << "Success." << std::endl;

  return 0;
}