diff --git a/src/frontends/lean/pp.cpp b/src/frontends/lean/pp.cpp index 9856ebb3a..d56281763 100644 --- a/src/frontends/lean/pp.cpp +++ b/src/frontends/lean/pp.cpp @@ -16,6 +16,7 @@ Author: Leonardo de Moura #include "library/private.h" #include "library/explicit.h" #include "library/num.h" +#include "library/simple_formatter.h" #include "frontends/lean/pp.h" #include "frontends/lean/pp_options.h" #include "frontends/lean/token_table.h" diff --git a/src/kernel/formatter.cpp b/src/kernel/formatter.cpp index 88e524ba7..efaaf93fb 100644 --- a/src/kernel/formatter.cpp +++ b/src/kernel/formatter.cpp @@ -6,264 +6,22 @@ Author: Leonardo de Moura */ #include #include "kernel/formatter.h" -#include "kernel/environment.h" -#include "kernel/find_fn.h" -#include "kernel/instantiate.h" -#include "kernel/free_vars.h" -#include "kernel/annotation.h" namespace lean { -bool is_used_name(expr const & t, name const & n) { - return static_cast( - find(t, [&](expr const & e, unsigned) { - return (is_constant(e) && const_name(e) == n) // t has a constant named n - || (is_local(e) && (mlocal_name(e) == n || local_pp_name(e) == n)); // t has a local constant named n - })); +static std::unique_ptr> g_print; + +void set_print_fn(std::function const & fn) { + g_print.reset(new std::function(fn)); } -name pick_unused_name(expr const & t, name const & s) { - name r = s; - unsigned i = 1; - while (is_used_name(t, r)) { - r = name(s).append_after(i); - i++; - } - return r; -} - -bool is_internal_name(name n) { - while (!n.is_atomic()) - n = n.get_prefix(); - return n.is_numeral(); -} - -static name g_x("x"); -pair binding_body_fresh(expr const & b, bool preserve_type) { - lean_assert(is_binding(b)); - name n = binding_name(b); - if (is_internal_name(n)) - n = g_x; - n = pick_unused_name(binding_body(b), n); - expr c = mk_local(n, preserve_type ? binding_domain(b) : expr(), binding_info(b)); - return mk_pair(instantiate(binding_body(b), c), c); -} - -static name g_internal("M"); -name fix_internal_name(name const & a) { - if (a.is_atomic()) { - if (a.is_numeral()) - return g_internal; - else - return a; - } else { - name p = fix_internal_name(a.get_prefix()); - if (p == a.get_prefix()) - return a; - else if (a.is_numeral()) - return name(p, a.get_numeral()); - else - return name(p, a.get_string()); - } -} - -/** - \brief Very basic printer for expressions. - It is mainly used when debugging code. -*/ -struct print_expr_fn { - std::ostream & m_out; - bool m_type0_as_bool; - - std::ostream & out() { return m_out; } - - bool is_atomic(expr const & a) { - return ::lean::is_atomic(a) || is_mlocal(a); - } - - void print_child(expr const & a) { - if (is_atomic(a)) { - print(a); - } else { - out() << "("; print(a); out() << ")"; - } - } - - bool print_let(expr const & a) { - if (!is_let_annotation(a)) - return false; - expr l = get_annotation_arg(a); - if (!is_app(l) || !is_lambda(app_fn(l))) - return false; - name n = binding_name(app_fn(l)); - expr t = binding_domain(app_fn(l)); - expr b = binding_body(app_fn(l)); - expr v = app_arg(l); - n = pick_unused_name(b, n); - expr c = mk_local(n, expr()); - b = instantiate(b, c); - out() << "let " << c; - out() << " : "; - print(t); - out() << " := "; - print(v); - out() << " in "; - print_child(b); - return true; - } - - void print_macro(expr const & a) { - if (!print_let(a)) { - macro_def(a).display(out()); - for (unsigned i = 0; i < macro_num_args(a); i++) { - out() << " "; print_child(macro_arg(a, i)); - } - } - } - - void print_sort(expr const & a) { - if (is_zero(sort_level(a))) { - if (m_type0_as_bool) - out() << "Prop"; - else - out() << "Type"; - } else if (m_type0_as_bool && is_one(sort_level(a))) { - out() << "Type"; - } else { - out() << "Type.{" << sort_level(a) << "}"; - } - } - - void print_app(expr const & e) { - expr const & f = app_fn(e); - if (is_app(f)) - print(f); - else - print_child(f); - out() << " "; - print_child(app_arg(e)); - } - - void print_arrow_body(expr const & a) { - if (is_atomic(a) || is_arrow(a)) - return print(a); - else - return print_child(a); - } - - void print_binding(char const * bname, expr e) { - expr_kind k = e.kind(); - out() << bname; - while (e.kind() == k) { - out() << " "; - auto p = binding_body_fresh(e); - expr const & n = p.second; - if (binding_info(e).is_implicit()) - out() << "{"; - else if (binding_info(e).is_cast()) - out() << "["; - else if (!binding_info(e).is_contextual()) - out() << "[["; - else if (binding_info(e).is_strict_implicit()) - out() << "{{"; - else - out() << "("; - out() << n << " : "; - print(binding_domain(e)); - if (binding_info(e).is_implicit()) - out() << "}"; - else if (binding_info(e).is_cast()) - out() << "]"; - else if (!binding_info(e).is_contextual()) - out() << "]]"; - else if (binding_info(e).is_strict_implicit()) - out() << "}}"; - else - out() << ")"; - e = p.first; - } - out() << ", "; - print_child(e); - } - - void print_const(expr const & a) { - list const & ls = const_levels(a); - out() << const_name(a); - if (!is_nil(ls)) { - out() << ".{"; - bool first = true; - for (auto l : ls) { - if (first) first = false; else out() << " "; - if (is_max(l) || is_imax(l)) - out() << "(" << l << ")"; - else - out() << l; - } - out() << "}"; - } - } - - void print(expr const & a) { - switch (a.kind()) { - case expr_kind::Meta: - out() << "?" << fix_internal_name(mlocal_name(a)); - break; - case expr_kind::Local: - out() << fix_internal_name(local_pp_name(a)); - break; - case expr_kind::Var: - out() << "#" << var_idx(a); - break; - case expr_kind::Constant: - print_const(a); - break; - case expr_kind::App: - print_app(a); - break; - case expr_kind::Lambda: - print_binding("fun", a); - break; - case expr_kind::Pi: - if (!is_arrow(a)) { - print_binding("Pi", a); - } else { - print_child(binding_domain(a)); - out() << " -> "; - print_arrow_body(lower_free_vars(binding_body(a), 1)); - } - break; - case expr_kind::Sort: - print_sort(a); - break; - case expr_kind::Macro: - print_macro(a); - break; - } - } - - print_expr_fn(std::ostream & out, bool type0_as_bool = true):m_out(out), m_type0_as_bool(type0_as_bool) {} - - void operator()(expr const & e) { - scoped_expr_caching set(false); - print(e); - } -}; - std::ostream & operator<<(std::ostream & out, expr const & e) { - print_expr_fn pr(out); - pr(e); + if (g_print) { + (*g_print)(out, e); + } else { + throw exception("print function is not available, Lean was not initialized correctly"); + } return out; } -formatter_factory mk_simple_formatter_factory() { - return [](environment const & env, options const & o) { // NOLINT - return formatter(o, [=](expr const & e, options const &) { - std::ostringstream s; - print_expr_fn pr(s, env.prop_proof_irrel()); - pr(e); - return format(s.str()); - }); - }; -} - void print(lean::expr const & a) { std::cout << a << std::endl; } } diff --git a/src/kernel/formatter.h b/src/kernel/formatter.h index ce015708d..d57134092 100644 --- a/src/kernel/formatter.h +++ b/src/kernel/formatter.h @@ -13,20 +13,6 @@ namespace lean { class expr; class environment; -/** \brief Return true iff \c t contains a constant named \c n or a local constant with named (pp or not) \c n */ -bool is_used_name(expr const & t, name const & n); -/** - \brief Return the body of the binding \c b, where variable #0 is replaced by a local constant with a "fresh" name. - The name is considered fresh if it is not used by a constant or local constant occuring in the body of \c b. - The fresh constant is also returned (second return value). - - \remark If preserve_type is false, then the local constant will not use binding_domain. -*/ -pair binding_body_fresh(expr const & b, bool preserve_type = false); - -/** \brief Return the body of the let-expression \c l, where variable #0 is replaced by a local constant with a "fresh" name. */ -pair let_body_fresh(expr const & l, bool preserve_type = false); - class formatter { std::function m_fn; options m_options; @@ -40,8 +26,8 @@ public: typedef std::function formatter_factory; std::ostream & operator<<(std::ostream & out, expr const & e); -/** \brief Create a simple formatter object based on operator */ -formatter_factory mk_simple_formatter_factory(); typedef std::function pp_fn; + +void set_print_fn(std::function const & fn); } diff --git a/src/library/CMakeLists.txt b/src/library/CMakeLists.txt index 838ab0bb0..2abb631dd 100644 --- a/src/library/CMakeLists.txt +++ b/src/library/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(library deep_copy.cpp expr_lt.cpp io_state.cpp occurs.cpp standard_kernel.cpp hott_kernel.cpp sorry.cpp unifier.cpp unifier_plugin.cpp inductive_unifier_plugin.cpp explicit.cpp num.cpp string.cpp opaque_hints.cpp head_map.cpp - match.cpp definition_cache.cpp declaration_index.cpp) + match.cpp definition_cache.cpp declaration_index.cpp + simple_formatter.cpp) target_link_libraries(library ${LEAN_LIBS}) diff --git a/src/library/io_state.cpp b/src/library/io_state.cpp index ce637ed4e..f313ab4a9 100644 --- a/src/library/io_state.cpp +++ b/src/library/io_state.cpp @@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #include "kernel/kernel_exception.h" +#include "library/simple_formatter.h" #include "library/io_state.h" namespace lean { diff --git a/src/library/kernel_bindings.cpp b/src/library/kernel_bindings.cpp index 8414ad20d..2961b56dd 100644 --- a/src/library/kernel_bindings.cpp +++ b/src/library/kernel_bindings.cpp @@ -31,6 +31,7 @@ Author: Leonardo de Moura #include "library/normalize.h" #include "library/module.h" #include "library/opaque_hints.h" +#include "library/simple_formatter.h" // Lua Bindings for the Kernel classes. We do not include the Lua // bindings in the kernel because we do not want to inflate the Kernel. diff --git a/src/library/simple_formatter.cpp b/src/library/simple_formatter.cpp new file mode 100644 index 000000000..ee39ed43d --- /dev/null +++ b/src/library/simple_formatter.cpp @@ -0,0 +1,269 @@ +/* +Copyright (c) 2014 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#include +#include "kernel/formatter.h" +#include "kernel/environment.h" +#include "kernel/find_fn.h" +#include "kernel/instantiate.h" +#include "kernel/free_vars.h" +#include "kernel/annotation.h" +#include "library/simple_formatter.h" + +namespace lean { +bool is_used_name(expr const & t, name const & n) { + return static_cast( + find(t, [&](expr const & e, unsigned) { + return (is_constant(e) && const_name(e) == n) // t has a constant named n + || (is_local(e) && (mlocal_name(e) == n || local_pp_name(e) == n)); // t has a local constant named n + })); +} + +name pick_unused_name(expr const & t, name const & s) { + name r = s; + unsigned i = 1; + while (is_used_name(t, r)) { + r = name(s).append_after(i); + i++; + } + return r; +} + +bool is_internal_name(name n) { + while (!n.is_atomic()) + n = n.get_prefix(); + return n.is_numeral(); +} + +static name g_x("x"); +pair binding_body_fresh(expr const & b, bool preserve_type) { + lean_assert(is_binding(b)); + name n = binding_name(b); + if (is_internal_name(n)) + n = g_x; + n = pick_unused_name(binding_body(b), n); + expr c = mk_local(n, preserve_type ? binding_domain(b) : expr(), binding_info(b)); + return mk_pair(instantiate(binding_body(b), c), c); +} + +static name g_internal("M"); +name fix_internal_name(name const & a) { + if (a.is_atomic()) { + if (a.is_numeral()) + return g_internal; + else + return a; + } else { + name p = fix_internal_name(a.get_prefix()); + if (p == a.get_prefix()) + return a; + else if (a.is_numeral()) + return name(p, a.get_numeral()); + else + return name(p, a.get_string()); + } +} + +/** + \brief Very basic printer for expressions. + It is mainly used when debugging code. +*/ +struct print_expr_fn { + std::ostream & m_out; + bool m_type0_as_bool; + + std::ostream & out() { return m_out; } + + bool is_atomic(expr const & a) { + return ::lean::is_atomic(a) || is_mlocal(a); + } + + void print_child(expr const & a) { + if (is_atomic(a)) { + print(a); + } else { + out() << "("; print(a); out() << ")"; + } + } + + bool print_let(expr const & a) { + if (!is_let_annotation(a)) + return false; + expr l = get_annotation_arg(a); + if (!is_app(l) || !is_lambda(app_fn(l))) + return false; + name n = binding_name(app_fn(l)); + expr t = binding_domain(app_fn(l)); + expr b = binding_body(app_fn(l)); + expr v = app_arg(l); + n = pick_unused_name(b, n); + expr c = mk_local(n, expr()); + b = instantiate(b, c); + out() << "let " << c; + out() << " : "; + print(t); + out() << " := "; + print(v); + out() << " in "; + print_child(b); + return true; + } + + void print_macro(expr const & a) { + if (!print_let(a)) { + macro_def(a).display(out()); + for (unsigned i = 0; i < macro_num_args(a); i++) { + out() << " "; print_child(macro_arg(a, i)); + } + } + } + + void print_sort(expr const & a) { + if (is_zero(sort_level(a))) { + if (m_type0_as_bool) + out() << "Prop"; + else + out() << "Type"; + } else if (m_type0_as_bool && is_one(sort_level(a))) { + out() << "Type"; + } else { + out() << "Type.{" << sort_level(a) << "}"; + } + } + + void print_app(expr const & e) { + expr const & f = app_fn(e); + if (is_app(f)) + print(f); + else + print_child(f); + out() << " "; + print_child(app_arg(e)); + } + + void print_arrow_body(expr const & a) { + if (is_atomic(a) || is_arrow(a)) + return print(a); + else + return print_child(a); + } + + void print_binding(char const * bname, expr e) { + expr_kind k = e.kind(); + out() << bname; + while (e.kind() == k) { + out() << " "; + auto p = binding_body_fresh(e); + expr const & n = p.second; + if (binding_info(e).is_implicit()) + out() << "{"; + else if (binding_info(e).is_cast()) + out() << "["; + else if (!binding_info(e).is_contextual()) + out() << "[["; + else if (binding_info(e).is_strict_implicit()) + out() << "{{"; + else + out() << "("; + out() << n << " : "; + print(binding_domain(e)); + if (binding_info(e).is_implicit()) + out() << "}"; + else if (binding_info(e).is_cast()) + out() << "]"; + else if (!binding_info(e).is_contextual()) + out() << "]]"; + else if (binding_info(e).is_strict_implicit()) + out() << "}}"; + else + out() << ")"; + e = p.first; + } + out() << ", "; + print_child(e); + } + + void print_const(expr const & a) { + list const & ls = const_levels(a); + out() << const_name(a); + if (!is_nil(ls)) { + out() << ".{"; + bool first = true; + for (auto l : ls) { + if (first) first = false; else out() << " "; + if (is_max(l) || is_imax(l)) + out() << "(" << l << ")"; + else + out() << l; + } + out() << "}"; + } + } + + void print(expr const & a) { + switch (a.kind()) { + case expr_kind::Meta: + out() << "?" << fix_internal_name(mlocal_name(a)); + break; + case expr_kind::Local: + out() << fix_internal_name(local_pp_name(a)); + break; + case expr_kind::Var: + out() << "#" << var_idx(a); + break; + case expr_kind::Constant: + print_const(a); + break; + case expr_kind::App: + print_app(a); + break; + case expr_kind::Lambda: + print_binding("fun", a); + break; + case expr_kind::Pi: + if (!is_arrow(a)) { + print_binding("Pi", a); + } else { + print_child(binding_domain(a)); + out() << " -> "; + print_arrow_body(lower_free_vars(binding_body(a), 1)); + } + break; + case expr_kind::Sort: + print_sort(a); + break; + case expr_kind::Macro: + print_macro(a); + break; + } + } + + print_expr_fn(std::ostream & out, bool type0_as_bool = true):m_out(out), m_type0_as_bool(type0_as_bool) {} + + void operator()(expr const & e) { + scoped_expr_caching set(false); + print(e); + } +}; + +formatter_factory mk_simple_formatter_factory() { + return [](environment const & env, options const & o) { // NOLINT + return formatter(o, [=](expr const & e, options const &) { + std::ostringstream s; + print_expr_fn pr(s, env.prop_proof_irrel()); + pr(e); + return format(s.str()); + }); + }; +} + +void init_default_print_fn() { + set_print_fn([](std::ostream & out, expr const & e) { + print_expr_fn pr(out); + pr(e); + }); +} +} diff --git a/src/library/simple_formatter.h b/src/library/simple_formatter.h new file mode 100644 index 000000000..d63cba0e1 --- /dev/null +++ b/src/library/simple_formatter.h @@ -0,0 +1,30 @@ +/* +Copyright (c) 2014 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#pragma once +#include "kernel/formatter.h" + +namespace lean { +/** \brief Return true iff \c t contains a constant named \c n or a local constant with named (pp or not) \c n */ +bool is_used_name(expr const & t, name const & n); +/** + \brief Return the body of the binding \c b, where variable #0 is replaced by a local constant with a "fresh" name. + The name is considered fresh if it is not used by a constant or local constant occuring in the body of \c b. + The fresh constant is also returned (second return value). + + \remark If preserve_type is false, then the local constant will not use binding_domain. +*/ +pair binding_body_fresh(expr const & b, bool preserve_type = false); + +/** \brief Return the body of the let-expression \c l, where variable #0 is replaced by a local constant with a "fresh" name. */ +pair let_body_fresh(expr const & l, bool preserve_type = false); + +/** \brief Create a simple formatter object based on operator */ +formatter_factory mk_simple_formatter_factory(); + +/** \brief Use simple formatter as the default print function */ +void init_default_print_fn(); +} diff --git a/src/library/unifier.cpp b/src/library/unifier.cpp index 02138dc98..0fed3cfcc 100644 --- a/src/library/unifier.cpp +++ b/src/library/unifier.cpp @@ -26,6 +26,7 @@ Author: Leonardo de Moura #include "library/opaque_hints.h" #include "library/unifier_plugin.h" #include "library/kernel_bindings.h" +#include "library/simple_formatter.h" #ifndef LEAN_DEFAULT_UNIFIER_MAX_STEPS #define LEAN_DEFAULT_UNIFIER_MAX_STEPS 20000 diff --git a/src/shell/lean.cpp b/src/shell/lean.cpp index 93f4d7a29..e362c5e10 100644 --- a/src/shell/lean.cpp +++ b/src/shell/lean.cpp @@ -26,6 +26,7 @@ Author: Leonardo de Moura #include "library/io_state_stream.h" #include "library/definition_cache.h" #include "library/declaration_index.h" +#include "library/simple_formatter.h" #include "library/error_handling/error_handling.h" #include "frontends/lean/parser.h" #include "frontends/lean/pp.h" @@ -150,6 +151,7 @@ public: int main(int argc, char ** argv) { lean::save_stack_info(); + lean::init_default_print_fn(); lean::register_modules(); bool export_objects = false; unsigned trust_lvl = 0; diff --git a/src/tests/kernel/CMakeLists.txt b/src/tests/kernel/CMakeLists.txt index 76a20db2d..59464eee4 100644 --- a/src/tests/kernel/CMakeLists.txt +++ b/src/tests/kernel/CMakeLists.txt @@ -11,13 +11,13 @@ add_executable(free_vars free_vars.cpp) target_link_libraries(free_vars "kernel" "util" ${EXTRA_LIBS}) add_test(free_vars ${CMAKE_CURRENT_BINARY_DIR}/free_vars) add_executable(replace replace.cpp) -target_link_libraries(replace "kernel" "util" ${EXTRA_LIBS}) +target_link_libraries(replace "library" "kernel" "util" ${EXTRA_LIBS}) add_test(replace ${CMAKE_CURRENT_BINARY_DIR}/replace) add_executable(environment environment.cpp) -target_link_libraries(environment "kernel" "util" ${EXTRA_LIBS}) +target_link_libraries(environment "library" "kernel" "util" ${EXTRA_LIBS}) add_test(environment ${CMAKE_CURRENT_BINARY_DIR}/environment) add_executable(metavar metavar.cpp) -target_link_libraries(metavar "kernel" "util" ${EXTRA_LIBS}) +target_link_libraries(metavar "library" "kernel" "util" ${EXTRA_LIBS}) add_test(metavar ${CMAKE_CURRENT_BINARY_DIR}/metavar) add_executable(instantiate instantiate.cpp) target_link_libraries(instantiate "kernel" "util" ${EXTRA_LIBS}) diff --git a/src/tests/kernel/environment.cpp b/src/tests/kernel/environment.cpp index 402817b84..cb334fc62 100644 --- a/src/tests/kernel/environment.cpp +++ b/src/tests/kernel/environment.cpp @@ -12,6 +12,7 @@ Author: Leonardo de Moura #include "kernel/type_checker.h" #include "kernel/abstract.h" #include "kernel/kernel_exception.h" +#include "library/simple_formatter.cpp" using namespace lean; static environment add_decl(environment const & env, declaration const & d) { @@ -243,6 +244,7 @@ public: int main() { save_stack_info(); + init_default_print_fn(); tst1(); tst2(); tst3(); diff --git a/src/tests/kernel/expr.cpp b/src/tests/kernel/expr.cpp index 16107d0f4..f4338fdc1 100644 --- a/src/tests/kernel/expr.cpp +++ b/src/tests/kernel/expr.cpp @@ -16,6 +16,7 @@ Author: Leonardo de Moura #include "kernel/abstract.h" #include "kernel/instantiate.h" #include "library/max_sharing.h" +#include "library/simple_formatter.h" #include "library/deep_copy.h" #include "library/kernel_serializer.h" using namespace lean; @@ -362,6 +363,7 @@ static void tst18() { int main() { save_stack_info(); + init_default_print_fn(); lean_assert(sizeof(expr) == sizeof(optional)); tst1(); tst2(); diff --git a/src/tests/kernel/max_sharing.cpp b/src/tests/kernel/max_sharing.cpp index 6ab383a74..ebe973546 100644 --- a/src/tests/kernel/max_sharing.cpp +++ b/src/tests/kernel/max_sharing.cpp @@ -6,8 +6,9 @@ Author: Leonardo de Moura */ #include #include "util/test.h" -#include "library/max_sharing.h" #include "kernel/abstract.h" +#include "library/max_sharing.h" +#include "library/simple_formatter.h" using namespace lean; static void tst1() { @@ -59,6 +60,7 @@ static void tst3() { int main() { save_stack_info(); + init_default_print_fn(); scoped_expr_caching set(false); tst1(); tst2(); diff --git a/src/tests/kernel/metavar.cpp b/src/tests/kernel/metavar.cpp index 9d92b409e..d653a8c88 100644 --- a/src/tests/kernel/metavar.cpp +++ b/src/tests/kernel/metavar.cpp @@ -14,6 +14,7 @@ Author: Leonardo de Moura #include "kernel/metavar.h" #include "kernel/instantiate.h" #include "kernel/abstract.h" +#include "library/simple_formatter.h" using namespace lean; void collect_assumptions(justification const & j, buffer & r) { @@ -145,6 +146,7 @@ static void tst4() { int main() { save_stack_info(); + init_default_print_fn(); tst1(); tst2(); tst3(); diff --git a/src/tests/kernel/replace.cpp b/src/tests/kernel/replace.cpp index 78b11ca76..df895ad6c 100644 --- a/src/tests/kernel/replace.cpp +++ b/src/tests/kernel/replace.cpp @@ -11,6 +11,7 @@ Author: Leonardo de Moura #include "kernel/instantiate.h" #include "kernel/expr_maps.h" #include "kernel/replace_fn.h" +#include "library/simple_formatter.h" using namespace lean; expr mk_big(expr f, unsigned depth, unsigned val) { @@ -58,6 +59,7 @@ public: int main() { save_stack_info(); + init_default_print_fn(); tst1(); tst2(); std::cout << "done" << "\n";