Merge remote-tracking branch 'origin/master' into init_auto
authorJean Privat <jean@pryen.org>
Thu, 30 Jun 2016 18:15:25 +0000 (14:15 -0400)
committerJean Privat <jean@pryen.org>
Thu, 30 Jun 2016 18:15:25 +0000 (14:15 -0400)
# Conflicts:
# tests/sav/base_init_basic_alt3.res
# tests/sav/base_init_super_call2_alt3.res
# tests/sav/base_init_super_call2_alt6.res
# tests/sav/error_class_glob.res
# tests/sav/nitdoc_args4.res
# tests/sav/nitlight_args1.res
# tests/sav/nitmetrics_args1.res
# tests/sav/nituml_args3.res
# tests/sav/nituml_args4.res
# tests/sav/test_highlight_args1.res

761 files changed:
.gitattributes
.gitignore
CONTRIBUTING.md [new file with mode: 0644]
NOTICE
README.md
benchmarks/bench_engines.sh
benchmarks/csv/README.md [new file with mode: 0644]
benchmarks/csv/csv_bench.sh [new file with mode: 0755]
benchmarks/csv/scripts/JavaCSV.java [new file with mode: 0644]
benchmarks/csv/scripts/csv_gen.nit [new file with mode: 0644]
benchmarks/csv/scripts/go_csv.go [new file with mode: 0644]
benchmarks/csv/scripts/nit_csv.nit [new file with mode: 0644]
benchmarks/csv/scripts/python_csv.py [new file with mode: 0644]
benchmarks/csv/scripts/python_stdcsv.py [new file with mode: 0644]
benchmarks/csv/scripts/ruby_csv.rb [new file with mode: 0644]
benchmarks/json/.gitignore [new file with mode: 0644]
benchmarks/json/Makefile
benchmarks/json/bench_json.sh
benchmarks/json/inputs/multiply_gov.sh
benchmarks/markdown/bench_markdown.sh
benchmarks/markdown/benches/gen_benches.nit
benchmarks/polygons/.gitignore [new file with mode: 0644]
contrib/action_nitro/Makefile
contrib/action_nitro/src/action_nitro.nit
contrib/asteronits/Makefile
contrib/asteronits/src/android.nit
contrib/asteronits/src/game_logic.nit
contrib/asteronits/src/touch_ui.nit
contrib/benitlux/.gitignore
contrib/benitlux/Makefile
contrib/benitlux/README.md
contrib/benitlux/android/res/.gitignore [new file with mode: 0644]
contrib/benitlux/android/res/values/styles.xml [new file with mode: 0644]
contrib/benitlux/art/icon.svg [new file with mode: 0644]
contrib/benitlux/art/notif.svg [new file with mode: 0644]
contrib/benitlux/ios/.gitignore [new file with mode: 0644]
contrib/benitlux/net.xymus.benitlux.txt [new file with mode: 0644]
contrib/benitlux/package.ini
contrib/benitlux/src/benitlux_model.nit
contrib/benitlux/src/client/android.nit [new file with mode: 0644]
contrib/benitlux/src/client/android_proto.nit [new file with mode: 0644]
contrib/benitlux/src/client/base.nit [new file with mode: 0644]
contrib/benitlux/src/client/client.nit [new file with mode: 0644]
contrib/benitlux/src/client/features/checkins.nit [new file with mode: 0644]
contrib/benitlux/src/client/features/debug.nit [new file with mode: 0644]
contrib/benitlux/src/client/features/push.nit [new file with mode: 0644]
contrib/benitlux/src/client/features/translations.nit [new file with mode: 0644]
contrib/benitlux/src/client/ios.nit [new file with mode: 0644]
contrib/benitlux/src/client/views/beer_views.nit [new file with mode: 0644]
contrib/benitlux/src/client/views/home_views.nit [new file with mode: 0644]
contrib/benitlux/src/client/views/social_views.nit [new file with mode: 0644]
contrib/benitlux/src/client/views/user_views.nit [new file with mode: 0644]
contrib/benitlux/src/server/benitlux_controller.nit [moved from contrib/benitlux/src/benitlux_controller.nit with 85% similarity]
contrib/benitlux/src/server/benitlux_daily.nit [moved from contrib/benitlux/src/benitlux_daily.nit with 100% similarity]
contrib/benitlux/src/server/benitlux_db.nit [moved from contrib/benitlux/src/benitlux_db.nit with 93% similarity]
contrib/benitlux/src/server/benitlux_social.nit [moved from contrib/benitlux/src/benitlux_social.nit with 99% similarity]
contrib/benitlux/src/server/benitlux_view.nit [moved from contrib/benitlux/src/benitlux_view.nit with 100% similarity]
contrib/benitlux/src/server/server.nit [moved from contrib/benitlux/src/benitlux_web.nit with 80% similarity]
contrib/crazy_moles/Makefile
contrib/friendz/Makefile
contrib/friendz/src/friendz.nit
contrib/github_search_for_jni/Makefile
contrib/header_keeper/Makefile
contrib/inkscape_tools/Makefile
contrib/jwrapper/Makefile
contrib/jwrapper/src/model.nit
contrib/memory/Makefile
contrib/memplot/memplot.nit [new file with mode: 0644]
contrib/memplot/memplot.r [new file with mode: 0644]
contrib/memplot/package.ini [new file with mode: 0644]
contrib/mnit_test/Makefile
contrib/model_viewer/Makefile
contrib/model_viewer/README.md
contrib/model_viewer/src/model_viewer.nit
contrib/neo_doxygen/Makefile
contrib/nitcc/src/autom.nit
contrib/nitcc/src/nitcc.sablecc
contrib/nitcc/src/nitcc_parser_gen.nit
contrib/nitcc/src/re2nfa.nit
contrib/nitcc/tests/lexer-prefixes.sablecc [new file with mode: 0644]
contrib/nitcc/tests/sav/lexer-prefixes.input.res [new file with mode: 0644]
contrib/nitester/Makefile
contrib/nitiwiki/Makefile
contrib/nitiwiki/src/wiki_base.nit
contrib/nitiwiki/src/wiki_html.nit
contrib/nitiwiki/src/wiki_links.nit
contrib/nitrpg/Makefile
contrib/nitrpg/src/test_helper.nit
contrib/objcwrapper/Makefile
contrib/online_ide/Makefile
contrib/online_ide/sources/nit/pnacl_nit.nit
contrib/opportunity/Makefile
contrib/opportunity/src/opportunity_controller.nit
contrib/opportunity/src/opportunity_model.nit
contrib/pep8analysis/Makefile
contrib/pep8analysis/package.ini
contrib/refund/Makefile
contrib/refund/src/refund_json.nit
contrib/refund/tests/res/recl_soin_error2.res
contrib/rss_downloader/Makefile
contrib/simplan/Makefile
contrib/simplan/simplan.nit
contrib/sort_downloads/Makefile
contrib/tinks/Makefile
contrib/tinks/src/client/client.nit
contrib/tinks/src/client/linux_client.nit
contrib/tinks/src/client/tinks_vr.nit [new file with mode: 0644]
contrib/tinks/src/game/framework.nit
contrib/tinks/src/game/tanks.nit
contrib/tnitter/Makefile
contrib/tnitter/package.ini
contrib/tnitter/src/action.nit
contrib/tnitter/src/push.nit
contrib/tnitter/src/tnitter_app.nit
contrib/tnitter/src/tnitter_app_android.nit
contrib/xymus_net/.gitignore [new file with mode: 0644]
contrib/xymus_net/Makefile [new file with mode: 0644]
contrib/xymus_net/README.md [new file with mode: 0644]
contrib/xymus_net/package.ini [new file with mode: 0644]
contrib/xymus_net/xymus_net.nit [moved from lib/nitcorn/examples/src/xymus_net.nit with 96% similarity]
examples/calculator/Makefile
examples/calculator/README.md
examples/calculator/doc/android-scientific.png [new file with mode: 0644]
examples/calculator/doc/ios-scientific.png [new file with mode: 0644]
examples/calculator/doc/linux-scientific.png [new file with mode: 0644]
examples/calculator/package.ini
examples/calculator/src/android_calculator.nit [new file with mode: 0644]
examples/calculator/src/calculator.nit
examples/calculator/src/calculator_logic.nit
examples/calculator/src/calculator_test.nit
examples/calculator/src/ios_calculator.nit [new file with mode: 0644]
examples/calculator/src/scientific_calculator.nit [new file with mode: 0644]
examples/fibonacci.nit
examples/rosettacode/perlin_noise.nit
lib/a_star.nit
lib/android/NitActivity.java
lib/android/README.md
lib/android/android.nit
lib/android/dalvik.nit
lib/android/game.nit [new file with mode: 0644]
lib/android/input_events.nit
lib/android/key_event.nit [new file with mode: 0644]
lib/android/landscape.nit
lib/android/nit_activity.nit
lib/android/portrait.nit
lib/android/sensors.nit
lib/android/ui/native_ui.nit
lib/android/ui/ui.nit
lib/android/wifi.nit
lib/app/README.md
lib/app/audio.nit
lib/app/data_store.nit
lib/app/examples/.gitignore [new file with mode: 0644]
lib/app/examples/Makefile [new file with mode: 0644]
lib/app/examples/http_request_example.nit [new file with mode: 0644]
lib/app/examples/ui_example.nit [new file with mode: 0644]
lib/app/http_request.nit
lib/app/package.ini
lib/app/ui.nit
lib/base64.nit
lib/binary/binary.nit
lib/bucketed_game.nit
lib/cocoa/foundation.nit
lib/core/bytes.nit
lib/core/codecs/iso8859_1.nit
lib/core/codecs/utf8.nit
lib/core/collection/abstract_collection.nit
lib/core/collection/array.nit
lib/core/collection/list.nit
lib/core/error.nit
lib/core/exec.nit
lib/core/file.nit
lib/core/fixed_ints.nit
lib/core/kernel.nit
lib/core/math.nit
lib/core/re.nit
lib/core/stream.nit
lib/core/text/abstract_text.nit
lib/core/text/flat.nit
lib/core/text/native.nit
lib/core/text/ropes.nit
lib/core/text/string_search.nit
lib/core/text/test_abstract_text.nit [new file with mode: 0644]
lib/crapto/crapto.nit [new file with mode: 0644]
lib/crapto/english_utils.nit [new file with mode: 0644]
lib/crapto/examples/repeating_key_xor_cipher.txt [new file with mode: 0644]
lib/crapto/examples/repeating_key_xor_solve.nit [new file with mode: 0644]
lib/crapto/package.ini [new file with mode: 0644]
lib/crapto/xor.nit [new file with mode: 0644]
lib/crypto/basic_ciphers.nit [moved from lib/crypto.nit with 89% similarity]
lib/crypto/crypto.nit [new file with mode: 0644]
lib/crypto/package.ini [moved from lib/crypto.ini with 89% similarity]
lib/crypto/xor_ciphers.nit [new file with mode: 0644]
lib/csv/csv.nit
lib/csv/test_csv.nit [deleted file]
lib/dot/dot.nit [new file with mode: 0644]
lib/dot/exemples/clusters.nit [new file with mode: 0644]
lib/dot/exemples/hello.nit [new file with mode: 0644]
lib/dot/exemples/undirected_clusters.nit [new file with mode: 0644]
lib/dot/package.ini [new file with mode: 0644]
lib/gamnit/cameras.nit
lib/gamnit/depth/depth.nit
lib/gamnit/depth/depth_core.nit
lib/gamnit/depth/model_dimensions.nit [new file with mode: 0644]
lib/gamnit/depth/more_materials.nit
lib/gamnit/depth/more_meshes.nit
lib/gamnit/depth/more_models.nit
lib/gamnit/depth/selection.nit [new file with mode: 0644]
lib/gamnit/display.nit
lib/gamnit/display_android.nit
lib/gamnit/display_linux.nit
lib/gamnit/egl.nit
lib/gamnit/flat.nit
lib/gamnit/limit_fps.nit
lib/gamnit/programs.nit
lib/gamnit/texture_atlas_parser.nit [moved from contrib/asteronits/src/texture_atlas_parser.nit with 98% similarity]
lib/gamnit/textures.nit
lib/geometry/polygon.nit
lib/glesv2/glesv2.nit
lib/gtk/v3_4/gtk_assistant.nit
lib/gtk/v3_4/gtk_core.nit
lib/gtk/v3_4/gtk_dialogs.nit
lib/gtk/v3_4/gtk_widgets_ext.nit
lib/ios/ui/ui.nit
lib/java/collections.nit
lib/java/ffi_support.nit [moved from lib/java/base.nit with 94% similarity]
lib/java/io.nit
lib/java/java.nit
lib/json/serialization.nit
lib/json/static.nit
lib/json/string_parser.nit
lib/jvm.nit
lib/libevent.nit
lib/linux/ui.nit
lib/logic/lexpr.nit [new file with mode: 0644]
lib/logic/logic.nit [new file with mode: 0644]
lib/logic/package.ini [new file with mode: 0644]
lib/markdown/Makefile
lib/mnit/android/android_app.nit
lib/mnit/mnit_fps.nit
lib/more_collections.nit
lib/mpd.nit
lib/mpi/mpi.nit
lib/nitcorn/README.md
lib/nitcorn/examples/Makefile
lib/nitcorn/examples/src/htcpcp_server.nit
lib/nitcorn/examples/www/hello_world/dir/binary_file.png [new file with mode: 0644]
lib/nitcorn/file_server.nit
lib/nitcorn/http_request.nit
lib/nitcorn/http_response.nit
lib/nitcorn/media_types.nit
lib/nitcorn/reactor.nit
lib/nitcorn/restful.nit
lib/nitcorn/server_config.nit
lib/nitcorn/sessions.nit
lib/nitcorn/vararg_routes.nit
lib/noise.nit
lib/performance_analysis.nit
lib/popcorn/.gitignore [new file with mode: 0644]
lib/popcorn/Makefile [new file with mode: 0644]
lib/popcorn/README.md [new file with mode: 0644]
lib/popcorn/examples/angular/example_angular.nit [new file with mode: 0644]
lib/popcorn/examples/angular/test_example_angular.nit [new file with mode: 0644]
lib/popcorn/examples/angular/test_example_angular.sav/test_example_angular.res [new file with mode: 0644]
lib/popcorn/examples/angular/www/index.html [new file with mode: 0644]
lib/popcorn/examples/angular/www/javascripts/ng-example.js [new file with mode: 0644]
lib/popcorn/examples/angular/www/views/index.html [new file with mode: 0644]
lib/popcorn/examples/handlers/example_post_handler.nit [new file with mode: 0644]
lib/popcorn/examples/handlers/example_query_string.nit [new file with mode: 0644]
lib/popcorn/examples/handlers/test_example_post_handler.nit [new file with mode: 0644]
lib/popcorn/examples/handlers/test_example_post_handler.sav/test_example_post_handler.res [new file with mode: 0644]
lib/popcorn/examples/handlers/test_example_query_string.nit [new file with mode: 0644]
lib/popcorn/examples/handlers/test_example_query_string.sav/test_example_query_string.res [new file with mode: 0644]
lib/popcorn/examples/hello_world/example_hello.nit [new file with mode: 0644]
lib/popcorn/examples/hello_world/test_example_hello.nit [new file with mode: 0644]
lib/popcorn/examples/hello_world/test_example_hello.sav/test_example_hello.res [new file with mode: 0644]
lib/popcorn/examples/middlewares/example_advanced_logger.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/example_html_error_handler.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/example_simple_error_handler.nit [moved from tests/test_realtime.nit with 58% similarity]
lib/popcorn/examples/middlewares/example_simple_logger.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_advanced_logger.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_advanced_logger.sav/test_example_advanced_logger.res [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_html_error_handler.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_html_error_handler.sav/test_example_html_error_handler.res [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_simple_error_handler.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_simple_error_handler.sav/test_example_simple_error_handler.res [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_simple_logger.nit [new file with mode: 0644]
lib/popcorn/examples/middlewares/test_example_simple_logger.sav/test_example_simple_logger.res [new file with mode: 0644]
lib/popcorn/examples/mongodb/example_mongodb.nit [new file with mode: 0644]
lib/popcorn/examples/routing/example_glob_route.nit [new file with mode: 0644]
lib/popcorn/examples/routing/example_param_route.nit [new file with mode: 0644]
lib/popcorn/examples/routing/example_router.nit [new file with mode: 0644]
lib/popcorn/examples/routing/test_example_glob_route.nit [new file with mode: 0644]
lib/popcorn/examples/routing/test_example_glob_route.sav/test_example_glob_route.res [new file with mode: 0644]
lib/popcorn/examples/routing/test_example_param_route.nit [new file with mode: 0644]
lib/popcorn/examples/routing/test_example_param_route.sav/test_example_param_route.res [new file with mode: 0644]
lib/popcorn/examples/routing/test_example_router.nit [new file with mode: 0644]
lib/popcorn/examples/routing/test_example_router.sav/test_example_router.res [new file with mode: 0644]
lib/popcorn/examples/sessions/example_session.nit [new file with mode: 0644]
lib/popcorn/examples/sessions/test_example_session.nit [new file with mode: 0644]
lib/popcorn/examples/sessions/test_example_session.sav/test_example_session.res [new file with mode: 0644]
lib/popcorn/examples/static_files/example_static.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/example_static_default.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/example_static_multiple.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/files/index.html [new file with mode: 0644]
lib/popcorn/examples/static_files/public/css/style.css [new file with mode: 0644]
lib/popcorn/examples/static_files/public/default.html [new file with mode: 0644]
lib/popcorn/examples/static_files/public/hello.html [new file with mode: 0644]
lib/popcorn/examples/static_files/public/images/trollface.jpg [new file with mode: 0644]
lib/popcorn/examples/static_files/public/js/app.js [new file with mode: 0644]
lib/popcorn/examples/static_files/test_example_static.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/test_example_static.sav/test_example_static.res [new file with mode: 0644]
lib/popcorn/examples/static_files/test_example_static_default.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/test_example_static_default.sav/test_example_static_default.res [new file with mode: 0644]
lib/popcorn/examples/static_files/test_example_static_multiple.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/test_example_static_multiple.sav/test_example_static_multiple.res [new file with mode: 0644]
lib/popcorn/package.ini [new file with mode: 0644]
lib/popcorn/pop_handlers.nit [new file with mode: 0644]
lib/popcorn/pop_middlewares.nit [new file with mode: 0644]
lib/popcorn/pop_routes.nit [new file with mode: 0644]
lib/popcorn/pop_tests.nit [new file with mode: 0644]
lib/popcorn/popcorn.nit [new file with mode: 0644]
lib/popcorn/test_pop_routes.nit [new file with mode: 0644]
lib/popcorn/test_popcorn.nit [new file with mode: 0644]
lib/popcorn/test_popcorn.sav/test_router.res [new file with mode: 0644]
lib/popcorn/test_popcorn.sav/test_routes.res [new file with mode: 0644]
lib/postgresql/native_postgres.nit [new file with mode: 0644]
lib/postgresql/package.ini [new file with mode: 0644]
lib/postgresql/postgres.nit [new file with mode: 0644]
lib/pthreads/pthreads.nit
lib/readline.ini [new file with mode: 0644]
lib/readline.nit [new file with mode: 0644]
lib/realtime.nit
lib/ropes_debug.nit
lib/rubix.ini [new file with mode: 0644]
lib/rubix.nit [new file with mode: 0644]
lib/serialization/caching.nit
lib/sha1.nit
lib/sqlite3/native_sqlite3.nit
lib/sqlite3/sqlite3.nit
lib/template/macro.nit
lib/text_stat.nit
misc/docker/Dockerfile [new file with mode: 0644]
misc/docker/README.md [new file with mode: 0644]
misc/docker/full/Dockerfile [new file with mode: 0644]
misc/docker/hello/Dockerfile [new file with mode: 0644]
misc/docker/hello/src/hello.nit [new file with mode: 0644]
share/man/nit.md
share/man/nitc.md
share/man/nitdoc.md
share/man/nitunit.md
share/nitweb/directives/contributor-list.html [new file with mode: 0644]
share/nitweb/directives/entity/card.html [new file with mode: 0644]
share/nitweb/directives/entity/defcard.html [new file with mode: 0644]
share/nitweb/directives/entity/doc.html [new file with mode: 0644]
share/nitweb/directives/entity/linearization.html [new file with mode: 0644]
share/nitweb/directives/entity/link.html [new file with mode: 0644]
share/nitweb/directives/entity/list.html [new file with mode: 0644]
share/nitweb/directives/entity/location.html [new file with mode: 0644]
share/nitweb/directives/entity/namespace.html [new file with mode: 0644]
share/nitweb/directives/entity/signature.html [new file with mode: 0644]
share/nitweb/directives/entity/tag.html [new file with mode: 0644]
share/nitweb/directives/group-block.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-button-vis.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-field.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-form.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-group-vis.html [new file with mode: 0644]
share/nitweb/index.html [new file with mode: 0644]
share/nitweb/javascripts/docdown.js [new file with mode: 0644]
share/nitweb/javascripts/entities.js [new file with mode: 0644]
share/nitweb/javascripts/index.js [new file with mode: 0644]
share/nitweb/javascripts/model.js [new file with mode: 0644]
share/nitweb/javascripts/nitweb.js [new file with mode: 0644]
share/nitweb/javascripts/ui.js [new file with mode: 0644]
share/nitweb/stylesheets/nitweb.css [new file with mode: 0644]
share/nitweb/stylesheets/nitweb_bootstrap.css [new file with mode: 0644]
share/nitweb/views/class.html [new file with mode: 0644]
share/nitweb/views/classdef.html [new file with mode: 0644]
share/nitweb/views/doc.html [new file with mode: 0644]
share/nitweb/views/docdown.html [new file with mode: 0644]
share/nitweb/views/group.html [new file with mode: 0644]
share/nitweb/views/index.html [new file with mode: 0644]
share/nitweb/views/module.html [new file with mode: 0644]
share/nitweb/views/package.html [new file with mode: 0644]
share/nitweb/views/propdef.html [new file with mode: 0644]
share/nitweb/views/property.html [new file with mode: 0644]
src/catalog.nit
src/compiler/abstract_compiler.nit
src/compiler/compiler.nit
src/compiler/global_compiler.nit
src/compiler/memory_logger.nit [new file with mode: 0644]
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/doc/doc_commands.nit
src/doc/doc_down.nit
src/doc/doc_phases/doc_html.nit
src/doc/html_templates/html_model.nit
src/doc/html_templates/model_html.nit [moved from src/web/model_html.nit with 93% similarity]
src/examples/README.md [new file with mode: 0644]
src/examples/get_mclasses.nit [new file with mode: 0644]
src/examples/nitlight_as_a_service.nit [new file with mode: 0644]
src/examples/test_loader.nit [new file with mode: 0644]
src/highlight.nit
src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit
src/interpreter/naive_interpreter.nit
src/loader.nit
src/location.nit
src/metrics/inheritance_metrics.nit
src/metrics/mclasses_metrics.nit
src/metrics/mendel_metrics.nit
src/metrics/metrics_base.nit
src/metrics/mmodules_metrics.nit
src/metrics/nullables_metrics.nit
src/metrics/rta_metrics.nit
src/model/mmodule.nit
src/model/model.nit
src/model/model_base.nit
src/model/model_collect.nit
src/model/model_json.nit [new file with mode: 0644]
src/model/model_views.nit
src/model/model_visitor.nit
src/model/mpackage.nit
src/modelbuilder_base.nit
src/modelize/modelize_class.nit
src/modelize/modelize_property.nit
src/neo.nit
src/nit.nit
src/nitcatalog.nit
src/nitlight.nit
src/nitls.nit
src/nitunit.nit
src/nitweb.nit
src/package.ini
src/parser/README.md
src/platform/app_annotations.nit
src/rapid_type_analysis.nit
src/semantize/typing.nit
src/test_highlight.nit
src/test_model_visitor.nit
src/testing/testing_base.nit
src/testing/testing_doc.nit
src/testing/testing_suite.nit
src/toolcontext.nit
src/web/api_catalog.nit [new file with mode: 0644]
src/web/api_docdown.nit [new file with mode: 0644]
src/web/api_graph.nit [new file with mode: 0644]
src/web/model_api.nit [new file with mode: 0644]
src/web/web.nit
src/web/web_actions.nit [deleted file]
src/web/web_base.nit
src/web/web_views.nit [deleted file]
tests/Darwin.skip
tests/base_gen_infinite2.nit [new file with mode: 0644]
tests/base_is_optional.nit [new file with mode: 0644]
tests/base_name_conflict.nit [new file with mode: 0644]
tests/base_new_factory.nit [new file with mode: 0644]
tests/base_redef.nit [new file with mode: 0644]
tests/bench_string_super.nit
tests/bench_string_tos.nit
tests/error_names.nit [new file with mode: 0644]
tests/error_unk_class.nit
tests/error_unk_class2.nit [new file with mode: 0644]
tests/get_mclasses.args [new file with mode: 0644]
tests/listfull.sh
tests/module_1.nit
tests/module_1b.nit [new file with mode: 0644]
tests/names/README.md [new file with mode: 0644]
tests/names/n0.nit [new file with mode: 0644]
tests/names/n1.nit [new file with mode: 0644]
tests/names/n2.nit [new file with mode: 0644]
tests/names/n3.nit [new file with mode: 0644]
tests/names/package.ini [moved from tests/sav/xymus_net.res with 100% similarity]
tests/names1.nit [new file with mode: 0644]
tests/nit.args
tests/nit_args8.inputs [new file with mode: 0644]
tests/nitcatalog.args [new file with mode: 0644]
tests/niti.skip
tests/nitls.args
tests/nitpick.args
tests/nitunit.args
tests/nitvm.skip
tests/project1/subdir/subdir2/subdir3/submodule.nit [new file with mode: 0644]
tests/project1/uselessdir/uselessdir2/uselessfile [new file with mode: 0644]
tests/repeating_key_xor_solve.args [new file with mode: 0644]
tests/rope_substring_test.nit [new file with mode: 0644]
tests/sav/base_class_name.res
tests/sav/base_classid.res
tests/sav/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/base_init_basic_alt3.res
tests/sav/base_is_optional.res [new file with mode: 0644]
tests/sav/base_is_optional_alt1.res [new file with mode: 0644]
tests/sav/base_isa3.res
tests/sav/base_isa_gen1.res
tests/sav/base_isa_gen2.res
tests/sav/base_isa_gen4.res
tests/sav/base_isa_gen5.res
tests/sav/base_isa_nullable1.res
tests/sav/base_isa_nullable2.res
tests/sav/base_name_conflict.res [new file with mode: 0644]
tests/sav/base_name_conflict_alt1.res [new file with mode: 0644]
tests/sav/base_name_conflict_alt2.res [new file with mode: 0644]
tests/sav/base_name_conflict_alt3.res [new file with mode: 0644]
tests/sav/base_new_factory.res [new file with mode: 0644]
tests/sav/base_redef.res [new file with mode: 0644]
tests/sav/base_redef_alt1.res [new file with mode: 0644]
tests/sav/base_redef_alt2.res [new file with mode: 0644]
tests/sav/base_upcast2.res
tests/sav/base_upcast2_alt1.res
tests/sav/base_upcast2_alt10.res
tests/sav/base_upcast2_alt2.res
tests/sav/base_upcast2_alt3.res
tests/sav/base_upcast2_alt4.res
tests/sav/base_upcast2_alt5.res
tests/sav/base_upcast2_alt6.res
tests/sav/base_upcast2_alt7.res
tests/sav/base_upcast2_alt8.res
tests/sav/base_upcast2_alt9.res
tests/sav/base_vararg_alt1.res
tests/sav/base_vararg_alt2.res
tests/sav/base_vararg_alt3.res
tests/sav/base_vararg_alt4.res
tests/sav/base_vararg_alt5.res
tests/sav/base_vararg_alt6.res
tests/sav/base_vararg_alt7.res
tests/sav/base_vararg_alt8.res
tests/sav/error_gen_f_inh_clash.res
tests/sav/error_names.res [new file with mode: 0644]
tests/sav/error_prop_loc_alt1.res
tests/sav/error_redef_class.res
tests/sav/error_type_not_ok5.res
tests/sav/error_type_unk_alt2.res
tests/sav/error_unk_class.res
tests/sav/error_unk_class2.res [new file with mode: 0644]
tests/sav/fixme/base_gen_f.res
tests/sav/get_mclasses.res [new file with mode: 0644]
tests/sav/get_mclasses_args1.res [new file with mode: 0644]
tests/sav/module_1b.res [new file with mode: 0644]
tests/sav/nit_args6.res
tests/sav/nit_args8.res [new file with mode: 0644]
tests/sav/nitcatalog_args1.res [new file with mode: 0644]
tests/sav/nitce/base_class_name.res
tests/sav/nitce/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitce/base_isa_gen1.res
tests/sav/nitce/base_isa_gen4.res
tests/sav/nitce/base_isa_gen5.res
tests/sav/nitce/base_isa_nullable1.res
tests/sav/nitce/base_isa_nullable2.res
tests/sav/nitce/fixme/base_gen_reassign_alt4.res
tests/sav/nitce/fixme/base_gen_reassign_alt5.res
tests/sav/nitce/fixme/base_gen_reassign_alt6.res
tests/sav/nitce/test_json_deserialization_alt1.res
tests/sav/nitce/test_json_deserialization_alt3.res [new file with mode: 0644]
tests/sav/nitce/test_meta.res
tests/sav/nitce/test_serialization.res
tests/sav/nitce/test_serialization_alt2.res
tests/sav/nitce/test_serialization_alt3.res
tests/sav/nitce/test_serialization_alt4.res
tests/sav/nitce/test_serialization_alt5.res
tests/sav/nitce/test_serialization_redef.res
tests/sav/nitce/test_serialization_redef_alt0.res
tests/sav/nitce/test_serialization_redef_alt1.res
tests/sav/nitce/test_serialization_redef_alt2.res
tests/sav/nitcg/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcs/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcsg/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitdoc_args4.res
tests/sav/nitlight_args1.res
tests/sav/nitls_args1.res
tests/sav/nitls_args2.res
tests/sav/nitls_args3.res
tests/sav/nitls_args4.res
tests/sav/nitls_args6.res
tests/sav/nitls_args7.res [new file with mode: 0644]
tests/sav/nitls_args8.res [new file with mode: 0644]
tests/sav/nitmetrics_args1.res
tests/sav/nituml_args3.res
tests/sav/nituml_args4.res
tests/sav/nitunit_args1.res
tests/sav/nitunit_args4.res
tests/sav/nitunit_args5.res
tests/sav/nitunit_args6.res
tests/sav/nitunit_args7.res
tests/sav/nitunit_args8.res
tests/sav/nitunit_args9.res
tests/sav/repeating_key_xor_solve.res [new file with mode: 0644]
tests/sav/repeating_key_xor_solve_args1.res [new file with mode: 0644]
tests/sav/rope_substring_test.res [new file with mode: 0644]
tests/sav/test_augmented.res
tests/sav/test_base64.res
tests/sav/test_basename_perf_args1.res
tests/sav/test_copy_to_native.res [new file with mode: 0644]
tests/sav/test_copy_to_native_alt1.res [new file with mode: 0644]
tests/sav/test_copy_to_native_alt2.res [new file with mode: 0644]
tests/sav/test_csv.res [new file with mode: 0644]
tests/sav/test_deriving.res
tests/sav/test_deriving_alt1.res
tests/sav/test_deriving_alt2.res
tests/sav/test_deriving_alt3.res
tests/sav/test_deriving_alt4.res
tests/sav/test_flatbuf_from.res [new file with mode: 0644]
tests/sav/test_flatbuf_from_alt1.res [new file with mode: 0644]
tests/sav/test_flatbuf_from_alt2.res [new file with mode: 0644]
tests/sav/test_highlight_args1.res
tests/sav/test_json_deserialization.res
tests/sav/test_json_deserialization_alt1.res
tests/sav/test_json_deserialization_alt2.res
tests/sav/test_json_deserialization_alt3.res [new file with mode: 0644]
tests/sav/test_json_deserialization_alt4.res [new file with mode: 0644]
tests/sav/test_json_deserialization_plain.res
tests/sav/test_loader.res [new file with mode: 0644]
tests/sav/test_loader_args1.res [new file with mode: 0644]
tests/sav/test_loader_args2.res [new file with mode: 0644]
tests/sav/test_loader_args3.res [new file with mode: 0644]
tests/sav/test_loader_args4.res [new file with mode: 0644]
tests/sav/test_loader_args5.res [new file with mode: 0644]
tests/sav/test_loader_args6.res [new file with mode: 0644]
tests/sav/test_loader_args7.res [new file with mode: 0644]
tests/sav/test_loader_args8.res [new file with mode: 0644]
tests/sav/test_loader_args9.res [new file with mode: 0644]
tests/sav/test_meta.res
tests/sav/test_model_visitor_args1.res
tests/sav/test_model_visitor_args2.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from_alt1.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from_alt2.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from_alt3.res [new file with mode: 0644]
tests/sav/test_neo_args1.res
tests/sav/test_new_native_alt1.res
tests/sav/test_nitcorn.res [new file with mode: 0644]
tests/sav/test_postgres_native.res [new file with mode: 0644]
tests/sav/test_postgres_nity.res [new file with mode: 0644]
tests/sav/test_readline.res [new file with mode: 0644]
tests/sav/test_realtime.res [deleted file]
tests/sav/test_rope_bytes.res [new file with mode: 0644]
tests/sav/test_rubix_cube.res [new file with mode: 0644]
tests/sav/test_rubix_cube_alt1.res [new file with mode: 0644]
tests/sav/test_rubix_visual.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args1.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args10.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args11.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args12.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args13.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args14.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args15.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args16.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args17.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args18.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args19.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args2.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args20.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args21.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args22.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args23.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args24.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args25.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args26.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args27.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args28.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args29.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args3.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args30.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args31.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args32.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args33.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args34.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args4.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args5.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args6.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args7.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args8.res [new file with mode: 0644]
tests/sav/test_rubix_visual_alt1_args9.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args1.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args10.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args11.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args12.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args13.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args14.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args15.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args16.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args17.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args18.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args19.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args2.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args20.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args21.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args22.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args23.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args24.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args25.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args26.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args27.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args28.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args29.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args3.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args30.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args31.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args32.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args33.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args34.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args4.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args5.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args6.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args7.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args8.res [new file with mode: 0644]
tests/sav/test_rubix_visual_args9.res [new file with mode: 0644]
tests/sav/test_serialization.res
tests/sav/test_serialization_alt1.res
tests/sav/test_serialization_alt2.res
tests/sav/test_serialization_alt3.res
tests/sav/test_serialization_alt4.res
tests/sav/test_serialization_alt5.res
tests/sav/test_serialization_redef.res
tests/sav/test_serialization_redef_alt0.res
tests/sav/test_serialization_redef_alt1.res
tests/sav/test_serialization_redef_alt2.res
tests/sav/test_sort_perf_args1.res
tests/sav/test_substring.res
tests/sav/test_super_param2.res
tests/sav/test_text_stat.res
tests/sublib/bar/bar.nit [new file with mode: 0644]
tests/sublib/bar/foo.nit [new file with mode: 0644]
tests/sublib/foo.nit [new file with mode: 0644]
tests/sublib/packages.ini [new file with mode: 0644]
tests/test_base64.nit
tests/test_buffer_unicode.nit
tests/test_copy_to_native.nit [new file with mode: 0644]
tests/test_csv.nit [new file with mode: 0644]
tests/test_flatbuf_from.nit [new file with mode: 0644]
tests/test_json_deserialization.nit
tests/test_jvm.nit
tests/test_kill_process.nit
tests/test_loader.args [new file with mode: 0644]
tests/test_model_visitor.args
tests/test_nativestring_fill_from.nit [new file with mode: 0644]
tests/test_nitcorn.nit [new file with mode: 0644]
tests/test_nitunit.nit
tests/test_nitunit4/sav/test_sav_conflict.res [new file with mode: 0644]
tests/test_nitunit4/test_bad_comp.nit [new file with mode: 0644]
tests/test_nitunit4/test_bad_comp2.nit [new file with mode: 0644]
tests/test_nitunit4/test_baz.res [new file with mode: 0644]
tests/test_nitunit4/test_nitunit4.nit
tests/test_nitunit4/test_nitunit4.sav/test_bar.res [new file with mode: 0644]
tests/test_nitunit4/test_nitunit4.sav/test_sav_conflict.res [new file with mode: 0644]
tests/test_nitunit4/test_nitunit4_base.nit
tests/test_nitunit4/test_sav_conflict.res [new file with mode: 0644]
tests/test_postgres_native.nit [new file with mode: 0644]
tests/test_postgres_nity.nit [new file with mode: 0644]
tests/test_prog/README.md
tests/test_prog/game/excluded.nit [new file with mode: 0644]
tests/test_prog/game/excluded_dir/more.nit [new file with mode: 0644]
tests/test_prog/package.ini [new file with mode: 0644]
tests/test_readline.inputs [new file with mode: 0644]
tests/test_readline.nit [new file with mode: 0644]
tests/test_rope_bytes.nit [new file with mode: 0644]
tests/test_rubix_cube.nit [new file with mode: 0644]
tests/test_rubix_visual.args [new file with mode: 0644]
tests/test_rubix_visual.nit [new file with mode: 0644]
tests/test_sqlite3_native.nit
tests/test_substring.nit
tests/tests.sh

index ca8864b..e326f39 100644 (file)
@@ -6,4 +6,5 @@ tables_nit.c            -diff
 c_src/**               -diff
 
 tests/sav/**/*.res     -whitespace
+*.res                  -whitespace
 *.patch                        -whitespace
index edf0d66..a739d3f 100644 (file)
@@ -1,5 +1,6 @@
 *.bak
 *.swp
+*.swo
 *~
 .project
 EIFGENs
@@ -55,3 +56,4 @@ nitunit.xml
 
 .metadata/*
 .github_data
+.DS_Store
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..8850808
--- /dev/null
@@ -0,0 +1,22 @@
+# Contributing to Nit
+
+Thank you for your interest in contributing to Nit!
+We accept all forms of contribution, ranging from small example programs to improvements and additions to the libraries and posting of issues.
+
+
+## Contribution process
+
+All the contributions to the Nit language, its tools and libraries, are done through Github Pull-Requests.
+
+All the information on our contribution policy is available on our [website](http://nitlanguage.org/internal/).
+We encourage you to take a look at the sections available here for a walthrough of what is required to submit a new PR.
+
+## Other suggestions
+
+We are always open for suggestions or any other kind of contribution.
+
+If you do not want to submit code for some reason, you are always welcome to ask questions about the language or related topics via the issue system.
+
+You may also proof-read and correct documentation if you are willing!
+All the documents on this repository are written in English, however most of our contributors are not native anglophones.
+Therefore we encourage people, especially those coming of an english-speaking culture to read the documentation available and submit patches to correct bad formulations, typos or related mistakes on our part through the usual system.
diff --git a/NOTICE b/NOTICE
index 2e08fad..514ed50 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -2,39 +2,49 @@ This product includes software developed as part of the Nit Language
 project ( http://nitlanguage.org ).
 
 Files: *
-Copyright: 2004-2015 Jean Privat <jean@pryen.org>
+Copyright: 2004-2016 Jean Privat <jean@pryen.org>
            2006-2008 Floréal Morandat <morandat@lirmm.fr>
            2009      Julien Chevalier <chevjulien@gmail.com>
            2009-2011 Jean-Sebastien Gelinas <calestar@gmail.com>
-           2009-2015 Alexis Laferrière <alexis.laf@xymus.net>
+           2009-2016 Alexis Laferrière <alexis.laf@xymus.net>
            2011      Matthieu Auger <matthieu.auger@gmail.com>
-           2011-2015 Alexandre Terrasa <alexandre@moz-code.org>
+           2011-2016 Alexandre Terrasa <alexandre@moz-code.org>
            2012      Alexandre Pennetier <alexandre.pennetier@me.com>
-           2013-2015 Lucas Bajolet <r4pass@hotmail.com>
+           2013-2016 Lucas Bajolet <r4pass@hotmail.com>
            2013      Stefan Lage <lagestfan@gmail.com>
            2013      Nathan Heu <heu.nathan@courrier.uqam.ca>
            2013      Matthieu Lucas <lucasmatthieu@gmail.com>
-           2014-2015 Romain Chanoir <romain.chanoir@viacesi.fr>
-           2014      Frédéric Vachon <fredvac@gmail.com>
+           2014-2016 Romain Chanoir <romain.chanoir@viacesi.fr>
+           2014-2015 Frédéric Vachon <fredvac@gmail.com>
            2014      Johan Kayser <johan.kayser@viacesi.fr>
            2014-2015 Julien Pagès <julien.projet@gmail.com>
            2014      Geoffrey Hecht <geoffrey.hecht@gmail.com>
-           2014      Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
+           2014-2016 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
            2015      Arthur Delamare <arthur.delamare@viacesi.fr>
+           2015-2016 Mehdi Ait Younes <overpex@gmail.com>
+           2015      Renata Carvalho <renatawm@gmail.com>
+           2015      Simon Zeni <simonzeni@gmail.com>
+           2015      Anis Boubaker <anis@boubaker.ca>
+           2015      Istvan SZALAI <szalai972@gmail.com>
+           2015      Hervé Matysiak <herve.matysiak@viacesi.fr>
+           2015      Jean-Philippe Caissy <jean-philippe.caissy@shopify.com>
+           2015      Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
+           2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+           2016      Tony Gaetani <tony.gaetani@gmail.com>
 License: Apache 2.0 (see LICENSE)
 Comment: The main license of the work, except for the following exceptions
 
 Files: lib/*
        clib/*
        share/nitdoc/*
-Copyright: 2004-2015 Jean Privat <jean@pryen.org>
+Copyright: 2004-2016 Jean Privat <jean@pryen.org>
            2006-2008 Floréal Morandat <morandat@lirmm.fr>
            2009-2011 Jean-Sebastien Gelinas <calestar@gmail.com>
-           2009-2015 Alexis Laferrière <alexis.laf@xymus.net>
+           2009-2016 Alexis Laferrière <alexis.laf@xymus.net>
            2009      Julien Chevalier <chevjulien@gmail.com>
-           2011-2015 Alexandre Terrasa <alexandre@moz-concept.com>
+           2011-2016 Alexandre Terrasa <alexandre@moz-concept.com>
            2012      Alexandre Pennetier <alexandre.pennetier@me.com>
-           2013-2015 Lucas Bajolet <r4pass@hotmail.com>
+           2013-2016 Lucas Bajolet <r4pass@hotmail.com>
            2013      Nathan Heu <heu.nathan@courrier.uqam.ca>
            2013      Matthieu Lucas <lucasmatthieu@gmail.com>
            2013      Stefan Lage <lagestfan@gmail.com>
@@ -48,7 +58,11 @@ Copyright: 2004-2015 Jean Privat <jean@pryen.org>
            2014      Maxime Leroy <maxime.leroy76@gmail.com>
            2014      Johann Dubois <johann.dubois@outlook.com>
            2014-2015 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
-           2014      Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
+           2014-2015 Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
+           2015      Mehdi Ait Younes <overpex@gmail.com>
+           2015      Budi Kurniawan <budi2020@gmail.com>
+           2015-2016 Philippe Pepos Petitclerc <ppeposp@gmail.com>
+           2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
 Licence: BSD 2 (see LICENSE-BSD)
 Comment: Use of libraries and resources is basically unrestricted. We hold the copyright
          on the compiler and the tools but not on the programs made by the users.
index d84731a..2679f4d 100644 (file)
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Requirements:
  * gcc         http://gcc.gnu.org/ (or a compatible C compiler)
  * pkg-config  http://www.freedesktop.org/wiki/Software/pkg-config/
  * ccache      http://ccache.samba.org/        to improve recompilation
- * libgc-dev   http://www.hpl.hp.com/personal/Hans_Boehm/gc/
+ * libgc-dev   http://hboehm.info/gc/
  * graphviz    http://www.graphviz.org/        to enable graphs with the nitdoc tool
  * libunwind   http://nongnu.org/libunwind
 
@@ -65,6 +65,11 @@ To have your environment automatically configured at login, just source it with
 
     $ . misc/nit_env.sh install
 
+Contributing:
+
+To contribute to Nit, please see [CONTRIBUTING](CONTRIBUTING.md).
+
+The best way to ask the team for advice, submit bugs or request features, is through the use of Github issues, using the appropriate tag (`forum`, `feature-request`, `bug`).
 
 Information, contacts and help:
 
index 37b2fb9..505a6ac 100755 (executable)
@@ -48,6 +48,8 @@ function run_compiler()
                run_command "$@" ../tests/bench_bintree_gen.nit -o "bintrees.$title.bin"
                bench_command "bintrees" "bench_bintree_gen 16" "./bintrees.$title.bin" 16
        else
+               rm -r out 2> /dev/null
+               mkdir out 2> /dev/null
                run_command "$@" ../src/nitc.nit -o "nitc.$title.bin"
                bench_command "nitc-g" "nitc --global --no-cc ../src/nitls.nit" "./nitc.$title.bin" -v --global --no-cc ../src/nitls.nit
                bench_command "nitc-s" "nitc --separate ../src/nitc.nit" "./nitc.$title.bin" -v --no-cc --separate ../src/nitc.nit
@@ -56,9 +58,9 @@ function run_compiler()
                bench_command "nit-nitcc" "nit nitcc.nit calc.sablecc" "./nit.$title.bin" ../contrib/nitcc/src/nitcc.nit ../contrib/nitcc/examples/calc.sablecc
                rm calc* 2> /dev/null # remove generated cruft
                run_command "$@" ../src/nitdoc.nit -o "nitdoc.$title.bin"
-               rm -r out 2> /dev/null
-               mkdir out 2> /dev/null
                bench_command "nitdoc" "nitdoc ../src/nitls.nit" "./nitdoc.$title.bin" -v ../src/nitls.nit -d out
+               run_command "$@" ../src/nitlight.nit -o "nitlight.$title.bin"
+               bench_command "nitlight" "nitlight ../lib/[a-f]*/" "./nitlight.$title.bin" ../lib/[a-f]*/ -d out
                run_command "$@" ../examples/shoot/src/shoot_logic.nit -o "shoot.$title.bin"
                bench_command "shoot" "shoot_logic 15" "./shoot.$title.bin" 15
                run_command "$@" ../tests/bench_bintree_gen.nit -o "bintrees.$title.bin"
@@ -71,6 +73,8 @@ function run_compiler()
                bench_command "puzzle" "puzzle 15-hard" "./puzzle.$title.bin" kleg.mondcafjhbi
                run_command "$@" "markdown/engines/nitmd/nitmd.nit" -o "nitmd.$title.bin"
                bench_command "nitmd" "markdown" "./nitmd.$title.bin" markdown/benches/out/mixed.md 80
+               run_command "$@" ../contrib/jwrapper/src/jwrapper.nit -o "jwrapper.$title.bin"
+               bench_command "jwrapper" "jwrapper ant.jar" "./jwrapper.$title.bin" /usr/share/java/ant.jar -o out/ant_jar.nit
        fi
 
        rm -r *.bin out 2> /dev/null
@@ -123,6 +127,7 @@ cp ../bin/nitc .
 if test -z "$fast"; then
        make -C markdown/benches
        make -C ../contrib/nitcc
+       make pre-build -C ../contrib/jwrapper
 fi
 
 ## EFFECTIVE BENCHS ##
diff --git a/benchmarks/csv/README.md b/benchmarks/csv/README.md
new file mode 100644 (file)
index 0000000..5f913bd
--- /dev/null
@@ -0,0 +1,11 @@
+# CSV Bench
+
+This is a simple benchmark for CSV parsers between different languages.
+
+Are required for testing (all packages can be apt-get'd):
+
+* Python 2
+* Python 3
+* Java JDK 6+, Apache-commons-csv
+* Ruby
+* Go language compiler
diff --git a/benchmarks/csv/csv_bench.sh b/benchmarks/csv/csv_bench.sh
new file mode 100755 (executable)
index 0000000..badd1e6
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/bash
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Shell script to bench json parsers over different documents
+
+source ../bench_common.sh
+source ../bench_plot.sh
+
+## CONFIGURATION OPTIONS ##
+
+# Default number of times a command must be run with bench_command
+# Can be overrided with 'the option -n'
+count=5
+
+## HANDLE OPTIONS ##
+
+function init_repo()
+{
+       mkdir -p inputs
+       nitc --semi-global scripts/csv_gen.nit -o scripts/csv_gen
+       echo "Generating 1000 lines documents"
+       ./scripts/csv_gen 10 1000 inputs/1000_l.csv
+       ./scripts/csv_gen 10 1000 inputs/1000_uni_l.csv --unicode
+       echo "Generating 10000 lines documents"
+       ./scripts/csv_gen 10 10000 inputs/10000_l.csv
+       ./scripts/csv_gen 10 10000 inputs/10000_uni_l.csv --unicode
+       echo "Generating 100000 lines documents"
+       ./scripts/csv_gen 10 100000 inputs/100000_l.csv
+       ./scripts/csv_gen 10 100000 inputs/100000_uni_l.csv --unicode
+       echo "Generating 1000000 lines documents"
+       ./scripts/csv_gen 10 1000000 inputs/1000000_l.csv
+       ./scripts/csv_gen 10 1000000 inputs/1000000_uni_l.csv --unicode
+}
+
+function usage()
+{
+       echo "run_bench: ./csv_bench.sh [options]"
+       echo "  -v: verbose mode"
+       echo "  -n count: number of execution for each bar (default: $count)"
+       echo "  -h: this help"
+}
+
+stop=false
+fast=false
+while [ "$stop" = false ]; do
+       case "$1" in
+               -v) verbose=true; shift;;
+               --fast) fast=true; shift;;
+               -h) usage; exit;;
+               -n) count="$2"; shift; shift;;
+               *) stop=true
+       esac
+done
+
+if [ -z "$fast" ]; then
+       init_repo
+fi
+
+mkdir -p out
+
+echo "Compiling engines"
+
+echo "Java Parser"
+
+javac -cp './scripts/commons-csv-1.3.jar' scripts/JavaCSV.java
+
+echo "Go parser"
+
+go build -o scripts/go_csv scripts/go_csv.go
+
+echo "Nit/Ad-Hoc Parser"
+
+nitc --semi-global scripts/nit_csv.nit -o scripts/nit_csv
+
+declare -a script_names=('Python 3 - Pandas' 'Python 2 - Pandas' 'Go' 'Nit' 'Python 3 - Standard' 'Python 2 - Standard' 'Java - Apache commons' 'Ruby')
+declare -a script_cmds=('python3 scripts/python_csv.py' 'python2 scripts/python_csv.py' './scripts/go_csv' './scripts/nit_csv' 'python3 scripts/python_stdcsv.py' 'python2 scripts/python_stdcsv.py' "java -cp /usr/share/java/commons-csv.jar:. scripts.JavaCSV" 'ruby scripts/ruby_csv.rb')
+
+for script in `seq 1 ${#script_cmds[@]}`; do
+       echo "Preparing res for ${script_names[$script - 1]}"
+       prepare_res "./out/${script_names[$script - 1]}.dat" "${script_names[$script - 1]}" "${script_names[$script - 1]}"
+       for file in inputs/*.csv; do
+               fname=`basename $file .csv`
+               bench_command $file "Benching file $file using ${script_cmds[$script - 1]} parser" ${script_cmds[$script - 1]} $file
+       done;
+done;
+
+rm scripts/nit_csv
+rm scripts/JavaCSV.class
+rm scripts/go_csv
+
+plot out/bench_csv.gnu
diff --git a/benchmarks/csv/scripts/JavaCSV.java b/benchmarks/csv/scripts/JavaCSV.java
new file mode 100644 (file)
index 0000000..f8264ca
--- /dev/null
@@ -0,0 +1,18 @@
+package scripts;
+
+import java.io.File;
+import java.util.List;
+import java.nio.charset.Charset;
+import org.apache.commons.csv.*;
+
+class JavaCSV {
+       public static void main(String[] args) {
+               try {
+                       File csvData = new File(args[0]);
+                       CSVParser parser = CSVParser.parse(csvData, Charset.forName("UTF-8"), CSVFormat.RFC4180);
+                       List<CSVRecord> r = parser.getRecords();
+               } catch(Exception e) {
+                       System.err.println("Major fail");
+               }
+       }
+}
diff --git a/benchmarks/csv/scripts/csv_gen.nit b/benchmarks/csv/scripts/csv_gen.nit
new file mode 100644 (file)
index 0000000..123dbb6
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import csv
+
+if args.length < 3 then
+       print "Usage ./csv_gen record_length record_nb out_filepath [--unicode]"
+       exit 1
+end
+
+var record_length = args[0].to_i
+var record_nb = args[1].to_i
+var outpath = args[2]
+var unicode = false
+
+if args.length == 4 then
+       if not args[3] == "--unicode" then
+               print "Usage ./csv_gen record_length record_nb [--unicode]"
+               exit 1
+       end
+       unicode = true
+end
+
+var ocsv = new CsvDocument
+ocsv.eol = "\r\n"
+
+var sep = ocsv.separator.to_s
+var eol = ocsv.eol
+var del = ocsv.delimiter.to_s
+
+for i in [0 .. record_length[ do ocsv.header.add "Col{i}"
+
+var c = if unicode then "á" else "a"
+for i in [0 .. record_nb[ do
+       var line = new Array[String].with_capacity(record_length)
+       for j in [0 .. record_length[ do
+               var add_sep = 100.rand > 70
+               var add_del = 100.rand > 70
+               var add_eol = 100.rand > 70
+               var ln = 10.rand
+               var s = c * ln
+               if add_sep then s = sep + s
+               if add_del then s += del
+               if add_eol then s += eol
+               line.add s
+       end
+       ocsv.records.add line
+end
+
+ocsv.write_to_file(outpath)
diff --git a/benchmarks/csv/scripts/go_csv.go b/benchmarks/csv/scripts/go_csv.go
new file mode 100644 (file)
index 0000000..5fff932
--- /dev/null
@@ -0,0 +1,18 @@
+package main
+
+import "encoding/csv"
+import "os"
+import "fmt"
+
+func main() {
+       if len(os.Args) == 1 {
+               fmt.Println("Usage ./go_csv file")
+               os.Exit(-1)
+       }
+       file, err := os.Open(os.Args[1])
+       if err != nil { panic(err) }
+
+       var read = csv.NewReader(file)
+       _, r := read.ReadAll()
+       if r != nil { panic(err) }
+}
diff --git a/benchmarks/csv/scripts/nit_csv.nit b/benchmarks/csv/scripts/nit_csv.nit
new file mode 100644 (file)
index 0000000..c8422d1
--- /dev/null
@@ -0,0 +1,25 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import csv
+
+if args.is_empty then
+       print "Usage: ./nit_csv in.csv"
+       exit 1
+end
+
+var csv = new CsvReader(new FileReader.open(args[0]))
+csv.eol = "\r\n"
+
+csv.read_all
diff --git a/benchmarks/csv/scripts/python_csv.py b/benchmarks/csv/scripts/python_csv.py
new file mode 100644 (file)
index 0000000..d8addda
--- /dev/null
@@ -0,0 +1,4 @@
+import sys
+from pandas import read_csv
+
+csv = read_csv(sys.argv[1])
diff --git a/benchmarks/csv/scripts/python_stdcsv.py b/benchmarks/csv/scripts/python_stdcsv.py
new file mode 100644 (file)
index 0000000..b78cb15
--- /dev/null
@@ -0,0 +1,8 @@
+import sys
+import csv
+
+lst = list();
+with open(sys.argv[1], 'r') as f:
+    reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)
+    for row in reader:
+        list.append(lst, row)
diff --git a/benchmarks/csv/scripts/ruby_csv.rb b/benchmarks/csv/scripts/ruby_csv.rb
new file mode 100644 (file)
index 0000000..6b1fe02
--- /dev/null
@@ -0,0 +1,3 @@
+require 'csv'
+
+CSV.read(ARGV.first)
diff --git a/benchmarks/json/.gitignore b/benchmarks/json/.gitignore
new file mode 100644 (file)
index 0000000..a6595d2
--- /dev/null
@@ -0,0 +1 @@
+inputs/*.json
index 1c05dd2..25c04f9 100644 (file)
@@ -1,2 +1,5 @@
 all:
        ./bench_json.sh
+
+check:
+       ./bench_json.sh -n 1 --fast
index 1e00026..4a3ece2 100755 (executable)
@@ -68,13 +68,16 @@ function usage()
        echo "run_bench: ./bench_json.sh [options]"
        echo "  -v: verbose mode"
        echo "  -n count: number of execution for each bar (default: $count)"
+       echo "  -fast: check only Nit"
        echo "  -h: this help"
 }
 
 stop=false
+fast=
 while [ "$stop" = false ]; do
        case "$1" in
                -v) verbose=true; shift;;
+               --fast) fast=true; shift;;
                -h) usage; exit;;
                -n) count="$2"; shift; shift;;
                *) stop=true
@@ -84,16 +87,27 @@ done
 init_repo
 
 mkdir -p out
+html="index.html"
+echo >"$html" "<html><head></head><body>"
 
 echo "Compiling engines"
 
-echo "C JSON Parser"
+if [ -z "$fast" ]; then
+       declare -a script_names=('C' 'Python 3' 'Python 2' 'Go' 'Nit Ad-hoc UTF-8 No Ropes' 'Nit Ad-hoc UTF-8 + Ropes' 'Ruby ext')
+       declare -a script_cmds=('./scripts/c_parser' 'python3 scripts/python.py' 'python2 scripts/python.py' './scripts/json_parse' './scripts/nit_adhoc_utf_noropes' './scripts/nit_adhoc_utf_ropes' 'ruby scripts/json_ext.rb')
 
-gcc -O2 -I thirdparty/ujson4c/src -I thirdparty/ujson4c/3rdparty/ thirdparty/ujson4c/3rdparty/ultrajsondec.c scripts/c_parser.c -o scripts/c_parser -lm
+       echo "C JSON Parser"
 
-echo "Go JSON Parser"
+       gcc -O2 -I thirdparty/ujson4c/src -I thirdparty/ujson4c/3rdparty/ thirdparty/ujson4c/3rdparty/ultrajsondec.c scripts/c_parser.c -o scripts/c_parser -lm
 
-go build -o scripts/json_parse scripts/json_parse.go
+       echo "Go JSON Parser"
+
+       go build -o scripts/json_parse scripts/json_parse.go
+else
+       declare -a script_names=('Nit Ad-hoc UTF-8 No Ropes' 'Nit Ad-hoc UTF-8 + Ropes')
+       declare -a script_cmds=('./scripts/nit_adhoc_utf_noropes' './scripts/nit_adhoc_utf_ropes')
+
+fi
 
 echo "Nit/NitCC Parser"
 
@@ -107,8 +121,6 @@ echo "Nit/Ad-Hoc UTF-8 Parser, With Ropes"
 
 nitc --semi-global scripts/nit_adhoc_utf_ropes.nit -o scripts/nit_adhoc_utf_ropes
 
-declare -a script_names=('C' 'Python 3' 'Python 2' 'Go' 'Nit Ad-hoc UTF-8 No Ropes' 'Nit Ad-hoc UTF-8 + Ropes' 'Ruby ext')
-declare -a script_cmds=('./scripts/c_parser' 'python3 scripts/python.py' 'python2 scripts/python.py' './scripts/json_parse' './scripts/nit_adhoc_utf_noropes' './scripts/nit_adhoc_utf_ropes' 'ruby scripts/json_ext.rb')
 
 for script in `seq 1 ${#script_cmds[@]}`; do
        echo "Preparing res for ${script_names[$script - 1]}"
@@ -126,3 +138,11 @@ rm scripts/nit_adhoc_utf_noropes
 rm scripts/nit_adhoc_utf_ropes
 
 plot out/bench_json.gnu
+
+echo >>"$html" "</body></html>"
+
+if test -n "$died"; then
+       echo "Some commands failed"
+       exit 1
+fi
+exit 0
index 03eeac4..28cbb64 100755 (executable)
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 echo "[" > big_gov_data.json
-for i in $(seq 10); do
+for i in $(seq 20); do
   test "$i" != "1" && echo "," >> big_gov_data.json
     cat gov_data.json >> big_gov_data.json
 done
index 485b944..d6826e1 100755 (executable)
@@ -131,7 +131,8 @@ function bench_pandoc()
                bench_command "$bench" "" "$engdir/pandoc/pandoc" "$file" "$s"
        done
 }
-bench_pandoc
+# FIXME: Fix an reactivate the pandoc engine
+#bench_pandoc
 
 if test "$#" -gt 0; then
     plot $outdir/bench_markdown.gnu
index 4d974a6..f336524 100644 (file)
@@ -282,7 +282,9 @@ var txt = ctx.rest.first.to_path.read_all
 var lst = [new ManualBreakDecorator, new BlockQuoteDecorator, new CodeBlockDecorator,
                new UnorderedListDecorator, new MixedDecorator, new EmphasisDecorator,
                new StrongDecorator, new InlineCodeDecorator, new FastLinkDecorator,
-               new SpecialXmlCharsDecorator, new InlineHtmlDecorator, new FullLinkDecorator,
+               # FIXME XML is to slow with Nit
+               # new SpecialXmlCharsDecorator,
+               new InlineHtmlDecorator, new FullLinkDecorator,
                new FullImageDecorator, new RefLinkDecorator: MarkdownDecorator]
 
 for dec in lst do
diff --git a/benchmarks/polygons/.gitignore b/benchmarks/polygons/.gitignore
new file mode 100644 (file)
index 0000000..243db26
--- /dev/null
@@ -0,0 +1,11 @@
+java/add_vertex/
+java/contain/
+java/convexity/
+java/intersection/
+java/sort_vertices/
+nit/add_vertex/
+nit/contain/
+nit/convex_hull/
+nit/convexity/
+nit/intersection/
+nit/sort_vertices/
index 0926cd2..59b2d87 100644 (file)
@@ -1,9 +1,9 @@
-NITC=../../bin/nitc
-NITLS=../../bin/nitls
+NITC=nitc
+NITLS=nitls
 
 all: bin/action_nitro
 
-bin/action_nitro: $(shell ${NITLS} -M src/action_nitro.nit linux) ${NITC} pre-build
+bin/action_nitro: $(shell ${NITLS} -M src/action_nitro.nit linux) pre-build
        ${NITC} src/action_nitro.nit -m linux -o $@
 
 src/gen/texts.nit: art/texts.svg
index 17c7b05..dc0fac2 100644 (file)
@@ -216,7 +216,7 @@ redef class App
                var p = altitude / world.boss_altitude
                var ip = 1.0 - p
                glClearColor(0.3*ip, 0.3*ip, ip, 1.0)
-               stars.alpha = (1.4*p-0.4).min(1.0).max(0.0)
+               stars.alpha = (1.4*p-0.4).clamp(0.0, 1.0)
 
                # Randomly add smoke
                var poss = [
index f110c83..fef1d76 100644 (file)
@@ -1,22 +1,22 @@
-NITC=../../bin/nitc
-NITLS=../../bin/nitls
+NITC=nitc
+NITLS=nitls
 
 all: bin/asteronits
 
-bin/asteronits: $(shell ${NITLS} -M src/asteronits.nit linux) ${NITC} pre-build
+bin/asteronits: $(shell ${NITLS} -M src/asteronits.nit linux) pre-build
        ${NITC} src/asteronits.nit -m linux -o $@
 
-bin/texture_atlas_parser: src/texture_atlas_parser.nit
-       ${NITC} src/texture_atlas_parser.nit -o $@
+bin/texture_atlas_parser: ../../lib/gamnit/texture_atlas_parser.nit
+       ${NITC} ../../lib/gamnit/texture_atlas_parser.nit -o $@
 
 src/controls.nit: art/controls.svg
        make -C ../inkscape_tools/
        ../inkscape_tools/bin/svg_to_png_and_nit art/controls.svg -a assets/ -s src/ -x 2.0 -g
 
-src/spritesheet_city.nit: bin/texture_atlas_parser
+src/spritesheet.nit: bin/texture_atlas_parser
        bin/texture_atlas_parser art/sheet.xml --dir src/ -n spritesheet
 
-pre-build: src/controls.nit src/spritesheet_city.nit
+pre-build: src/controls.nit src/spritesheet.nit
 
 check: bin/asteronits
        NIT_TESTING=true bin/asteronits
@@ -25,10 +25,10 @@ check: bin/asteronits
 # Android
 
 android: bin/asteronits.apk
-bin/asteronits.apk: $(shell ${NITLS} -M src/asteronits.nit android) ${NITC} android/res/ pre-build
+bin/asteronits.apk: $(shell ${NITLS} -M src/asteronits.nit android) android/res/ pre-build
        ${NITC} src/android.nit -m android -o $@
 
-android-release: $(shell ${NITLS} -M src/asteronits.nit android) ${NITC} android/res/ pre-build
+android-release: $(shell ${NITLS} -M src/asteronits.nit android) android/res/ pre-build
        ${NITC} src/android.nit -m android -o bin/asteronits.apk --release
 
 android/res/: art/icon.svg
index 4042f6d..e7ac9df 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import ::android::platform
+import ::android
 import ::android::vibration
 
 import asteronits
index b7ec3f4..785fe9d 100644 (file)
@@ -163,7 +163,7 @@ abstract class SpacialObject
                # Realistic rotation, kept for reference and reality minded individuals
                #var r = applied_rotation * 0.2
                #rotation_inertia += r
-               #rotation_inertia = rotation_inertia.min(2.0).max(-2.0)
+               #rotation_inertia = rotation_inertia.clamp(-2.0, 2.0)
 
                # Inertia to position
                rotation += rotation_inertia * dt
index 3971426..388cd35 100644 (file)
@@ -62,16 +62,16 @@ redef class App
                                        if dy > 0.0 then
                                                # Bottom part of the joystick, turns left or right
                                                if dx < 0.0 then
-                                                       ship.applied_rotation = -1.0
-                                               else
                                                        ship.applied_rotation = 1.0
+                                               else
+                                                       ship.applied_rotation = -1.0
                                                end
                                        else
                                                # Upper part of the joystick, detect action using 45d angles
                                                if dx < dy then
-                                                       ship.applied_rotation = -1.0
-                                               else if dx > -dy then
                                                        ship.applied_rotation = 1.0
+                                               else if dx > -dy then
+                                                       ship.applied_rotation = -1.0
                                                else
                                                        ship.applied_thrust = 1.0
                                                end
@@ -98,20 +98,20 @@ redef class App
 
                # Add the joystick to the UI
                ui_sprites.add new Sprite(spritesheet_controls.forward,
-                       ui_camera.bottom_left.offset(joystick_x, -200.0, 0.0))
+                       ui_camera.bottom_left.offset(joystick_x, 200.0, 0.0))
                ui_sprites.add new Sprite(spritesheet_controls.left,
-                       ui_camera.bottom_left.offset(joystick_x-100.0, -joystick_y, 0.0))
+                       ui_camera.bottom_left.offset(joystick_x-100.0, joystick_y, 0.0))
                ui_sprites.add new Sprite(spritesheet_controls.right,
-                       ui_camera.bottom_left.offset(joystick_x+100.0, -joystick_y, 0.0))
+                       ui_camera.bottom_left.offset(joystick_x+100.0, joystick_y, 0.0))
 
                # Purely cosmetic joystick background
                ui_sprites.add new Sprite(spritesheet_controls.joystick_back,
-                       ui_camera.bottom_left.offset(joystick_x, -joystick_y, -1.0)) # In the back
+                       ui_camera.bottom_left.offset(joystick_x, joystick_y, -1.0)) # In the back
                ui_sprites.add new Sprite(spritesheet_controls.joystick_down,
                        ui_camera.bottom_left.offset(joystick_x, 0.0, 1.0))
 
                # Add the "open fire" button
                ui_sprites.add new Sprite(spritesheet_controls.fire,
-                       ui_camera.bottom_right.offset(-150.0, -150.0, 0.0))
+                       ui_camera.bottom_right.offset(-150.0, 150.0, 0.0))
        end
 end
index 562fb6d..ccbe098 100644 (file)
@@ -1,4 +1,4 @@
-src/benitlux_restful.nit
+src/server/benitlux_restful.nit
 *.db
 *.email
 benitlux_corrections.txt
index c14d648..4ae4000 100644 (file)
@@ -1,25 +1,86 @@
 SERVER ?= localhost:8080
 
-all: server
+all: server bin/report bin/benitlux
 
 server: bin/benitlux_daily bin/benitlux_web
-bin/benitlux_daily: $(shell ../../bin/nitls -M src/benitlux_daily.nit)
+bin/benitlux_daily: $(shell nitls -M src/server/benitlux_daily.nit)
        mkdir -p bin/
-       ../../bin/nitc -o $@ src/benitlux_daily.nit
+       nitc -o $@ src/server/benitlux_daily.nit
 
-bin/benitlux_web: $(shell ../../bin/nitls -M src/benitlux_web.nit) src/benitlux_restful.nit
+bin/benitlux_web: $(shell nitls -M src/server/server.nit) src/server/benitlux_restful.nit
        mkdir -p bin/
-       ../../bin/nitc -o $@ src/benitlux_web.nit -D iface=$(SERVER)
+       nitc -o $@ src/server/server.nit -D iface=$(SERVER)
 
-pre-build: src/benitlux_restful.nit
-src/benitlux_restful.nit: $(shell ../../bin/nitls -M src/benitlux_controller.nit)
-       ../../bin/nitrestful -o $@ src/benitlux_controller.nit
+pre-build: src/server/benitlux_restful.nit
+src/server/benitlux_restful.nit: $(shell nitls -M src/server/benitlux_controller.nit)
+       nitrestful -o $@ src/server/benitlux_controller.nit
 
 # ---
 # Report
 
-bin/report: $(shell ../../bin/nitls -M src/report.nit)
-       ../../bin/nitc -o bin/report src/report.nit
+bin/report: $(shell nitls -M src/report.nit)
+       nitc -o bin/report src/report.nit
 
 report: bin/report
        bin/report
+
+# ---
+# GTK+ client
+
+bin/benitlux: $(shell nitls -M src/client/client.nit)
+       mkdir -p bin/
+       nitc -o bin/benitlux src/client/client.nit -m linux -D benitlux_rest_server_uri=http://$(SERVER)/
+
+# ---
+# Android
+
+# Main icon
+android/res/drawable-hdpi/icon.png:
+       ../inkscape_tools/bin/svg_to_icons art/icon.svg --android --out android/res/
+
+# Notification icon, white only
+android/res/drawable-hdpi/notif.png:
+       ../inkscape_tools/bin/svg_to_icons art/notif.svg --android --out android/res/ --name notif
+
+android-res: android/res/drawable-hdpi/icon.png android/res/drawable-hdpi/notif.png
+
+# Dev / debug app
+android: bin/benitlux.apk
+bin/benitlux.apk: $(shell nitls -M src/client/android.nit) android-res
+       mkdir -p bin/ res/
+       nitc -o $@ src/client/android.nit -m src/client/features/debug.nit \
+               -D benitlux_rest_server_uri=http://$(SERVER)/
+
+# Pure portable prototype, for comparison
+bin/proto.apk: $(shell nitls -M src/client/android_proto.nit) android-res
+       mkdir -p bin/ res/
+       nitc -o $@ src/client/android_proto.nit \
+               -D benitlux_rest_server_uri=http://$(SERVER)/
+
+# Release version
+android-release: $(shell nitls -M src/client/android.nit) android-res
+       mkdir -p bin/ res/
+       nitc -o bin/benitlux.apk src/client/android.nit \
+               -D benitlux_rest_server_uri=http://xymus.net/benitlux/ --release
+
+# ---
+# iOS
+
+ios: bin/benitlux.app
+bin/benitlux.app: $(shell nitls -M src/client/ios.nit) ios/AppIcon.appiconset/Contents.json
+       mkdir -p bin/
+       rm -rf bin/benitlux.app/
+       nitc -o bin/benitlux.app src/client/ios.nit -D benitlux_rest_server_uri=http://$(SERVER)/
+
+bin/proto.app: $(shell nitls -M src/client/ios_proto.nit) ios/AppIcon.appiconset/Contents.json
+       mkdir -p bin/ res/
+       nitc -o $@ src/client/ios_proto.nit \
+               -D benitlux_rest_server_uri=http://$(SERVER)/
+
+ios-release: $(shell nitls -M src/client/ios.nit) ios/AppIcon.appiconset/Contents.json
+       mkdir -p bin/
+       nitc -o bin/benitlux.app src/client/ios.nit -D benitlux_rest_server_uri=http://$(SERVER)/
+
+ios/AppIcon.appiconset/Contents.json: art/icon.svg
+       mkdir -p ios
+       ../inkscape_tools/bin/svg_to_icons art/icon.svg --ios --out ios/AppIcon.appiconset/
index 6f4ce46..b8aed58 100644 (file)
@@ -1,21 +1,45 @@
-An unofficial mailing list and other tools to keep faithful bargoers informed of the beers available at the excellent Brasserie Bénélux.
+An unofficial app and mailing list to keep faithful bargoers informed of the beers available at the excellent Brasserie Bénélux.
 
-This project is composed of two softwares:
+This project is composed of three softwares:
 
-* a Web interface to subscribe and unsubscribe,
-* and a daily background program which updates the BD and send emails.
+* A mobile app and social network,
+* a server with a RESTful API for the mobile app and a web interface to subscribe to the mailing list
+* and a daily background program which updates the DB and send emails.
 
-The web interface is currently published at <http://benitlux.xymus.net/>
+The mobile app is available on the Nit F-Droid repository, see http://nitlanguage.org/fdroid.
+The web interface is currently published at http://benitlux.xymus.net.
 
 # Compile and execute
 
-Make sure all the required packages are installed. Under Debian or Ubuntu, you can use: `apt-get install libevent-dev libsqlite3-dev libcurl4-gnutls-dev sendmail`
+First, choose a server and set the `SERVER` environment variable accordingly.
+It can be localhost, a local development server or the official server.
 
-To compile, run: `make`
+* `SERVER` defaults to `localhost:8080`.
+  This is enough to test running the server and the GNU/Linux client on the same machine.
 
-To launch the daily background program, run: `bin/benitlux_daily` (the argument `-e` activates sending emails)
+* Set `SERVER=192.168.0.1` or to your IP to quickly setup a development server.
+  This allows you to work and test both the clients and the server.
 
-To launch the Web interface, run: `bin/benitlux_web`
+* Set `SERVER=benitlux.xymus.net` to use the official server, it should work with all clients.
+  It is not advised to use the official server with unstable clients.
+
+## Mobile client
+
+Build and run on GNU/Linux with `make bin/benitlux && bin/benitlux`
+
+Build and install for Android with: `make bin/benitlux.apk && adb install -rd bin/benitlux.apk`
+
+Build and simulate for iOS with: `make bin/benitlux.app && ios-sim launch bin/benitlux.app`
+
+## Server
+
+Install all required development packages. Under Debian or Ubuntu, you can use: `apt-get install libevent-dev libsqlite3-dev libcurl4-gnutls-dev sendmail`
+
+Compile with: `make`
+
+Launch the daily background program with: `bin/benitlux_daily` (the argument `-e` sends the emails)
+
+Launch the server with: `bin/benitlux_web`
 
 The Web interface will be accessible at <http://localhost:8080/>
 
@@ -25,10 +49,9 @@ The Web interface will be accessible at <http://localhost:8080/>
 - [x] Daily mailer
 - [x] Web interface
 - [x] Serialization and deserialization of data classes
-- [ ] Android app
-- [ ] iOS app
+- [x] Android app
+- [x] iOS app
 - [ ] Charlevoix location support
-- [ ] Customize mails (daily, on change, per locations)
 - [ ] Authenticate unsubscribe actions over GET
-- [ ] Social network and location updates
+- [x] Social network and location updates
 - [ ] Event updates
diff --git a/contrib/benitlux/android/res/.gitignore b/contrib/benitlux/android/res/.gitignore
new file mode 100644 (file)
index 0000000..46ce728
--- /dev/null
@@ -0,0 +1 @@
+drawable*
diff --git a/contrib/benitlux/android/res/values/styles.xml b/contrib/benitlux/android/res/values/styles.xml
new file mode 100644 (file)
index 0000000..c435c5f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+       <color name="item_background">#000000</color>
+</resources>
diff --git a/contrib/benitlux/art/icon.svg b/contrib/benitlux/art/icon.svg
new file mode 100644 (file)
index 0000000..779a3e3
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="icon.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.7"
+     inkscape:cx="239.85532"
+     inkscape:cy="187.76324"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1196"
+     inkscape:window-height="1109"
+     inkscape:window-x="2732"
+     inkscape:window-y="1283"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-540.36218)">
+    <path
+       sodipodi:type="arc"
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       id="path2986"
+       sodipodi:cx="270.72089"
+       sodipodi:cy="251.38065"
+       sodipodi:rx="241.42645"
+       sodipodi:ry="241.42645"
+       d="m 512.14734,251.38065 a 241.42645,241.42645 0 1 1 -482.852906,0 241.42645,241.42645 0 1 1 482.852906,0 z"
+       transform="matrix(1.0603643,0,0,1.0603643,-31.062773,529.80711)" />
+    <text
+       xml:space="preserve"
+       style="font-size:394.38067627px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+       x="120.70995"
+       y="938.47345"
+       id="text2987"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2989"
+         x="120.70995"
+         y="938.47345"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Webdings;-inkscape-font-specification:Webdings Bold">B</tspan></text>
+    <path
+       transform="matrix(0.96890975,0,0,0.96890975,-6.3041178,552.79701)"
+       d="m 512.14734,251.38065 a 241.42645,241.42645 0 1 1 -482.852906,0 241.42645,241.42645 0 1 1 482.852906,0 z"
+       sodipodi:ry="241.42645"
+       sodipodi:rx="241.42645"
+       sodipodi:cy="251.38065"
+       sodipodi:cx="270.72089"
+       id="path2988"
+       style="fill:none;stroke:#ffffff;stroke-width:10.02402973;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       sodipodi:type="arc" />
+  </g>
+</svg>
diff --git a/contrib/benitlux/art/notif.svg b/contrib/benitlux/art/notif.svg
new file mode 100644 (file)
index 0000000..871b01c
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="icon.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.7"
+     inkscape:cx="331.40838"
+     inkscape:cy="413.97896"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1196"
+     inkscape:window-height="1109"
+     inkscape:window-x="2732"
+     inkscape:window-y="297"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-540.36218)">
+    <text
+       xml:space="preserve"
+       style="font-size:419.40737915px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+       x="112.12482"
+       y="947.49194"
+       id="text2987"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2989"
+         x="112.12482"
+         y="947.49194"
+         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Webdings;-inkscape-font-specification:Webdings Bold">B</tspan></text>
+    <path
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:10.02402973;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="M 256 2.0625 C 115.82007 2.0625 2.0625 115.82007 2.0625 256 C 2.0625 396.17993 115.82007 509.9375 256 509.9375 C 396.17993 509.9375 509.9375 396.17993 509.9375 256 C 509.9375 115.82007 396.17993 2.0625 256 2.0625 z M 256 37.0625 C 376.96769 37.0625 474.9375 135.03232 474.9375 256 C 474.9375 376.96769 376.96769 474.90625 256 474.90625 C 135.03232 474.90625 37.0625 376.96769 37.0625 256 C 37.0625 135.03232 135.03232 37.0625 256 37.0625 z "
+       transform="translate(0,540.36218)"
+       id="path2988" />
+  </g>
+</svg>
diff --git a/contrib/benitlux/ios/.gitignore b/contrib/benitlux/ios/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
diff --git a/contrib/benitlux/net.xymus.benitlux.txt b/contrib/benitlux/net.xymus.benitlux.txt
new file mode 100644 (file)
index 0000000..fa717a9
--- /dev/null
@@ -0,0 +1,11 @@
+Categories:Nit,Internet
+License:Apache2
+Web Site:http://xymus.net/benitlux
+Source Code:http://nitlanguage.org/nit.git/tree/HEAD:/contrib/benitlux
+Issue Tracker:https://github.com/nitlang/nit/issues
+
+Summary:Mobile client for the Benitlux social network
+Description:
+View the beer menu, rate beers, view community rating, and receive notifications
+of the daily menu changes and when friends are on location.
+.
index e53853c..38eeee8 100644 (file)
@@ -1,12 +1,13 @@
 [package]
 name=benitlux
-tags=network
+tags=mobile,web
 maintainer=Alexis Laferrière <alexis.laf@xymus.net>
 license=Apache-2.0
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/contrib/benitlux/
 git=https://github.com/nitlang/nit.git
 git.directory=contrib/benitlux/
-homepage=http://nitlanguage.org
+homepage=http://xymus.net/benitlux/
 issues=https://github.com/nitlang/nit/issues
-tryit=http://benitlux.xymus.net/
+tryit=http://xymus.net/benitlux/
+apk=http://nitlanguage.org/fdroid/apk/tnitter.apk
index 987047f..48a807a 100644 (file)
@@ -234,6 +234,14 @@ class CheckinReport
        var users = new Array[User]
 end
 
+# Daily menu notifications
+class DailyNotification
+       serialize
+
+       # All beers on the menu today
+       var beers: Array[BeerAndRatings]
+end
+
 # Server or API usage error
 class BenitluxError
        super Error
diff --git a/contrib/benitlux/src/client/android.nit b/contrib/benitlux/src/client/android.nit
new file mode 100644 (file)
index 0000000..f3391bf
--- /dev/null
@@ -0,0 +1,268 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android variant improved with platform specific services
+module android is
+       android_manifest_activity """android:theme="@android:style/Theme.DeviceDefault" """
+       android_api_min 16 # For BigTextStyle
+       android_api_target 16
+end
+
+import ::android::portrait
+import ::android::toast
+import ::android::wifi
+import ::android::service::at_boot
+
+import client
+import push
+import checkins
+
+redef class App
+
+       redef fun on_create
+       do
+               super
+
+               # Launch service with app, if it wasn't already launched at boot
+               start_service
+       end
+
+       # Use Android toasts if there is an activity, otherwise fallback on the log
+       redef fun feedback(text)
+       do
+               if activities.not_empty then
+                       app.toast(text.to_s, false)
+               else super
+       end
+
+       # Register to callback `async_wifi_scan_available` when a wifi scan is available
+       private fun notify_on_wifi_scan(context: NativeContext)
+       import async_wifi_scan_available in "Java" `{
+
+               android.content.IntentFilter filter = new android.content.IntentFilter();
+               filter.addAction(android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+               final int final_self = self;
+
+               context.registerReceiver(
+                       new android.content.BroadcastReceiver() {
+                               @Override
+                               public void onReceive(android.content.Context context, android.content.Intent intent) {
+                                       if (intent.getAction().equals(android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                                               App_async_wifi_scan_available(final_self);
+                                       }
+                               }
+                       }, filter);
+       `}
+
+       private fun async_wifi_scan_available do run_on_ui_thread task_on_wifi_scan_available
+
+       private var task_on_wifi_scan_available = new WifiScanAvailable is lazy
+end
+
+redef class Service
+       redef fun on_start_command(intent, flags, id)
+       do
+               app.notify_on_wifi_scan native
+
+               # Check token validity
+               (new PushHttpRequest("push/check_token?token={app.token}")).start
+
+               return start_sticky
+       end
+end
+
+# Task ran on the UI thread when a wifi scan is available
+private class WifiScanAvailable
+       super Task
+
+       redef fun main
+       do
+               jni_env.push_local_frame 4
+               var manager = app.native_context.wifi_manager
+               var networks = manager.get_scan_results
+               var found_ben = false
+               for i in networks.length.times do
+                       jni_env.push_local_frame 4
+                       var net = networks[i]
+                       var ssid = net.ssid.to_s
+
+                       # TODO use BSSID instead
+                       #var bssid = net.bssid.to_s
+                       var target_ssids = ["Benelux"]
+                       if target_ssids.has(ssid) then # and bssid == "C8:F7:33:81:B0:E6" then
+                               found_ben = true
+                               break
+                       end
+                       jni_env.pop_local_frame
+               end
+               jni_env.pop_local_frame
+
+               if found_ben then
+                       app.on_check_in
+               else app.on_check_out
+       end
+end
+
+redef class SectionTitle
+       init do set_text_style(native, app.native_context)
+
+       private fun set_text_style(view: NativeTextView, context: NativeContext) in "Java" `{
+               view.setTextAppearance(context, android.R.style.TextAppearance_Large);
+       `}
+end
+
+redef class ItemView
+       init do set_backgroud(native, app.native_context)
+
+       private fun set_backgroud(view: NativeView, context: NativeContext) in "Java" `{
+               int color = context.getResources().getIdentifier("item_background", "color", context.getPackageName());
+               view.setBackgroundResource(color);
+       `}
+end
+
+# Use Android notifications
+redef fun notify(title, content, id)
+do
+       var service = app.service
+       assert service != null
+       native_notify(service.native, id, title.to_java_string, content.to_java_string)
+end
+
+private fun native_notify(context: NativeService, id: Int, title, content: JavaString)
+in "Java" `{
+       int icon = context.getResources().getIdentifier(
+               "notif", "drawable", context.getPackageName());
+
+       android.app.Notification.BigTextStyle style =
+               new android.app.Notification.BigTextStyle();
+       style.bigText(content);
+
+       android.content.Intent intent = new android.content.Intent(
+               context, nit.app.NitActivity.class);
+       android.app.PendingIntent pendingIntent = android.app.PendingIntent.getActivity(
+               context, 0, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
+
+       android.app.Notification notif = new android.app.Notification.Builder(context)
+               .setContentTitle(title)
+               .setContentText(content)
+               .setSmallIcon(icon)
+               .setAutoCancel(true)
+               .setOngoing(false)
+               .setStyle(style)
+               .setContentIntent(pendingIntent)
+               .setDefaults(android.app.Notification.DEFAULT_SOUND |
+                            android.app.Notification.DEFAULT_LIGHTS)
+               .build();
+
+       android.app.NotificationManager notificationManager =
+         (android.app.NotificationManager)context.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
+
+       notificationManager.notify((int)id, notif);
+`}
+
+
+# Use `RatingBar` as the beer rating control
+redef class BeerView
+       redef fun setup_stars(rating)
+       do
+               var title = "Review %0".t.format(beer_info.beer.name).to_java_string
+               native_setup_stars(app.native_context, top_line_layout.native, rating, title, app.user != null)
+       end
+
+       private fun native_setup_stars(context: NativeContext, layout: NativeViewGroup, rating: Int, title: JavaString, loggedin: Bool)
+       import on_review in "Java" `{
+               // Set an indicator/non-interactive display
+               final android.widget.RatingBar view = new android.widget.RatingBar(
+                       context, null, android.R.attr.ratingBarStyleIndicator);
+               view.setNumStars(5);
+               view.setRating(rating);
+               view.setIsIndicator(true);
+
+               final android.view.ViewGroup.MarginLayoutParams params = new android.view.ViewGroup.MarginLayoutParams(
+                       android.widget.LinearLayout.LayoutParams.WRAP_CONTENT,
+                       android.widget.LinearLayout.LayoutParams.FILL_PARENT);
+               layout.addView(view, params);
+
+               // Make some variables final to used in anonymous class and delayed methods
+               final android.content.Context final_context = context;
+               final long final_rating = rating;
+               final String final_title = title;
+               final boolean final_loggedin = loggedin;
+
+               final int final_self = self;
+               BeerView_incr_ref(self); // Nit GC
+
+               view.setOnTouchListener(new android.view.View.OnTouchListener() {
+                       @Override
+                       public boolean onTouch(android.view.View v, android.view.MotionEvent event) {
+                               if (event.getAction() != android.view.MotionEvent.ACTION_UP) return true;
+
+                               // Don't show dialog if not logged in
+                               if (!final_loggedin) {
+                                       android.widget.Toast toast = android.widget.Toast.makeText(
+                                               final_context, "You must login first to post reviews",
+                                               android.widget.Toast.LENGTH_SHORT);
+                                       toast.show();
+                                       return true;
+                               }
+
+                               // Build dialog with a simple interactive RatingBar
+                               final android.app.AlertDialog.Builder dialog_builder = new android.app.AlertDialog.Builder(final_context);
+                               final android.widget.RatingBar rating = new android.widget.RatingBar(final_context);
+                               rating.setNumStars(5);
+                               rating.setStepSize(1.0f);
+                               rating.setRating(final_rating);
+
+                               // Header bar
+                               int icon = final_context.getResources().getIdentifier("notif", "drawable", final_context.getPackageName());
+                               dialog_builder.setIcon(icon);
+                               dialog_builder.setTitle(final_title);
+
+                               // Rating control
+                               android.widget.LinearLayout l = new android.widget.LinearLayout(final_context);
+                               l.addView(rating, params);
+                               l.setHorizontalGravity(android.view.Gravity.CENTER_HORIZONTAL);
+                               dialog_builder.setView(l);
+
+                               // OK button
+                               dialog_builder.setPositiveButton(android.R.string.ok,
+                                       new android.content.DialogInterface.OnClickListener() {
+                                               public void onClick(android.content.DialogInterface dialog, int which) {
+                                                       dialog.dismiss();
+
+                                                       long r = (long)rating.getRating();
+                                                       view.setRating(r); // Update static control
+                                                       view.invalidate(); // For not refreshing bug
+
+                                                       BeerView_on_review(final_self, r); // Callback
+                                                       BeerView_decr_ref(final_self); // Nit GC
+                                               }
+                                       });
+
+                               // Cancel button
+                               dialog_builder.setNegativeButton(android.R.string.cancel,
+                                       new android.content.DialogInterface.OnClickListener() {
+                                               public void onClick(android.content.DialogInterface dialog, int id) {
+                                                       dialog.cancel();
+                                                       BeerView_decr_ref(final_self); // Nit GC
+                                               }
+                                       });
+
+                               dialog_builder.create();
+                               dialog_builder.show();
+                               return true;
+                       }
+               });
+       `}
+end
diff --git a/contrib/benitlux/src/client/android_proto.nit b/contrib/benitlux/src/client/android_proto.nit
new file mode 100644 (file)
index 0000000..22f0953
--- /dev/null
@@ -0,0 +1,28 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android variant without modification, pure prototype
+#
+# Usually, compiling with `nitc -m android client.nit` is enough.
+# In this case, for research purposes we set a different `app_namespace`.
+# This allows both the proto and the adaptation to be installed on the same device.
+module android_proto is
+       app_name "Ben Proto"
+       app_namespace "net.xymus.benitlux_proto"
+       android_api_target 16
+end
+
+import ::android
+
+import client
diff --git a/contrib/benitlux/src/client/base.nit b/contrib/benitlux/src/client/base.nit
new file mode 100644 (file)
index 0000000..7cfde72
--- /dev/null
@@ -0,0 +1,158 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common services for the Benitlux app
+module base
+
+import app::ui
+import app::data_store
+import app::http_request
+import android::aware
+import json::serialization
+
+import benitlux_model
+import translations
+
+# Show debug output?
+fun debug: Bool do return true
+
+# Root URI of the remote RESTfule server
+fun benitlux_rest_server_uri: String do return "http://localhost:8080/"
+
+redef class App
+
+       # Current connection token, or "none"
+       var token: String is lazy, writable do
+               var token = app.data_store["token"]
+               if token isa String then return token
+               return "none"
+       end
+
+       # Name of the currently logged in user
+       var user: nullable String is lazy, writable do
+               var user = app.data_store["user"]
+               if user isa nullable String then return user
+               return null
+       end
+
+       # Event when user logs in or out
+       fun on_log_in do on_save_state
+
+       redef fun on_save_state
+       do
+               app.data_store["user"] = user
+               app.data_store["token"] = token
+               super
+       end
+
+       # Has this app state been restored yet?
+       var restored = false
+
+       redef fun on_restore_state
+       do
+               super
+
+               # TODO this may happen before the lazy loading above
+               restored = true
+
+               if token != "none" then on_log_in
+       end
+
+       # Show simple feedback to the user on important errors
+       fun feedback(text: Text) do print_error text
+end
+
+# Show a notification to the user
+fun notify(title, content: Text, uniqueness_id: Int)
+do print "Notification {uniqueness_id}: {title}; {content}"
+
+# View for an item in a list, like a beer or a person
+abstract class ItemView
+       super View
+end
+
+# Basic async HTTP request for this app
+#
+# Note that connection errors are passed to `on_fail`, and
+# server errors or authentification errors are received by `on_load`
+# and should be passed to `intercept_error`.
+class BenitluxHttpRequest
+       super AsyncHttpRequest
+
+       redef fun uri_root do return benitlux_rest_server_uri
+
+       redef var uri_tail
+
+       redef fun on_fail(error)
+       do
+               if error isa IOError then
+                       # This should be a normal network error like being offline.
+                       # Print to log, but don't show to the user.
+                       print_error error.class_name
+               else
+                       # This could be a deserialization error,
+                       # it may be related to an outdated client.
+                       # Report to user.
+                       print_error "Request Error: {uri} with {error}"
+                       app.feedback "Request Error: {error}"
+               end
+       end
+
+       # Intercept known server side errors
+       fun intercept_error(res: nullable Object): Bool
+       do
+               if res isa BenitluxTokenError then
+                       app.token = "none"
+                       app.user = null
+                       return true
+               else if res isa BenitluxError then
+                       feedback((res.user_message or else res.message).t)
+                       return true
+               else if res isa Error then
+                       feedback res.message.t
+                       return true
+               end
+               return false
+       end
+
+       # Show feedback pertinent to the user, defaults to a platform specific popup
+       fun feedback(text: String) do app.feedback text
+end
+
+# Async request with services to act on the windows of the app
+class WindowHttpRequest
+       super BenitluxHttpRequest
+
+       autoinit window, uri_tail
+
+       # Type of the related `window`
+       type W: Window
+
+       # `Window` on which to apply the results of this request
+       var window: W
+
+       # `Views` to disable while this request is in progress
+       var affected_views = new Array[View]
+
+       redef fun before do for view in affected_views do view.enabled = false
+
+       redef fun after do for view in affected_views do view.enabled = true
+end
+
+redef class Text
+       # Ellipsize `self` so it fits within `max_length` characters
+       #
+       # FIXME Remove this when labels are correctly ellipsized on iOS.
+       fun ellipsize: Text do return self
+end
diff --git a/contrib/benitlux/src/client/client.nit b/contrib/benitlux/src/client/client.nit
new file mode 100644 (file)
index 0000000..e2fc2a9
--- /dev/null
@@ -0,0 +1,44 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Portable Benitlux app
+module client is
+       app_name "Benitlux"
+       app_version(0, 3, git_revision)
+       app_namespace "net.xymus.benitlux"
+end
+
+import home_views
+import beer_views
+import social_views
+import user_views
+
+# ---
+# Services
+
+redef class Deserializer
+       redef fun deserialize_class(name)
+       do
+               if name == "Array[Beer]" then return new Array[Beer].from_deserializer(self)
+               if name == "Array[User]" then return new Array[User].from_deserializer(self)
+               if name == "Array[BeerBadge]" then return new Array[BeerBadge].from_deserializer(self)
+               if name == "Array[BeerAndRatings]" then return new Array[BeerAndRatings].from_deserializer(self)
+               if name == "Array[String]" then return new Array[String].from_deserializer(self)
+               if name == "Array[UserAndFollowing]" then return new Array[UserAndFollowing].from_deserializer(self)
+               return super
+       end
+end
+
+set_fr
+super
diff --git a/contrib/benitlux/src/client/features/checkins.nit b/contrib/benitlux/src/client/features/checkins.nit
new file mode 100644 (file)
index 0000000..6131d98
--- /dev/null
@@ -0,0 +1,179 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# On location checkin services
+module checkins
+
+import client
+
+redef class App
+
+       # Should we share our checkins with the server and friends?
+       fun share_checkins: Bool
+       do return app.data_store["share_checkins"].as(nullable Bool) or else true
+
+       # Should we share our checkins with the server and friends?
+       fun share_checkins=(value: Bool)
+       do
+               # Notify server
+               if currently_on_location then
+                       if value then
+                               server_check_in
+                       else server_check_out
+               end
+
+               app.data_store["share_checkins"] = value
+       end
+
+       # Are we currently at the location?
+       fun currently_on_location: Bool
+       do return app.data_store["currently_on_location"].as(nullable Bool) or else false
+
+       # Are we currently at the location?
+       fun currently_on_location=(value: Bool) do app.data_store["currently_on_location"] = value
+
+       # Request beer menu from the server
+       #
+       # It includes a diff if `checkins` remembers a previous visit.
+       fun request_menu
+       do
+               var checkins = checkins
+               var since = checkins.latest
+               if since != null then
+                       var today = today
+                       if since == today then
+                               since = checkins.previous
+                       end
+               end
+
+               (new MenuHttpRequest("rest/since?token={token}&date={since or else ""}")).start
+       end
+
+       # User checks in
+       fun on_check_in
+       do
+               if currently_on_location then return
+
+               if share_checkins then server_check_in
+
+               currently_on_location = true
+               request_menu
+               checkins.update today
+       end
+
+       # User checks out
+       fun on_check_out
+       do
+               if not currently_on_location then return
+
+               if share_checkins then server_check_out
+               currently_on_location = false
+       end
+
+       # Notify server of checkin
+       private fun server_check_in do (new BenitluxHttpRequest("rest/checkin?token={app.token}&is_in=true")).start
+
+       # Notify server of checkout
+       private fun server_check_out do (new BenitluxHttpRequest("rest/checkin?token={app.token}&is_in=false")).start
+
+       # History of the last 1 or 2 checkins
+       var checkins = new SimpleMemory
+
+       redef fun on_save_state
+       do
+               super
+               app.data_store["checkins"] = checkins
+       end
+
+       redef fun on_restore_state
+       do
+               var checkins = app.data_store["checkins"]
+               if checkins isa SimpleMemory then self.checkins = checkins
+
+               super
+       end
+end
+
+# Request the menu from the server for a notification
+class MenuHttpRequest
+       super BenitluxHttpRequest
+
+       redef fun on_load(data, status)
+       do
+               if not data isa Array[BeerAndRatings] then
+                       on_fail new Error("Server sent unexpected data {data or else "null"}")
+                       return
+               end
+
+               var content = data.beers_to_notification
+
+               notify("Passing by the Benelux?".t, content, 2)
+       end
+end
+
+# ---
+# Support services
+
+# Memory of an element and the previous one, avoiding duplication
+#
+# Used to remember the last day at the location,
+# ignoring multiple reports on the same day.
+class SimpleMemory
+       serialize
+
+       # Before latest remembered entry
+       var previous: nullable String = null
+
+       # Last remembered entry
+       var latest: nullable String = null
+
+       # Update `latest` if `value` is different
+       fun update(value: String)
+       do
+               if value == latest then return
+
+               previous = latest
+               latest = value
+       end
+end
+
+# ---
+# UI
+
+redef class UserWindow
+
+       private var lbl_checkins_options_title = new Label(parent=layout,
+               text="Share options".t)
+
+       private var chk_share_checkins = new CheckBox(parent=layout,
+               text="Share checkins with your friends".t)
+
+       init
+       do
+               chk_share_checkins.is_checked = app.share_checkins
+               lbl_checkins_options_title.size = 1.5
+       end
+
+       redef fun on_event(event)
+       do
+               super
+
+               if event isa ToggleEvent then
+                       var sender = event.sender
+                       if sender == chk_share_checkins then
+                               app.share_checkins = sender.is_checked
+                       end
+               end
+       end
+end
diff --git a/contrib/benitlux/src/client/features/debug.nit b/contrib/benitlux/src/client/features/debug.nit
new file mode 100644 (file)
index 0000000..e34fcc2
--- /dev/null
@@ -0,0 +1,58 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Debugging features accessible from the user preference menu
+module debug
+
+import client
+import push
+import checkins
+
+redef class UserWindow
+
+       private var layout_debug = new VerticalLayout(parent=layout)
+
+       private var lbl_debug_title = new Label(parent=layout_debug,
+               text="Debug options".t, size=1.5)
+
+       private var but_test_notif = new Button(parent=layout_debug,
+               text="Test notifications".t)
+
+       private var but_test_checkin = new Button(parent=layout_debug,
+               text="Test checkin".t)
+
+       private var but_test_checkout = new Button(parent=layout_debug,
+               text="Test checkout".t)
+
+       private var but_test_menu = new Button(parent=layout_debug,
+               text="Test menu diff".t)
+
+       redef fun on_event(event)
+       do
+               super
+
+               if event isa ButtonPressEvent then
+                       var sender = event.sender
+                       if sender == but_test_notif then
+                               notify("Test Notification", "Some content\nmultiline", 5)
+                       else if sender == but_test_checkin then
+                               app.on_check_in
+                       else if sender == but_test_checkout then
+                               app.on_check_out
+                       else if sender == but_test_menu then
+                               app.request_menu
+                       end
+               end
+       end
+end
diff --git a/contrib/benitlux/src/client/features/push.nit b/contrib/benitlux/src/client/features/push.nit
new file mode 100644 (file)
index 0000000..62b8fc2
--- /dev/null
@@ -0,0 +1,217 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Push notification support
+module push
+
+import app::http_request
+
+import client
+
+redef class App
+       redef fun on_log_in
+       do
+               super
+               #(new PushHttpRequest("push/check_token?token={app.token}")).start
+       end
+
+       # Names of the known users currently on location
+       var users_on_location = new Set[String]
+
+       # Should we show a daily notification when new beers are available?
+       fun notify_on_new_beers: Bool
+       do return app.data_store["notify_on_new_beers"].as(nullable Bool) or else true
+
+       # Should we show a daily notification when new beers are available?
+       fun notify_on_new_beers=(value: Bool) do app.data_store["notify_on_new_beers"] = value
+
+       # Should we show a daily notification of the menu?
+       fun notify_menu_daily: Bool
+       do return app.data_store["notify_menu_daily"].as(nullable Bool) or else false
+
+       # Should we show a daily notification of the menu?
+       fun notify_menu_daily=(value: Bool) do app.data_store["notify_menu_daily"] = value
+
+       # Should we show a notification when friends check in at the location?
+       fun notify_on_checkins: Bool
+       do return app.data_store["notify_on_checkins"].as(nullable Bool) or else true
+
+       # Should we show a notification when friends check in at the location?
+       fun notify_on_checkins=(value: Bool) do app.data_store["notify_on_checkins"] = value
+end
+
+# Open push notification request
+class PushHttpRequest
+       super BenitluxHttpRequest
+
+       redef fun on_fail(error)
+       do
+               if app.user == null then return
+
+               super
+
+               print_error "{class_name}: on_fail {error}"
+
+               var t = new PushHttpRequest("push/?token={app.token}")
+               t.delay = 10.0
+               t.start
+       end
+
+       redef fun on_load(data, status)
+       do
+               if app.user == null then return
+
+               var delay = 0.0
+               if data isa Pushable then
+                       data.apply_push_if_desired
+               else if data isa BenitluxError then
+                       # TODO if forbidden ask for a new token
+                       delay = 5.0*60.0
+               else
+                       print_error "{class_name}: Received {data or else "null"}"
+               end
+
+               var t = new PushHttpRequest("push/?token={app.token}")
+               t.delay = delay
+               t.start
+       end
+end
+
+# ---
+# Objects sent from the server to the client
+
+# Objects sent as push notifications by the server
+interface Pushable
+       # Act on this push notification
+       fun apply_push do print_error "Unimplemented `apply_push` on {class_name}"
+
+       # Consider to `apply_push` if the user preferences wants to
+       fun apply_push_if_desired do apply_push
+end
+
+redef class CheckinReport
+       super Pushable
+
+       # Flattened array of the name of users
+       var user_names: Array[String] = [for u in users do u.name] is lazy
+
+       redef fun apply_push_if_desired
+       do
+               if not app.notify_on_checkins then return
+
+               var there_is_a_new_user = false
+               for new_users in user_names do
+                       if not app.users_on_location.has(new_users) then
+                               there_is_a_new_user = true
+                               break
+                       end
+               end
+
+               app.users_on_location.clear
+               app.users_on_location.add_all user_names
+
+               # Apply only if there is someone new on location
+               if there_is_a_new_user then super
+       end
+
+       redef fun apply_push
+       do
+               if users.is_empty then
+                       #app.notif_push.cancel
+                       #self.cancel(tag, (int)id);
+                       return
+               end
+
+               var title = "TTB!".t
+               var names = [for user in users do user.name]
+               var content = "From %0".t.format(names.join(", "))
+
+               notify(title, content, 1)
+       end
+end
+
+redef class DailyNotification
+       super Pushable
+
+       redef fun apply_push_if_desired
+       do
+               if app.notify_menu_daily then
+                       super
+                       return
+               end
+
+               if app.notify_on_new_beers then
+                       for beer in beers do
+                               if beer.is_new then
+                                       super
+                                       return
+                               end
+                       end
+               end
+       end
+
+       redef fun apply_push
+       do
+               var title = if beers.has_new_beers then
+                       "New beers are on the menu".t
+               else "Beer Menu".t
+
+               var content = beers.beers_to_notification
+               notify(title, content, 3)
+       end
+end
+
+# ---
+# UI
+
+redef class UserWindow
+
+       private var layout_push_options = new VerticalLayout(parent=layout)
+
+       private var lbl_push_options_title = new Label(parent=layout_push_options,
+               text="Notifications options".t, size=1.5)
+
+       private var chk_notify_on_new_beers = new CheckBox(parent=layout_push_options,
+               text="Notify when there are new beers".t)
+
+       private var chk_notify_menu_daily = new CheckBox(parent=layout_push_options,
+               #text="Show the menu every work day?".t)
+               text="Show the menu every work day".t)
+
+       private var chk_notify_on_checkins = new CheckBox(parent=layout_push_options,
+               text="Notify when a friend checks in".t)
+
+       init
+       do
+               chk_notify_on_new_beers.is_checked = app.notify_on_new_beers
+               chk_notify_menu_daily.is_checked = app.notify_menu_daily
+               chk_notify_on_checkins.is_checked = app.notify_on_checkins
+       end
+
+       redef fun on_event(event)
+       do
+               super
+
+               if event isa ToggleEvent then
+                       var sender = event.sender
+                       if sender == chk_notify_on_new_beers then
+                               app.notify_on_new_beers = sender.is_checked
+                       else if sender == chk_notify_menu_daily then
+                               app.notify_menu_daily = sender.is_checked
+                       else if sender == chk_notify_on_checkins then
+                               app.notify_on_checkins = sender.is_checked
+                       end
+               end
+       end
+end
diff --git a/contrib/benitlux/src/client/features/translations.nit b/contrib/benitlux/src/client/features/translations.nit
new file mode 100644 (file)
index 0000000..be42f57
--- /dev/null
@@ -0,0 +1,113 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+
+# Support for translating the app to different languages, implements French
+module translations
+
+redef class Text
+       # Translate `self` according to the current language `sys.lang`
+       fun t: String
+       do
+               var lang = sys.lang_map
+               if lang == null then return to_s
+
+               if lang.keys.has(self) then return lang[self]
+
+               print "Translation miss ({sys.lang}): {self}"
+               return to_s
+       end
+end
+
+redef class Sys
+       # Name of the language in use
+       var lang = "C"
+
+       # Translation map for the language in use
+       var lang_map: nullable Map[Text, String] = null
+end
+
+# Set French as the current language
+fun set_fr
+do
+       var map = new Map[Text, String]
+
+       # Home views
+       map["Welcome %0"] = "Bienvenue %0"
+       map["Welcome"] = "Bienvenue"
+       map["Beer Menu"] = "Menu de bières"
+       map["View all"] = "Menu complet"
+       map["Preferences"] = "Préférences"
+       map["Friends"] = "Amis"
+       map["Manage"] = "Gérer"
+       map["Events"] = "Événements"
+       map["Loading..."] = "Chargement..."
+       map["Login or signup"] = "S'authentifier"
+       map["On location?"] = "Sur place?"
+       map["Leaving?"] = "Vous quittez?"
+
+       # User/login views
+       map["Account options"] = "Options du compte"
+       map["Share options"] = "Options de partage"
+       map["Notifications options"] = "Options de notification"
+       map["Please login"] = "Veuillez vous identifier"
+       map["Welcome %0!"] = "Bienvenue %0!"
+       map["Logged in as %0"] = "Connecté en tant que %0"
+       map["Username"] = "Nom d'utilisateur"
+       map["Password"] = "Mot de passe"
+       map["Repeat password"] = "Répéter le mot de passe"
+       map["Email"] = "Courriel"
+       map["Login"] = "Se connecter"
+       map["Loging in..."] = "Authentification..."
+       map["Logout"] = "Se déconnecter"
+       map["Signup"] = "Créer un compte"
+       map["Signing up..."] = "Création du compte..."
+
+       map["Passwords must be composed of at least 6 characters."] = "Le mot de passe doit avoir au moins 6 charactères."
+       map["Fill the following fields to sign up."] = "Remplissez les champs suivants pour créer un compte."
+
+       map["Passwords do not match."] = "Les mots de passe ne correspondent pas."
+       map["Invalid username."] = "Nom d'utilisateur invalide."
+       map["Invalid password."] = "Mot de passe invalide."
+       map["Username already in use."] = "Le nom d'utilisateur est déjà réservé."
+       map["Invalid username and password combination."] = "La combinaison de nom et mot de passe n'est pas reconnue."
+
+       # Social views
+       map["Follow"] = "Suivre"
+       map["Unfollow"] = "Ne plus suivre"
+       map["Search"] = "Rechercher"
+       map["Favorites: %0"] = "Favoris: %0"
+       map["No favorites yet"] = "Pas de favoris"
+       map["List followed"] = "Personnes suivies"
+       map["List followers"] = "Personnes vous suivant"
+
+       # Beer views
+       map["Review %0"] = "Évaluer %0"
+       map["%0★ %1 reviews"] = "%0★ %1 avis"
+       map["No reviews yet"] = "Aucun avis"
+       map[", friends: %0☆ %1 reviews"] = ", amis: %0☆ %1 avis"
+       map[" (New)"] = " (Nouveau)"
+       map["Similar to %0."] = "Similaire à %0."
+       map["Favorite beer on the menu."] = "Bière préférée sur le menu."
+       map["Favorite of %0"] = "Préférée de %0"
+
+       # Preferences
+       map["Notify when a friend checks in"] = "Lorsqu'un ami est sur place"
+       map["Show the menu every work day"] = "Menu journalier en semaine"
+       map["Notify when there are new beers"] = "Lorsqu'il y a de nouvelles bières"
+       map["Share checkins with your friends"] = "Partager lorsque vous êtes sur place"
+       map["Passing by the Benelux?"] = "De passage au Bénélux?"
+
+       # Update Sys
+       sys.lang = "fr"
+       sys.lang_map = map
+end
diff --git a/contrib/benitlux/src/client/ios.nit b/contrib/benitlux/src/client/ios.nit
new file mode 100644 (file)
index 0000000..f8d99a2
--- /dev/null
@@ -0,0 +1,146 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# iOS variant using a button to check in/out and local notifications
+module ios
+
+import ::ios
+intrude import app::ui
+
+import client
+import push
+import checkins
+
+redef class HomeWindow
+       init
+       do
+               title = "Benitlux"
+               update_checkin_text
+       end
+
+       # TODO hide when not logged in
+       private var layout_login_checkin = new HorizontalLayout(parent=layout_user)
+       private var checkin_label = new Label(parent=layout_login_checkin)
+       private var checkin_button = new Button(parent=layout_login_checkin)
+
+       redef fun on_event(event)
+       do
+               super
+
+               if event isa ButtonPressEvent then
+                       var sender = event.sender
+                       if sender == checkin_button then
+                               if app.currently_on_location then
+                                       app.on_check_out
+                               else app.on_check_in
+                       end
+               end
+       end
+
+       private fun update_checkin_text
+       do
+               if app.currently_on_location then
+                       checkin_label.text = "Leaving?".t
+                       checkin_button.text = "Check out".t
+               else
+                       checkin_label.text = "On location?".t
+                       checkin_button.text = "Check in".t
+               end
+       end
+end
+
+redef class App
+       redef fun on_check_in
+       do
+               super
+               var window = window
+               if window isa HomeWindow then window.update_checkin_text
+       end
+
+       redef fun on_check_out
+       do
+               super
+               var window = window
+               if window isa HomeWindow then window.update_checkin_text
+       end
+
+       redef fun did_finish_launching_with_options
+       do
+               ui_application.register_user_notification_settings
+               return super
+       end
+end
+
+redef class UserWindow
+       init do title = "Preferences".t
+end
+
+redef class BeersWindow
+       init do title = "Beers".t
+end
+
+redef class SocialWindow
+       init do title = "People".t
+end
+
+# --- Notifications
+
+redef fun notify(title, content, id)
+do native_notify(title.to_nsstring, content.to_nsstring)
+
+private fun native_notify(title, content: NSString) in "ObjC" `{
+       UILocalNotification* notif = [[UILocalNotification alloc] init];
+       notif.alertTitle = title;
+       notif.alertBody = content;
+       notif.timeZone = [NSTimeZone defaultTimeZone];
+       [[UIApplication sharedApplication] presentLocalNotificationNow: notif];
+`}
+
+redef class UIApplication
+
+       # Register this app to display notifications
+       private fun register_user_notification_settings
+       in "ObjC" `{
+               if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){
+                       [self registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
+               }
+       `}
+end
+
+# ---
+# Shorten labels
+
+redef class Label
+       # Ellipsize `text` so it fits within `max_length` characters
+       #
+       # FIXME Remove this when labels are correctly ellipsized on iOS.
+       redef fun text=(text)
+       do
+               if text == null then
+                       super
+                       return
+               end
+
+               var max_length = 50
+               if parent isa HorizontalLayout and parent.parent isa BeerView then
+                       # This is the name of a beer, remember its a hack
+                       max_length = 20
+               end
+
+               if text.length > max_length then
+                       text = text.substring(0, max_length - 3).to_s + "..."
+               end
+               super text
+       end
+end
diff --git a/contrib/benitlux/src/client/views/beer_views.nit b/contrib/benitlux/src/client/views/beer_views.nit
new file mode 100644 (file)
index 0000000..59ae555
--- /dev/null
@@ -0,0 +1,328 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Window to list beers and other beer-related views
+module beer_views
+
+import base
+
+# View about a beer, with its name, description and rating
+class BeerView
+       super VerticalLayout
+       super ItemView
+
+       autoinit beer_info, parent
+
+       # Beer information
+       var beer_info: BeerAndRatings
+
+       # Buttons to realize the rating buttons
+       var star_buttons = new Array[StarButton]
+
+       # Layout of the first line with the name and `star_buttons`
+       var top_line_layout = new HorizontalLayout(parent=self)
+
+       init
+       do
+               var lbl_name = new Label(parent=top_line_layout, text=beer_info.beer.name)
+               lbl_name.size = 1.25
+
+               var desc = beer_info.beer.desc
+               if beer_info.is_new then desc += " (New)".t
+               var lbl_desc = new Label(parent=self, text=desc)
+
+               var lbl_stats = new Label(parent=self, text=beer_info.rating_text)
+               lbl_stats.size = 0.5
+
+               var badges = beer_info.badges
+               if badges != null then
+                       var lbl_comment = new Label(parent=self, text=badges.join(" "))
+                       lbl_comment.size = 0.5
+               end
+
+               var rating = beer_info.user_rating or else 0
+               setup_stars rating
+       end
+
+       # Prepare and display the rating controls
+       fun setup_stars(rating: Int)
+       do
+               var l_stars = new HorizontalLayout(parent=top_line_layout)
+
+               for i in [1..5] do
+                       var but = new StarButton(beer_info.beer, i, i <= rating, parent=l_stars)
+                       but.size = 1.5
+                       star_buttons.add but
+               end
+       end
+
+       redef fun on_event(event)
+       do
+               assert event isa ButtonPressEvent
+
+               var sender = event.sender
+               if sender isa StarButton then
+                       on_review sender.rating
+               end
+       end
+
+       # Post a user review
+       fun on_review(rating: Int)
+       do
+               var beer_id = beer_info.beer.id
+               (new ReviewAction(app.window, "rest/review?token={app.token}&beer={beer_id}&rating={rating}")).start
+
+               # Update UI
+               var i = 1
+               for but in star_buttons do
+                       but.on = i <= rating
+                       i += 1
+               end
+       end
+end
+
+# Beers pane listing the available beers
+class BeersWindow
+       super Window
+
+       private var layout = new VerticalLayout(parent=self)
+       private var list_beers = new ListLayout(parent=layout)
+
+       init
+       do
+               if debug then print "BenitluxWindow::init"
+
+               action_list_beers
+       end
+
+       # Send HTTP request to list beers
+       fun action_list_beers
+       do (new ListBeersAction(self, "rest/list?token={app.token}")).start
+end
+
+# ---
+# Customized buttons
+
+# View to describe and rate a eer
+class RatingView
+       super View
+
+       autoinit beer, init_rating, parent, enabled
+
+       # Beer id
+       var beer: Beer
+
+       # Previous rating, 0 for none
+       var init_rating: Int
+
+       redef fun parent=(layout) do end
+
+       redef fun enabled=(value) do end
+end
+
+# Button with a star, filled or not, for rating beers
+class StarButton
+       super Button
+
+       autoinit beer, rating, on, parent, enabled
+
+       # Info on the beer to rate
+       var beer: Beer
+
+       # Rating of `beer`
+       var rating: Int
+
+       # Set if the star is filled
+       fun on=(on: Bool) is autoinit do text = if on then "★" else "☆"
+end
+
+redef class BeerAndRatings
+       # Text version of the ratings
+       fun rating_text: String
+       do
+               var txt = new Array[String]
+
+               var global = global
+               if global != null and global.count > 0 then
+                       txt.add "%0★ %1 reviews".t.format(global.average.to_precision(1), global.count)
+               else txt.add "No reviews yet".t
+
+               var local = followed
+               if local != null and local.count > 0 then
+                       txt.add ", friends: %0☆ %1 reviews".t.format(local.average.to_precision(1), local.count)
+               end
+
+               return txt.join
+       end
+end
+
+redef class Beer
+       # Capitalize first letter for a prettier display
+       redef fun desc
+       do
+               var desc = super
+               if desc.length == 0 then return desc
+
+               var first_letter = desc.first.to_upper
+               return first_letter.to_s + desc.substring_from(1)
+       end
+end
+
+# Comparator of beers
+class BeerComparator
+       super Comparator
+
+       redef type COMPARED: BeerAndRatings
+
+       redef fun compare(a, b) do return value_of(a) <=> value_of(b)
+
+       private fun value_of(beer: COMPARED): Float
+       do
+               var max = 0.0
+               var value = 0.0
+
+               var rating = beer.user_rating
+               if rating != null then
+                       max += 20.0
+                       value += rating.to_f * 4.0
+               end
+
+               var followed = beer.followed
+               if followed != null then
+                       max += 10.0
+                       value += followed.average * 2.0
+               end
+
+               var global = beer.global
+               if global != null then
+                       max += 5.0
+                       value += global.average
+               end
+
+               return (max - value)/max
+       end
+end
+
+# Async request to submit a review
+class ReviewAction
+       super WindowHttpRequest
+
+       redef fun on_load(res, status)
+       do
+               if intercept_error(res) then return
+       end
+end
+
+# Async request to update the beer list
+class ListBeersAction
+       super WindowHttpRequest
+
+       redef type W: BeersWindow
+
+       redef fun on_load(beers, status)
+       do
+               window.layout.remove window.list_beers
+               window.list_beers = new ListLayout(parent=window.layout)
+
+               if intercept_error(beers) then return
+
+               if not beers isa Array[BeerAndRatings] then
+                       app.feedback "Communication Error".t
+                       return
+               end
+
+               # Sort beers per preference
+               var comparator = new BeerComparator
+               comparator.sort beers
+
+               # Populate the list
+               for beer_and_rating in beers do
+                       var view = new BeerView(beer_and_rating, parent=window.list_beers)
+               end
+       end
+end
+
+redef class BestBeerBadge
+       redef fun to_s do return "Favorite beer on the menu.".t
+end
+
+redef class FavoriteBeerBadge
+       redef fun to_s do return "Favorite of %0.".t.format(users.join(", ", " & "))
+end
+
+redef class SimilarBeerBadge
+       redef fun to_s do return "Similar to %0.".t.format(beers.join(", ", " & "))
+end
+
+redef class Array[E]
+       # Pretty compressed list of this list of beer as a pseudo diff
+       #
+       # Require: `self isa Array[BeerAndRatings]`
+       fun beers_to_notification: String
+       do
+               assert self isa Array[BeerAndRatings]
+
+               # Sort beers per preference
+               var comparator = new BeerComparator
+               comparator.sort self
+
+               # Organize the notification line per line
+               # First the new beers, then the fixed one.
+               var lines = new Array[String]
+               var fix_beers = new Array[String]
+               for bar in self do
+                       var beer = bar.beer
+                       if bar.is_new then
+                               lines.add "+ {beer.name}: {beer.desc}"
+                       else fix_beers.add beer.name
+               end
+
+               # Show a few fixed beers per line
+               if fix_beers.not_empty then
+                       var line = new FlatBuffer
+                       line.append "= "
+                       for i in fix_beers.length.times, beer in fix_beers do
+
+                               if i > 0 then line.append ", "
+
+                               var l = line.length + beer.length
+                               if l < 42 then # Very approximate width of a notification on Android
+                                       line.append beer
+                                       continue
+                               end
+
+                               lines.add line.to_s
+
+                               line = new FlatBuffer
+                               line.append "= "
+                               line.append beer
+                       end
+
+                       lines.add line.to_s
+               end
+
+               return lines.join("\n")
+       end
+
+       # Does `self` has a new beer?
+       #
+       # Require: `self isa Array[BeerAndRatings]`
+       fun has_new_beers: Bool
+       do
+               assert self isa Array[BeerAndRatings]
+
+               for beer in self do if beer.is_new then return true
+               return false
+       end
+end
diff --git a/contrib/benitlux/src/client/views/home_views.nit b/contrib/benitlux/src/client/views/home_views.nit
new file mode 100644 (file)
index 0000000..d9f2483
--- /dev/null
@@ -0,0 +1,204 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Main home window
+module home_views
+
+import beer_views
+import social_views
+import user_views
+
+redef class App
+       redef fun on_create
+       do
+               if debug then print "App::on_create"
+
+               # Create the main window
+               show_home
+               super
+       end
+
+       # Show the home/main windows
+       fun show_home
+       do
+               var window = new HomeWindow
+               window.refresh
+               push_window window
+       end
+
+       redef fun on_log_in
+       do
+               super
+
+               # Send back to the home window when logging in
+               if not window isa HomeWindow then pop_window
+       end
+end
+
+# Social pane with networking features
+class HomeWindow
+       super Window
+
+       private var layout = new ListLayout(parent=self)
+
+       # Cut-point for the iOS adaptation
+       var layout_user = new VerticalLayout(parent=layout)
+       private var layout_login = new HorizontalLayout(parent=layout_user)
+       private var lbl_login_status = new Label(parent=layout_login, text="Welcome".t, size=1.5)
+       private var but_login = new Button(parent=layout_login, text="Login or signup".t)
+       private var but_preferences = new Button(parent=layout_login, text="Preferences".t)
+
+       private var layout_beers = new VerticalLayout(parent=layout)
+       var layout_beers_title = new HorizontalLayout(parent=layout_beers)
+       var title_beers = new SectionTitle(parent=layout_beers_title, text="Beer Menu".t, size=1.5)
+       private var beer_button = new Button(parent=layout_beers_title, text="View all".t)
+       private var beer_list = new VerticalLayout(parent=layout_beers)
+       private var beer_temp_lbl = new Label(parent=beer_list, text="Loading...".t)
+
+       private var layout_social = new VerticalLayout(parent=layout)
+       private var social_header = new HorizontalLayout(parent=layout_social)
+       private var social_title = new SectionTitle(parent=social_header, text="Friends".t, size=1.5)
+       private var social_button = new Button(parent=social_header, text="Manage".t)
+       private var social_list = new VerticalLayout(parent=layout_social)
+       private var social_temp_lbl = new Label(parent=social_list, text="Loading...".t)
+
+       private var layout_news = new VerticalLayout(parent=layout)
+       private var news_header = new HorizontalLayout(parent=layout_news)
+       private var news_title = new SectionTitle(parent=news_header, text="Events".t, size=1.5)
+       #private var news_button = new Button(parent=news_header, text="Open website") # TODO
+       private var news_label = new Label(parent=layout_news, text="Bière en cask le jeudi!")
+
+       redef fun on_resume do refresh
+
+       # Refresh content of this page
+       fun refresh
+       do
+               if not app.restored then return
+
+               layout_login.clear
+               if app.user != null then
+                       # Logged in
+                       lbl_login_status.parent = layout_login
+                       but_preferences.parent = layout_login
+                       lbl_login_status.set_welcome
+               else
+                       but_login.parent = layout_login
+                       but_preferences.parent = layout_login
+               end
+
+               # Fill beers
+               (new ListDiffAction(self, "rest/since?token={app.token}")).start
+
+               # Fill people
+               (new HomeListPeopleAction(self, "rest/friends?token={app.token}")).start
+
+               # Check if token is still valid
+               (new CheckTokenAction(self, "rest/check_token?token={app.token}")).start
+       end
+
+       redef fun on_event(event)
+       do
+               if debug then print "BenitluxWindow::on_event {event}"
+
+               if event isa ButtonPressEvent then
+                       var sender = event.sender
+                       if sender == but_preferences then
+                               app.push_window new UserWindow
+                               return
+                       else if sender == but_login then
+                               app.push_window new SignupWindow
+                               return
+                       else if sender == beer_button then
+                               app.push_window new BeersWindow
+                               return
+                       else if sender == social_button then
+                               app.push_window new SocialWindow
+                               return
+                       #else if sender == news_button then
+                               # TODO open browser?
+                       end
+               end
+
+               super
+       end
+end
+
+# `Label` used in section headers
+class SectionTitle super Label end
+
+# Async request to update the beer list on the home screen
+class ListDiffAction
+       super WindowHttpRequest
+
+       redef type W: HomeWindow
+
+       redef fun on_load(beers, status)
+       do
+               window.layout_beers.remove window.beer_list
+               window.beer_list = new VerticalLayout(parent=window.layout_beers)
+
+               if intercept_error(beers) then return
+
+               if not beers isa Array[BeerAndRatings] then
+                       app.feedback "Communication Error".t
+                       return
+               end
+
+               # Sort beers per preference
+               var comparator = new BeerComparator
+               comparator.sort beers
+
+               var max_beers = 6
+               while beers.length > max_beers do beers.pop
+
+               for bar in beers do
+                       var view = new BeerView(bar, parent=window.beer_list)
+               end
+       end
+end
+
+# Async request to list users
+class HomeListPeopleAction
+       super WindowHttpRequest
+
+       redef type W: HomeWindow
+
+       redef fun on_load(users, status)
+       do
+               window.layout_social.remove window.social_list
+               window.social_list = new VerticalLayout(parent=window.layout_social)
+
+               if intercept_error(users) then return
+
+               if users isa Array[UserAndFollowing] then for uaf in users do
+                       var view = new PeopleView(uaf, true, parent=window.social_list)
+               end
+       end
+end
+
+# Async request to check if `app.token` is still valid
+class CheckTokenAction
+       super WindowHttpRequest
+
+       redef type W: HomeWindow
+
+       redef fun on_load(res, status) do intercept_error(res)
+end
+
+# Today's date as a `String`
+fun today: String
+do
+       var tm = new Tm.localtime
+       return "{tm.year+1900}-{tm.mon+1}-{tm.mday}"
+end
diff --git a/contrib/benitlux/src/client/views/social_views.nit b/contrib/benitlux/src/client/views/social_views.nit
new file mode 100644 (file)
index 0000000..f9d5b5f
--- /dev/null
@@ -0,0 +1,196 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Window to list beers and other beer-related views
+module social_views
+
+import base
+
+# Social pane with networking features
+class SocialWindow
+       super Window
+
+       private var layout = new VerticalLayout(parent=self)
+
+       private var list_search = new ListLayout(parent=layout)
+
+       private var layout_header = new VerticalLayout(parent=list_search)
+       private var layout_search = new HorizontalLayout(parent=layout_header)
+       private var txt_query = new TextInput(parent=layout_search)
+       private var but_search = new Button(parent=layout_search, text="Search".t)
+
+       private var layout_list = new HorizontalLayout(parent=layout_header)
+       private var but_followed = new Button(parent=layout_list, text="List followed".t)
+       private var but_followers = new Button(parent=layout_list, text="List followers".t)
+
+       init
+       do
+               # Load friends and suggestions
+               (new ListUsersAction(self, "rest/friends?token={app.token}&n=16")).start
+       end
+
+       redef fun on_event(event)
+       do
+               if debug then print "BenitluxWindow::on_event {event}"
+
+               if event isa ButtonPressEvent then
+                       var sender = event.sender
+                       if sender == but_search then
+                               search
+                       else if sender == but_followed then
+                               var cmd = "rest/followed?token={app.token}"
+                               (new ListUsersAction(self, cmd)).start
+                       else if sender == but_followers then
+                               var cmd = "rest/followers?token={app.token}"
+                               (new ListUsersAction(self, cmd)).start
+                       end
+               end
+
+               super
+       end
+
+       # Execute search with `txt_query.text`
+       fun search
+       do
+               var query = txt_query.text
+               if query == null or query.is_empty then return
+
+               var res = "rest/search?token={app.token}&query={query}&offset=0"
+               (new ListUsersAction(self, res)).start
+       end
+
+       # Fill `list_search` with views for each of `users`
+       fun list_users(users: Array[UserAndFollowing])
+       do
+               for uaf in users do
+                       var view = new PeopleView(uaf, false, parent=list_search)
+               end
+       end
+end
+
+# View to describe, and follow a person
+class PeopleView
+       super VerticalLayout
+       super ItemView
+
+       autoinit user_and_following, home_window_mode, parent
+
+       # Description of the user
+       var user_and_following: UserAndFollowing
+
+       # Toggle tweaks for the home window where the is no "unfollow" buttons
+       var home_window_mode: Bool
+
+       init
+       do
+               var user = user_and_following.user
+
+               var layout_top_line = new HorizontalLayout(parent=self)
+               var lbl_name = new Label(parent=layout_top_line, text=user.name)
+
+               if app.user != null then
+
+                       # Show unfollow button if not on the home screen
+                       if not home_window_mode or not user_and_following.following then
+                               var but = new FollowButton(user.id, user_and_following.following, user_and_following.followed, parent=layout_top_line)
+                       end
+               end
+
+               var favs = if not user_and_following.favs.is_empty then
+                       "Favorites: %0".t.format(user_and_following.favs)
+               else "No favorites yet".t
+               var lbl_desc = new Label(parent=self, text=favs, size=0.5)
+       end
+end
+
+# Button to follow or unfollow a user
+class FollowButton
+       super Button
+
+       autoinit followed_id, following, followed_by, parent, enabled, text
+
+       # Id of the user to be followd/unfollow
+       var followed_id: Int
+
+       # Does the local user already follows `followed_id`
+       var following: Bool
+
+       # Does `followed_id` already follows the local user
+       var followed_by: Bool
+
+       # Update the visible text according to `following`
+       fun update_text do text = if following then "Unfollow".t else "Follow".t
+
+       init do update_text
+
+       redef fun on_event(event)
+       do
+               assert event isa ButtonPressEvent
+               var cmd = "rest/follow?token={app.token}&user_to={followed_id}&follow={not following}"
+               enabled = false
+               text = "Updating...".t
+               (new FollowAction(app.window, cmd, self)).start
+       end
+end
+
+# Async request to receive and display a list of users
+#
+# This is used by many features of the social window:
+# search, list followed and list followers.
+class ListUsersAction
+       super WindowHttpRequest
+
+       redef type W: SocialWindow
+
+       init do affected_views.add_all([window.but_search, window.but_followed, window.but_followers])
+
+       redef fun on_load(users, status)
+       do
+               window.layout.remove window.list_search
+               window.list_search = new ListLayout(parent=window.layout)
+               window.layout_header.parent = window.list_search
+
+               if intercept_error(users) then return
+
+               if users isa Array[UserAndFollowing] then window.list_users users
+       end
+end
+
+# Async request to follow or unfollow a user
+class FollowAction
+       super WindowHttpRequest
+
+       private var button: FollowButton
+       init do affected_views.add(button)
+
+       redef fun on_load(res, status)
+       do
+               if intercept_error(res) then return
+       end
+
+       redef fun after
+       do
+               button.following = not button.following
+               button.update_text
+               button.enabled = true
+
+               super
+       end
+
+       redef fun before
+       do
+               button.enabled = false
+               super
+       end
+end
diff --git a/contrib/benitlux/src/client/views/user_views.nit b/contrib/benitlux/src/client/views/user_views.nit
new file mode 100644 (file)
index 0000000..e485d4b
--- /dev/null
@@ -0,0 +1,202 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# User preference window and other user-related view
+module user_views
+
+import base
+
+redef class Label
+       # Update the content of `lbl_welcome`
+       fun set_user_name
+       do
+               var name = app.user
+               self.text = if name != null then
+                       "Logged in as %0".t.format(name)
+               else "Not logged in".t
+       end
+
+       # Set `text` to welcome an authentified user or invite to authentify
+       fun set_welcome
+       do
+               var name = app.user
+               self.text = if name != null then
+                       "Welcome %0".t.format(name)
+               else ""
+       end
+end
+
+# User preference window
+class UserWindow
+       super Window
+
+       # Main window layout
+       var layout = new ListLayout(parent=self)
+
+       private var layout_user_options = new VerticalLayout(parent=layout)
+
+       private var lbl_user_options_title = new Label(parent=layout_user_options,
+               text="Account options".t)
+
+       private var lbl_welcome = new Label(parent=layout_user_options)
+       private var but_logout = new Button(parent=layout_user_options, text="Logout".t)
+
+       # Refesh displayed text
+       fun refresh
+       do
+               lbl_user_options_title.size = 1.5
+               lbl_welcome.set_user_name
+               but_logout.enabled = app.user != null
+       end
+
+       init do refresh
+
+       redef fun on_event(event)
+       do
+               if event isa ButtonPressEvent then
+                       var sender = event.sender
+                       if sender == but_logout then
+                               app.user = null
+                               app.token = "none"
+                               app.on_log_in
+                               refresh
+                       end
+               end
+
+               super
+       end
+end
+
+# Window for signing up a new user or logging in
+class SignupWindow
+       super Window
+
+       private var list = new ListLayout(parent=self)
+       private var lbl_feedback = new Label(parent=list, text="Welcome")
+
+       private var layout_login = new VerticalLayout(parent=list)
+
+       # ---
+       # First the login options
+
+       # Name
+       private var name_line = new HorizontalLayout(parent=layout_login)
+       private var lbl_name = new Label(parent=name_line, text="Username".t)
+       private var txt_name = new TextInput(parent=name_line, text=app.user)
+
+       # Password
+       private var pass_line = new HorizontalLayout(parent=layout_login)
+       private var lbl_pass = new Label(parent=pass_line, text="Password".t)
+       private var txt_pass = new TextInput(parent=pass_line, is_password=true)
+       private var lbl_pass_desc = new Label(parent=layout_login, size = 0.5,
+               text="Passwords must be composed of at least 6 characters.".t)
+
+       private var but_login = new Button(parent=layout_login, text="Login".t)
+
+       # ---
+       # Then, the signup options
+
+       private var layout_register = new VerticalLayout(parent=list)
+
+       private var lbl_signup_desc = new Label(parent=layout_register, size = 0.5,
+               text="Fill the following fields to sign up.".t)
+
+       # Repeat password
+       private var pass_line2 = new HorizontalLayout(parent=layout_register)
+       private var lbl_pass2 = new Label(parent=pass_line2, text="Repeat password".t)
+       private var txt_pass2 = new TextInput(parent=pass_line2, is_password=true)
+
+       # Email
+       private var email_line = new HorizontalLayout(parent=layout_register)
+       private var lbl_email = new Label(parent=email_line, text="Email".t)
+       private var txt_email = new TextInput(parent=email_line)
+
+       private var but_signup = new Button(parent=layout_register, text="Signup".t)
+
+       redef fun on_event(event)
+       do
+               if debug then print "BenitluxWindow::on_event {event}"
+
+               if event isa ButtonPressEvent then
+                       var sender = event.sender
+                       if sender == but_login or sender == but_signup then
+
+                               var name = txt_name.text
+                               if name == null or not name.name_is_ok then
+                                       feedback "Invalid username.".t
+                                       return
+                               end
+
+                               var pass = txt_pass.text
+                               if pass == null or not pass.pass_is_ok then
+                                       feedback "Invalid password.".t
+                                       return
+                               end
+
+                               if sender == but_login then
+                                       feedback "Logging in...".t
+                                       (new LoginOrSignupAction(self, "rest/login?name={name}&pass={pass.pass_hash}")).start
+                               else if sender == but_signup then
+                                       if pass != txt_pass2.text then
+                                               feedback "Passwords do not match.".t
+                                               return
+                                       end
+
+                                       var email = txt_email.text
+                                       if email == null or email.is_empty then
+                                               feedback "Invalid email".t
+                                               return
+                                       end
+
+                                       feedback "Signing up...".t
+                                       (new LoginOrSignupAction(self, "rest/signup?name={name}&pass={pass.pass_hash}&email={email}")).start
+                               end
+                       end
+               end
+
+               super
+       end
+
+       # Show lasting feedback to the user in a label
+       fun feedback(text: String) do lbl_feedback.text = text
+end
+
+# ---
+# Async RESTful actions
+
+# Async request for login in or signing up
+class LoginOrSignupAction
+       super WindowHttpRequest
+
+       redef type W: SignupWindow
+
+       init do affected_views.add_all([window.but_login, window.but_signup])
+
+       redef fun on_load(res, status)
+       do
+               if intercept_error(res) then return
+
+               if not res isa LoginResult then
+                       on_fail new Error("Server sent unexpected data {res or else "null"}")
+                       return
+               end
+
+               app.token = res.token
+               app.user = res.user.name
+
+               app.on_log_in
+       end
+
+       redef fun feedback(text) do window.feedback text
+end
@@ -133,6 +133,16 @@ class BenitluxRESTAction
                return new HttpResponse.ok(log)
        end
 
+       # Is `token` valid?
+       #
+       # check_token?token=a -> true | BenitluxError
+       fun check_token(token: String): HttpResponse
+       is restful do
+               var user_id = db.token_to_id(token)
+               if user_id == null then return new HttpResponse.invalid_token
+               return new HttpResponse.ok(true)
+       end
+
        # Search a user
        #
        # search?token=b&query=a&offset=0 -> Array[UserAndFollowing] | BenitluxError
@@ -322,6 +332,57 @@ redef class Sys
 end
 
 # ---
+# Administration
+
+# Path to the secret used to authenticate admin requests
+fun secret_path: String do return "benitlux.secret"
+
+# Services reserved to administrators
+class BenitluxAdminAction
+       super BenitluxAction
+       super RestfulAction
+
+       private fun server_secret: String do return secret_path.to_path.read_all
+
+       # Trigger sending daily menu to connected clients
+       #
+       # This should usually be called by an external cron program.
+       # send_daily_updates?secret=shared_secret -> true | BenitluxError
+       fun send_daily_updates(secret: nullable String): HttpResponse
+       is restful do
+               # Check secrets
+               var server_secret = server_secret
+               if server_secret.is_empty then
+                       print_error "The admin interface needs a secret at '{secret_path}'"
+                       return new HttpResponse.server_error
+               end
+
+               if server_secret != secret then
+                       return new HttpResponse.invalid_token
+               end
+
+               # Load beer menu
+               var list = db.list_beers_and_rating
+               if list == null then return new HttpResponse.server_error
+
+               var msg = new DailyNotification(list)
+
+               # Broadcast updates
+               for conn in push_connections.values.to_a do
+                       if not conn.closed then
+                               conn.respond new HttpResponse.ok(msg)
+                               conn.close
+                       end
+               end
+               push_connections.clear
+
+               return new HttpResponse.ok(true)
+       end
+
+       redef fun answer(request, turi) do return new HttpResponse.bad_request
+end
+
+# ---
 # Misc services
 
 redef class Text
@@ -348,7 +409,7 @@ redef class HttpResponse
        init ok(data: Serializable)
        do
                init 200
-               body = data.to_json_string
+               body = data.serialize_to_json
        end
 
        # Respond with a `BenitluxError` in JSON and a code 403
@@ -356,7 +417,7 @@ redef class HttpResponse
        do
                init 403
                var error = new BenitluxTokenError("Forbidden", "Invalid or outdated token.")
-               body = error.to_json_string
+               body = error.serialize_to_json
        end
 
        # Respond with a `BenitluxError` in JSON and a code 400
@@ -364,7 +425,7 @@ redef class HttpResponse
        do
                init 400
                var error = new BenitluxError("Bad Request", "Application error, or it needs to be updated.")
-               body = error.to_json_string
+               body = error.serialize_to_json
        end
 
        # Respond with a `BenitluxError` in JSON and a code 500
@@ -372,6 +433,6 @@ redef class HttpResponse
        do
                init 500
                var error = new BenitluxError("Internal Server Error", "Server error, try again later.")
-               body = error.to_json_string
+               body = error.serialize_to_json
        end
 end
similarity index 93%
rename from contrib/benitlux/src/benitlux_db.nit
rename to contrib/benitlux/src/server/benitlux_db.nit
index 81ec6bf..c899ede 100644 (file)
@@ -85,7 +85,7 @@ class BenitluxDB
                        last_weekday = "date('now', 'weekday 6', '-7 day')"
                else last_weekday = "date('now', '-1 day')"
 
-               return beer_events_since(last_weekday)
+               return beer_events_since_sql(last_weekday)
        end
 
        # Build and return a `BeerEvents` for today compared to `prev_day`
@@ -93,26 +93,35 @@ class BenitluxDB
        # Return `null` on error
        fun beer_events_since(prev_day: String): nullable BeerEvents
        do
+               prev_day = prev_day.to_sql_date_string
+               return beer_events_since_sql("date({prev_day})")
+       end
+
+       # `BeerEvents` since the SQLite formatted date command `sql_date`
+       #
+       # Return `null` on error
+       private fun beer_events_since_sql(sql_date: String): nullable BeerEvents
+       do
                var events = new BeerEvents
 
                # New
                var stmt = select("ROWID, name, desc FROM beers WHERE " +
                                  "ROWID IN (SELECT beer FROM daily WHERE day=(SELECT MAX(day) FROM daily)) AND " +
-                                 "NOT ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))")
+                                 "NOT ROWID IN (SELECT beer FROM daily WHERE date(day) = {sql_date})")
                if stmt == null then return null
                for row in stmt do events.new_beers.add row.to_beer
 
                # Gone
                stmt = select("ROWID, name, desc FROM beers WHERE " +
                              "NOT ROWID IN (SELECT beer FROM daily WHERE day=(SELECT MAX(day) FROM daily)) AND " +
-                             "ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))")
+                             "ROWID IN (SELECT beer FROM daily WHERE date(day) = {sql_date})")
                if stmt == null then return null
                for row in stmt do events.gone_beers.add row.to_beer
 
                # Fix
                stmt = select("ROWID, name, desc FROM beers WHERE " +
                              "ROWID IN (SELECT beer FROM daily WHERE day=(SELECT MAX(day) FROM daily)) AND " +
-                             "ROWID IN (SELECT beer FROM daily WHERE date(day) = date({prev_day}))")
+                             "ROWID IN (SELECT beer FROM daily WHERE date(day) = {sql_date})")
                if stmt == null then return null
                for row in stmt do events.fix_beers.add row.to_beer
 
similarity index 99%
rename from contrib/benitlux/src/benitlux_social.nit
rename to contrib/benitlux/src/server/benitlux_social.nit
index bdd83b4..7764c75 100644 (file)
@@ -182,7 +182,7 @@ GROUP BY beer0, beer1""") else
                # Check if already in user
                var stmt = select("ROWID FROM users WHERE lower({user.to_sql_string}) = lower(name)")
                assert stmt != null else print_error "Select 'sign_up' failed with: {error or else "?"}"
-               if not stmt.iterator.to_a.is_empty then return "Username already in use"
+               if not stmt.iterator.to_a.is_empty then return "Username already in use."
 
                # Check email use
                stmt = select("ROWID FROM users WHERE lower({email.to_sql_string}) = lower(email)")
similarity index 80%
rename from contrib/benitlux/src/benitlux_web.nit
rename to contrib/benitlux/src/server/server.nit
index d5bab54..f6d5d41 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Web server for Benitlux
-module benitlux_web
+module server
 
 import benitlux_model
 import benitlux_view
@@ -25,6 +25,9 @@ import benitlux_restful
 # Listening interface
 fun iface: String do return "localhost:8080"
 
+# Listening interface for admin commands
+fun iface_admin: String do return "localhost:8081"
+
 # Sqlite3 database
 var db_path = "benitlux_sherbrooke.db"
 var db = new BenitluxDB.open(db_path)
@@ -40,10 +43,14 @@ vh.routes.add new Route("/rest/", new BenitluxRESTAction(db))
 vh.routes.add new Route("/push/", new BenitluxPushAction(db))
 vh.routes.add new Route(null, new BenitluxSubscriptionAction(db))
 
+var vh_admin = new VirtualHost(iface_admin)
+vh_admin.routes.add new Route(null, new BenitluxAdminAction(db))
+
 var factory = new HttpFactory.and_libevent
 factory.config.virtual_hosts.add vh
+factory.config.virtual_hosts.add vh_admin
 
-print "Launching server on http://{iface}/"
+print "Launching server on http://{iface}/ and http://{iface_admin}/"
 factory.run
 
 db.close
index 3495918..7b91216 100644 (file)
@@ -6,18 +6,18 @@ default: bin/moles
 
 pre-build: assets/images/drawing.png
 
-bin/moles: $(shell ../../bin/nitls -M src/moles_linux.nit) assets/images/drawing.png
+bin/moles: $(shell nitls -M src/moles_linux.nit) assets/images/drawing.png
        mkdir -p bin
-       ../../bin/nitc -o bin/moles src/moles_linux.nit
+       nitc -o bin/moles src/moles_linux.nit
 
 android: bin/moles.apk
-bin/moles.apk: android/res/ $(shell ../../bin/nitls -M src/moles_android.nit) assets/images/drawing.png
+bin/moles.apk: android/res/ $(shell nitls -M src/moles_android.nit) assets/images/drawing.png
        mkdir -p bin
-       ../../bin/nitc -o bin/moles.apk src/moles_android.nit
+       nitc -o bin/moles.apk src/moles_android.nit
 
-android-release: android/res/ $(shell ../../bin/nitls -M src/moles_android.nit) assets/images/drawing.png
+android-release: android/res/ $(shell nitls -M src/moles_android.nit) assets/images/drawing.png
        mkdir -p bin
-       ../../bin/nitc -o bin/moles.apk src/moles_android.nit --release
+       nitc -o bin/moles.apk src/moles_android.nit --release
 
 $(SVG2ICONS):
        $(MAKE) -C ../inkscape_tools
index 1654a48..7b7d953 100644 (file)
@@ -2,15 +2,15 @@ default: linux
 
 linux:
        mkdir -p bin
-       ../../bin/nitc -o bin/friendz src/friendz_linux.nit
+       nitc -o bin/friendz src/friendz_linux.nit
 
 android: android/res/drawable-hdpi/icon.png
        mkdir -p bin
-       ../../bin/nitc -o bin/friendz.apk src/friendz_android.nit
+       nitc -o bin/friendz.apk src/friendz_android.nit
 
 android-release: android/res/drawable-hdpi/icon.png
        mkdir -p bin
-       ../../bin/nitc -o bin/friendz.apk src/friendz_android.nit --release
+       nitc -o bin/friendz.apk src/friendz_android.nit --release
 
 android/res/drawable-hdpi/icon.png: art/icon.svg
        mkdir -p android/res
@@ -18,7 +18,7 @@ android/res/drawable-hdpi/icon.png: art/icon.svg
 
 doc:
        mkdir -p doc
-       ../../bin/nitdoc -d doc/ src/friendz.nit src/friendz_linux.nit
+       nitdoc -d doc/ src/friendz.nit src/friendz_linux.nit
 
 clean:
        rm -rf bin/ doc/ android/
index c0554fe..762eb1d 100644 (file)
@@ -1609,22 +1609,10 @@ redef class App
                game.save
        end
 
-       # Maximum wanted frame per second
-       var max_fps = 30
-
-       # clock used to track FPS
-       private var clock = new Clock
-
        redef fun frame_core(display)
        do
                game.step
                game.draw(display)
-               var dt = clock.lapse
-               var target_dt = 1000000000 / max_fps
-               if dt.sec == 0 and dt.nanosec < target_dt then
-                       var sleep_t = target_dt - dt.nanosec
-                       sys.nanosleep(0, sleep_t)
-               end
        end
 
        redef fun input(input_event)
index 3ffabe8..c1c24a0 100644 (file)
@@ -1,3 +1,3 @@
 default:
        mkdir -p bin
-       ../../bin/nitc -o bin/github_search_for_jni src/github_search_for_jni.nit
+       nitc -o bin/github_search_for_jni src/github_search_for_jni.nit
index 90ac840..ca099e4 100644 (file)
@@ -1,6 +1,6 @@
 bin/header_keeper:
        mkdir -p bin
-       ../../bin/nitc --dir bin src/header_keeper.nit
+       nitc --dir bin src/header_keeper.nit
 
 check: tests
 tests: bin/header_keeper
index 55cc9be..db40075 100644 (file)
@@ -1,6 +1,6 @@
 all:
        mkdir -p bin
-       ../../bin/nitc --dir bin src/svg_to_png_and_nit.nit src/svg_to_icons.nit
+       nitc --dir bin src/svg_to_png_and_nit.nit src/svg_to_icons.nit
 
 check: test-tinks test-app
 
index c1901bf..10f7b97 100644 (file)
@@ -11,12 +11,12 @@ src/javap_test_parser.nit: ../nitcc/src/nitcc grammar/javap.sablecc
        mv javap_*.nit src/
        mv javap* gen/
 
-src/serial.nit: $(shell ../../bin/nitls -M src/jwrapper.nit)
-       ../../bin/nitserial -o src/serial.nit src/jwrapper.nit
+src/serial.nit: $(shell nitls -M src/jwrapper.nit)
+       nitserial -o src/serial.nit src/jwrapper.nit
 
-bin/jwrapper: src/javap_test_parser.nit src/serial.nit $(shell ../../bin/nitls -M src/jwrapper.nit) ../../bin/nitc
+bin/jwrapper: src/javap_test_parser.nit src/serial.nit $(shell nitls -M src/jwrapper.nit)
        mkdir -p bin
-       ../../bin/nitc src/jwrapper.nit -o bin/jwrapper -m src/serial.nit
+       nitc src/jwrapper.nit -o bin/jwrapper -m src/serial.nit
 
 clean:
        rm -f bin/javap_test_parser bin/jwrapper
@@ -26,19 +26,19 @@ clean:
 check: bin/jwrapper tests/wildcards.javap
        mkdir -p tmp
        bin/jwrapper -v -u stub -o tests/statics.nit tests/statics.javap
-       ../../bin/nitpick -q tests/statics.nit
+       nitpick -q tests/statics.nit
        bin/jwrapper -v -u comment -o tests/generics.nit tests/generics.javap
-       ../../bin/nitpick -q tests/generics.nit
+       nitpick -q tests/generics.nit
        bin/jwrapper -v -u comment -o tests/long.nit tests/long.javap
-       ../../bin/nitpick -q tests/long.nit
+       nitpick -q tests/long.nit
        bin/jwrapper -v -u comment -o tests/inits.nit tests/inits.javap
-       ../../bin/nitpick -q tests/inits.nit
+       nitpick -q tests/inits.nit
        bin/jwrapper -v -u comment -o tests/testjvm.nit tests/testjvm.javap
-       ../../bin/nitpick -q tests/testjvm.nit
+       nitpick -q tests/testjvm.nit
        bin/jwrapper -v -u comment -o tests/many.nit tests/many.javap
-       ../../bin/nitpick -q tests/many.nit
+       nitpick -q tests/many.nit
        bin/jwrapper -v -u comment -o tests/wildcards.nit tests/wildcards.javap
-       ../../bin/nitpick -q tests/wildcards.nit
+       nitpick -q tests/wildcards.nit
        make -C examples/queue/ check
        make -C examples/java_api/ check
 
index bfbbdf0..01600ae 100644 (file)
@@ -107,16 +107,16 @@ class JavaType
        end
 
        # Short name of the class, mangled to remove `$` (e.g. `Set`)
-       fun id: String do return identifier.last.replace("$", "")
+       var id: String is lazy do return identifier.last.replace("$", "")
 
        # Full name of this class as used in java code (e.g. `java.lang.Set`)
-       fun java_full_name: String do return identifier.join(".").replace("$", ".")
+       var java_full_name: String is lazy do return identifier.join(".").replace("$", ".")
 
        # Full name of this class as used by jni (e.g. `android.graphics.BitmapFactory$Options`)
-       fun jni_full_name: String do return identifier.join(".")
+       var jni_full_name: String is lazy do return identifier.join(".")
 
        # Name of this class for the extern declaration in Nit (e.g. `java.lang.Set[]`)
-       fun extern_equivalent: String do return jni_full_name + "[]" * array_dimension
+       var extern_equivalent: String is lazy do return jni_full_name + "[]" * array_dimension
 
        # Full name of this class with arrays and generic values (e.g. `java.lang.Set<E>[]`)
        redef fun to_s do
index c8f6f13..5163888 100644 (file)
@@ -18,7 +18,7 @@ pre-build: assets/images/drawing.png
 
 bin/memory: assets/images/drawing.png src/*.nit
        mkdir -p bin
-       ../../bin/nitc -o bin/memory src/memory.nit -m ../../lib/mnit/linux/linux.nit
+       nitc -o bin/memory src/memory.nit -m ../../lib/mnit/linux/linux.nit
 
 assets/images/drawing.png: art/drawing.svg
        mkdir -p assets/images/
@@ -31,8 +31,8 @@ android/res/: art/icon.svg
 android: bin/memory.apk
 bin/memory.apk: assets/images/drawing.png src/*.nit android/res/
        mkdir -p bin
-       ../../bin/nitc -o bin/memory.apk src/memory.nit -m ../../lib/mnit/android/android.nit -m ../../lib/android/landscape.nit
+       nitc -o bin/memory.apk src/memory.nit -m ../../lib/mnit/android/android.nit -m ../../lib/android/landscape.nit
 
 android-release: assets/images/drawing.png src/*.nit android/res/
        mkdir -p bin
-       ../../bin/nitc -o bin/memory.apk src/memory.nit -m ../../lib/mnit/android/android.nit -m ../../lib/android/landscape.nit  --release
+       nitc -o bin/memory.apk src/memory.nit -m ../../lib/mnit/android/android.nit -m ../../lib/android/landscape.nit  --release
diff --git a/contrib/memplot/memplot.nit b/contrib/memplot/memplot.nit
new file mode 100644 (file)
index 0000000..2bd1727
--- /dev/null
@@ -0,0 +1,198 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Program to transform `memory.log` files produced by `nitc --trace-memory` into a csv file
+module memplot
+
+import counter
+import template
+
+# An aggregated time-slice
+class Aggreg
+       # The start time of the slice
+       var time: Float
+
+       # Number of allocations
+       var acpt = new Counter[String]
+
+       # Number of allocated bytes
+       var asiz = new Counter[String]
+
+       # Number of deallocations
+       var dcpt = new Counter[String]
+
+       # Number of deallocated bytes
+       var dsiz = new Counter[String]
+
+       # Total number of allocations since the beginning
+       var cpttot = new Counter[String]
+
+       # Total number of allocated bytes since the beginning
+       var siztot = new Counter[String]
+
+       # Map of all the various counters.
+       var fields = new Map[String, Counter[String]]
+
+       init do
+               fields["acpt"] = acpt
+               fields["asiz"] = asiz
+               fields["dcpt"] = dcpt
+               fields["dsiz"] = dsiz
+               fields["cpttot"] = cpttot
+               fields["siztot"] = siztot
+       end
+end
+
+# Main class that does the job
+class MemProg
+       # The `memory.log` file.
+       var filepath: String
+
+       # The delay of an aggregation
+       var time_slice: Float
+
+       # Total number of events
+       var events = 0
+
+       # List a all time aggregations
+       var aggregs = new Array[Aggreg]
+
+       # Total number of allocations
+       var cpttot = new Counter[String]
+
+       # Total number of allocated bytes
+       var siztot = new Counter[String]
+
+       # Parse the log file `filepath` and fill `aggregs`.
+       fun parse do
+               # Current lines (not yet put in an aggreg)
+               var lines = new Counter[String]
+
+               var time = 0.0
+               var dt = 100.0
+               dt = 100.0
+               for l in "memory.log".to_path.each_line do
+                       if l[0] == '#' then
+                               var t = l.substring_from(2).to_f
+
+                               while t > time + dt do
+                                       aggreg(lines, time)
+                                       time += dt
+                               end
+                               #if time > 1000.0 then break
+                               continue
+                       end
+                       events += 1
+                       lines.inc(l)
+               end
+               aggreg(lines, time)
+       end
+
+       # Create and register a new aggregation
+       fun aggreg(lines: Counter[String], t1: Float) do
+               var aggreg = new Aggreg(t1)
+               aggregs.add aggreg
+               print "events:{events} aggregs:{aggregs.length} {t1}ms"
+
+               # Process each distinct line
+               for l, v in lines do
+                       var a = l.split('\t')
+                       if a.length != 3 then
+                               print "Error {a.length}. {l}"
+                               continue
+                       end
+                       var c = a[0]
+                       var s = a[1].to_i
+                       var e = a[2]
+                       if c == "+" then
+                               aggreg.acpt[e] += v
+                               aggreg.asiz[e] += v * s
+                       else if c == "-" then
+                               aggreg.dcpt[e] += v
+                               aggreg.dsiz[e] += v * s
+                       else abort
+               end
+               lines.clear
+
+               # Sum all information
+               for e, v in aggreg.acpt do cpttot[e] += v
+               for e, v in aggreg.asiz do siztot[e] += v
+               for e, v in aggreg.dcpt do cpttot[e] -= v
+               for e, v in aggreg.dsiz do siztot[e] -= v
+
+               # Copy current total
+               aggreg.cpttot.add_all cpttot
+               aggreg.siztot.add_all siztot
+
+               cpttot.print_elements(2)
+       end
+
+       # Generate a *long* CVS file, to use with statistical tools
+       fun tolong: Writable
+       do
+               var res = new Template
+
+               # Write the header
+               res.add "time, class"
+               for f, c in aggregs.first.fields do
+                       res.add ", "
+                       res.add f
+               end
+               res.add "\n"
+
+               # Collect the largest tags, add add an `other` tag.
+               var elts = siztot.sort.reversed.sub(0,10).reversed
+               elts.add "other"
+
+               for a in aggregs do
+                       var t = a.time.to_s
+
+                       # For each field compute the value of `other`
+                       for f, c in a.fields do
+                               var o = c.sum
+                               for e in elts do
+                                       if e == "other" then continue
+                                       o -= c[e]
+                               end
+                               c["other"] = o
+                       end
+
+                       # For each tag (incl. other) produce a line
+                       for e in elts do
+                               res.add t
+                               res.add ", "
+                               res.add e
+
+                               for f, v in a.fields do
+                                       res.add ", "
+                                       res.add v[e].to_s
+                               end
+                               res.add "\n"
+                       end
+               end
+               return res
+       end
+end
+
+var m = new MemProg("memory.log", 100.0)
+m.parse
+
+m.cpttot.print_summary
+m.siztot.print_summary
+
+m.tolong.write_to_file "memory.csv"
+
+if "memplot.r".file_exists then
+       system("r memplot.r")
+end
diff --git a/contrib/memplot/memplot.r b/contrib/memplot/memplot.r
new file mode 100644 (file)
index 0000000..adda5b8
--- /dev/null
@@ -0,0 +1,29 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# R program to draw a nice plot diagram
+
+mem <- read.csv("memory.csv")
+
+library(ggplot2)
+
+order <- rev(unique(mem$class))
+
+ggplot(mem, aes(x=time, y=siztot, fill=class)) +
+       geom_area(color='black', size=0.02) +
+       scale_fill_brewer(palette="Spectral", breaks=order) +
+       theme(legend.text=element_text(size=7))
+
+ggsave("memory.pdf")
+ggsave("memory.png")
diff --git a/contrib/memplot/package.ini b/contrib/memplot/package.ini
new file mode 100644 (file)
index 0000000..fa7a860
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=memplot
+tags=devel
+maintainer=Jean Privat <jean@pryen.org>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/contrib/memplot/
+git=https://github.com/nitlang/nit.git
+git.directory=contrib/memplot/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
index a198c27..73bff6b 100644 (file)
@@ -2,17 +2,17 @@ default: linux
 
 linux:
        mkdir -p bin
-       ../../bin/nitc -o bin/simple src/simple_linux.nit
+       nitc -o bin/simple src/simple_linux.nit
 
 android:
        mkdir -p bin
-       ../../bin/nitc -o bin/complete.apk src/complete_simple_android.nit --semi-global
-       ../../bin/nitc -o bin/minimal.apk src/simple_android.nit --semi-global
+       nitc -o bin/complete.apk src/complete_simple_android.nit --semi-global
+       nitc -o bin/minimal.apk src/simple_android.nit --semi-global
 
 android-release:
        mkdir -p bin
-       ../../bin/nitc -o bin/complete.apk src/complete_simple_android.nit --semi-global --release
-       ../../bin/nitc -o bin/minimal.apk src/simple_android.nit --semi-global --release
+       nitc -o bin/complete.apk src/complete_simple_android.nit --semi-global --release
+       nitc -o bin/minimal.apk src/simple_android.nit --semi-global --release
 
 clean:
        rm -rf bin
index b2dc37a..de05fa9 100644 (file)
@@ -1,9 +1,9 @@
-NITC=../../bin/nitc
-NITLS=../../bin/nitls
+NITC=nitc
+NITLS=nitls
 
 all: bin/model_viewer
 
-bin/model_viewer: $(shell ${NITLS} -M src/model_viewer.nit linux) ${NITC}
+bin/model_viewer: $(shell ${NITLS} -M src/model_viewer.nit linux)
        ${NITC} src/model_viewer.nit -m linux -o $@
 
 check: bin/model_viewer
@@ -13,10 +13,10 @@ check: bin/model_viewer
 # Android
 
 android: bin/model_viewer.apk
-bin/model_viewer.apk: $(shell ${NITLS} -M src/model_viewer.nit android) ${NITC} android/res/
+bin/model_viewer.apk: $(shell ${NITLS} -M src/model_viewer.nit android) android/res/
        ${NITC} src/model_viewer.nit -m android -o $@
 
-android-release: $(shell ${NITLS} -M src/model_viewer.nit android) ${NITC} android/res/
+android-release: $(shell ${NITLS} -M src/model_viewer.nit android) android/res/
        ${NITC} src/model_viewer.nit -m android -o bin/model_viewer.apk --release
 
 android/res/: art/icon.png
@@ -30,10 +30,10 @@ android/res/: art/icon.png
        convert -resize 144x144 art/icon.png android/res/drawable-xxhdpi/icon.png
        convert -resize 192x192 art/icon.png android/res/drawable-xxxhdpi/icon.png
 
-bin/model_viewer_vr.apk: $(shell ${NITLS} -M src/model_viewer.nit android) ${NITC} android/res/ android/libs/cardboard.jar
+bin/model_viewer_vr.apk: $(shell ${NITLS} -M src/model_viewer.nit android) android/res/ android/libs/cardboard.jar
        ${NITC} src/model_viewer.nit -m android -m ../../lib/gamnit/depth/vr.nit -o $@
 
 android/libs/cardboard.jar:
        mkdir -p android/libs
-       curl --progress-bar -o libs/cardboard.jar \
-       https://raw.githubusercontent.com/googlesamples/cardboard-java/master/CardboardSample/libs/cardboard.jar
+       curl --progress-bar -o android/libs/cardboard.jar \
+       https://raw.githubusercontent.com/googlevr/gvr-android-sdk/e226f15c/CardboardSample/libs/cardboard.jar
index abbcecf..5c91a58 100644 (file)
@@ -8,7 +8,15 @@ It renders the earth with an displaced surface, a cloud layer, city lights and P
 
 # Variations
 
-* The standard application is compiled to a desktop executable at `bin/model_viewer` and an Android package at `bin/model_viewer.apk`.
+* For the desktop, the application is compiled to `bin/model_viewer`.
+
+       This variation can show more models specified on the command line.
+
+       ~~~
+       bin/model_viewer [path_to_model ...]
+       ~~~
+
+* For Android, the standard application is compiled to `bin/model_viewer.apk`.
 * The virtual reality variant `bin/model_viewer_vr.apk` targets Android and uses Google Cardboard for head tracking.
 
 # Art
index ab2e4b3..60bc422 100644 (file)
@@ -55,12 +55,12 @@ redef class App
                var logo = new Texture("splash.png")
                show_splash_screen logo
 
-               if args.length > 0 then
-                       # Load a model passed as the first command line argument
-                       var model_path = args.first
-                       if model_path.has_prefix("assets/") then model_path = model_path.substring_from(7)
+               # Load all models passed as command line argument
+               for arg in args.to_a.reversed do
+                       # Force an absolute path, this only works on desktop, but so does command args
+                       arg = getcwd / arg
 
-                       var model = new Model(model_path)
+                       var model = new Model(arg)
                        models.unshift model
                end
 
@@ -91,15 +91,18 @@ redef class App
        # Set the currently displayed model
        fun model=(model: Model)
        do
+               if model isa ModelAsset then print "Model: {model.path}"
+
                var actor = new Actor(model, new Point3d[Float](0.0, 0.0, 0.0))
 
-               model = model.leaves.first
-               actor.center.x -= model.mesh.center.x
-               actor.center.y -= model.mesh.center.y
-               actor.center.z -= model.mesh.center.z
+               # Align on Y only
+               actor.center.y -= model.center.y
 
-               var height = model.mesh.dimensions.y
-               world_camera.reset_height(height * 2.5)
+               # Fit in viewport
+               var height = model.dimensions.x
+               height = height.max(model.dimensions.y)
+               height = height.max(model.dimensions.z)
+               world_camera.reset_height(height * 1.5)
 
                actors.clear
                actors.add actor
index c78f7e6..d628914 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-NITC=../../bin/nitc
+NITC=nitc
 NITC_FLAGS=--dir bin
 NEO4J_DIR=/var/lib/neo4j
 OLD_PWD=${PWD}
index 6971e7c..ac69b3b 100644 (file)
@@ -360,8 +360,8 @@ class Automaton
 
                # Remove their transitions
                for s in bads do
-                       for t in s.ins do t.delete
-                       for t in s.outs do t.delete
+                       for t in s.ins.to_a do t.delete
+                       for t in s.outs.to_a do t.delete
                end
 
                # Keep only the good stuff
@@ -373,14 +373,18 @@ class Automaton
        # REQUIRE: self is a DFA
        fun to_minimal_dfa: Automaton
        do
+               assert_valid
+
                trim
 
+               # Graph of known distinct states.
                var distincts = new HashMap[State, Set[State]]
                for s in states do
                        distincts[s] = new HashSet[State]
                end
 
-               # split accept states
+               # split accept states.
+               # An accept state is distinct with a non accept state.
                for s1 in states do
                        for s2 in states do
                                if distincts[s1].has(s2) then continue
@@ -390,7 +394,7 @@ class Automaton
                                        distincts[s2].add(s1)
                                        continue
                                end
-                               if tags[s1] != tags[s2] then
+                               if tags.get_or_null(s1) != tags.get_or_null(s2) then
                                        distincts[s1].add(s2)
                                        distincts[s2].add(s1)
                                        continue
@@ -398,24 +402,36 @@ class Automaton
                        end
                end
 
+               # Fixed point algorithm.
+               # * Get 2 states s1 and s2 not yet distinguished.
+               # * Get a symbol w.
+               # * If s1.trans(w) and s2.trans(w) are distinguished, then
+               #   distinguish s1 and s2.
                var changed = true
-               var ints = new Array[Int]
+               var ints = new Array[Int] # List of symbols to check
                while changed do
                        changed = false
                        for s1 in states do for s2 in states do
                                if distincts[s1].has(s2) then continue
+
+                               # The transitions use intervals. Therefore, for the states s1 and s2,
+                               # we need to check only the meaningful symbols. They are the `first`
+                               # symbol of each interval and the first one after the interval (`last+1`).
                                ints.clear
+                               # Check only `s1`; `s2` will be checked later when s1 and s2 are switched.
                                for t in s1.outs do
                                        var sym = t.symbol
                                        assert sym != null
                                        ints.add sym.first
                                        var l = sym.last
-                                       if l != null then ints.add l
+                                       if l != null then ints.add l + 1
                                end
+
+                               # Check each symbol
                                for i in ints do
                                        var ds1 = s1.trans(i)
                                        var ds2 = s2.trans(i)
-                                       if ds1 == null and ds2 == null then continue
+                                       if ds1 == ds2 then continue
                                        if ds1 != null and ds2 != null and not distincts[ds1].has(ds2) then continue
                                        distincts[s1].add(s2)
                                        distincts[s2].add(s1)
@@ -425,6 +441,8 @@ class Automaton
                        end
                end
 
+               # We need to unify not-distinguished states.
+               # Just add an epsilon-transition and DFAize the automaton.
                for s1 in states do for s2 in states do
                        if distincts[s1].has(s2) then continue
                        s1.add_trans(s2, null)
@@ -433,6 +451,21 @@ class Automaton
                return to_dfa
        end
 
+       # Assert that `self` is a valid automaton or abort
+       fun assert_valid
+       do
+               assert states.has(start)
+               assert states.has_all(accept)
+               for s in states do
+                       for t in s.outs do assert states.has(t.to)
+                       for t in s.ins do assert states.has(t.from)
+               end
+               assert states.has_all(tags.keys)
+               for t, ss in retrotags do
+                       assert states.has_all(ss)
+               end
+       end
+
        # Produce a graphvis file for the automaton
        fun to_dot(filepath: String)
        do
@@ -498,6 +531,8 @@ class Automaton
        # note: the DFA is not minimized.
        fun to_dfa: Automaton
        do
+               assert_valid
+
                trim
 
                var dfa = new Automaton.empty
index 18d52cc..d435c77 100644 (file)
@@ -64,6 +64,7 @@ re3 {-> re} =
        {plus:} re3 '+' |
        {shortest:} 'Shortest' '(' re ')' |
        {longest:} 'Longest' '(' re ')' |
+       {prefixes:} 'Prefixes' '(' re ')' |
        {id:} id |
        {par:} '(' re ')' |
        {class:} text '.' '.' text |
index bf3d57b..bfbdbb5 100644 (file)
@@ -85,6 +85,7 @@ var t_and = new Token("and")
 var t_except = new Token("except")
 var t_shortest = new Token("shortest")
 var t_longest = new Token("longest")
+var t_prefixes = new Token("prefixes")
 var t_ch_dec = new Token("ch_dec")
 var t_ch_hex = new Token("ch_hex")
 g.tokens.add_all([t_opar,
@@ -111,6 +112,7 @@ g.tokens.add_all([t_opar,
        t_except,
        t_shortest,
        t_longest,
+       t_prefixes,
        t_ch_dec,
        t_ch_hex])
 
@@ -139,6 +141,7 @@ p_re3.new_alt("re_ques", p_re3, t_ques)
 p_re3.new_alt("re_plus", p_re3, t_plus)
 p_re3.new_alt("re_shortest", t_shortest, t_opar, p_re, t_cpar)
 p_re3.new_alt("re_longest", t_longest, t_opar, p_re, t_cpar)
+p_re3.new_alt("re_prefixes", t_prefixes, t_opar, p_re, t_cpar)
 p_re3.new_alt("re_par", t_opar, p_re, t_cpar)
 p_re3.new_alt("re_class", p_text, t_dot, t_dot, p_text)
 p_re3.new_alt("re_openclass", p_text, t_dot, t_dot, t_dot)
index d523e51..12cd600 100644 (file)
@@ -173,6 +173,16 @@ redef class Nre_longest
        end
 end
 
+redef class Nre_prefixes
+       redef fun make_rfa
+       do
+               var a = children[2].make_rfa
+               a.trim
+               a.accept.add_all a.states
+               return a
+       end
+end
+
 redef class Nre_conc
        redef fun make_rfa
        do
diff --git a/contrib/nitcc/tests/lexer-prefixes.sablecc b/contrib/nitcc/tests/lexer-prefixes.sablecc
new file mode 100644 (file)
index 0000000..9e38697
--- /dev/null
@@ -0,0 +1,13 @@
+Grammar x;
+
+Lexer
+    m = 'abcd' | 'x'* 'y'+ 'z'?;
+    pm = Prefixes(m) Except '';
+    err = ('a'..'z') Except pm;
+
+blank = #10 | #13 | #32;
+Parser
+Ignored blank;
+
+    s = p+;
+    p = pm | err;
diff --git a/contrib/nitcc/tests/sav/lexer-prefixes.input.res b/contrib/nitcc/tests/sav/lexer-prefixes.input.res
new file mode 100644 (file)
index 0000000..108c10a
--- /dev/null
@@ -0,0 +1,52 @@
+Start
+  s
+    Nodes[Np]
+      p_0
+        pm@(1:1-1:2)='a'
+      p_1
+        err@(1:3-1:4)='b'
+      p_0
+        pm@(1:5-1:7)='ab'
+      p_0
+        pm@(1:8-1:11)='abc'
+      p_0
+        pm@(1:12-1:16)='abcd'
+      p_0
+        pm@(1:17-1:21)='abcd'
+      p_1
+        err@(1:21-1:22)='e'
+      p_0
+        pm@(1:23-1:24)='a'
+      p_0
+        pm@(1:24-1:26)='ab'
+      p_1
+        err@(1:26-1:27)='b'
+      p_1
+        err@(1:27-1:28)='c'
+      p_1
+        err@(1:28-1:29)='c'
+      p_1
+        err@(1:29-1:30)='d'
+      p_1
+        err@(1:30-1:31)='d'
+      p_0
+        pm@(2:1-2:2)='x'
+      p_0
+        pm@(2:3-2:4)='y'
+      p_1
+        err@(2:5-2:6)='z'
+      p_0
+        pm@(2:7-2:10)='xyz'
+      p_0
+        pm@(2:11-2:12)='x'
+      p_1
+        err@(2:12-2:13)='z'
+      p_0
+        pm@(2:14-2:16)='xy'
+      p_0
+        pm@(2:17-2:19)='yz'
+      p_0
+        pm@(2:20-2:25)='xxyyz'
+      p_1
+        err@(2:25-2:26)='z'
+  Eof@(3:1-3:1)=''
index ccefcf6..86e69e1 100644 (file)
@@ -1,3 +1,3 @@
 all:
        mkdir -p bin/
-       ../../bin/nitc src/nitester.nit -o bin/nitester
+       nitc src/nitester.nit -o bin/nitester
index f040751..19c3811 100644 (file)
@@ -2,16 +2,16 @@ all: nitiwiki bin/nitiwiki_server
 
 nitiwiki:
        mkdir -p bin
-       ../../bin/nitc src/nitiwiki.nit -o bin/nitiwiki
+       nitc src/nitiwiki.nit -o bin/nitiwiki
 
-bin/nitiwiki_server: $(shell ../../bin/nitls -M src/wiki_edit.nit)
-       ../../bin/nitc -o $@ src/wiki_edit.nit
+bin/nitiwiki_server: $(shell nitls -M src/wiki_edit.nit)
+       nitc -o $@ src/wiki_edit.nit
 
 check: nitiwiki
        cd tests; make
 
 doc:
-       ../../bin/nitdoc -d doc src/nitiwiki.nit
+       nitdoc -d doc src/nitiwiki.nit
 
 clean:
        rm -rf bin
index 70cf35f..d3adc26 100644 (file)
@@ -94,7 +94,7 @@ class Nitiwiki
                                var entry = entries[path]
                                if not entry.is_dirty then continue
                                var name = entry.name
-                               if entry.has_source then name = entry.src_path.to_s
+                               if entry.has_source then name = entry.src_path.as(not null)
                                if entry.is_new then
                                        print " + {name}"
                                else
@@ -141,7 +141,7 @@ class Nitiwiki
        fun need_render(src, target: String): Bool do
                if force_render then return true
                if not target.file_exists then return true
-               return src.file_stat.mtime >= target.file_stat.mtime
+               return src.file_stat.as(not null).mtime >= target.file_stat.as(not null).mtime
        end
 
        # Create a new `WikiSection`.
@@ -244,7 +244,7 @@ class Nitiwiki
        # Used to translate ids in beautiful page names.
        fun pretty_name(name: String): String do
                name = name.replace("_", " ")
-               name = name.capitalized
+               name = name.capitalized(keep_upper=true)
                return name
        end
 end
@@ -283,7 +283,7 @@ abstract class WikiEntry
        # Returns `-1` if not `has_source`.
        fun create_time: Int do
                if not has_source then return -1
-               return src_full_path.file_stat.ctime
+               return src_full_path.as(not null).file_stat.as(not null).ctime
        end
 
        # Entry last modification time.
@@ -291,7 +291,7 @@ abstract class WikiEntry
        # Returns `-1` if not `has_source`.
        fun last_edit_time: Int do
                if not has_source then return -1
-               return src_full_path.file_stat.mtime
+               return src_full_path.as(not null).file_stat.as(not null).mtime
        end
 
        # Entry list rendering time.
@@ -299,7 +299,7 @@ abstract class WikiEntry
        # Returns `-1` if `is_new`.
        fun last_render_time: Int do
                if is_new then return -1
-               return out_full_path.file_stat.mtime
+               return out_full_path.file_stat.as(not null).mtime
        end
 
        # Entries hierarchy
@@ -400,7 +400,7 @@ abstract class WikiEntry
        # then returns the main wiki template file.
        fun template_file: String do
                if is_root then return wiki.config.template_file
-               return parent.template_file
+               return parent.as(not null).template_file
        end
 
        # Header template file for `self`.
@@ -408,7 +408,7 @@ abstract class WikiEntry
        # Behave like `template_file`.
        fun header_file: String do
                if is_root then return wiki.config.header_file
-               return parent.header_file
+               return parent.as(not null).header_file
        end
 
        # Footer template file for `self`.
@@ -416,7 +416,7 @@ abstract class WikiEntry
        # Behave like `template_file`.
        fun footer_file: String do
                if is_root then return wiki.config.footer_file
-               return parent.footer_file
+               return parent.as(not null).footer_file
        end
 
        # Menu template file for `self`.
@@ -424,7 +424,7 @@ abstract class WikiEntry
        # Behave like `template_file`.
        fun menu_file: String do
                if is_root then return wiki.config.menu_file
-               return parent.menu_file
+               return parent.as(not null).menu_file
        end
 
        # Display the entry `name`.
@@ -442,7 +442,7 @@ class WikiSection
 
        redef fun title do
                if has_config then
-                       var title = config.title
+                       var title = config.as(not null).title
                        if title != null then return title
                end
                return super
@@ -452,7 +452,7 @@ class WikiSection
        #
        # Hidden section are rendered but not linked in menus.
        fun is_hidden: Bool do
-               if has_config then return config.is_hidden
+               if has_config then return config.as(not null).is_hidden
                return false
        end
 
@@ -461,7 +461,7 @@ class WikiSection
                if parent == null then
                        return wiki.config.source_dir
                else
-                       return wiki.expand_path(parent.src_path, name)
+                       return wiki.expand_path(parent.as(not null).src_path, name)
                end
        end
 
@@ -486,41 +486,41 @@ class WikiSection
        # Also check custom config.
        redef fun template_file do
                if has_config then
-                       var tpl = config.template_file
+                       var tpl = config.as(not null).template_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.template_file
-               return parent.template_file
+               return parent.as(not null).template_file
        end
 
        # Also check custom config.
        redef fun header_file do
                if has_config then
-                       var tpl = config.header_file
+                       var tpl = config.as(not null).header_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.header_file
-               return parent.header_file
+               return parent.as(not null).header_file
        end
 
        # Also check custom config.
        redef fun footer_file do
                if has_config then
-                       var tpl = config.footer_file
+                       var tpl = config.as(not null).footer_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.footer_file
-               return parent.footer_file
+               return parent.as(not null).footer_file
        end
 
        # Also check custom config.
        redef fun menu_file do
                if has_config then
-                       var tpl = config.menu_file
+                       var tpl = config.as(not null).menu_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.menu_file
-               return parent.menu_file
+               return parent.as(not null).menu_file
        end
 end
 
@@ -534,7 +534,8 @@ class WikiArticle
        # Articles can only have `WikiSection` as parents.
        redef type PARENT: WikiSection
 
-       redef fun title: String do
+       redef fun title do
+               var parent = self.parent
                if name == "index" and parent != null then return parent.title
                return super
        end
@@ -551,7 +552,7 @@ class WikiArticle
                content = md
        end
 
-       redef var src_full_path: nullable String = null
+       redef var src_full_path = null
 
        redef fun src_path do
                var src_full_path = self.src_full_path
@@ -567,7 +568,7 @@ class WikiArticle
        # REQUIRE: `has_source`.
        var md: nullable String is lazy do
                if not has_source then return null
-               var file = new FileReader.open(src_full_path.to_s)
+               var file = new FileReader.open(src_full_path.as(not null))
                var md = file.read_all
                file.close
                return md
@@ -578,7 +579,7 @@ class WikiArticle
        redef fun is_dirty do
                if super then return true
                if has_source then
-                       return wiki.need_render(src_full_path.to_s, out_full_path)
+                       return wiki.need_render(src_full_path.as(not null), out_full_path)
                end
                return false
        end
@@ -779,7 +780,7 @@ class WikiConfig
        var sidebar_blocks: Array[String] is lazy do
                var res = new Array[String]
                if not has_key("wiki.sidebar.blocks") then return res
-               for val in at("wiki.sidebar.blocks").values do
+               for val in at("wiki.sidebar.blocks").as(not null).values do
                        res.add val
                end
                return res
index ab79f09..15ae4d7 100644 (file)
@@ -70,7 +70,8 @@ end
 redef class WikiSection
 
        # Output directory (where to ouput the HTML pages for this section).
-       redef fun out_path: String do
+       redef fun out_path do
+               var parent = self.parent
                if parent == null then
                        return wiki.config.out_dir
                else
@@ -104,7 +105,7 @@ redef class WikiSection
        # Copy attached files from `src_path` to `out_path`.
        private fun copy_files do
                assert has_source
-               var dir = src_full_path.to_s
+               var dir = src_full_path.as(not null).to_s
                for name in dir.files do
                        if name == wiki.config_filename then continue
                        if name.has_suffix(".md") then continue
@@ -167,7 +168,8 @@ end
 
 redef class WikiArticle
 
-       redef fun out_path: String do
+       redef fun out_path do
+               var parent = self.parent
                if parent == null then
                        return wiki.expand_path(wiki.config.out_dir, "{name}.html")
                else
index ac3e171..33dc61f 100644 (file)
@@ -31,7 +31,7 @@ redef class Nitiwiki
        # Returns `null` if no article can be found.
        fun lookup_entry_by_name(context: WikiEntry, name: String): nullable WikiEntry do
                var section: nullable WikiEntry = context.parent or else context
-               var res = section.lookup_entry_by_name(name)
+               var res = section.as(not null).lookup_entry_by_name(name)
                if res != null then return res
                while section != null do
                        if section.name == name then return section
@@ -52,7 +52,7 @@ redef class Nitiwiki
        # Returns `null` if no article can be found.
        fun lookup_entry_by_title(context: WikiEntry, title: String): nullable WikiEntry do
                var section: nullable WikiEntry = context.parent or else context
-               var res = section.lookup_entry_by_title(title)
+               var res = section.as(not null).lookup_entry_by_title(title)
                if res != null then return res
                while section != null do
                        if section.title.to_lower == title.to_lower then return section
@@ -200,6 +200,7 @@ redef class WikiArticle
        fun is_index: Bool do return name == "index"
 
        redef fun href do
+               var parent = self.parent
                if parent == null then
                        return "{name}.html"
                else
index b6c4be9..ac3f3a8 100644 (file)
@@ -14,8 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-NITC=../../bin/nitc
-NITU=../../bin/nitunit
+NITC=nitc
+NITU=nitunit
 
 all: listener web
 
index 1ac8314..ecdc358 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Test tools for NitRPG.
-module test_helper is test_suite
+module test_helper
 
 import test_suite
 import game
@@ -57,7 +57,8 @@ abstract class NitrpgTestHelper
 
        # Gen a test db with a random name (to avoid race conditions).
        fun gen_test_db: MongoDb do
-               var db_name = "test_nitrpg_{get_time}_{1000.rand}"
+               var testid = "NIT_TESTING_ID".environ.to_i
+               var db_name = "test_nitrpg_{testid}"
                var db = load_db(db_name)
                test_dbs.add db
                return db
index 33ed110..fe6fece 100644 (file)
@@ -10,11 +10,11 @@ src/objc_parser.nit: ../nitcc/src/nitcc grammar/objc.sablecc
        mv *.nit src/
        mv objc* gen/
 
-bin/objcwrapper: $(shell ../../bin/nitls -M src/objcwrapper.nit) src/objc_parser.nit
-       ../../bin/nitc -o bin/objcwrapper src/objcwrapper.nit --semi-global
+bin/objcwrapper: $(shell nitls -M src/objcwrapper.nit) src/objc_parser.nit
+       nitc -o bin/objcwrapper src/objcwrapper.nit --semi-global
 
-bin/objc_test_parser: $(shell ../../bin/nitls -M src/objc_test_parser.nit)
-       ../../bin/nitc -o bin/objc_test_parser src/objc_test_parser.nit --semi-global
+bin/objc_test_parser: $(shell nitls -M src/objc_test_parser.nit)
+       nitc -o bin/objc_test_parser src/objc_test_parser.nit --semi-global
 
 check: bin/objc_test_parser bin/objcwrapper
        # Test the parser
@@ -22,20 +22,20 @@ check: bin/objc_test_parser bin/objcwrapper
 
        # Test objcwrapper
        bin/objcwrapper tests/MyClass.h -o tests/MyClass.nit
-       ../../bin/nitpick tests/MyClass.nit
+       nitpick tests/MyClass.nit
 
 # Test on classes of libgnustep-base-dev
 check-gnustep: bin/objcwrapper
        bin/objcwrapper /usr/include/GNUstep/Foundation/*.h -o tests/gnustep.nit -p "-I /usr/include/GNUstep/ -Wno-deprecated -Wno-cpp"
-       ../../bin/nitpick tests/nsarray.nit
+       nitpick tests/nsarray.nit
 
 # Test on classes of the Apple Foundation framework
 check-apple: bin/objcwrapper
        bin/objcwrapper /System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSArray.h -o tests/nsarray.nit
-       ../../bin/nitpick tests/nsarray.nit
+       nitpick tests/nsarray.nit
 
        bin/objcwrapper /System/Library/Frameworks/AppKit.framework/Headers/NSAlert.h -o tests/nsalert.nit
-       ../../bin/nitpick tests/nsalert.nit
+       nitpick tests/nsalert.nit
 
-bin/header_static: $(shell ../../bin/nitls -M src/header_static.nit)
-       ../../bin/nitc --dir bin src/header_static.nit
+bin/header_static: $(shell nitls -M src/header_static.nit)
+       nitc --dir bin src/header_static.nit
index c8d231e..efc07ba 100644 (file)
@@ -2,7 +2,7 @@
 ACE_BUILDS ?= ../../../ace-builds/
 
 default:
-       ../../bin/nitc --semi-global sources/nit/pnacl_nit.nit
+       nitc --semi-global sources/nit/pnacl_nit.nit
        cp pnacl_nit/pnacl_nit.pexe www/pnacl/ -f
        rm -rf pnacl_nit/
 
index 122b507..ddc3f13 100644 (file)
@@ -56,8 +56,8 @@ redef class Sys
 end
 
 redef class ToolContext
-       # We don't need 'the compute_nit_dir'.
-       redef fun compute_nit_dir
+       # We don't need 'the locate_nit_dir'.
+       redef fun locate_nit_dir
        do
                return "/pnacl"
        end
index 28b8f78..8b4469e 100644 (file)
@@ -1,6 +1,6 @@
 all: fr
        mkdir -p bin/
-       ../../bin/nitc --dir bin/ src/opportunity_web.nit
+       nitc --dir bin/ src/opportunity_web.nit
 
 fr:
        make -C src/templates/languages/fr/LC_MESSAGES/
index 26a4ef6..214c843 100644 (file)
@@ -41,7 +41,6 @@ class OpportunityWelcome
        super OpportunityAction
 
        redef fun answer(request, url) do
-               print "Received request for {url}"
                var get = request.get_args
                var rq = url.split("/")
                if rq.has("meetup_create") then
@@ -89,18 +88,22 @@ class OpportunityWelcome
                                rsp.body = meetpage.write_to_string
                                return rsp
                        end
-                       if not meet.commit(db) then
+
+                       var success = meet.commit(db)
+                       if not success then
                                db.close
-                               var rsp = new HttpResponse(200)
+                               var rsp = new HttpResponse(500)
                                var meetpage = new MeetupCreationPage
                                meetpage.meet = meet
                                meetpage.ans = ansset
-                               meetpage.error = """<p>Could not create Meetup.</p>
-                               <p>Hmm, that's embarassing, the database indicates that your meetup already exists.</p>
-                               <p>If this is not a duplicated submission, please contact the mainainers of the website, you might have found a bug !</p>"""
+                               meetpage.error = """
+<p>Failed to create event</p>
+<p>This is a server side error, it has been logged.
+   You may still want to contact the maintainers of this website.</p>"""
                                rsp.body = meetpage.write_to_string
                                return rsp
                        end
+
                        for v in ansset do
                                var ans = new Answer(v)
                                ans.meetup = meet
@@ -134,7 +137,6 @@ class OpportunityRESTAction
        super OpportunityAction
 
        redef fun answer(request, uri) do
-               print "Received REST request from {uri}"
                var req = uri.split("/")
                if req.has("people") then
                        return (new OpportunityPeopleREST).answer(request, uri)
index cc72f23..b6f8f4b 100644 (file)
@@ -32,25 +32,25 @@ class OpportunityDB
        # Creates the tables and triggers for Opportunity (SQLite3 DB)
        fun create_db do
                assert create_table("IF NOT EXISTS meetups (id CHAR(40) PRIMARY KEY, name TEXT, date TEXT, place TEXT, answer_mode INTEGER DEFAULT 0);") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
                assert create_table("IF NOT EXISTS people(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT);") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
                assert create_table("IF NOT EXISTS answers(id INTEGER PRIMARY KEY AUTOINCREMENT, meetup_id CHAR(40), name TEXT, FOREIGN KEY(meetup_id) REFERENCES meetups(id));") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
                assert create_table("IF NOT EXISTS part_answers(id_part INTEGER, id_ans INTEGER, value INTEGER, FOREIGN KEY(id_part) REFERENCES people(id), FOREIGN KEY(id_ans) REFERENCES answers(id));") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
                #NOTE: The following triggers could be replaced by ON DELETE CASCADE clauses
                # Thing is, SQLite does not seem to support those operations (well, not by default, it seems
                # we must re-compile the lib to support it. So, well, let's just create triggers heh.
                assert execute("CREATE TRIGGER IF NOT EXISTS answers_clean AFTER DELETE ON meetups BEGIN DELETE FROM answers WHERE answers.meetup_id=OLD.id;END;") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
                assert execute("CREATE TRIGGER IF NOT EXISTS ans_clean AFTER DELETE ON answers BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_ans;END;") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
                assert execute("CREATE TRIGGER IF NOT EXISTS ppl_clean AFTER DELETE ON people BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_part;END;")
        end
@@ -88,12 +88,12 @@ class OpportunityDB
        fun change_answer(pid: Int, ansid: Int, resp: Int): Bool do
                var p = find_people_by_id(pid)
                if p == null then
-                       print "Error while updating answer {ansid}:{pid}"
+                       print_error "Opportunity error while updating answer {ansid}:{pid}"
                        return false
                end
                var a = find_answer_by_id(ansid)
                if a == null then
-                       print "Error while updating answer {ansid}:{pid}"
+                       print_error "Opportunity error while updating answer {ansid}:{pid}"
                        return false
                end
                p.answers[a] = resp
@@ -107,8 +107,8 @@ class OpportunityDB
        fun remove_people_by_id(id: Int): Bool do
                var rq = execute("DELETE FROM people WHERE id = {id};")
                if not rq then
-                       print "Cannot delete people {id}"
-                       print error or else "Unknown error"
+                       print_error "Opportunity error deleting people {id}"
+                       print_error error or else "Unknown error"
                        return false
                end
                return true
@@ -166,15 +166,15 @@ class People
        redef fun commit(db) do
                if id == -1 then
                        if not db.execute("INSERT INTO people (name,surname) VALUES ({name.html_escape.to_sql_string}, {surname.html_escape.to_sql_string});") then
-                               print "Error while adding people {self}"
-                               print db.error or else "Unknown error"
+                               print_error "Opportunity error while adding people {self}"
+                               print_error db.error or else "Unknown error"
                                return false
                        end
                        id = db.last_insert_rowid
                else
                        if not db.execute("UPDATE people SET name={name.html_escape.to_sql_string}, surname={surname.html_escape.to_sql_string} WHERE ID={id};") then
-                               print "Error while updating people {self}"
-                               print db.error or else "Unknown error"
+                               print_error "Opportunity error while updating people {self}"
+                               print_error db.error or else "Unknown error"
                                return false
                        end
                end
@@ -184,15 +184,15 @@ class People
                        var s = db.select("* FROM part_answers WHERE id_part={id} AND id_ans={i.id}")
                        if s != null and s.iterator.is_ok then
                                if not db.execute("UPDATE part_answers SET value={j} WHERE id_part={id} AND id_ans={i.id};") then
-                                       print "Error while updating part_answers {id}|{i.id} = {j}"
-                                       print db.error or else "Unknown error"
+                                       print_error "Opportunity error while updating part_answers {id}|{i.id} = {j}"
+                                       print_error db.error or else "Unknown error"
                                        return false
                                end
                                continue
                        end
                        if not db.execute("INSERT INTO part_answers(id_part, id_ans, value) VALUES ({id},{i.id},{val});") then
-                               print("Error while adding part_answers {id}|{i.id}|{j}")
-                               print db.error or else "Unknown error"
+                               print_error "Opportunity error while adding part_answers {id}|{i.id}|{j}"
+                               print_error db.error or else "Unknown error"
                                return false
                        end
                end
@@ -249,8 +249,8 @@ class Meetup
                        var time = get_time
                        var tmpid = (name + date + place + time.to_s).sha1.hexdigest
                        if not db.execute("INSERT INTO meetups (id, name, date, place, answer_mode) VALUES({tmpid.to_sql_string}, {name.html_escape.to_sql_string}, {date.html_escape.to_sql_string}, {place.html_escape.to_sql_string}, {answer_mode});") then
-                               print "Error recording entry Meetup {self}"
-                               print db.error or else "Null error"
+                               print_error "Opportunity error recording entry Meetup {self}"
+                               print_error db.error or else "Unknown error"
                                return false
                        end
                        id = tmpid
@@ -260,9 +260,7 @@ class Meetup
                end
        end
 
-       redef fun to_s do
-               return "Event : {name}\nWhen : {date}\nWhere : {place}"
-       end
+       redef fun to_s do return "Event: {name}, date: {date}, place: {place}"
 end
 
 # An answer linked to a Meetup in the database
@@ -332,21 +330,21 @@ class Answer
                if m == null then return false
                if m.id == "" then
                        if not m.commit(db) then
-                               print "Error when creating meetup {m}"
+                               print_error "Opportunity error when creating meetup {m}"
                                return false
                        end
                end
                if id == -1 then
                        if not db.execute("INSERT INTO answers (name, meetup_id) VALUES({name.html_escape.to_sql_string}, {m.id.to_sql_string});") then
-                               print "Cannot create {self} in database"
-                               print db.error or else "Unknown error"
+                               print_error "Opportunity error creating {self} in database"
+                               print_error db.error or else "Unknown error"
                                return false
                        end
                        id = db.last_insert_rowid
                else
                        if not db.execute("UPDATE answers SET name=({name.html_escape.to_sql_string}) WHERE meetup_id={m.id.to_sql_string};") then
-                               print "Error updating {self} in database"
-                               print db.error or else "Unknown error"
+                               print_error "Opportunity error updating {self} in database"
+                               print_error db.error or else "Unknown error"
                                return false
                        end
                end
index d5e0137..a687cc8 100644 (file)
@@ -1,9 +1,9 @@
 bin/pep8analysis:
        mkdir -p bin
-       ../../bin/nitc -o bin/pep8analysis src/pep8analysis.nit
+       nitc -o bin/pep8analysis src/pep8analysis.nit
 
 doc/index.html:
-       ../../bin/nitdoc src/pep8analysis.nit
+       nitdoc src/pep8analysis.nit
 
 check: tests
 tests: bin/pep8analysis
@@ -11,7 +11,7 @@ tests: bin/pep8analysis
        diff test.out test.sav
 
 www/pep8analysis.js:
-       ../../bin/nitc -o www/pep8analysis.js --semi-global src/pep8analysis_web.nit
+       nitc -o www/pep8analysis.js --semi-global src/pep8analysis_web.nit
        mkdir -p www/samples
        cp tests/micro/*.pep tests/privat/02-fibo.pep tests/privat/06-calc-non-pur.pep www/samples
 
index 43d5e4a..bbf3802 100644 (file)
@@ -3,6 +3,8 @@ name=pep8analysis
 tags=educ,web,cli
 maintainer=Alexis Laferrière <alexis.laf@xymus.net>
 license=Apache-2.0
+[source]
+exclude=src/parser/parser_abs.nit
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/contrib/pep8analysis/
 git=https://github.com/nitlang/nit.git
index def014e..6cbe02f 100644 (file)
@@ -18,7 +18,7 @@ all: refund
 
 refund:
        mkdir -p bin
-       ../../bin/nitc src/refund.nit -o bin/refund
+       nitc src/refund.nit -o bin/refund
 
 check: refund
        cd tests; make
index 33032b3..4da71f5 100644 (file)
@@ -72,6 +72,9 @@ redef class RefundProcessor
                if json isa JsonParseError then
                        die("Wrong input file ({json.message})")
                        abort
+               else if json == null then
+                       die("Unable to parse input file as json (got null)")
+                       abort
                else if not json isa JsonObject then
                        die("Wrong input type (expected JsonObject got {json.class_name})")
                        abort
@@ -130,7 +133,7 @@ redef class RefundProcessor
                return new RefundStats.from_json(content)
        end
 
-       redef fun save_stats(stats: RefundStats) do
+       redef fun save_stats(stats) do
                write_output(stats.to_json.to_pretty_json, stats_file)
        end
 end
@@ -165,13 +168,19 @@ redef class ReclamationSheet
                proc.check_key(json, "reclamations")
                var res = new Array[Reclamation]
                var recls = json["reclamations"]
-               if not recls isa JsonArray then
+               if recls == null then
+                       proc.die("Wrong type for `number` (expected JsonArray got null)")
+                       abort
+               else if not recls isa JsonArray then
                        proc.die("Wrong type for `number` (expected JsonArray got {recls.class_name})")
                        abort
                end
                var i = 0
                for obj in recls do
-                       if not obj isa JsonObject then
+                       if obj == null then
+                               proc.die("Wrong type for `reclamations#{i}` (expected JsonObject got null)")
+                               abort
+                       else if not obj isa JsonObject then
                                proc.die("Wrong type for `reclamations#{i}` " +
                                        "(expected JsonObject got {obj.class_name})")
                                abort
@@ -197,7 +206,10 @@ redef class ReclFile
        init from_json(proc: RefundProcessor, json: JsonObject) do
                proc.check_key(json, "dossier")
                var id = json["dossier"]
-               if not id isa String then
+               if id == null then
+                       proc.die("Wrong type for `dossier` (expected String got null)")
+                       abort
+               else if not id isa String then
                        proc.die("Wrong type for `dossier` (expected String got {id.class_name})")
                        abort
                end
@@ -232,7 +244,10 @@ redef class ReclMonth
        init from_json(proc: RefundProcessor, json: JsonObject) do
                proc.check_key(json, "mois")
                var month = json["mois"]
-               if not month isa String then
+               if month == null then
+                       proc.die("Wrong type for `mois` (expected String got null)")
+                       return
+               else if not month isa String then
                        proc.die("Wrong type for `mois` (expected String got {month.class_name})")
                        return
                end
@@ -264,7 +279,10 @@ redef class ReclDate
        init from_json(proc: RefundProcessor, json: JsonObject) do
                proc.check_key(json, "date")
                var date = json["date"]
-               if not date isa String then
+               if date == null then
+                       proc.die("Wrong type for `date` (expected String got null)")
+                       abort
+               else if not date isa String then
                        proc.die("Wrong type for `date` (expected String got {date.class_name})")
                        abort
                end
@@ -302,7 +320,10 @@ redef class Reclamation
        private fun parse_care_id(proc: RefundProcessor, json: JsonObject): Int do
                proc.check_key(json, "soin")
                var id = json["soin"]
-               if not id isa Int then
+               if id == null then
+                       proc.die("Wrong type for `soin` (expected Int got null)")
+                       abort
+               else if not id isa Int then
                        proc.die("Wrong type for `soin` (expected Int got {id.class_name})")
                        abort
                end
@@ -313,7 +334,10 @@ redef class Reclamation
        private fun parse_fees(proc: RefundProcessor, json: JsonObject): Dollar do
                proc.check_key(json, "montant")
                var fees = json["montant"]
-               if not fees isa String then
+               if fees == null then
+                       proc.die("Wrong type for `fees` (expected String got null)")
+                       abort
+               else if not fees isa String then
                        proc.die("Wrong type for `fees` (expected String got {fees.class_name})")
                        abort
                end
index c2a092d..e57a13e 100644 (file)
@@ -1,3 +1,3 @@
 {
-       "message": "Wrong type for `soin` (expected Int got FlatString)"
+       "message": "Wrong type for `soin` (expected Int got ASCIIFlatString)"
 }
index 3c71d2e..d8371a2 100644 (file)
@@ -1,3 +1,3 @@
 all:
        mkdir -p bin/
-       ../../bin/nitc --dir bin/ src/*.nit
+       nitc --dir bin/ src/*.nit
index 367868e..cf6d36c 100644 (file)
@@ -1,4 +1,4 @@
-NITC=../../bin/nitc
+NITC=nitc
 NITCC=../nitcc/src/nitcc
 
 all: simplan
index ba32497..301c564 100644 (file)
@@ -80,7 +80,7 @@ class PlanProblem
                        print n.message
                        exit 1
                end
-               n = n.children.first.children.first.as(not null)
+               n = n.children.first.as(not null).children.first.as(not null)
                if n isa Nplan then
                        print "Error: expected a problem, got a plan."
                        exit 1
@@ -88,7 +88,7 @@ class PlanProblem
                assert n isa Nproblem
 
                # Load all locations
-               for n2 in n.n_locations.n_list.children do
+               for n2 in n.n_locations.n_list.as(not null).children do
                        var e = new Location(locations.length, n2.n_name.text, n2.n_x.text.to_f, n2.n_y.text.to_f)
                        assert not locations.has_key(e.name)
                        locations[e.name] = e
@@ -97,7 +97,7 @@ class PlanProblem
 
                # Load all roads
                var nbr = 0
-               for n2 in n.n_roads.n_list.children do
+               for n2 in n.n_roads.n_list.as(not null).children do
                        var o = locations.get_or_null(n2.n_orig.text)
                        var d = locations.get_or_null(n2.n_dest.text)
                        assert o != null and d != null
@@ -132,7 +132,7 @@ class PlanProblem
 
                # Load the robot
                var robot = null
-               for n2 in n.n_robots.n_list.children do
+               for n2 in n.n_robots.n_list.as(not null).children do
                        var name = n2.n_name.text
                        robot = locations.get_or_null(n2.n_emplacement.text)
                        assert name == robot_name and robot != null
@@ -142,7 +142,7 @@ class PlanProblem
 
                # Load the parcels
                var parcel_locations = new Array[nullable Location]
-               for n2 in n.n_parcels.n_list.children do
+               for n2 in n.n_parcels.n_list.as(not null).children do
                        var name = n2.n_name.text
                        var e = locations.get_or_null(n2.n_emplacement.text)
                        assert e != null
@@ -155,7 +155,7 @@ class PlanProblem
                print "# {parcels.length} parcels"
 
                # Load the goal of parcels
-               for n2 in n.n_goal.n_list.children do
+               for n2 in n.n_goal.n_list.as(not null).children do
                        var parcel = parcel_by_name.get_or_null(n2.n_name.text)
                        var e = locations.get_or_null(n2.n_emplacement.text)
                        assert parcel != null and e != null
@@ -179,7 +179,7 @@ class PlanProblem
                        print n.message
                        exit 1
                end
-               n = n.children.first.children.first.as(not null)
+               n = n.children.first.as(not null).children.first.as(not null)
                if n isa Nproblem then
                        print "Error: expected a plan, got a problem."
                        exit 1
@@ -189,7 +189,7 @@ class PlanProblem
                var res = new Plan(self)
                var e = initial_state
                var cost = 0.0
-               for n2 in n.n_actions.children do
+               for n2 in n.n_actions.as(not null).children do
                        if n2 isa Naction_load then
                                var parcel = parcel_by_name.get_or_null(n2.n_parcel.text)
                                assert parcel != null
index 4c04e8e..903d020 100644 (file)
@@ -1,6 +1,6 @@
 build:
        mkdir -p bin/
-       ../../bin/nitc --dir bin/ src/*.nit
+       nitc --dir bin/ src/*.nit
 
 install:
        install bin/sort_downloads /usr/local/bin/
index f74ff97..c254b80 100644 (file)
@@ -17,34 +17,34 @@ all: bin/server bin/tinks bin/tinks3d
 pre-build: assets/images/drawing.png src/server/server_serialize.nit
 
 # Client
-bin/tinks: assets/images/drawing.png src/client/client.nit $(shell ../../bin/nitls -M src/client/linux_client.nit)
-       ../../bin/nitserial -o src/client/client_serialize.nit src/client/client.nit
-       ../../bin/nitc -o bin/tinks src/client/linux_client.nit -m src/client/client_serialize.nit
+bin/tinks: assets/images/drawing.png src/client/client.nit $(shell nitls -M src/client/linux_client.nit)
+       nitserial -o src/client/client_serialize.nit src/client/client.nit
+       nitc -o bin/tinks src/client/linux_client.nit -m src/client/client_serialize.nit
 
-bin/tinks3d: $(shell ../../bin/nitls -M src/client/client3d.nit -m linux)
-       ../../bin/nitserial -o src/client/client_serialize.nit src/client/client3d.nit
-       ../../bin/nitc -o bin/tinks3d src/client/client3d.nit \
+bin/tinks3d: $(shell nitls -M src/client/client3d.nit -m linux)
+       nitserial -o src/client/client_serialize.nit src/client/client3d.nit
+       nitc -o bin/tinks3d src/client/client3d.nit \
                -m src/client/client_serialize.nit -m linux
 
 assets/images/drawing.png: art/drawing.svg
        ../inkscape_tools/bin/svg_to_png_and_nit art/drawing.svg -a assets/ -s src/client/ -x 2.0
 
 # Server
-bin/server: src/server/server_serialize.nit $(shell ../../bin/nitls -M src/server/dedicated.nit)
-       ../../bin/nitc -o bin/server src/server/dedicated.nit -m src/server/server_serialize.nit
+bin/server: src/server/server_serialize.nit $(shell nitls -M src/server/dedicated.nit)
+       nitc -o bin/server src/server/dedicated.nit -m src/server/server_serialize.nit
 
-src/server/server_serialize.nit: $(shell ../../bin/nitls -M src/server/dedicated.nit)
-       ../../bin/nitserial -o src/server/server_serialize.nit src/server/dedicated.nit
+src/server/server_serialize.nit: $(shell nitls -M src/server/dedicated.nit)
+       nitserial -o src/server/server_serialize.nit src/server/dedicated.nit
 
 # Android
 android: bin/tinks.apk
-bin/tinks.apk: assets/images/drawing.png android/res/ $(shell ../../bin/nitls -M src/client/android_client.nit)
-       ../../bin/nitserial -o src/client/client_serialize.nit src/client/client.nit
-       ../../bin/nitc -o bin/tinks.apk src/client/android_client.nit -m src/client/client_serialize.nit
+bin/tinks.apk: assets/images/drawing.png android/res/ $(shell nitls -M src/client/android_client.nit)
+       nitserial -o src/client/client_serialize.nit src/client/client.nit
+       nitc -o bin/tinks.apk src/client/android_client.nit -m src/client/client_serialize.nit
 
-android-release: assets/images/drawing.png android/res/ $(shell ../../bin/nitls -M src/client/android_client.nit)
-       ../../bin/nitserial -o src/client/client_serialize.nit src/client/client.nit
-       ../../bin/nitc -o bin/tinks.apk src/client/android_client.nit -m src/client/client_serialize.nit --release
+android-release: assets/images/drawing.png android/res/ $(shell nitls -M src/client/android_client.nit)
+       nitserial -o src/client/client_serialize.nit src/client/client.nit
+       nitc -o bin/tinks.apk src/client/android_client.nit -m src/client/client_serialize.nit --release
 
 android/res/: art/icon.svg
        ../inkscape_tools/bin/svg_to_icons art/icon.svg --android --out android/res/
index ba508c9..bc434b7 100644 (file)
@@ -124,7 +124,7 @@ redef class App
                var local_tank = local_tank
                if local_tank != null then
                        var tank_speed = local_tank.direction_forwards*local_tank.rule.max_speed
-                       tank_speed = tank_speed.min(0.5).max(-0.5)
+                       tank_speed = tank_speed.clamp(-0.5, 0.5)
 
                        var prop_pos = local_tank.pos + local_tank.heading.to_vector(tank_speed * 16.0)
                        var old_pos = camera.center(display)
@@ -194,7 +194,7 @@ redef class App
                        var screen_pos = tank.pos.to_screen(camera)
 
                        var damage = tank.rule.max_health - tank.health
-                       damage = damage.max(0).min(tank.rule.base_images.length)
+                       damage = damage.clamp(0, tank.rule.base_images.length)
 
                        var base_image = tank.rule.base_images[damage]
                        display.blit_rotated(base_image, screen_pos.x, screen_pos.y, tank.heading)
index 6ef6b15..bd60702 100644 (file)
@@ -54,8 +54,7 @@ redef class App
 
                # Save the default config to pretty Json
                var cc = new ClientConfig
-               var json = cc.to_plain_json
-               json = json.replace(",", ",\n")
+               var json = cc.serialize_to_json(plain=true, pretty=true)
                json.write_to_file config_path
 
                return cc
diff --git a/contrib/tinks/src/client/tinks_vr.nit b/contrib/tinks/src/client/tinks_vr.nit
new file mode 100644 (file)
index 0000000..0e2a300
--- /dev/null
@@ -0,0 +1,22 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# VR mode for Android with Google Cardboard
+#
+# This version is not playable and very laggy as it is not modified
+# or optimized in any way for VR.
+# This module is made available as a minimal example of a VR game.
+module tinks_vr
+
+import gamnit::vr
index 49c74b0..9472da6 100644 (file)
@@ -41,7 +41,7 @@ class TGame
                var dt = clock.lapse
                tick += 1
 
-               var turn = new TTurn(self, tick, dt.to_f, dt.millisec)
+               var turn = new TTurn(self, tick, dt, ((dt-dt.floor)*1000.0).to_i)
                return turn
        end
 
index 1289415..9970941 100644 (file)
@@ -290,11 +290,11 @@ class TankDirectionOrder
        do
                # TODO use events
                var direction_heading = direction_heading
-               direction_heading = direction_heading.min(1.0).max(-1.0)
+               direction_heading = direction_heading.clamp(-1.0, 1.0)
                tank.direction_heading = direction_heading*tank.rule.max_direction
 
                var direction_forwards = direction_forwards
-               direction_forwards = direction_forwards.min(1.0).max(-1.0)
+               direction_forwards = direction_forwards.clamp(-1.0, 1.0)
                tank.direction_forwards = direction_forwards*tank.rule.max_speed
        end
 end
index 0fdb084..470b2b0 100644 (file)
@@ -2,25 +2,25 @@ SERVER ?= localhost:8080
 
 all: bin/tnitter_server bin/tnitter
 
-bin/tnitter_server: $(shell ../../bin/nitls -M src/tnitter.nit)
+bin/tnitter_server: $(shell nitls -M src/tnitter.nit)
        mkdir -p bin/
-       ../../bin/nitc -o bin/tnitter_server src/tnitter.nit -D tnitter_interface=$(SERVER)
+       nitc -o bin/tnitter_server src/tnitter.nit -D tnitter_interface=$(SERVER)
 
-bin/tnitter: $(shell ../../bin/nitls -M src/tnitter_app.nit)
+bin/tnitter: $(shell nitls -M src/tnitter_app.nit)
        mkdir -p bin/
-       ../../bin/nitc -o bin/tnitter src/tnitter_app.nit -m linux -D tnitter_server_uri=http://$(SERVER)
+       nitc -o bin/tnitter src/tnitter_app.nit -m linux -D tnitter_server_uri=http://$(SERVER)
 
 # ---
 # Android
 
 android: bin/tnitter.apk
-bin/tnitter.apk: $(shell ../../bin/nitls -M src/tnitter_app_android.nit) android/res/
+bin/tnitter.apk: $(shell nitls -M src/tnitter_app_android.nit) android/res/
        mkdir -p bin/
-       ../../bin/nitc -o bin/tnitter.apk src/tnitter_app_android.nit -D tnitter_server_uri=http://$(SERVER)
+       nitc -o bin/tnitter.apk src/tnitter_app_android.nit -D tnitter_server_uri=http://$(SERVER)
 
-android-release: $(shell ../../bin/nitls -M src/tnitter_app_android.nit) android/res/
+android-release: $(shell nitls -M src/tnitter_app_android.nit) android/res/
        mkdir -p bin/
-       ../../bin/nitc -o bin/tnitter.apk src/tnitter_app_android.nit --release -D tnitter_server_uri=http://tnitter.xymus.net
+       nitc -o bin/tnitter.apk src/tnitter_app_android.nit --release -D tnitter_server_uri=http://tnitter.xymus.net
 
 android/res/: art/icon.svg
        mkdir -p android
@@ -30,13 +30,13 @@ android/res/: art/icon.svg
 # iOS
 
 ios: bin/tnitter.app
-bin/tnitter.app: $(shell ../../bin/nitls -M src/tnitter_app.nit ios) ios/AppIcon.appiconset/Contents.json
+bin/tnitter.app: $(shell nitls -M src/tnitter_app.nit ios) ios/AppIcon.appiconset/Contents.json
        mkdir -p bin/
-       ../../bin/nitc -o bin/tnitter.app src/tnitter_app.nit -m ios -D tnitter_server_uri=http://$(SERVER)
+       nitc -o bin/tnitter.app src/tnitter_app.nit -m ios -D tnitter_server_uri=http://$(SERVER)
 
-ios-release: $(shell ../../bin/nitls -M src/tnitter_app.nit ios) ios/AppIcon.appiconset/Contents.json
+ios-release: $(shell nitls -M src/tnitter_app.nit ios) ios/AppIcon.appiconset/Contents.json
        mkdir -p bin/
-       ../../bin/nitc -o bin/tnitter.app src/tnitter_app.nit -m ios -D tnitter_server_uri=http://tnitter.xymus.net
+       nitc -o bin/tnitter.app src/tnitter_app.nit -m ios -D tnitter_server_uri=http://tnitter.xymus.net
 
 ios/AppIcon.appiconset/Contents.json: art/icon-ios.svg
        mkdir -p ios
index a01d725..4d7720f 100644 (file)
@@ -1,13 +1,13 @@
 [package]
 name=tnitter
-tags=web
+tags=web,mobile
 maintainer=Alexis Laferrière <alexis.laf@xymus.net>
 license=Apache-2.0
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/contrib/tnitter/
 git=https://github.com/nitlang/nit.git
 git.directory=contrib/tnitter/
-homepage=http://nitlanguage.org
+homepage=http://xymus.net/tnitter/
 issues=https://github.com/nitlang/nit/issues
 tryit=http://tnitter.xymus.net/
 apk=http://nitlanguage.org/fdroid/apk/tnitter.apk
index ad41a71..60ee384 100644 (file)
@@ -266,14 +266,14 @@ class TnitterREST
                        db.close
 
                        var response = new HttpResponse(200)
-                       response.body = posts.to_json_string
+                       response.body = posts.serialize_to_json
                        return response
                end
 
                # Format not recognized
                var error = new Error("Bad Request")
                var response = new HttpResponse(400)
-               response.body = error.to_json_string
+               response.body = error.serialize_to_json
                return response
        end
 end
index cdb2945..bc47789 100644 (file)
@@ -46,7 +46,7 @@ redef class DB
                # Everyone gets the same response
                var posts = list_posts(0, 16)
                var response = new HttpResponse(400)
-               response.body = posts.to_json_string
+               response.body = posts.serialize_to_json
 
                for conn in push_connections do
                        # Complete the answer to `conn`
index fccad93..cf1d162 100644 (file)
@@ -42,7 +42,7 @@ redef class App
        redef fun on_create
        do
                # Create the main window
-               window = new TnitterWindow
+               push_window new TnitterWindow
                super
        end
 end
@@ -100,9 +100,9 @@ abstract class AsyncTnitterRequest
 
        private var window: TnitterWindow
 
-       redef fun rest_server_uri do return tnitter_server_uri
+       redef fun uri_root do return tnitter_server_uri
 
-       redef var rest_action
+       redef var uri_tail
 
        # Should this request be delayed by `request_delay_on_error` seconds?
        fun after_error(value: Bool) is autoinit do if value then delay = request_delay_on_error
@@ -120,11 +120,11 @@ end
 class ListPostRequest
        super AsyncTnitterRequest
 
-       redef fun on_load(posts)
+       redef fun on_load(posts, status)
        do
                # Deal with server-side errors
                if posts isa Error then
-                       print_error "Server Error: '{posts.message}' from '{rest_server_uri / rest_action}'"
+                       print_error "Server Error: '{posts.message}' from '{uri}'"
                        return
                end
 
@@ -141,7 +141,7 @@ class ListPostRequest
 
        redef fun on_fail(error)
        do
-               print "Warning: Request {rest_server_uri/rest_action} failed with {error}"
+               print "Warning: Request {uri} failed with {error}"
                window.request_full_list_on_error
        end
 end
index 74eb1b6..dc24681 100644 (file)
@@ -19,8 +19,7 @@ end
 
 import tnitter_app
 
-import android::ui
-import android::http_request
+import android
 import android::portrait
 
 redef class LabelAuthor
diff --git a/contrib/xymus_net/.gitignore b/contrib/xymus_net/.gitignore
new file mode 100644 (file)
index 0000000..b2285d4
--- /dev/null
@@ -0,0 +1 @@
+xymus.net
diff --git a/contrib/xymus_net/Makefile b/contrib/xymus_net/Makefile
new file mode 100644 (file)
index 0000000..f15da53
--- /dev/null
@@ -0,0 +1,9 @@
+all: xymus.net
+
+xymus.net: ../benitlux/src/server/benitlux_restful.nit $(shell nitls -M xymus_net.nit)
+       nitc -o $@ xymus_net.nit
+
+../benitlux/src/server/benitlux_restful.nit:
+       make -C ../benitlux src/server/benitlux_restful.nit
+
+pre-build: ../benitlux/src/server/benitlux_restful.nit
diff --git a/contrib/xymus_net/README.md b/contrib/xymus_net/README.md
new file mode 100644 (file)
index 0000000..5bb2c31
--- /dev/null
@@ -0,0 +1,5 @@
+Web server source and config of xymus.net
+
+This module acts also as an example to merge multiple `nitcorn` projects into one server.
+
+See the server online at http://xymus.net/.
diff --git a/contrib/xymus_net/package.ini b/contrib/xymus_net/package.ini
new file mode 100644 (file)
index 0000000..249a39a
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=xymus_net
+tags=web,example
+maintainer=Alexis Laferrière <alexis.laf@xymus.net>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/contrib/xymus.net/
+git=https://github.com/nitlang/nit.git
+git.directory=contrib/xymus.net/
+homepage=http://xymus.net/
+issues=https://github.com/nitlang/nit/issues
similarity index 96%
rename from lib/nitcorn/examples/src/xymus_net.nit
rename to contrib/xymus_net/xymus_net.nit
index 1233db4..726cb6a 100644 (file)
@@ -1,6 +1,6 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2014-2015 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014-2016 Alexis Laferrière <alexis.laf@xymus.net>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import privileges
 # Use actions defined by contribs
 import tnitter
 import benitlux::benitlux_controller
+import benitlux::benitlux_restful
 import opportunity::opportunity_controller
 import nitiwiki::wiki_edit
 
@@ -173,6 +174,7 @@ var vps_vh = new VirtualHost("vps.xymus.net:80")
 var tnitter_vh = new VirtualHost("tnitter.xymus.net:80")
 var pep8_vh = new VirtualHost("pep8.xymus.net:80")
 var benitlux_vh = new VirtualHost("benitlux.xymus.net:80")
+var benitlux_admin_vh = new VirtualHost("localhost:8081")
 
 var factory = new HttpFactory.and_libevent
 factory.config.virtual_hosts.add default_vh
@@ -180,6 +182,7 @@ factory.config.virtual_hosts.add vps_vh
 factory.config.virtual_hosts.add tnitter_vh
 factory.config.virtual_hosts.add pep8_vh
 factory.config.virtual_hosts.add benitlux_vh
+factory.config.virtual_hosts.add benitlux_admin_vh
 
 # Ports are open, drop to a low-privileged user if we are root
 var user_group = new UserGroup("nitcorn", "nitcorn")
@@ -228,6 +231,8 @@ benitlux_vh.routes.add new Route("/push/", benitlux_push)
 benitlux_vh.routes.add new Route("/static/", shared_file_server)
 benitlux_vh.routes.add new Route(null, benitlux_sub)
 
+benitlux_admin_vh.routes.add new Route(null, new BenitluxAdminAction(benitlux_db))
+
 # Opportunity service
 var opportunity = new OpportunityWelcome
 var opportunity_rest = new OpportunityRESTAction
index c97809c..8265899 100644 (file)
@@ -7,18 +7,22 @@ bin/calculator: $(shell ${NITLS} -M src/calculator.nit linux) ${NITC}
        mkdir -p bin
        ${NITC} -o $@ src/calculator.nit -m linux
 
+bin/scientific_calculator: $(shell ${NITLS} -M src/scientific_calculator.nit linux) ${NITC}
+       mkdir -p bin
+       ${NITC} -o $@ src/scientific_calculator.nit -m linux
+
 # ---
 # Android
 
 android: bin/calculator.apk
 
-bin/calculator.apk: $(shell ${NITLS} -M src/calculator.nit android) ${NITC} android/res/
+bin/calculator.apk: $(shell ${NITLS} -M src/scientific_calculator.nit src/android_calculator.nit) ${NITC} android/res/
        mkdir -p bin
-       ${NITC} -o $@ src/calculator.nit -m ../../lib/android/ui/ -D debug
+       ${NITC} -o $@ src/scientific_calculator.nit -m src/android_calculator.nit -D debug
 
-android-release: $(shell ${NITLS} -M src/calculator.nit android) ${NITC} android/res/
+android-release: $(shell ${NITLS} -M src/scientific_calculator.nit src/android_calculator.nit) ${NITC} android/res/
        mkdir -p bin
-       ${NITC} -o bin/calculator.apk src/calculator.nit -m ../../lib/android/ui/ --release
+       ${NITC} -o bin/calculator.apk src/scientific_calculator.nit -m src/android_calculator.nit --release
 
 android/res/: art/icon.svg ../../contrib/inkscape_tools/bin/svg_to_icons
        mkdir -p android/res
@@ -33,9 +37,9 @@ android-install: bin/calculator.apk
 # ---
 # iOS
 
-bin/calculator.app: $(shell ${NITLS} -M src/calculator.nit ios) ${NITC} ios/AppIcon.appiconset/Contents.json
+bin/calculator.app: $(shell ${NITLS} -M src/scientific_calculator.nit src/ios_calculator.nit) ${NITC} ios/AppIcon.appiconset/Contents.json
        mkdir -p bin
-       ${NITC} -o $@ src/calculator.nit -m ios -D debug
+       ${NITC} -o $@ src/scientific_calculator.nit -m src/ios_calculator.nit -D debug
 
 ios/AppIcon.appiconset/Contents.json: art/icon-ios.svg
        mkdir -p ios
index 0073c26..aa93fed 100644 (file)
@@ -5,6 +5,9 @@ Portable calculator built using _app.nit_
 * `calculator_logic` defines `CalculatorContext` with all the business logic of a calculator.
   It takes as input operations and numbers, and outputs the text to display.
 * `calculator` implements the portable graphical interface using the _app.nit_ framework
+* `scientific_calculator` refines `calculator` to add scientific operations.
+* `android_calculator` refines `calculator` to get a nicer aesthetic on Android.
+* `ios_calculator` refines `calculator` to get a nicer aesthetic on iOS.
 * `calculator_test` test `CalculatorContext` as a black box.
 
 # Compilation
@@ -29,3 +32,11 @@ Portable calculator built using _app.nit_
        make bin/android.app
        ios-sim launch bin/calculator.app
        ~~~
+
+# Screenshots
+
+![Scientific calculator on Linux with GTK+](doc/linux-scientific.png)
+
+![Scientific calculator on Android](doc/android-scientific.png)
+
+![Scientific calculator on iOS](doc/ios-scientific.png)
diff --git a/examples/calculator/doc/android-scientific.png b/examples/calculator/doc/android-scientific.png
new file mode 100644 (file)
index 0000000..f23d675
Binary files /dev/null and b/examples/calculator/doc/android-scientific.png differ
diff --git a/examples/calculator/doc/ios-scientific.png b/examples/calculator/doc/ios-scientific.png
new file mode 100644 (file)
index 0000000..8505c72
Binary files /dev/null and b/examples/calculator/doc/ios-scientific.png differ
diff --git a/examples/calculator/doc/linux-scientific.png b/examples/calculator/doc/linux-scientific.png
new file mode 100644 (file)
index 0000000..58b653e
Binary files /dev/null and b/examples/calculator/doc/linux-scientific.png differ
index 80b32cb..8237327 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name=calculator
-tags=example
+tags=example,mobile
 maintainer=Alexis Laferrière <alexis.laf@xymus.net>
 license=Apache-2.0
 [upstream]
diff --git a/examples/calculator/src/android_calculator.nit b/examples/calculator/src/android_calculator.nit
new file mode 100644 (file)
index 0000000..1d51073
--- /dev/null
@@ -0,0 +1,36 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Aesthetic adaptations for Android
+module android_calculator
+
+import calculator
+import android
+
+redef class Button
+       init do set_android_style(native, (text or else "?").is_int)
+
+       private fun set_android_style(java_button: NativeButton, is_number: Bool)
+       in "Java" `{
+               // Flatten the background and use a different color for digit buttons
+               int color = is_number? android.graphics.Color.DKGRAY: android.graphics.Color.TRANSPARENT;
+               java_button.setBackgroundColor(color);
+
+               // Center the label on both horizontal and vertical axes
+               java_button.setGravity(android.view.Gravity.CENTER);
+
+               // Set lowercase text to correctly display constants like e and π
+               java_button.setAllCaps(false);
+       `}
+end
index 76c3a0a..3b89ec7 100644 (file)
@@ -14,7 +14,7 @@
 
 # Portable calculator UI
 module calculator is
-app_name "app.nit Calc."
+       app_name "app.nit Calc."
        app_version(0, 1, git_revision)
        app_namespace "org.nitlanguage.calculator"
 
@@ -38,7 +38,7 @@ redef class App
                if debug then print "App::on_create"
 
                # Create the main window
-               window = new CalculatorWindow
+               push_window new CalculatorWindow
                super
        end
 end
@@ -51,13 +51,13 @@ class CalculatorWindow
        private var context = new CalculatorContext
 
        # Main window layout
-       private var layout = new VerticalLayout(parent=self)
+       var layout = new VerticalLayout(parent=self)
 
        # Main display, at the top of the screen
        private var display = new TextInput(parent=layout)
 
        # Maps operators as `String` to their `Button`
-       private var buttons = new HashMap[String, Button]
+       var buttons = new HashMap[String, Button]
 
        init
        do
@@ -66,8 +66,8 @@ class CalculatorWindow
                # All the button labels, row by row
                var rows = [["7", "8", "9", "+"],
                            ["4", "5", "6", "-"],
-                           ["1", "2", "3", "*"],
-                           ["0", ".", "C", "/"],
+                           ["1", "2", "3", "×"],
+                           ["0", ".", "C", "÷"],
                            ["="]]
 
                for row in rows do
@@ -75,7 +75,6 @@ class CalculatorWindow
 
                        for op in row do
                                var but = new Button(parent=row_layout, text=op)
-                               but.observers.add self
                                buttons[op] = but
                        end
                end
@@ -94,9 +93,9 @@ class CalculatorWindow
                        else if op.is_numeric then
                                var n = op.to_i
                                context.push_digit n
-                       else
+                       else if op != null then
                                buttons["."].enabled = true
-                               context.push_op op.chars.first
+                               context.push_op op
                        end
 
                        display.text = context.display_text
index e5e492b..840ed84 100644 (file)
@@ -24,33 +24,35 @@ class CalculatorContext
        # Result of the last operation
        var result: nullable Numeric = null
 
-       # Last operation pushed with `push_op`, to be executed on the next push
-       var last_op: nullable Char = null
+       # Last operation pushed with `push_op`
+       var last_op: nullable Text = null
+
+       # Is `last_op` an unary operation or a '='?
+       var last_op_was_unary = false
 
        # Value currently being entered
-       var current: nullable FlatBuffer = null
+       var current: nullable String = null
 
        # Text to display on screen
        fun display_text: String
        do
-               var result = result
-               var last_op = last_op
-               var current = current
-
                var buf = new FlatBuffer
 
-               if result != null and (current == null or last_op != '=') then
-                       if last_op == '=' then buf.append "= "
+               var last_op = last_op
+               var result = result
+               if result != null then
+                       if last_op_was_unary then buf.append "{last_op or else "?"} "
 
                        buf.append result.to_s
                        buf.add ' '
                end
 
-               if last_op != null and last_op != '=' then
-                       buf.add last_op
+               if last_op != null and not last_op_was_unary then
+                       buf.append last_op
                        buf.add ' '
                end
 
+               var current = current
                if current != null then
                        buf.append current.to_s
                        buf.add ' '
@@ -60,40 +62,95 @@ class CalculatorContext
        end
 
        # Push operation `op`, will usually execute the last operation
-       fun push_op(op: Char)
+       fun push_op(op: Text)
        do
+               # Constants
+               # TODO Protect constants to preserve full precision and to forbid appending extra digits
+               if op == "π" then
+                       if last_op_was_unary then clear
+                       current = pi.to_s
+                       return
+               else if op == "e" then
+                       if last_op_was_unary then clear
+                       current = 2.718282.to_s
+                       return
+
+               # Clear screen
+               else if op == "C" then
+                       clear
+                       return
+
+               # Unary -
+               else if op == "-" then
+                       if current == null then
+                               if last_op_was_unary then clear
+                               current = "-"
+                               return
+                       else if current == "-" then
+                               current = null
+                               return
+                       end
+               end
+
+               # For all operators, apply pending operators
                apply_last_op_if_any
-               if op == 'C' then
-                       self.result = null
-                       last_op = null
+
+               var result = self.result or else 0
+
+               last_op = op
+               last_op_was_unary = true
+
+               # Unary operators
+               if op == "√" then
+                       self.result = result.to_f.sqrt
+               else if op == "x²" then
+                       self.result = result.to_f.pow(2.0)
+               else if op == "x!" then
+                       self.result = result.to_i.factorial
+               else if op == "sin" then
+                       self.result = result.to_f.sin
+               else if op == "cos" then
+                       self.result = result.to_f.cos
+               else if op == "tan" then
+                       self.result = result.to_f.tan
+
+               # =
+               else if op == "=" then
+                       current = null
+
+               # Binary operators
                else
-                       last_op = op # store for next push_op
+                       self.result = result # Set as same or 0
+                       last_op_was_unary = false
+                       current = null
                end
+       end
 
-               # prepare next current
-               self.current = null
+       # Clear all state
+       private fun clear
+       do
+               result = null
+               last_op = null
+               current = null
        end
 
        # Push a digit
        fun push_digit(digit: Int)
        do
+               if last_op_was_unary then clear
+
                var current = current
-               if current == null then current = new FlatBuffer
-               current.add digit.to_s.chars.first
+               if current == null then current = ""
+               current += digit.to_s
                self.current = current
-
-               if last_op == '=' then
-                       self.result = null
-                       last_op = null
-               end
        end
 
        # Switch entry mode from integer to decimal
        fun switch_to_decimals
        do
                var current = current
-               if current == null then current = new FlatBuffer.from("0")
-               if not current.chars.has('.') then current.add '.'
+               if current == null then current = "0"
+               if not current.chars.has('.') then current += "."
                self.current = current
        end
 
@@ -110,14 +167,20 @@ class CalculatorContext
                if op == null then
                        result = current.to_n
                else if result != null then
-                       if op == '+' then
+                       if op == "+" then
                                result = result.add(current.to_n)
-                       else if op == '-' then
+                       else if op == "-" then
                                result = result.sub(current.to_n)
-                       else if op == '/' then
+                       else if op == "/" or op == "÷" then
                                result = result.div(current.to_n)
-                       else if op == '*' then
+                       else if op == "*" or op == "×" then
                                result = result.mul(current.to_n)
+                       else if op == "%" then
+                               result = result.to_i % current.to_i
+                       else if op == "xⁿ" then
+                               result = result.to_f.pow(current.to_f)
+                       else if op == "log" then
+                               result = result.to_f.log_base(current.to_f)
                        end
                end
 
index 270854c..610b7a9 100644 (file)
@@ -23,11 +23,11 @@ import calculator_logic
 var context = new CalculatorContext
 context.push_digit( 1 )
 context.push_digit( 2 )
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 3 )
-context.push_op( '*' )
+context.push_op( "*" )
 context.push_digit( 2 )
-context.push_op( '=' )
+context.push_op( "=" )
 var r = context.result
 assert r == 30 else print r or else "-"
 
@@ -36,16 +36,16 @@ context.push_digit( 1 )
 context.push_digit( 4 )
 context.switch_to_decimals
 context.push_digit( 1 )
-context.push_op( '*' )
+context.push_op( "*" )
 context.push_digit( 3 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 42.3 else print r or else "-"
 
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 1 )
 context.push_digit( 1 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 53.3 else print r or else "-"
 
@@ -54,9 +54,9 @@ context.push_digit( 4 )
 context.push_digit( 2 )
 context.switch_to_decimals
 context.push_digit( 3 )
-context.push_op( '/' )
+context.push_op( "/" )
 context.push_digit( 3 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 14.1 else print r or else "-"
 
@@ -68,20 +68,20 @@ context.switch_to_decimals
 context.push_digit( 1 )
 context.push_digit( 2 )
 context.push_digit( 3 )
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 1 )
-context.push_op( '=' )
+context.push_op( "=" )
 r = context.result
 assert r == 51.123 else print r or else "-"
 
-#test 'C' button
+#test "C" button
 context = new CalculatorContext
 context.push_digit( 1 )
 context.push_digit( 0 )
-context.push_op( '+' )
+context.push_op( "+" )
 context.push_digit( 1 )
 context.push_digit( 0 )
-context.push_op( '=' )
-context.push_op( 'C' )
+context.push_op( "=" )
+context.push_op( "C" )
 r = context.result
 assert r == null else print r
diff --git a/examples/calculator/src/ios_calculator.nit b/examples/calculator/src/ios_calculator.nit
new file mode 100644 (file)
index 0000000..23fba4c
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Aesthetic adaptations for iOS
+module ios_calculator
+
+import calculator
+import ios
+
+redef class CalculatorWindow
+       init do title = "app.nit Calculator"
+end
+
+redef class TextInput
+       init do set_ios_style(native)
+
+       private fun set_ios_style(objc_text_field: UITextField)
+       in "ObjC" `{
+               objc_text_field.textAlignment = NSTextAlignmentCenter;
+       `}
+end
diff --git a/examples/calculator/src/scientific_calculator.nit b/examples/calculator/src/scientific_calculator.nit
new file mode 100644 (file)
index 0000000..b050aa3
--- /dev/null
@@ -0,0 +1,39 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Extends the portable calculator app with scientific operations
+module scientific_calculator
+
+import calculator
+
+redef class CalculatorWindow
+       init
+       do
+               # All the button labels, row by row
+               var rows = [["√",  "x²", "xⁿ", "e"  ],
+                           ["log","ln", "%",  "x!" ],
+                           ["π",  "sin","cos","tan"]]
+
+               for row in rows do
+                       var row_layout = new HorizontalLayout(parent=layout)
+
+                       for op in row do
+                               var but = new Button(parent=row_layout, text=op)
+                               buttons[op] = but
+                       end
+               end
+
+               super
+       end
+end
index d4330ba..66cbfb0 100644 (file)
@@ -21,12 +21,9 @@ redef class Int
        # Calculate the self-th element of the fibonacci sequence.
        fun fibonacci: Int
        do
-               if self < 2 then
-                       return 1
-               else
-                       return (self-2).fibonacci + (self-1).fibonacci
-               end
-       end 
+               if self < 2 then return self
+               return (self-2).fibonacci + (self-1).fibonacci
+       end
 end
 
 # Print usage and exit.
index 0733bbf..a5cd314 100644 (file)
@@ -8,69 +8,7 @@
 # See: <http://rosettacode.org/wiki/Perlin_noise>
 module perlin_noise
 
-redef universal Float
-       # Smoothened `self`
-       fun fade: Float do return self*self*self*(self*(self*6.0-15.0)+10.0)
-end
-
-# Improved noise
-class ImprovedNoise
-       # Permutations
-       var p: Array[Int] = [151,160,137,91,90,15,
-               131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
-               190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
-               88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
-               77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
-               102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
-               135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
-               5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
-               223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
-               129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
-               251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
-               49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
-               138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]
-
-       # Noise value in [-1..1] at 3d coordinates `x, y, z`
-       fun noise(x, y, z: Float): Float
-       do
-               var xx = x.to_i & 255
-               var yy = y.to_i & 255
-               var zz = z.to_i & 255
-
-               x -= x.floor
-               y -= y.floor
-               z -= z.floor
-
-               var u = x.fade
-               var v = y.fade
-               var w = z.fade
-
-               var a  = p[xx  ] + yy
-               var aa = p[a   ] + zz
-               var ab = p[a+1 ] + zz
-               var b  = p[xx+1] + yy
-               var ba = p[b   ] + zz
-               var bb = p[b+1 ] + zz
-
-               return w.lerp(v.lerp(u.lerp(grad(p[aa  ], x,     y,     z    ),
-                                           grad(p[ba  ], x-1.0, y,     z    )),
-                                    u.lerp(grad(p[ab  ], x,     y-1.0, z    ),
-                                           grad(p[bb  ], x-1.0, y-1.0, z    ))),
-                      v.lerp(u.lerp(grad(p[aa+1], x,     y,     z-1.0),
-                                           grad(p[ba+1], x-1.0, y,     z-1.0)),
-                                    u.lerp(grad(p[ab+1], x,     y-1.0, z-1.0),
-                                           grad(p[bb+1], x-1.0, y-1.0, z-1.0))))
-       end
-
-       # Value at a corner of the grid
-       fun grad(hash: Int, x, y, z: Float): Float
-       do
-               var h = hash & 15
-               var u = if h < 8 then x else y
-               var v = if h < 4 then y else if h == 12 or h == 14 then x else z
-               return (if h.is_even then u else -u) + (if h & 2 == 0 then v else -v)
-       end
-end
+import noise
 
 var map = new ImprovedNoise
 print map.noise(3.14, 42.0, 7.0).to_precision(17)
index 7b7ce02..cc0cba3 100644 (file)
@@ -188,6 +188,14 @@ class Node
                end
        end
 
+       # Find the closest node accepted by `cond` under `max_cost`
+       fun find_closest(max_cost: Int, context: PathContext, cond: nullable TargetCondition[N]): nullable N
+       do
+               var path = path_to_alts(null, max_cost, context, cond)
+               if path == null then return null
+               return path.nodes.last
+       end
+
        # We customize the serialization process to avoid problems with recursive
        # serialization engines. These engines, such as `JsonSerializer`,
        # are at danger to serialize the graph as a very deep tree.
index 3744988..cbe6ad4 100644 (file)
@@ -17,6 +17,7 @@ package nit.app;
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.view.KeyEvent;
 
 /*
  * Entry point to Nit applications on Android, redirect most calls to Nit
@@ -47,6 +48,11 @@ public class NitActivity extends Activity {
        protected native void nitOnDestroy(int activity);
        protected native void nitOnSaveInstanceState(int activity, Bundle savedInstanceState);
        protected native void nitOnRestoreInstanceState(int activity, Bundle savedInstanceState);
+       protected native boolean nitOnBackPressed(int activity);
+       protected native boolean nitOnKeyDown(int activity, int keyCode, KeyEvent event);
+       protected native boolean nitOnKeyLongPress(int activity, int keyCode, KeyEvent event);
+       protected native boolean nitOnKeyMultiple(int activity, int keyCode, int count, KeyEvent event);
+       protected native boolean nitOnKeyUp(int activity, int keyCode, KeyEvent event);
 
        /*
         * Implementation of OS callbacks
@@ -108,4 +114,34 @@ public class NitActivity extends Activity {
                super.onRestoreInstanceState(savedInstanceState);
                nitOnRestoreInstanceState(nitActivity, savedInstanceState);
        }
+
+       @Override
+       public void onBackPressed() {
+               if (!nitOnBackPressed(nitActivity))
+                       super.onBackPressed();
+       }
+
+       @Override
+       public boolean onKeyDown(int keyCode, KeyEvent event) {
+               return nitOnKeyDown(nitActivity, keyCode, event)
+                       || super.onKeyDown(keyCode, event);
+       }
+
+       @Override
+       public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+               return nitOnKeyLongPress(nitActivity, keyCode, event)
+                       || super.onKeyLongPress(keyCode, event);
+       }
+
+       @Override
+       public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+               return nitOnKeyMultiple(nitActivity, keyCode, count, event)
+                       || super.onKeyMultiple(keyCode, count, event);
+       }
+
+       @Override
+       public boolean onKeyUp(int keyCode, KeyEvent event) {
+               return nitOnKeyUp(nitActivity, keyCode, event)
+                       || super.onKeyUp(keyCode, event);
+       }
 }
index 47e0cec..b6bcf7e 100644 (file)
@@ -64,7 +64,16 @@ integer as argument. They are applied in the Android manifest as
   only be used by low-level implementations of Nit on Android.
   Its usefulness will be extended in the future to customize user applications.
 
-## Project entry points
+## Android implementation
+
+There is two core implementation for Nit apps on Android.
+`android::nit_activity` is used by apps with standard windows and native UI controls.
+`android::game` is used by, well, games and the game frameworks `mnit` and `gamnit`.
+
+Clients don't have to select the core implementation, it is imported by other relevant modules.
+For example, a module importing `app::ui` and `android` will trigger the importation of `android::nit_activity`.
+
+## Lock app orientation
 
 Importing `android::landscape` or `android::portrait` locks the generated
 application in the specified orientation. This can be useful for games and
index b5aa04f..58e296a 100644 (file)
 module android
 
 import platform
-import native_app_glue
 import dalvik
 private import log
-private import assets
-
-redef class App
-       redef fun init_window
-       do
-               super
-               on_create
-               on_restore_state
-               on_start
-       end
-
-       redef fun term_window
-       do
-               super
-               on_stop
-       end
-
-       # Is the application currently paused?
-       var paused = true
-
-       redef fun pause
-       do
-               paused = true
-               on_pause
-               super
-       end
-
-       redef fun resume
-       do
-               paused = false
-               on_resume
-               super
-       end
-
-       redef fun save_state do on_save_state
-
-       redef fun lost_focus
-       do
-               paused = true
-               super
-       end
-
-       redef fun gained_focus
-       do
-               paused = false
-               super
-       end
-
-       redef fun destroy do on_destroy
-end
index 486d140..b1dae81 100644 (file)
@@ -29,11 +29,8 @@ redef class App
        fun native_context: NativeContext do return native_activity
 end
 
-extern class JavaClassLoader in "Java" `{java.lang.ClassLoader`}
-       super JavaObject
-end
-
 redef class Sys
+
        # We cannot create a JVM on Android
        #
        # This method is not reachable on this platform anyway.
@@ -43,7 +40,9 @@ redef class Sys
        redef fun jni_env do return jvm.attach_current_thread
 
        private var class_loader: nullable JavaObject = null
+
        private var class_loader_method: nullable JMethodID = null
+
        redef fun load_jclass(name)
        do
                var class_loader = self.class_loader
diff --git a/lib/android/game.nit b/lib/android/game.nit
new file mode 100644 (file)
index 0000000..2d17054
--- /dev/null
@@ -0,0 +1,71 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android services and implementation of app.nit for gamnit and mnit
+module game
+
+import platform
+import native_app_glue
+import dalvik
+private import log
+private import assets
+
+redef class App
+       redef fun init_window
+       do
+               super
+               on_create
+               on_restore_state
+               on_start
+       end
+
+       redef fun term_window
+       do
+               super
+               on_stop
+       end
+
+       # Is the application currently paused?
+       var paused = true
+
+       redef fun pause
+       do
+               paused = true
+               on_pause
+               super
+       end
+
+       redef fun resume
+       do
+               paused = false
+               on_resume
+               super
+       end
+
+       redef fun save_state do on_save_state
+
+       redef fun lost_focus
+       do
+               paused = true
+               super
+       end
+
+       redef fun gained_focus
+       do
+               paused = false
+               super
+       end
+
+       redef fun destroy do on_destroy
+end
index 9597eea..5ee9d87 100644 (file)
@@ -18,7 +18,7 @@
 module input_events
 
 import mnit::input
-import android
+import android::game
 
 in "C header" `{
        #include <android/log.h>
@@ -70,6 +70,8 @@ private extern class NativeAndroidMotionEvent `{AInputEvent *`}
        `}
 
        fun action: AMotionEventAction `{ return AMotionEvent_getAction(self); `}
+
+       fun native_down_time: Int `{ return AMotionEvent_getDownTime(self); `}
 end
 
 private extern class AMotionEventAction `{ int32_t `}
@@ -149,6 +151,11 @@ class AndroidMotionEvent
                        return null
                end
        end
+
+       # Time when the user originally pressed down to start a stream of position events
+       #
+       # The return value is in the `java.lang.System.nanoTime()` time base.
+       fun down_time: Int do return native.native_down_time
 end
 
 # A pointer event
diff --git a/lib/android/key_event.nit b/lib/android/key_event.nit
new file mode 100644 (file)
index 0000000..b5b39e4
--- /dev/null
@@ -0,0 +1,192 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module key_event
+
+import platform
+
+# Java class: android.view.KeyEvent
+extern class NativeKeyEvent in "Java" `{ android.view.KeyEvent `}
+       super JavaObject
+
+       # Java implementation: boolean android.view.KeyEvent.isSystem()
+       fun is_system: Bool in "Java" `{
+               return self.isSystem();
+       `}
+
+       # Java implementation:  android.view.KeyEvent.setSource(int)
+       fun set_source(arg0: Int) in "Java" `{
+               self.setSource((int)arg0);
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getMetaState()
+       fun meta_state: Int in "Java" `{
+               return self.getMetaState();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getModifiers()
+       fun modifiers: Int in "Java" `{
+               return self.getModifiers();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getFlags()
+       fun flags: Int in "Java" `{
+               return self.getFlags();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.hasNoModifiers()
+       fun has_no_modifiers: Bool in "Java" `{
+               return self.hasNoModifiers();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.hasModifiers(int)
+       fun has_modifiers(arg0: Int): Bool in "Java" `{
+               return self.hasModifiers((int)arg0);
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isAltPressed()
+       fun is_alt_pressed: Bool in "Java" `{
+               return self.isAltPressed();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isShiftPressed()
+       fun is_shift_pressed: Bool in "Java" `{
+               return self.isShiftPressed();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isSymPressed()
+       fun is_sym_pressed: Bool in "Java" `{
+               return self.isSymPressed();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isCtrlPressed()
+       fun is_ctrl_pressed: Bool in "Java" `{
+               return self.isCtrlPressed();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isMetaPressed()
+       fun is_meta_pressed: Bool in "Java" `{
+               return self.isMetaPressed();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isFunctionPressed()
+       fun is_function_pressed: Bool in "Java" `{
+               return self.isFunctionPressed();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isCapsLockOn()
+       fun is_caps_lock_on: Bool in "Java" `{
+               return self.isCapsLockOn();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isNumLockOn()
+       fun is_num_lock_on: Bool in "Java" `{
+               return self.isNumLockOn();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isScrollLockOn()
+       fun is_scroll_lock_on: Bool in "Java" `{
+               return self.isScrollLockOn();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getAction()
+       fun action: Int in "Java" `{
+               return self.getAction();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isCanceled()
+       fun is_canceled: Bool in "Java" `{
+               return self.isCanceled();
+       `}
+
+       # Java implementation:  android.view.KeyEvent.startTracking()
+       fun start_tracking in "Java" `{
+               self.startTracking();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isTracking()
+       fun is_tracking: Bool in "Java" `{
+               return self.isTracking();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isLongPress()
+       fun is_long_press: Bool in "Java" `{
+               return self.isLongPress();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getKeyCode()
+       fun key_code: Int in "Java" `{
+               return self.getKeyCode();
+       `}
+
+       # Java implementation: java.lang.String android.view.KeyEvent.getCharacters()
+       fun characters: JavaString in "Java" `{
+               return self.getCharacters();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getScanCode()
+       fun scan_code: Int in "Java" `{
+               return self.getScanCode();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getRepeatCount()
+       fun repeat_count: Int in "Java" `{
+               return self.getRepeatCount();
+       `}
+
+       # Java implementation: long android.view.KeyEvent.getDownTime()
+       fun down_time: Int in "Java" `{
+               return self.getDownTime();
+       `}
+
+       # Java implementation: long android.view.KeyEvent.getEventTime()
+       fun event_time: Int in "Java" `{
+               return self.getEventTime();
+       `}
+
+       # Java implementation: char android.view.KeyEvent.getDisplayLabel()
+       fun display_label: Char in "Java" `{
+               return self.getDisplayLabel();
+       `}
+
+       # Java implementation: int android.view.KeyEvent.getUnicodeChar()
+       fun unicode_char: Int in "Java" `{
+               return self.getUnicodeChar();
+       `}
+
+       # Java implementation: char android.view.KeyEvent.getNumber()
+       fun number: Char in "Java" `{
+               return self.getNumber();
+       `}
+
+       # Java implementation: boolean android.view.KeyEvent.isPrintingKey()
+       fun is_printing_key: Bool in "Java" `{
+               return self.isPrintingKey();
+       `}
+
+       redef fun new_global_ref import sys, Sys.jni_env `{
+               Sys sys = NativeKeyEvent_sys(self);
+               JNIEnv *env = Sys_jni_env(sys);
+               return (*env)->NewGlobalRef(env, self);
+       `}
+
+       redef fun pop_from_local_frame_with_env(jni_env) `{
+               return (*jni_env)->PopLocalFrame(jni_env, self);
+       `}
+end
+
+# Java getter: android.view.KeyEvent.KEYCODE_BACK
+fun android_view_key_event_keycode_back: Int in "Java" `{
+       return android.view.KeyEvent.KEYCODE_BACK;
+`}
index be79eb5..c313efb 100644 (file)
@@ -20,4 +20,4 @@ module landscape is
        android_manifest_activity """android:screenOrientation="sensorLandscape" """
 end
 
-import platform
+import android
index 87d9d22..1668147 100644 (file)
@@ -43,6 +43,7 @@ end
 import platform
 import log
 import activities
+import key_event
 import bundle
 import dalvik
 
@@ -133,6 +134,36 @@ in "C body" `{
        {
                Activity_on_restore_instance_state((Activity)nit_activity, saved_state);
        }
+
+       JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnBackPressed
+         (JNIEnv *env, jobject java_activity, jint nit_activity)
+       {
+               return (jboolean)Activity_on_back_pressed((Activity)nit_activity);
+       }
+
+       JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyDown
+         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jobject event)
+       {
+               return (jboolean)Activity_on_key_down((Activity)nit_activity, keyCode, event);
+       }
+
+       JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyLongPress
+         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jobject event)
+       {
+               return (jboolean)Activity_on_key_long_press((Activity)nit_activity, keyCode, event);
+       }
+
+       JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyMultiple
+         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jint count, jobject event)
+       {
+               return (jboolean)Activity_on_key_multiple((Activity)nit_activity, keyCode, count, event);
+       }
+
+       JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyUp
+         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jobject event)
+       {
+               return (jboolean)Activity_on_key_up((Activity)nit_activity, keyCode, event);
+       }
 `}
 
 # Wrapper to our Java `NitActivity`
@@ -158,7 +189,10 @@ redef class App
        Activity.on_create, Activity.on_destroy,
        Activity.on_start, Activity.on_restart, Activity.on_stop,
        Activity.on_pause, Activity.on_resume,
-       Activity.on_save_instance_state, Activity.on_restore_instance_state `{
+       Activity.on_save_instance_state, Activity.on_restore_instance_state,
+       Activity.on_back_pressed,
+       Activity.on_key_down, Activity.on_key_long_press,
+       Activity.on_key_multiple, Activity.on_key_up `{
                App_incr_ref(self);
                global_app = self;
        `}
@@ -251,6 +285,31 @@ class Activity
 
        # Notification from Android, the current device configuration has changed
        fun on_configuration_changed do end
+
+       # The back key has been pressed
+       #
+       # Return `true` if the event has been handled.
+       fun on_back_pressed: Bool do return false
+
+       # A key has been pressed
+       #
+       # Return `true` if the event has been handled.
+       fun on_key_down(key_code: Int, event: NativeKeyEvent): Bool do return false
+
+       # A key has been long pressed
+       #
+       # Return `true` if the event has been handled.
+       fun on_key_long_press(key_code: Int, event: NativeKeyEvent): Bool do return false
+
+       # Multiple down/up pairs of the same key have occurred in a row
+       #
+       # Return `true` if the event has been handled.
+       fun on_key_multiple(key_code, count: Int, event: NativeKeyEvent): Bool do return false
+
+       # A key has been released
+       #
+       # Return `true` if the event has been handled.
+       fun on_key_up(key_code: Int, event: NativeKeyEvent): Bool do return false
 end
 
 # Set up global data in C and leave it to Android to callback Java, which we relay to Nit
index e8f5720..0759c40 100644 (file)
@@ -17,4 +17,4 @@ module portrait is android_manifest_activity """
                android:screenOrientation="portrait"
 """
 
-import platform
+import android
index b969389..0681e4a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This module is used to manipulate android sensors
-# The sensor support is implemented in android_app module, so the user can enable the type of sensor he wants to use.
-# There is an example of how you can use the android sensors in nit/examples/mnit_ballz :
+# Access Android sensors
+#
+# Sensors are to be enabled when `App` is created.
+# The following example enables all sensors.
+# The events (`SensorEvent`, `ASensorAccelerometer`, `ASensorMagneticField`...)
+# are sent to the `input` callback of `App`
 #
 # ~~~~nitish
-# #FIXME rewrite the example
 # redef class App
-#      sensors_support_enabled = true
-#      accelerometer.enabled = true
-#      accelerometer.eventrate = 10000
-#      magnetic_field.enabled = true
-#      gyroscope.enabled = true
-#      light.enabled = true
-#      proximity.enabled = true
+#     init
+#     do
+#         sensors_support_enabled = true
+#         accelerometer.enabled = true
+#         accelerometer.eventrate = 10000
+#         magnetic_field.enabled = true
+#         gyroscope.enabled = true
+#         light.enabled = true
+#         proximity.enabled = true
+#     end
 # end
 # ~~~~
-#
-# In this example, we enable the sensor support, then enable all types of sensors supported by the API, directly with `App` attributes
-# As a result, you get all type of SensorEvent (ASensorAccelerometer, ASensorMagneticField ...) in the `input` callback of `App`
 module sensors
 
-import android
+import game
 import mnit
 
 in "C header" `{
index e4dd670..b1b45f2 100644 (file)
@@ -175,8 +175,6 @@ end
 extern class NativeEditText in "Java" `{ android.widget.EditText `}
        super NativeTextView
 
-       redef type SELF: NativeEditText
-
        new (context: NativeActivity) in "Java" `{ return new android.widget.EditText(context); `}
 
        fun width=(val: Int) in "Java" `{ self.setWidth((int)val); `}
@@ -193,8 +191,6 @@ end
 extern class NativeButton in "Java" `{ android.widget.Button `}
        super NativeTextView
 
-       redef type SELF: NativeButton
-
        redef fun new_global_ref import sys, Sys.jni_env `{
                Sys sys = NativeButton_sys(self);
                JNIEnv *env = Sys_jni_env(sys);
index 65535e8..7e14b19 100644 (file)
 # limitations under the License.
 
 # Views and services to use the Android native user interface
-module ui
+module ui is
+       # `adjustPan` allows to use EditText in a ListLayout
+       android_manifest_activity """android:windowSoftInputMode="adjustPan""""
+end
 
 # Implementation note:
 #
@@ -78,6 +81,19 @@ redef class App
        end
 end
 
+redef class Activity
+       redef fun on_back_pressed
+       do
+               var window = app.window
+               if window.enable_back_button then
+                       window.on_back_button
+                       return true
+               end
+
+               return false
+       end
+end
+
 # On Android, a window is implemented with the fragment `native`
 redef class Window
        redef var native = (new Android_app_Fragment(self)).new_global_ref
@@ -228,7 +244,7 @@ redef class TextView
                else // if (align > 0.5d)
                        g = android.view.Gravity.RIGHT;
 
-               view.setGravity(g);
+               view.setGravity(g | android.view.Gravity.CENTER_VERTICAL);
        `}
 end
 
@@ -240,9 +256,26 @@ end
 redef class CheckBox
        redef type NATIVE: Android_widget_CompoundButton
        redef var native do return (new Android_widget_CheckBox(app.native_activity)).new_global_ref
+       init do set_callback_on_toggle(native)
 
        redef fun is_checked do return native.is_checked
        redef fun is_checked=(value) do native.set_checked(value)
+
+       private fun on_toggle do notify_observers new ToggleEvent(self)
+
+       private fun set_callback_on_toggle(view: NATIVE)
+       import on_toggle in "Java" `{
+               final int final_sender_object = self;
+               CheckBox_incr_ref(final_sender_object);
+
+               view.setOnCheckedChangeListener(
+                       new android.widget.CompoundButton.OnCheckedChangeListener() {
+                               @Override
+                               public void onCheckedChanged(android.widget.CompoundButton buttonView, boolean isChecked) {
+                                       CheckBox_on_toggle(final_sender_object);
+                               }
+                       });
+       `}
 end
 
 redef class TextInput
@@ -315,3 +348,18 @@ redef class Android_app_Fragment
                };
        `}
 end
+
+redef class Text
+       redef fun open_in_browser
+       do to_java_string.native_open_in_browser(app.native_activity)
+end
+
+redef class JavaString
+       private fun native_open_in_browser(context: NativeContext)
+       in "Java" `{
+               android.content.Intent intent = new android.content.Intent(
+                       android.content.Intent.ACTION_VIEW,
+                       android.net.Uri.parse(self));
+               context.startActivity(intent);
+       `}
+end
index 1cc68bb..19ef541 100644 (file)
@@ -146,13 +146,13 @@ extern class NativeScanResult in "Java" `{ android.net.wifi.ScanResult `}
 end
 
 # Java list of `NativeScanResult`
-extern class NativeListOfScanResult in "Java" `{ java.util.List `}
+extern class NativeListOfScanResult in "Java" `{ java.util.List<android.net.wifi.ScanResult> `}
 
        # Number of elements in this list
        fun length: Int in "Java" `{ return self.size();`}
 
        # Element at `index`
        fun [](index: Int): NativeScanResult in "Java" `{
-               return ((java.util.List<android.net.wifi.ScanResult>)self).get((int)index);
+               return self.get((int)index);
        `}
 end
index c5caa4b..0e0187b 100644 (file)
@@ -5,13 +5,14 @@ The framework provides services to manage common needs of modern mobile applicat
 * Life-cycle
 * User interface
 * Persistence
+* Async HTTP requests
 * Package metadata
 * Compilation and packaging
 
 The features offered by _app.nit_ are common to all platforms, but
 may not be available on all devices.
 
-## Application Life-Cycle
+# Application Life-Cycle
 
 The _app.nit_ application life-cycle is compatible with all target platforms.
 It relies on the following sequence of events, represented here by their callback method name:
@@ -44,7 +45,7 @@ The `App` instance is the first to be notified of these events.
 Other UI elements, from the `ui` submodule, are notified of the same events using a simple depth first visit.
 So all UI elements can react separately to live-cycle events.
 
-## User Interface
+# User Interface
 
 The `app::ui` module defines an abstract API to build a portable graphical application.
 The API is composed of interactive `Control`s, visible `View`s and an active `Window`.
@@ -65,21 +66,20 @@ So there is two ways  to customize the behavior on a given event:
 
 * Add an observer to a `Button` instance, and implement `on_event` in the observer.
 
-### Usage Example
+## Usage Example
 
-The calculator example (at `../../examples/calculator/src/calculator.nit`) is a concrete,
-simple and complete use of the _app.nit_ portable UI.
+The example at `examples/ui_example.nit` shows off most features of `app::ui` in a minimal program.
+You can also take a look at the calculator (`../../examples/calculator/src/calculator.nit`) which is a concrete usage example.
 
-### Platform-specific UI
+## Platform-specific UI
 
 You can go beyond the portable UI API of _app.nit_ by using the natives services of a platform.
 
 The suggested approach is to use platform specific modules to customize the application on a precise platform.
-This module redefine `Window::on_start` to call the native language of the platform and setup a native UI.
+See the calculator example for an adaptation of the UI on Android,
+the interesting module is in this repository at ../../examples/calculator/src/android_calculator.nit
 
-_TODO complete description and add concrete examples_
-
-## Persistent State with data\_store
+# Persistent State with data\_store
 
 _app.nit_ offers the submodule `app::data_store` to easily save the application state and user preferences.
 The service is accessible by the method `App::data_store`. The `DataStore` itself defines 2 methods:
@@ -90,7 +90,7 @@ Pass `null` to clear the value associated to a key.
 * `DataStore::[]` returns the object associated to a `String` key.
 It returns `null` if nothing is associated to the key.
 
-### Usage Example
+## Usage Example
 
 ~~~
 import app::data_store
@@ -123,7 +123,15 @@ redef class App
 end
 ~~~
 
-## Metadata annotations
+# Async HTTP request
+
+The module `app::http_request` provides services to execute asynchronous HTTP request.
+The class `AsyncHttpRequest` hides the complex parallel logic and
+lets the user implement methods acting only on the UI thread.
+See the documentation of `AsyncHttpRequest` for more information and
+the full example at `examples/http_request_example.nit`.
+
+# Metadata annotations
 
 The _app.nit_ framework defines three annotations to customize the application package.
 
@@ -142,7 +150,7 @@ The _app.nit_ framework defines three annotations to customize the application p
   The special function `git_revision` will use the prefix of the hash of the latest git commit.
   By default, the version is 0.1.
 
-### Usage Example
+## Usage Example
 
 ~~~
 module my_module is
@@ -152,33 +160,38 @@ module my_module is
 end
 ~~~
 
-## Compiling and Packaging an Application
+# Compiling and Packaging an Application
 
 The Nit compiler detects the target platform from the importations and generates the appropriate application format and package.
 
 Applications using only the portable services of _app.nit_ require some special care at compilation.
 Such an application, let's say `calculator.nit`, does not depend on a specific platform and use the portable UI.
-The target platform must be specifed to the compiler for it to produce the correct application package.
+The target platform must be specified to the compiler for it to produce the correct application package.
 There is two main ways to achieve this goal:
 
-* The the mixin option (`-m path`) loads an additionnal module before compiling.
+* The mixin option (`-m module`) imports an additional module before compiling.
   It can be used to load platform specific implementations of the _app.nit_ portable UI.
 
   ~~~
   # GNU/Linux version, using GTK
-  nitc calculator.nit -m NIT_DIR/lib/linux/ui.nit
+  nitc calculator.nit -m linux
 
   # Android version
-  nitc calculator.nit -m NIT_DIR/lib/android/ui/
+  nitc calculator.nit -m android
+
+  # iOS version
+  nitc calculator.nit -m ios
   ~~~
 
 * A common alternative for larger projects is to use platform specific modules.
-  Continuing with the `calculator.nit` example, it can be accompagnied by the module `calculator_linux.nit`.
-  This module imports both `calculator` and `linux::ui`, and can also use other GNU/Linux specific code.
+  Continuing with the calculator example, it is adapted for Android by the module `android_calculator.nit`.
+  This module imports both `calculator` and `android`, it can then use Android specific code.
 
   ~~~
-  module calculator_linux
+  module android_calculator
 
   import calculator
-  import linux::ui
+  import android
+
+  # ...
   ~~~
index 42d90ae..7f22c78 100644 (file)
@@ -27,7 +27,6 @@ import app_base
 import core::error
 
 # Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
 import linux::audio is conditional(linux)
 import android::audio is conditional(android)
 
index 4db4b22..ebebe83 100644 (file)
@@ -23,7 +23,6 @@ import app_base
 import serialization
 
 # Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
 import linux::data_store is conditional(linux)
 import android::data_store is conditional(android)
 import ios::data_store is conditional(ios)
diff --git a/lib/app/examples/.gitignore b/lib/app/examples/.gitignore
new file mode 100644 (file)
index 0000000..f594638
--- /dev/null
@@ -0,0 +1,4 @@
+http_request_example
+ui_example
+*.apk
+*.app
diff --git a/lib/app/examples/Makefile b/lib/app/examples/Makefile
new file mode 100644 (file)
index 0000000..2dfc094
--- /dev/null
@@ -0,0 +1,26 @@
+all: http_request_example ui_example
+
+android: http_request_example.apk ui_example.apk
+
+ios: http_request_example.app ui_example.app
+
+http_request_example: $(shell nitls -M http_request_example.nit linux)
+       nitc http_request_example.nit -m linux
+
+http_request_example.apk: $(shell nitls -M http_request_example.nit android)
+       nitc http_request_example.nit -m android
+
+http_request_example.app: $(shell nitls -M http_request_example.nit ios)
+       nitc http_request_example.nit -m ios
+
+ui_example: $(shell nitls -M ui_example.nit linux)
+       nitc ui_example.nit -m linux
+
+ui_example.apk: $(shell nitls -M ui_example.nit android)
+       nitc ui_example.nit -m android
+
+ui_example.app: $(shell nitls -M ui_example.nit ios)
+       nitc ui_example.nit -m ios
+
+clean:
+       rm -rf http_request_example http_request_example.apk http_request_example.app ui_example ui_example.apk ui_example.app
diff --git a/lib/app/examples/http_request_example.nit b/lib/app/examples/http_request_example.nit
new file mode 100644 (file)
index 0000000..daa5314
--- /dev/null
@@ -0,0 +1,91 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example for the `app::http_request` main service `AsyncHttpRequest`
+module http_request_example is
+       app_name "app.nit HTTP"
+       app_namespace "org.nitlanguage.http_example"
+       android_api_target 15
+end
+
+import app::ui
+import app::http_request
+import android::aware # for android_api_target
+
+# Simple asynchronous HTTP request to http://example.com/ displaying feedback to the window
+class MyHttpRequest
+       super AsyncHttpRequest
+
+       # Back reference to the window to show feedback to the user
+       var win: HttpRequestClientWindow
+
+       # ---
+       # Config the request
+
+       redef fun uri do return "http://example.com/"
+       redef fun deserialize_json do return false
+
+       # ---
+       # Customize callbacks
+
+       redef fun before
+       do
+               win.label_response.text = "Sending request..."
+
+               # Disable button to prevent double requests
+               win.button_request.enabled = false
+       end
+
+       redef fun on_load(data, status)
+       do win.label_response.text = "Received response code {status} with {data.as(Text).byte_length} bytes"
+
+       redef fun on_fail(error)
+       do win.label_response.text = "Connection error: {error}"
+
+       redef fun after do win.button_request.enabled = true
+end
+
+# Simpe window with a label and a button
+class HttpRequestClientWindow
+       super Window
+
+       # Root layout
+       var layout = new ListLayout(parent=self)
+
+       # Button to send request
+       var button_request = new Button(parent=layout, text="Press to send HTTP request")
+
+       # Label displaying feedback to user
+       var label_response = new Label(parent=layout, text="No response yet.")
+
+       init do button_request.observers.add self
+
+       redef fun on_event(event)
+       do
+               if event isa ButtonPressEvent and event.sender == button_request then
+                       # Prepare and send request
+                       var request = new MyHttpRequest(self)
+                       request.start
+               end
+       end
+end
+
+redef class App
+       redef fun on_create
+       do
+               # Create the main window
+               push_window new HttpRequestClientWindow
+               super
+       end
+end
diff --git a/lib/app/examples/ui_example.nit b/lib/app/examples/ui_example.nit
new file mode 100644 (file)
index 0000000..43628af
--- /dev/null
@@ -0,0 +1,92 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# User interface example using `app::ui`
+module ui_example is
+       app_name "app.nit UI"
+       app_namespace "org.nitlanguage.ui_example"
+       android_api_target 15
+end
+
+import app::ui
+import app::data_store
+import android::aware # for android_api_target
+
+# Window showing off some the available controls
+class UiExampleWindow
+       super Window
+
+       # Root layout
+       var layout = new ListLayout(parent=self)
+
+       # Some label
+       var some_label = new Label(parent=layout, text="This Window uses a ListLayout.")
+
+       # A checkbox
+       var checkbox = new CheckBox(parent=layout, text="A CheckBox")
+
+       # Horizontal organization
+       var h_layout = new HorizontalLayout(parent=layout)
+
+       # Description for the `user_input`
+       var user_input_label = new Label(parent=h_layout, text="Input some text:", align=0.5)
+
+       # Field for the user to enter data
+       var user_input = new TextInput(parent=h_layout, text="Default text")
+
+       # Button to open a new window with a ListLayout
+       var button_window = new Button(parent=layout, text="Open a new window")
+
+       # URL to open
+       var example_url = "http://nitlanguage.org/"
+
+       # Button to open the browser
+       var button_browser = new Button(parent=layout, text="Open {example_url}")
+
+       redef fun on_event(event)
+       do
+               if event isa ButtonPressEvent then
+                       if event.sender == button_browser then
+                               example_url.open_in_browser
+                       else if event.sender == button_window then
+                               app.push_window new SecondWindow
+                       end
+               else if event isa ToggleEvent then
+                       if event.sender == checkbox then checkbox.text = if checkbox.is_checked then "Checked" else "Unchecked"
+               end
+       end
+end
+
+# Another window with a small `VerticalLayout`
+class SecondWindow
+       super Window
+
+       # Root layout
+       var layout = new VerticalLayout(parent=self)
+
+       # Some label
+       var a_label = new Label(parent=layout, text="This window uses a VerticalLayout.")
+
+       # Another label
+       var another_label = new Label(parent=layout, text="Close it by tapping the back button.")
+end
+
+redef class App
+       redef fun on_create
+       do
+               # Create the main window
+               push_window new UiExampleWindow
+               super
+       end
+end
index 9a5feca..3fc48c6 100644 (file)
@@ -28,22 +28,35 @@ redef class App
        fun run_on_ui_thread(task: Task) is abstract
 end
 
-# Thread executing an HTTP request and deserializing JSON asynchronously
+# Thread executing an HTTP request asynchronously
 #
-# This class defines four methods acting on the main/UI thread,
-# they should be implemented as needed:
-# * before
-# * on_load
-# * on_fail
-# * after
-class AsyncHttpRequest
+# The request is sent to `uri`.
+# Either `uri`, or `uri_root` and `uri_tail`, must be set in subclasses.
+#
+# If `deserialize_json`, the default behavior, the response is deserialized from JSON
+#
+# If `delay > 0.0`, sending the request is delayed by the given `delay` in seconds.
+# It can be used to delay resending a request on error.
+#
+# Four callback methods act on the main/UI thread,
+# they should be implemented as needed in subclasses:
+# * `before`
+# * `on_load`
+# * `on_fail`
+# * `after`
+#
+# See full example at `examples/http_request_example.nit`.
+abstract class AsyncHttpRequest
        super Thread
 
-       # Root URI of the remote server
-       fun rest_server_uri: String is abstract
+       # URI target of this request, by default it is composed of `uri_root / uri_tail`
+       fun uri: Text do return uri_root / uri_tail
+
+       # Root URI of the remote server, usually the scheme and remote host
+       fun uri_root: String is abstract
 
-       # Action, or path, for this request within the `rest_server_uri`
-       fun rest_action: String is abstract
+       # Right part of the URI, after `uri_root`, often the resource path and the query
+       fun uri_tail: String do return ""
 
        # Should the response content be deserialized from JSON?
        var deserialize_json = true is writable
@@ -62,7 +75,7 @@ class AsyncHttpRequest
                var delay = delay
                if delay > 0.0 then delay.sleep
 
-               var uri = rest_server_uri / rest_action
+               var uri = uri
 
                # Execute REST request
                var rep = uri.http_get
@@ -71,19 +84,23 @@ class AsyncHttpRequest
                        return null
                end
 
-               if not deserialize_json then
-                       app.run_on_ui_thread new RestRunnableOnLoad(self, rep)
+               if deserialize_json then
+                       # Deserialize
+                       var deserializer = new JsonDeserializer(rep.value)
+                       var res = deserializer.deserialize
+                       if deserializer.errors.not_empty then
+                               app.run_on_ui_thread new RestRunnableOnFail(self, deserializer.errors.first)
+                       else
+                               app.run_on_ui_thread new RestRunnableOnLoad(self, res, rep.code)
+                       end
+               else
+                       # Return text data
+                       app.run_on_ui_thread new RestRunnableOnLoad(self, rep.value, rep.code)
                        return null
                end
 
-               # Deserialize
-               var deserializer = new JsonDeserializer(rep.value)
-               var res = deserializer.deserialize
-               if deserializer.errors.not_empty then
-                       app.run_on_ui_thread new RestRunnableOnFail(self, deserializer.errors.first)
-               end
+               app.run_on_ui_thread new RestRunnableJoin(self)
 
-               app.run_on_ui_thread new RestRunnableOnLoad(self, res)
                return null
        end
 
@@ -96,15 +113,32 @@ class AsyncHttpRequest
        # In this case, `result` may be any deserialized object.
        #
        # Otherwise, if `not deserialize_json`, `result` contains the content of the response as a `String`.
-       fun on_load(result: nullable Object) do end
+       fun on_load(result: nullable Object, http_status_code: Int) do end
 
        # Invoked when the HTTP request has failed and no data was received or deserialization failed
-       fun on_fail(error: Error) do print_error "REST request '{rest_action}' failed with: {error}"
+       fun on_fail(error: Error) do print_error "HTTP request '{uri}' failed with: {error}"
 
        # Complete this request whether it was a success or not
        fun after do end
 end
 
+# Minimal implementation of `AsyncHttpRequest` where `uri` is an attribute
+#
+# Prints on communication errors and when the server returns an HTTP status code not in the 200s.
+#
+# ~~~
+# var request = new SimpleAsyncHttpRequest("http://example.com")
+# request.start
+# ~~~
+class SimpleAsyncHttpRequest
+       super AsyncHttpRequest
+
+       redef var uri
+
+       redef fun on_load(data, status) do if status < 200 or status >= 299
+       then print_error "HTTP request '{uri}' received HTTP status code: {status}"
+end
+
 redef class Text
        # Execute an HTTP GET request synchronously at the URI `self`
        #
@@ -132,6 +166,7 @@ class HttpRequestResult
        var maybe_code: nullable Int
 
        # The status code
+       #
        # Require: `not is_error`
        fun code: Int do return maybe_code.as(not null)
 end
@@ -148,9 +183,11 @@ private class RestRunnableOnLoad
 
        var res: nullable Object
 
+       var code: Int
+
        redef fun main
        do
-               sender_thread.on_load(res)
+               sender_thread.on_load(res, code)
                sender_thread.after
        end
 end
@@ -166,3 +203,9 @@ private class RestRunnableOnFail
                sender_thread.after
        end
 end
+
+private class RestRunnableJoin
+       super HttpRequestTask
+
+       redef fun main do sender_thread.join
+end
index d5c48c1..88a75fb 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name=app
-tags=lib
+tags=lib,mobile
 maintainer=Alexis Laferrière <alexis.laf@xymus.net>
 license=Apache-2.0
 [upstream]
index 4efbb87..5f66ab4 100644 (file)
@@ -18,9 +18,8 @@ module ui
 import app_base
 
 # Platform variations
-# TODO: move on the platform once qualified names are understand in the condition
 import linux::ui is conditional(linux)
-import android::ui is conditional(android) # FIXME it should be conditional to `android::platform`
+import android::ui is conditional(android)
 import ios::ui is conditional(ios)
 
 redef class App
@@ -28,8 +27,32 @@ redef class App
 
        # The current `Window` of this activity
        #
-       # This attribute must be set by refinements of `App`.
-       var window: Window is writable
+       # This attribute is set by `push_window`.
+       var window: Window is noinit
+
+       # Make visible and push `window` on the top of `pop_window`
+       #
+       # This method must be called at least once within `App::on_create`.
+       # It can be called at any times while the app is active.
+       fun push_window(window: Window)
+       do
+               window_stack.add window
+               self.window = window
+       end
+
+       # Pop the current `window` from the stack and show the previous one
+       #
+       # Require: `window_stack.not_empty`
+       fun pop_window
+       do
+               assert window_stack.not_empty
+               window_stack.pop
+               window = window_stack.last
+               window.on_resume
+       end
+
+       # Stack of active windows
+       var window_stack = new Array[Window]
 
        redef fun on_create do window.on_create
 
@@ -84,11 +107,17 @@ class Control
 
        # Direct parent `Control` in the control tree
        #
+       # The parents (direct and indirect) receive all events from `self`,
+       # like the `observers`.
+       #
        # If `null` then `self` is at the root of the tree, or not yet attached.
        var parent: nullable CompositeControl = null is private writable(set_parent)
 
        # Direct parent `Control` in the control tree
        #
+       # The parents (direct and indirect) receive all events from `self`,
+       # like the `observers`.
+       #
        # Setting `parent` calls `remove` on the old parent and `add` on the new one.
        fun parent=(parent: nullable CompositeControl)
        is autoinit     do
@@ -101,12 +130,25 @@ class Control
 
                set_parent parent
        end
+
+       # Also notify the parents (both direct and indirect)
+       redef fun notify_observers(event)
+       do
+               super
+
+               var p = parent
+               while p != null do
+                       p.on_event event
+                       p = p.parent
+               end
+       end
 end
 
 # A `Control` grouping other controls
 class CompositeControl
        super Control
 
+       # Child controls composing this control
        protected var items = new Array[Control]
 
        # Add `item` as a child of `self`
@@ -141,6 +183,12 @@ end
 # A window, root of the `Control` tree
 class Window
        super CompositeControl
+
+       # Should the back button be shown and used to go back to a previous window?
+       fun enable_back_button: Bool do return app.window_stack.length > 1
+
+       # The back button has been pressed, usually to open the previous window
+       fun on_back_button do app.pop_window
 end
 
 # A viewable `Control`
@@ -182,7 +230,7 @@ abstract class TextView
        # depending on the customization options of each platform.
        # For consistent results, it is recommended to use only on instances
        # of `Label` and `size` should be either 0.0, 0.5 or 1.0.
-       fun align=(center: nullable Float) is autoinit do end
+       fun align=(align: nullable Float) is autoinit do end
 end
 
 # A control for the user to enter custom `text`
@@ -211,12 +259,29 @@ class CheckBox
        var is_checked = false is writable
 end
 
+# Event sent from a `VIEW`
+class ViewEvent
+       super AppEvent
+
+       # The `VIEW` that raised this event
+       var sender: VIEW
+
+       # Type of the `sender`
+       type VIEW: View
+end
+
 # A `Button` press event
 class ButtonPressEvent
-       super AppEvent
+       super ViewEvent
 
-       # The `Button` that raised this event
-       var sender: Button
+       redef type VIEW: Button
+end
+
+# The `CheckBox` `sender` has been toggled
+class ToggleEvent
+       super ViewEvent
+
+       redef type VIEW: CheckBox
 end
 
 # A layout to visually organize `Control`s
@@ -240,3 +305,8 @@ class ListLayout
        super View
        super CompositeControl
 end
+
+redef class Text
+       # Open the URL `self` with the default browser
+       fun open_in_browser do print_error "Text::open_in_browser not implemented on this platform."
+end
index cb73b3b..9418c95 100644 (file)
 # Offers the base 64 encoding and decoding algorithms
 module base64
 
-redef class NativeString
-       # Alphabet used by the base64 algorithm
-       private fun base64_chars : SequenceRead[Byte]
-       do
-               return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".bytes
+redef class Char
+       # Is `self` a valid Base64 character ?
+       fun is_base64_char: Bool do
+               if code_point >= 127 then return false
+               return ascii.is_base64_char
        end
+end
 
-       # Reversed alphabet for base64
-       private fun inverted_base64_chars : HashMap[Byte, Byte]
-       do
-               var inv_base64_chars = new HashMap[Byte, Byte]
-               var l = base64_chars.length
-               for k in [0 .. l[ do
-                       inv_base64_chars[base64_chars[k]] = k.to_b
+redef class Byte
+       # Is `self` a valid Base64 character ?
+       fun is_base64_char: Bool do
+               if self == b'+' then return true
+               if self == b'/' then return true
+               if self > b'Z' then
+                       if self < b'a' then return false
+                       if self <= b'z' then return true
+                       return false
+               end
+               if self >= b'A' then return true
+               if self <= b'9' and self >= b'0' then return true
+               return false
+       end
+
+       # Returns the `base64` equivalent of `self`
+       #
+       # REQUIRE `self`.`is_base64_char`
+       fun to_base64_char: Byte do
+               if self == b'+' then return 62u8
+               if self == b'/' then return 63u8
+               if self > b'Z' then
+                       if self < b'a' then abort
+                       if self <= b'z' then return self - 71u8
+                       abort
                end
-               return inv_base64_chars
+               if self >= b'A' then return self - 0x41u8
+               if self <= b'9' and self >= b'0' then return self + 4u8
+               abort
+       end
+end
+
+redef class NativeString
+       # Alphabet used by the base64 algorithm
+       private fun base64_chars : Bytes
+       do
+               return b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
        end
 
        # Encodes `self` to base64.
@@ -40,9 +69,8 @@ redef class NativeString
        # By default, uses "=" for padding.
        #
        #     assert "string".encode_base64 == "c3RyaW5n"
-       private fun encode_base64(length: Int, padding: nullable Byte): Bytes do
+       private fun encode_base64(length: Int): Bytes do
                var base64_bytes = once base64_chars
-               if padding == null then padding = '='.ascii
                var steps = length / 3
                var bytes_in_last_step = length % 3
                var result_length = steps * 4
@@ -70,63 +98,102 @@ redef class NativeString
                        result.add base64_bytes[((self[in_off + 1] & 0b0000_1111u8) << 2).to_i]
                end
                var rempad = if bytes_in_last_step > 0 then 3 - bytes_in_last_step else 0
-               for i in [0 .. rempad[ do result.add padding
+               for i in [0 .. rempad[ do result.add b'='
 
                return result
        end
 
        # Decodes `self` from base64
        #
-       #      assert "c3RyaW5n".decode_base64 == "string"
+       #      assert "c3RyaW5n".decode_base64.to_s == "string"
+       #      assert "c3Rya\nW5n".decode_base64.to_s == "string"
+       #      assert "c3RyaW5nCg==".decode_base64.to_s == "string\n"
+       #      assert "c3RyaW5nCg".decode_base64.to_s == "string\n"
+       #      assert "c3RyaW5neQo=".decode_base64.to_s == "stringy\n"
+       #      assert "c3RyaW5neQo".decode_base64.to_s == "stringy\n"
        #
-       # REQUIRE: `length % 4 == 0`
-       private fun decode_base64(length: Int, padding: nullable Byte): Bytes do
-               if padding == null then padding = '='.ascii
-               var inv = once inverted_base64_chars
+       private fun decode_base64(length: Int): Bytes do
                if length == 0 then return new Bytes.empty
-               assert length % 4 == 0 else print "base64::decode_base64 only supports strings of length multiple of 4"
 
-               var bytes = self
-               var steps = length / 4
-               var result_length = steps * 3
-
-               var epos = length - 1
-               var padding_len = 0
-               while epos >= 0 and bytes[epos] == padding do
-                       epos -= 1
-                       padding_len += 1
+               # Avoids constant unboxing
+               var pad = b'='
+
+               var result = new Bytes.with_capacity((length / 4 + 1) * 3)
+
+               var curr = 0
+               var cnt = 0
+               var endpos = -1
+               for i in [0 .. length[ do
+                       var b = self[i]
+                       if b == pad then
+                               endpos = i
+                               break
+                       end
+                       # Ignore whitespaces
+                       if b <= 0x20u8 then continue
+                       if not b.is_base64_char then continue
+                       curr <<= 6
+                       curr += b.to_base64_char.to_i
+                       cnt += 1
+                       if cnt == 4 then
+                               result.add(((curr & 0xFF0000) >> 16).to_b)
+                               result.add(((curr & 0xFF00) >> 8).to_b)
+                               result.add((curr & 0xFF).to_b)
+                               curr = 0
+                               cnt = 0
+                       end
                end
+               if endpos != -1 or cnt != 0 then
+                       var pads = 0
+                       for i in [endpos .. length[ do
+                               var b = self[i]
+                               if b <= 0x20u8 then continue
+                               pads += 1
+                       end
+                       if cnt == 2 then
+                               curr >>= 4
+                               result.add(curr.to_b)
+                       else if cnt == 3 then
+                               curr >>= 2
+                               result.add(((curr & 0xFF00) >> 8).to_b)
+                               result.add((curr & 0xFF).to_b)
+                       end
+               end
+               return result
+       end
 
-               if padding_len != 0 then steps -= 1
-               if padding_len == 1 then result_length -= 1
-               if padding_len == 2 then result_length -= 2
-
-               var result = new Bytes.with_capacity(result_length + 1)
+       # Is `self` a well-formed Base64 entity ?
+       #
+       # ~~~nit
+       #       assert "Qn03".is_base64
+       #       assert not "#sd=".is_base64
+       # ~~~
+       fun is_base64(length: Int): Bool do return check_base64(length) == null
 
-               for s in [0 .. steps[ do
-                       var c0 = inv[bytes[s * 4]]
-                       var c1 = inv[bytes[s * 4 + 1]]
-                       var c2 = inv[bytes[s * 4 + 2]]
-                       var c3 = inv[bytes[s * 4 + 3]]
-                       result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4))
-                       result.add (((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2))
-                       result.add (((c2 & 0b0000_0011u8) << 6) | (c3 & 0b0011_1111u8))
+       # Is `self` a well-formed Base64 entity ?
+       #
+       # Will return an Error otherwise with info on which part is erroneous.
+       fun check_base64(length: Int): nullable Error do
+               var rlen = 0
+               var opos = length
+               for i in [0 .. length[ do
+                       if self[i] == b'=' then
+                               opos = i
+                               break
+                       end
+                       if self[i].is_whitespace then continue
+                       if not self[i].is_base64_char then return new Error("Invalid Base64 character at position {i}: {self[i].ascii}")
+                       rlen += 1
+                       if rlen > 4 then rlen -= 4
                end
-
-               var last_start = steps * 4
-               if padding_len == 1 then
-                       var c0 = inv[bytes[last_start]]
-                       var c1 = inv[bytes[last_start + 1]]
-                       var c2 = inv[bytes[last_start + 2]]
-                       result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4))
-                       result.add (((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2))
-               else if padding_len == 2 then
-                       var c0 = inv[bytes[last_start]]
-                       var c1 = inv[bytes[last_start + 1]]
-                       result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4))
+               var pad = 0
+               for i in [opos .. length[ do
+                       if self[i].is_whitespace then continue
+                       if self[i] != b'=' then return new Error("Invalid padding character {self[i].ascii} at position {i}")
+                       pad += 1
                end
-
-               return result
+               if rlen + pad != 4 then return new Error("Invalid padding length")
+               return null
        end
 end
 
@@ -135,35 +202,49 @@ redef class Bytes
        # Encodes the receiver string to base64 using a custom padding character.
        #
        # If using the default padding character `=`, see `encode_base64`.
-       fun encode_base64(padding: nullable Byte): Bytes
-       do
-               return items.encode_base64(length, padding)
-       end
+       fun encode_base64: Bytes do return items.encode_base64(length)
 
        # Decodes the receiver string to base64 using a custom padding character.
        #
        # Default padding character `=`
-       fun decode_base64(padding : nullable Byte) : Bytes
-       do
-               return items.decode_base64(length, padding)
-       end
+       fun decode_base64: Bytes do return items.decode_base64(length)
+
+       # Is `self` a well-formed Base64 entity ?
+       fun is_base64: Bool do return items.is_base64(length)
+
+       # Is `self` a well-formed Base64 entity ?
+       #
+       # Will return an Error otherwise with info on which part is erroneous.
+       fun check_base64: nullable Error do return items.check_base64(length)
 end
 
-redef class String
+redef class Text
 
        # Encodes the receiver string to base64 using a custom padding character.
        #
        # If using the default padding character `=`, see `encode_base64`.
-       fun encode_base64(padding: nullable Byte): String
-       do
-               return to_cstring.encode_base64(bytelen, padding).to_s
-       end
+       fun encode_base64: String do return to_cstring.encode_base64(byte_length).to_s
 
        # Decodes the receiver string to base64 using a custom padding character.
        #
        # Default padding character `=`
-       fun decode_base64(padding : nullable Byte) : String
-       do
-               return to_cstring.decode_base64(bytelen, padding).to_s
-       end
+       fun decode_base64: Bytes do return to_cstring.decode_base64(byte_length)
+
+       # Is `self` a well-formed Base64 entity ?
+       fun is_base64: Bool do return to_cstring.is_base64(byte_length)
+
+       # Is `self` a well-formed Base64 entity ?
+       #
+       # Will return an Error otherwise with info on which part is erroneous.
+       fun check_base64: nullable Error do return to_cstring.check_base64(byte_length)
+end
+
+redef class FlatText
+       redef fun encode_base64 do return fast_cstring.encode_base64(byte_length).to_s
+
+       redef fun decode_base64 do return fast_cstring.decode_base64(byte_length)
+
+       redef fun is_base64 do return fast_cstring.is_base64(byte_length)
+
+       redef fun check_base64 do return fast_cstring.check_base64(byte_length)
 end
index c15cb52..dbe07be 100644 (file)
@@ -107,7 +107,7 @@ redef abstract class Writer
        # Compared to `write_string`, this method supports null bytes in `text`.
        fun write_block(text: Text)
        do
-               write_int64 text.bytelen
+               write_int64 text.byte_length
                write text
        end
 
index b5542e4..28129a5 100644 (file)
@@ -26,6 +26,7 @@
 module bucketed_game is serialize
 
 import serialization
+import counter
 
 # Something acting on the game
 abstract class Turnable[G: Game]
@@ -65,6 +66,9 @@ class Buckets[G: Game]
        private var buckets: Array[BUCKET] =
                [for b in n_buckets.times do new HashSet[Bucketable[G]]] is lazy
 
+       # Stats on delays asked when adding an event with `act_in` and `act_next`
+       private var delays = new Counter[Int]
+
        # Add the Bucketable event `e` at `at_tick`.
        fun add_at(e: Bucketable[G], at_tick: Int)
        do
@@ -106,6 +110,26 @@ class Buckets[G: Game]
                        end
                end
        end
+
+       # Get some statistics on both the current held events and historic expired events
+       fun stats: String
+       do
+               var entries = 0
+               var instances = new HashSet[Bucketable[G]]
+               var max = 0
+               var min = 100000
+               for bucket in buckets do
+                       var len = bucket.length
+                       entries += len
+                       instances.add_all bucket
+                       min = min.min(len)
+                       max = max.max(len)
+               end
+               var avg = entries.to_f / buckets.length.to_f
+
+               return "{buckets.length} buckets; uniq/tot:{instances.length}/{entries}, avg:{avg.to_precision(1)}, min:{min}, max:{max}\n" +
+                       "history:{delays.sum}, avg:{delays.avg}, min:{delays[delays.min.as(not null)]}, max:{delays[delays.max.as(not null)]}"
+       end
 end
 
 # Game related event
@@ -123,16 +147,16 @@ end
 # Game logic on the client
 class ThinGame
 
-       # Game tick when `self` should act.
+       # Current game tick
        #
        # Default is 0.
-       var tick: Int = 0 is protected writable
+       var tick: Int = 0 is writable
 end
 
 # Game turn on the client
 class ThinGameTurn[G: ThinGame]
 
-       # Game tick when `self` should act.
+       # Game tick when happened this turn
        var tick: Int is protected writable
 
        # Game events occurred for `self`.
@@ -153,10 +177,18 @@ class GameTurn[G: Game]
        end
 
        # Insert the Bucketable event `e` to be executed at next tick.
-       fun act_next(e: Bucketable[G]) do game.buckets.add_at(e, tick + 1)
+       fun act_next(e: Bucketable[G])
+       do
+               game.buckets.add_at(e, tick + 1)
+               game.buckets.delays.inc(1)
+       end
 
        # Insert the Bucketable event `e` to be executed at tick `t`.
-       fun act_in(e: Bucketable[G], t: Int) do game.buckets.add_at(e, tick + t)
+       fun act_in(e: Bucketable[G], t: Int)
+       do
+               game.buckets.add_at(e, tick + t)
+               game.buckets.delays.inc(t)
+       end
 
        # Add and `apply` a game `event`.
        fun add_event( event : GameEvent )
index 33114e4..e7c3b17 100644 (file)
@@ -37,7 +37,7 @@ extern class NSString in "ObjC" `{ NSString * `}
        # Get an UTF8 encoded `char*` copy of `self`
        fun utf8_string: NativeString in "ObjC" `{ return (char *)[self UTF8String]; `}
 
-       redef fun to_s do return utf8_string.to_s
+       redef fun to_s do return utf8_string.to_s_with_copy
 end
 
 redef class NativeString
@@ -51,7 +51,7 @@ end
 
 redef class Text
        # Get a `NSString` from `self`
-       fun to_nsstring: NSString do return to_cstring.to_nsstring(bytelen)
+       fun to_nsstring: NSString do return to_cstring.to_nsstring(byte_length)
 end
 
 # Wrapper of byte buffers
index 7b25d61..85b457f 100644 (file)
@@ -456,7 +456,7 @@ class Bytes
        # Appends the bytes of `s` to `selftextextt`
        fun append_text(s: Text) do
                for i in s.substrings do
-                       append_ns(i.fast_cstring, i.bytelen)
+                       append_ns(i.fast_cstring, i.byte_length)
                end
        end
 
@@ -712,7 +712,7 @@ redef class Text
        # assert "String".to_bytes == [83u8, 116u8, 114u8, 105u8, 110u8, 103u8]
        # ~~~
        fun to_bytes: Bytes do
-               var b = new Bytes.with_capacity(bytelen)
+               var b = new Bytes.with_capacity(byte_length)
                append_to_bytes b
                return b
        end
@@ -730,7 +730,7 @@ redef class Text
        fun append_to_bytes(b: Bytes) do
                for s in substrings do
                        var from = if s isa FlatString then s.first_byte else 0
-                       b.append_ns_from(s.items, s.bytelen, from)
+                       b.append_ns_from(s.items, s.byte_length, from)
                end
        end
 
@@ -757,7 +757,7 @@ redef class Text
        #     assert "a b c".hexdigest_to_bytes.hexdigest == "0ABC"
        fun hexdigest_to_bytes: Bytes do
                var b = bytes
-               var max = bytelen
+               var max = byte_length
 
                var dlength = 0 # Number of hex digits
                var pos = 0
@@ -795,7 +795,7 @@ redef class Text
        #
        #     assert "&lt;STRING&#47;&rt;".hexdigest == "266C743B535452494E47262334373B2672743B"
        fun hexdigest: String do
-               var ln = bytelen
+               var ln = byte_length
                var outns = new NativeString(ln * 2)
                var oi = 0
                for i in [0 .. ln[ do
@@ -815,11 +815,11 @@ redef class Text
        #     assert "\\x41\\x42\\x43".unescape_to_bytes.chexdigest == "\\x41\\x42\\x43"
        #     assert "B\\n\\x41\\u0103D3".unescape_to_bytes.chexdigest == "\\x42\\x0A\\x41\\xF0\\x90\\x8F\\x93"
        fun unescape_to_bytes: Bytes do
-               var res = new Bytes.with_capacity(self.bytelen)
+               var res = new Bytes.with_capacity(self.byte_length)
                var was_slash = false
                var i = 0
                while i < length do
-                       var c = chars[i]
+                       var c = self[i]
                        if not was_slash then
                                if c == '\\' then
                                        was_slash = true
@@ -886,7 +886,7 @@ redef class Text
        fun binarydigest_to_bytes: Bytes
        do
                var b = bytes
-               var max = bytelen
+               var max = byte_length
 
                # Count bits
                var bitlen = 0
@@ -930,7 +930,7 @@ end
 redef class FlatText
        redef fun append_to_bytes(b) do
                var from = if self isa FlatString then first_byte else 0
-               b.append_ns_from(items, bytelen, from)
+               b.append_ns_from(items, byte_length, from)
        end
 end
 
index 02c39ef..21ea8a8 100644 (file)
@@ -40,7 +40,7 @@ private class ISO88591Codec
        end
 
        redef fun encode_string(s) do
-               var ns = new Bytes.with_capacity(s.bytelen)
+               var ns = new Bytes.with_capacity(s.byte_length)
                add_string_to(s, ns)
                return ns
        end
index bb257dd..c6fc71c 100644 (file)
@@ -41,14 +41,14 @@ private class UTF8Codec
        end
 
        redef fun encode_string(s) do
-               var buf = new Bytes.with_capacity(s.bytelen)
+               var buf = new Bytes.with_capacity(s.byte_length)
                add_string_to(s, buf)
                return buf
        end
 
        redef fun add_string_to(s, b) do
                s.append_to_bytes(b)
-               return s.bytelen
+               return s.byte_length
        end
 
        redef fun is_valid_char(ns, len) do
@@ -73,7 +73,7 @@ private class UTF8Codec
                if rit == ns then
                        var nns = new NativeString(len)
                        rit.copy_to(nns, len, 0, 0)
-                       return nns.to_s_full(ret.bytelen, ret.length)
+                       return nns.to_s_full(ret.byte_length, ret.length)
                end
                return ret
        end
index ade3bfc..5ed911f 100644 (file)
@@ -457,7 +457,9 @@ interface Set[E]
                var res = 23 + length
                # Note: the order of the elements must not change the hash value.
                # So, unlike usual hash functions, the accumulator is not combined with itself.
-               for e in self do res += e.hash
+               for e in self do
+                       if e != null then res += e.hash
+               end
                return res
        end
 
@@ -1163,6 +1165,30 @@ interface Sequence[E]
        #
        # REQUIRE `index >= 0 and index < length`
        fun remove_at(index: Int) is abstract
+
+       # Rotates the elements of self once to the left
+       #
+       # ~~~nit
+       # var a = [12, 23, 34, 45]
+       # a.rotate_left
+       # assert a == [23, 34, 45, 12]
+       # ~~~
+       fun rotate_left do
+               var fst = shift
+               push fst
+       end
+
+       # Rotates the elements of self once to the right
+       #
+       # ~~~nit
+       # var a = [12, 23, 34, 45]
+       # a.rotate_right
+       # assert a == [45, 12, 23, 34]
+       # ~~~
+       fun rotate_right do
+               var lst = pop
+               unshift lst
+       end
 end
 
 # Iterators on indexed collections.
index 79e7a70..f01bbb5 100644 (file)
@@ -321,7 +321,7 @@ class Array[E]
        redef fun [](index)
        do
                assert index: index >= 0 and index < _length
-               return _items[index]
+               return _items.as(not null)[index]
        end
 
        redef fun []=(index, item)
@@ -333,7 +333,7 @@ class Array[E]
                if _length <= index then
                        _length = index + 1
                end
-               _items[index] = item
+               _items.as(not null)[index] = item
        end
 
        redef fun add(item)
@@ -343,7 +343,7 @@ class Array[E]
                        enlarge(l + 1)
                end
                _length = l + 1
-               _items[l] = item
+               _items.as(not null)[l] = item
        end
 
        # Slight optimization for arrays
@@ -358,13 +358,13 @@ class Array[E]
                if items isa Array[E] then
                        var k = 0
                        while l < nl do
-                               _items[l] = items._items[k]
+                               _items.as(not null)[l] = items._items.as(not null)[k]
                                l += 1
                                k += 1
                        end
                else
                        for item in items do
-                               _items[l] = item
+                               _items.as(not null)[l] = item
                                l += 1
                        end
                end
@@ -404,7 +404,7 @@ class Array[E]
                if cap <= c then return
                while c <= cap do c = c * 2 + 2
                var a = new NativeArray[E](c)
-               if _capacity > 0 then _items.copy_to(a, _length)
+               if _capacity > 0 then _items.as(not null).copy_to(a, _length)
                _items = a
                _capacity = c
        end
@@ -474,9 +474,10 @@ class Array[E]
                # Efficient implementation
                var l = length
                if l != o.length then return false
+               if l == 0 then return true
                var i = 0
-               var it = _items
-               var oit = o._items
+               var it = _items.as(not null)
+               var oit = o._items.as(not null)
                while i < l do
                        if it[i] != oit[i] then return false
                        i += 1
@@ -921,10 +922,11 @@ class ArrayCmp[E: nullable Comparable]
 
        redef fun <=>(o)
        do
-               var it = _items
-               var oit = o._items
                var i = 0
                var l = length
+               if l == 0 then return 0
+               var it = _items.as(not null)
+               var oit = o._items.as(not null)
                var ol = o.length
                var len
                if l < ol then len = l else len = ol
index ab81a37..241238b 100644 (file)
@@ -21,21 +21,21 @@ class List[E]
 
 # Access
 
-       redef fun [](index) do return get_node(index).item
+       redef fun [](index) do return get_node(index).as(not null).item
 
-       redef fun []=(index, item) do get_node(index).item = item
+       redef fun []=(index, item) do get_node(index).as(not null).item = item
 
        # O(1)
-       redef fun first do return _head.item
+       redef fun first do return _head.as(not null).item
 
        # O(1)
-       redef fun first=(e) do _head.item = e
+       redef fun first=(e) do _head.as(not null).item = e
 
        # O(1)
-       redef fun last do return _tail.item
+       redef fun last do return _tail.as(not null).item
 
        # O(1)
-       redef fun last=(e) do _tail.item = e
+       redef fun last=(e) do _tail.as(not null).item = e
 
 # Queries
 
@@ -87,11 +87,12 @@ class List[E]
        redef fun push(e)
        do
                var node = new ListNode[E](e)
-               if _tail == null then
+               var tail = _tail
+               if tail == null then
                        _head = node
                else
-                       _tail.next = node
-                       node.prev = _tail
+                       tail.next = node
+                       node.prev = tail
                end
                _tail = node
                length += 1
@@ -101,11 +102,12 @@ class List[E]
        redef fun unshift(e)
        do
                var node = new ListNode[E](e)
-               if _head == null then
+               var head = _head
+               if head == null then
                        _tail = node
                else
-                       node.next = _head
-                       _head.prev = node
+                       node.next = head
+                       head.prev = node
                end
                _head = node
                length += 1
@@ -127,11 +129,12 @@ class List[E]
        # O(1)
        fun link(l: List[E])
        do
-               if _tail == null then
+               var tail = _tail
+               if tail == null then
                        _head = l._head
                else if l._head != null then
-                       _tail.next = l._head
-                       _tail.next.prev = _tail
+                       tail.next = l._head
+                       tail.next.as(not null).prev = tail
                end
                _tail = l._tail
                length += l.length
@@ -143,13 +146,13 @@ class List[E]
        # O(1)
        redef fun pop
        do
-               var node = _tail
+               var node = _tail.as(not null)
                _tail = node.prev
                node.prev = null
                if _tail == null then
                        _head = null
                else
-                       _tail.next = null
+                       _tail.as(not null).next = null
                end
                length -= 1
                return node.item
@@ -158,13 +161,13 @@ class List[E]
        # O(1)
        redef fun shift
        do
-               var node = _head
+               var node = _head.as(not null)
                _head = node.next
                node.next = null
                if _head == null then
                        _tail = null
                else
-                       _head.prev = null
+                       _head.as(not null).prev = null
                end
                length -= 1
                return node.item
@@ -236,14 +239,14 @@ class List[E]
                        if node.next == null then
                                _tail = null
                        else
-                               node.next.prev = null
+                               node.next.as(not null).prev = null
                        end
                else if node.next == null then
                        _tail = node.prev
-                       node.prev.next = null
+                       node.prev.as(not null).next = null
                else
-                       node.prev.next = node.next
-                       node.next.prev = node.prev
+                       node.prev.as(not null).next = node.next
+                       node.next.as(not null).prev = node.prev
                end
        end
 
@@ -266,16 +269,16 @@ end
 # This is the iterator class of List
 class ListIterator[E]
        super IndexedIterator[E]
-       redef fun item do return _node.item
+       redef fun item do return _node.as(not null).item
 
        # Set item `e` at self `index`.
-       fun item=(e: E) do _node.item = e
+       fun item=(e: E) do _node.as(not null).item = e
 
        redef fun is_ok do return not _node == null
 
        redef fun next
        do
-               _node = _node.next
+               _node = _node.as(not null).next
                _index += 1
        end
 
@@ -312,7 +315,7 @@ private class ListReverseIterator[E]
 
        redef fun next
        do
-               _node = _node.prev
+               _node = _node.as(not null).prev
                _index -= 1
        end
 
index ac13301..6a58480 100644 (file)
@@ -86,6 +86,6 @@ class MaybeError[V, E: Error]
        redef fun to_s do
                var e = maybe_error
                if e != null then return e.to_s
-               return value.to_s
+               return value.as(not null).to_s
        end
 end
index 10c5374..c1edff8 100644 (file)
@@ -59,49 +59,51 @@ class Process
        end
 
        # The status once finished
+       #
+       # Require: `is_finished`
        fun status: Int
        do
                assert is_finished
                return data.status
        end
 
-       # The executable run
-       # Is a filepath, or a executable found in PATH
-       var command: String
+       # The target executable
+       # Either a file path or the name of an executable available in PATH.
+       var command: Text
 
        # The arguments of the command
        # Starts with the first real arguments---ie. does not include the progname (`argv[0]`, in C)
-       var arguments: nullable Array[String]
+       var arguments: nullable Array[Text]
 
        # Launch a command with some arguments
-       init(command: String, arguments: String...) is old_style_init do
+       init(command: Text, arguments: Text...) is old_style_init do
                self.command = command
                self.arguments = arguments
                execute
        end
 
        # Launch a simple command with arguments passed as an array
-       init from_a(command: String, arguments: nullable Array[String])
+       init from_a(command: Text, arguments: nullable Array[Text])
        do
                self.command = command
                self.arguments = arguments
                execute
        end
 
-       # flags used internally to know whith pipe to open
+       # Flags used internally to know which pipe to open
        private fun pipeflags: Int do return 0
 
        # Internal code to handle execution
        protected fun execute
        do
-               # The pass the arguments as a big C string where elements are separated with '\0'
+               # Pass the arguments as a big C string where elements are separated with '\0'
                var args = new FlatBuffer
                var l = 1 # Number of elements in args
                args.append(command)
+               var arguments = self.arguments
                if arguments != null then
                        for a in arguments do
                                args.add('\0')
-                               #a.output_class_name
                                args.append(a)
                        end
                        l += arguments.length
@@ -291,7 +293,7 @@ class ProcessDuplex
        # ~~~
        fun write_and_read(input: Text): String
        do
-               var read = new Buffer #new Array[String]
+               var read = new Buffer
 
                # Main loop, read and write line by line
                var prev = 0
@@ -320,10 +322,13 @@ end
 
 redef class Sys
        # Execute a shell command and return its error code
-       fun system(command: String): Int
+       fun system(command: Text): Int
        do
                return command.to_cstring.system
        end
+
+       # The pid of the program
+       fun pid: Int `{ return getpid(); `}
 end
 
 redef class NativeString
index 0454f1d..bea9672 100644 (file)
@@ -49,23 +49,24 @@ abstract class FileStream
        # Return null in case of error
        fun file_stat: nullable FileStat
        do
-               var stat = _file.file_stat
+               var stat = _file.as(not null).file_stat
                if stat.address_is_null then return null
                return new FileStat(stat)
        end
 
        # File descriptor of this file
-       fun fd: Int do return _file.fileno
+       fun fd: Int do return _file.as(not null).fileno
 
        redef fun close
        do
-               if _file == null then return
-               if _file.address_is_null then
+               var file = _file
+               if file == null then return
+               if file.address_is_null then
                        if last_error != null then return
                        last_error = new IOError("Cannot close unopened file")
                        return
                end
-               var i = _file.io_close
+               var i = file.io_close
                if i != 0 then
                        last_error = new IOError("Close failed due to error {sys.errno.strerror}")
                end
@@ -83,7 +84,7 @@ abstract class FileStream
        # * `buffer_mode_none`
        fun set_buffering_mode(buf_size, mode: Int) do
                if buf_size <= 0 then buf_size = 512
-               if _file.set_buffering_type(buf_size, mode) != 0 then
+               if _file.as(not null).set_buffering_type(buf_size, mode) != 0 then
                        last_error = new IOError("Error while changing buffering type for FileStream, returned error {sys.errno.strerror}")
                end
        end
@@ -105,10 +106,10 @@ class FileReader
        #     assert l == f.read_line
        fun reopen
        do
-               if not eof and not _file.address_is_null then close
+               if not eof and not _file.as(not null).address_is_null then close
                last_error = null
-               _file = new NativeFile.io_open_read(path.to_cstring)
-               if _file.address_is_null then
+               _file = new NativeFile.io_open_read(path.as(not null).to_cstring)
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path.as(not null)}`: {sys.errno.strerror}")
                        end_reached = true
                        return
@@ -126,8 +127,8 @@ class FileReader
 
        redef fun fill_buffer
        do
-               var nb = _file.io_read(_buffer, _buffer_capacity)
-               if last_error == null and _file.ferror then
+               var nb = _file.as(not null).io_read(_buffer, _buffer_capacity)
+               if last_error == null and _file.as(not null).ferror then
                        last_error = new IOError("Cannot read `{path.as(not null)}`: {sys.errno.strerror}")
                        end_reached = true
                end
@@ -158,7 +159,7 @@ class FileReader
                self.path = path
                prepare_buffer(100)
                _file = new NativeFile.io_open_read(path.to_cstring)
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path}`: {sys.errno.strerror}")
                        end_reached = true
                end
@@ -171,7 +172,7 @@ class FileReader
                self.path = ""
                prepare_buffer(1)
                _file = fd.fd_to_stream(read_only)
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'")
                        end_reached = true
                end
@@ -223,13 +224,13 @@ class FileWriter
                        last_error = new IOError("Cannot write to non-writable stream")
                        return
                end
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Writing on a null stream")
                        _is_writable = false
                        return
                end
 
-               var err = _file.write_byte(value)
+               var err = _file.as(not null).write_byte(value)
                if err != 1 then
                        # Big problem
                        last_error = new IOError("Problem writing a byte: {err}")
@@ -251,12 +252,12 @@ class FileWriter
                        last_error = new IOError("Cannot write to non-writable stream")
                        return
                end
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Writing on a null stream")
                        _is_writable = false
                        return
                end
-               var err = _file.io_write(native, from, len)
+               var err = _file.as(not null).io_write(native, from, len)
                if err != len then
                        # Big problem
                        last_error = new IOError("Problem in writing : {err} {len} \n")
@@ -269,7 +270,7 @@ class FileWriter
                _file = new NativeFile.io_open_write(path.to_cstring)
                self.path = path
                _is_writable = true
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path}`: {sys.errno.strerror}")
                        is_writable = false
                end
@@ -280,7 +281,7 @@ class FileWriter
                self.path = ""
                _file = fd.fd_to_stream(wipe_write)
                _is_writable = true
-                if _file.address_is_null then
+                if _file.as(not null).address_is_null then
                         last_error = new IOError("Error: Opening stream from file descriptor {fd} failed with '{sys.errno.strerror}'")
                        _is_writable = false
                end
@@ -498,8 +499,8 @@ class Path
                var output = dest.open_wo
 
                while not input.eof do
-                       var buffer = input.read(1024)
-                       output.write buffer
+                       var buffer = input.read_bytes(1024)
+                       output.write_bytes buffer
                end
 
                input.close
@@ -663,6 +664,19 @@ class Path
                return res
        end
 
+       # Is `self` the path to an existing directory ?
+       #
+       # ~~~nit
+       # assert ".".to_path.is_dir
+       # assert not "/etc/issue".to_path.is_dir
+       # assert not "/should/not/exist".to_path.is_dir
+       # ~~~
+       fun is_dir: Bool do
+               var st = stat
+               if st == null then return false
+               return st.is_dir
+       end
+
        # Delete a directory and all of its content
        #
        # Does not go through symbolic links and may get stuck in a cycle if there
@@ -844,7 +858,7 @@ redef class Text
 
        private fun write_native_to(s: FileWriter)
        do
-               for i in substrings do s.write_native(i.to_cstring, 0, i.bytelen)
+               for i in substrings do s.write_native(i.to_cstring, 0, i.byte_length)
        end
 end
 
@@ -1271,7 +1285,7 @@ end
 redef class FlatString
        redef fun write_native_to(s)
        do
-               s.write_native(items, first_byte, bytelen)
+               s.write_native(items, first_byte, byte_length)
        end
 
        redef fun file_extension do
index c643eee..5e7c774 100644 (file)
@@ -900,11 +900,13 @@ redef class Text
        #     assert not "0xGE".is_int
        #     assert not "".is_int
        #     assert not "Not an Int".is_int
+       #     assert not "-".is_int
        fun is_int: Bool do
-               if bytelen == 0 then return false
+               if byte_length == 0 then return false
                var s = remove_all('_')
                var pos = 0
-               while s[pos] == '-' do
+               var len = s.length
+               while pos < len and s[pos] == '-' do
                        pos += 1
                end
                s = s.substring_from(pos)
index 399487a..fe45072 100644 (file)
@@ -699,6 +699,9 @@ universal Byte
                        return self
                end
        end
+
+       # Is `self` an ASCII whitespace ?
+       fun is_whitespace: Bool do return self == 0x7Fu8 or self <= 0x20u8
 end
 
 # Native integer numbers.
index 0f8325f..c8842fd 100644 (file)
@@ -169,6 +169,16 @@ redef class Int
                end
                return res
        end
+
+       # Is `self` a power of two ?
+       #
+       # ~~~nit
+       # assert not 3.is_pow2
+       # assert 2.is_pow2
+       # assert 1.is_pow2
+       # assert not 0.is_pow2
+       # ~~~
+       fun is_pow2: Bool do return self != 0 and (self & self - 1) == 0
 end
 
 redef class Byte
@@ -410,6 +420,17 @@ fun inf: Float do return 1.0 / 0.0
 # ~~~
 fun nan: Float do return 0.0 / 0.0
 
+redef class Comparable
+       # Constraint `self` within `[min..max]`
+       #
+       #     assert 1.clamp(5, 10) == 5
+       #     assert 7.clamp(5, 10) == 7
+       #     assert 15.clamp(5, 10) == 10
+       #     assert 1.5.clamp(1.0, 2.0) == 1.5
+       #     assert "a".clamp("b", "c") == "b"
+       fun clamp(min, max: OTHER): OTHER do return self.max(min).min(max)
+end
+
 redef class Collection[ E ]
        # Return a random element form the collection
        # There must be at least one element in the collection
index 917ec4f..94672b4 100644 (file)
@@ -183,7 +183,7 @@ class Regex
        # Cache of a single `regmatch_t` to prevent many calls to `malloc`
        private var native_match: NativeMatchArray is lazy do
                native_match_is_init = true
-               return new NativeMatchArray.malloc(native.re_nsub+1)
+               return new NativeMatchArray.malloc(native.as(not null).re_nsub+1)
        end
 
        private var native_match_is_init = false
@@ -368,7 +368,7 @@ class Regex
 
                # Actually execute
                var cstr = text.to_cstring
-               var rets = cstr.to_s_with_length(text.bytelen)
+               var rets = cstr.to_s_with_length(text.byte_length)
                var bytefrom = cstr.char_to_byte_index_cached(charfrom, 0, 0)
                var subcstr = cstr.fast_cstring(bytefrom)
                var eflags = gather_eflags
@@ -428,7 +428,7 @@ class Regex
                # Actually execute
                var cstr = text.to_cstring
                var subcstr = cstr
-               var rets = cstr.to_s_with_length(text.bytelen)
+               var rets = cstr.to_s_with_length(text.byte_length)
                var eflags = gather_eflags
                var eflags_or_notbol = eflags | flag_notbol
                var native_match = self.native_match
index 8a75543..a54c7e8 100644 (file)
@@ -198,7 +198,7 @@ abstract class Reader
                var rets = ""
                var pos = 0
                var str = s.items.clean_utf8(slen)
-               slen = str.bytelen
+               slen = str.byte_length
                var sits = str.items
                var remsp = slen
                while pos < slen do
@@ -211,10 +211,10 @@ abstract class Reader
                                break
                        end
                        var st = sits.find_beginning_of_char_at(pos + chunksz - 1)
-                       var bytelen = st - pos
-                       rets += new FlatString.with_infos(sits, bytelen, pos)
+                       var byte_length = st - pos
+                       rets += new FlatString.with_infos(sits, byte_length, pos)
                        pos = st
-                       remsp -= bytelen
+                       remsp -= byte_length
                end
                if rets isa Concat then return rets.balance
                return rets
@@ -417,6 +417,9 @@ abstract class Writer
        # Write a single byte
        fun write_byte(value: Byte) is abstract
 
+       # Writes a single char
+       fun write_char(c: Char) do write(c.to_s)
+
        # Can the stream be used to write
        fun is_writable: Bool is abstract
 end
@@ -729,5 +732,5 @@ class StringReader
                return new Bytes(nns, nslen, nslen)
        end
 
-       redef fun eof do return cursor >= source.bytelen
+       redef fun eof do return cursor >= source.byte_length
 end
index 7fb65ee..cb6f281 100644 (file)
@@ -50,9 +50,9 @@ abstract class Text
 
        # Number of bytes in `self`
        #
-       #     assert "12345".bytelen == 5
-       #     assert "あいうえお".bytelen == 15
-       fun bytelen: Int is abstract
+       #     assert "12345".byte_length == 5
+       #     assert "あいうえお".byte_length == 15
+       fun byte_length: Int is abstract
 
        # Create a substring.
        #
@@ -551,7 +551,7 @@ abstract class Text
                var res = new Buffer
                var underscore = false
                var start = 0
-               var c = chars[0]
+               var c = self[0]
 
                if c >= '0' and c <= '9' then
                        res.add('_')
@@ -560,7 +560,7 @@ abstract class Text
                        start = 1
                end
                for i in [start..length[ do
-                       c = chars[i]
+                       c = self[i]
                        if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
                                res.add(c)
                                underscore = false
@@ -590,10 +590,13 @@ abstract class Text
                return res.to_s
        end
 
-       # Escape " \ ' and non printable characters using the rules of literal C strings and characters
+       # Escape `"` `\` `'`, trigraphs and non printable characters using the rules of literal C strings and characters
        #
-       #     assert "abAB12<>&".escape_to_c         == "abAB12<>&"
+       #     assert "abAB12<>&".escape_to_c       == "abAB12<>&"
        #     assert "\n\"'\\".escape_to_c         == "\\n\\\"\\'\\\\"
+       #     assert "allo???!".escape_to_c        == "allo??\\?!"
+       #     assert "??=??/??'??(??)".escape_to_c == "?\\?=?\\?/??\\'?\\?(?\\?)"
+       #     assert "??!??<??>??-".escape_to_c    == "?\\?!?\\?<?\\?>?\\?-"
        #
        # Most non-printable characters (bellow ASCII 32) are escaped to an octal form `\nnn`.
        # Three digits are always used to avoid following digits to be interpreted as an element
@@ -617,6 +620,24 @@ abstract class Text
                                b.append("\\\'")
                        else if c == '\\' then
                                b.append("\\\\")
+                       else if c == '?' then
+                               # Escape if it is the last question mark of a ANSI C trigraph.
+                               var j = i + 1
+                               if j < length then
+                                       var next = chars[j]
+                                       # We ignore `??'` because it will be escaped as `??\'`.
+                                       if
+                                               next == '!' or
+                                               next == '(' or
+                                               next == ')' or
+                                               next == '-' or
+                                               next == '/' or
+                                               next == '<' or
+                                               next == '=' or
+                                               next == '>'
+                                       then b.add('\\')
+                               end
+                               b.add('?')
                        else if c.code_point < 32 then
                                b.add('\\')
                                var oct = c.code_point.to_base(8)
@@ -640,6 +661,7 @@ abstract class Text
        # The result might no be legal in C but be used in other languages
        #
        #     assert "ab|\{\}".escape_more_to_c("|\{\}") == "ab\\|\\\{\\\}"
+       #     assert "allo???!".escape_more_to_c("")     == "allo??\\?!"
        fun escape_more_to_c(chars: String): String
        do
                var b = new Buffer
@@ -822,7 +844,7 @@ abstract class Text
        #     assert "%c3%a9%e3%81%82%e3%81%84%e3%81%86".from_percent_encoding == "éあいう"
        fun from_percent_encoding: String
        do
-               var len = bytelen
+               var len = byte_length
                var has_percent = false
                for c in chars do
                        if c == '%' then
@@ -1159,7 +1181,7 @@ abstract class FlatText
 
        redef var length = 0
 
-       redef var bytelen = 0
+       redef var byte_length = 0
 
        redef fun output
        do
@@ -1204,11 +1226,11 @@ private abstract class StringByteView
 
        redef fun is_empty do return target.is_empty
 
-       redef fun length do return target.bytelen
+       redef fun length do return target.byte_length
 
        redef fun iterator do return self.iterator_from(0)
 
-       redef fun reverse_iterator do return self.reverse_iterator_from(target.bytelen - 1)
+       redef fun reverse_iterator do return self.reverse_iterator_from(target.byte_length - 1)
 end
 
 # Immutable sequence of characters.
@@ -1360,30 +1382,19 @@ abstract class String
        # Letters that follow a letter are lowercased
        # Letters that follow a non-letter are upcased.
        #
+       # If `keep_upper = true`, already uppercase letters are not lowercased.
+       #
        # SEE : `Char::is_letter` for the definition of letter.
        #
        #     assert "jAVASCRIPT".capitalized == "Javascript"
        #     assert "i am root".capitalized == "I Am Root"
        #     assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
-       fun capitalized: SELFTYPE do
+       #     assert "preserve my ACRONYMS".capitalized(keep_upper=true) == "Preserve My ACRONYMS"
+       fun capitalized(keep_upper: nullable Bool): SELFTYPE do
                if length == 0 then return self
 
                var buf = new Buffer.with_cap(length)
-
-               var curr = chars[0].to_upper
-               var prev = curr
-               buf[0] = curr
-
-               for i in [1 .. length[ do
-                       prev = curr
-                       curr = self[i]
-                       if prev.is_letter then
-                               buf[i] = curr.to_lower
-                       else
-                               buf[i] = curr.to_upper
-                       end
-               end
-
+               buf.capitalize(keep_upper=keep_upper, src=self)
                return buf.to_s
        end
 end
@@ -1400,9 +1411,6 @@ abstract class Buffer
 
        redef type SELFTYPE: Buffer is fixed
 
-       # Specific implementations MUST set this to `true` in order to invalidate caches
-       protected var is_dirty = true
-
        # Copy-On-Write flag
        #
        # If the `Buffer` was to_s'd, the next in-place altering
@@ -1478,6 +1486,13 @@ abstract class Buffer
        # Letters that follow a letter are lowercased
        # Letters that follow a non-letter are upcased.
        #
+       # If `keep_upper = true`, uppercase letters are not lowercased.
+       #
+       # When `src` is specified, this method reads from `src` instead of `self`
+       # but it still writes the result to the beginning of `self`.
+       # This requires `self` to have the capacity to receive all of the
+       # capitalized content of `src`.
+       #
        # SEE: `Char::is_letter` for the definition of a letter.
        #
        #     var b = new FlatBuffer.from("jAVAsCriPt")
@@ -1489,31 +1504,77 @@ abstract class Buffer
        #     b = new FlatBuffer.from("ab_c -ab0c ab\nc")
        #     b.capitalize
        #     assert b == "Ab_C -Ab0C Ab\nC"
-       fun capitalize do
+       #
+       #     b = new FlatBuffer.from("12345")
+       #     b.capitalize(src="foo")
+       #     assert b == "Foo45"
+       #
+       #     b = new FlatBuffer.from("preserve my ACRONYMS")
+       #     b.capitalize(keep_upper=true)
+       #     assert b == "Preserve My ACRONYMS"
+       fun capitalize(keep_upper: nullable Bool, src: nullable Text) do
+               src = src or else self
+               var length = src.length
                if length == 0 then return
-               var c = self[0].to_upper
+               keep_upper = keep_upper or else false
+
+               var c = src[0].to_upper
                self[0] = c
                var prev = c
                for i in [1 .. length[ do
                        prev = c
-                       c = self[i]
+                       c = src[i]
                        if prev.is_letter then
-                               self[i] = c.to_lower
+                               if keep_upper then
+                                       self[i] = c
+                               else
+                                       self[i] = c.to_lower
+                               end
                        else
                                self[i] = c.to_upper
                        end
                end
        end
 
-       redef fun hash
-       do
-               if is_dirty then hash_cache = null
-               return super
-       end
-
        # In Buffers, the internal sequence of character is mutable
        # Thus, `chars` can be used to modify the buffer.
        redef fun chars: Sequence[Char] is abstract
+
+       # Appends `length` chars from `s` starting at index `from`
+       #
+       # ~~~nit
+       #       var b = new Buffer
+       #       b.append_substring("abcde", 1, 2)
+       #       assert b == "bc"
+       #       b.append_substring("vwxyz", 2, 3)
+       #       assert b == "bcxyz"
+       #       b.append_substring("ABCDE", 4, 300)
+       #       assert b == "bcxyzE"
+       #       b.append_substring("VWXYZ", 400, 1)
+       #       assert b == "bcxyzE"
+       # ~~~
+       fun append_substring(s: Text, from, length: Int) do
+               if from < 0 then
+                       length += from
+                       from = 0
+               end
+               var ln = s.length
+               if (length + from) > ln then length = ln - from
+               if length <= 0 then return
+               append_substring_impl(s, from, length)
+       end
+
+       # Unsafe version of `append_substring` for performance
+       #
+       # NOTE: Use only if sure about `from` and `length`, no checks
+       # or bound recalculation is done
+       fun append_substring_impl(s: Text, from, length: Int) do
+               var pos = from
+               for i in [0 .. length[ do
+                       self.add s[pos]
+                       pos += 1
+               end
+       end
 end
 
 # View for chars on Buffer objects, extends Sequence
@@ -1755,8 +1816,20 @@ redef class Char
                return cp >= 0xD800 and cp <= 0xDFFF
        end
 
+       # Is `self` a UTF-16 high surrogate ?
+       fun is_hi_surrogate: Bool do
+               var cp = code_point
+               return cp >= 0xD800 and cp <= 0xDBFF
+       end
+
+       # Is `self` a UTF-16 low surrogate ?
+       fun is_lo_surrogate: Bool do
+               var cp = code_point
+               return cp >= 0xDC00 and cp <= 0xDFFF
+       end
+
        # Length of `self` in a UTF-8 String
-       private fun u8char_len: Int do
+       fun u8char_len: Int do
                var c = self.code_point
                if c < 0x80 then return 1
                if c <= 0x7FF then return 2
@@ -2054,7 +2127,12 @@ end
 # see `alpha_comparator`
 private class AlphaComparator
        super Comparator
-       redef fun compare(a, b) do return a.to_s <=> b.to_s
+       redef fun compare(a, b) do
+               if a == b then return 0
+               if a == null then return -1
+               if b == null then return 1
+               return a.to_s <=> b.to_s
+       end
 end
 
 # Stateless comparator that naively use `to_s` to compare things.
@@ -2100,7 +2178,7 @@ redef class NativeString
        # use only when the data has already been verified as valid UTF-8.
        fun to_s_unsafe(length: nullable Int): String is abstract
 
-       # Get a `String` from the raw `bytelen` bytes at `self` with `unilen` Unicode characters
+       # Get a `String` from the raw `byte_length` bytes at `self` with `unilen` Unicode characters
        #
        # The created `String` points to the data at `self`.
        # This method should be used when `self` was allocated by the Nit GC,
@@ -2110,8 +2188,13 @@ redef class NativeString
        # use only when the data has already been verified as valid UTF-8.
        #
        # SEE: `abstract_text::Text` for more info on the difference
-       # between `Text::bytelen` and `Text::length`.
-       fun to_s_full(bytelen, unilen: Int): String is abstract
+       # between `Text::byte_length` and `Text::length`.
+       fun to_s_full(byte_length, unilen: Int): String is abstract
+
+       # Copies the content of `src` to `self`
+       #
+       # NOTE: `self` must be large enough to withold `self.byte_length` bytes
+       fun fill_from(src: Text) do src.copy_to_native(self, src.byte_length, 0, 0)
 end
 
 redef class NativeArray[E]
index 87520c4..53c6e7a 100644 (file)
@@ -40,7 +40,7 @@ redef class FlatText
        protected fun first_byte: Int do return 0
 
        # Last byte of the NativeString
-       protected fun last_byte: Int do return first_byte + _bytelen - 1
+       protected fun last_byte: Int do return first_byte + _byte_length - 1
 
        # Cache of the latest position (char) explored in the string
        var position: Int = 0
@@ -138,7 +138,7 @@ redef class FlatText
                var its = _items
                var max = last_byte
                var pos = first_byte
-               var nlen = extra + _bytelen
+               var nlen = extra + _byte_length
                var nits = new NativeString(nlen)
                var outpos = 0
                while pos <= max do
@@ -225,6 +225,22 @@ redef class FlatText
                                req_esc += 1
                        else if c == 0x5Cu8 then
                                req_esc += 1
+                       else if c == 0x3Fu8 then
+                               var j = pos + 1
+                               if j < length then
+                                       var next = its[j]
+                                       # We ignore `??'` because it will be escaped as `??\'`.
+                                       if
+                                               next == 0x21u8 or
+                                               next == 0x28u8 or
+                                               next == 0x29u8 or
+                                               next == 0x2Du8 or
+                                               next == 0x2Fu8 or
+                                               next == 0x3Cu8 or
+                                               next == 0x3Du8 or
+                                               next == 0x3Eu8
+                                       then req_esc += 1
+                               end
                        else if c < 32u8 then
                                req_esc += 3
                        end
@@ -238,7 +254,7 @@ redef class FlatText
                if ln_extra == 0 then return self.to_s
                var its = _items
                var max = last_byte
-               var nlen = _bytelen + ln_extra
+               var nlen = _byte_length + ln_extra
                var nns = new NativeString(nlen)
                var pos = first_byte
                var opos = 0
@@ -280,6 +296,27 @@ redef class FlatText
                                nns[opos] = 0x5Cu8
                                nns[opos + 1] = 0x5Cu8
                                opos += 2
+                       else if c == 0x3Fu8 then
+                               var j = pos + 1
+                               if j < length then
+                                       var next = its[j]
+                                       # We ignore `??'` because it will be escaped as `??\'`.
+                                       if
+                                               next == 0x21u8 or
+                                               next == 0x28u8 or
+                                               next == 0x29u8 or
+                                               next == 0x2Du8 or
+                                               next == 0x2Fu8 or
+                                               next == 0x3Cu8 or
+                                               next == 0x3Du8 or
+                                               next == 0x3Eu8
+                                       then
+                                               nns[opos] = 0x5Cu8
+                                               opos += 1
+                                       end
+                               end
+                               nns[opos] = 0x3Fu8
+                               opos += 1
                        else if c < 32u8 then
                                nns[opos] = 0x5Cu8
                                nns[opos + 1] = 0x30u8
@@ -369,22 +406,26 @@ redef class FlatText
                end
                return res
        end
+
+       redef fun copy_to_native(dst, n, src_off, dst_off) do
+               _items.copy_to(dst, n, first_byte + src_off, dst_off)
+       end
 end
 
 # Immutable strings of characters.
-class FlatString
+abstract class FlatString
        super FlatText
        super String
 
        # Index at which `self` begins in `_items`, inclusively
        redef var first_byte is noinit
 
-       redef var chars = new FlatStringCharView(self) is lazy
+       redef fun chars do return new FlatStringCharView(self)
 
-       redef var bytes = new FlatStringByteView(self) is lazy
+       redef fun bytes do return new FlatStringByteView(self)
 
-       redef var to_cstring is lazy do
-               var blen = _bytelen
+       redef fun to_cstring do
+               var blen = _byte_length
                var new_items = new NativeString(blen + 1)
                _items.copy_to(new_items, blen, _first_byte, 0)
                new_items[blen] = 0u8
@@ -392,7 +433,7 @@ class FlatString
        end
 
        redef fun reversed do
-               var b = new FlatBuffer.with_capacity(_bytelen + 1)
+               var b = new FlatBuffer.with_capacity(_byte_length + 1)
                var i = _length - 1
                while i >= 0 do
                        b.add self.fetch_char_at(i)
@@ -405,22 +446,13 @@ class FlatString
 
        redef fun fast_cstring do return _items.fast_cstring(_first_byte)
 
-       redef fun substring_from(from) do
-               if from >= self._length then return empty
-               if from <= 0 then return self
-               var c = char_to_byte_index(from)
-               var st = c - _first_byte
-               var fln = bytelen - st
-               return new FlatString.full(items, fln, c, _length - from)
-       end
-
        redef fun substring(from, count)
        do
                if count <= 0 then return ""
 
                if from < 0 then
                        count += from
-                       if count < 0 then return ""
+                       if count <= 0 then return ""
                        from = 0
                end
 
@@ -457,7 +489,7 @@ class FlatString
 
        redef fun to_upper
        do
-               var outstr = new FlatBuffer.with_capacity(self._bytelen + 1)
+               var outstr = new FlatBuffer.with_capacity(self._byte_length + 1)
 
                var mylen = _length
                var pos = 0
@@ -472,7 +504,7 @@ class FlatString
 
        redef fun to_lower
        do
-               var outstr = new FlatBuffer.with_capacity(self._bytelen + 1)
+               var outstr = new FlatBuffer.with_capacity(self._byte_length + 1)
 
                var mylen = _length
                var pos = 0
@@ -498,26 +530,21 @@ class FlatString
        #
        # `_items` will be used as is, without copy, to retrieve the characters of the string.
        # Aliasing issues is the responsibility of the caller.
-       private init with_infos(items: NativeString, bytelen, from: Int)
+       private new with_infos(items: NativeString, byte_length, from: Int)
        do
-               self._items = items
-               self._bytelen = bytelen
-               _first_byte = from
-               _bytepos = from
-               _length = _items.utf8_length(_first_byte, bytelen)
+               var len = items.utf8_length(from, byte_length)
+               if byte_length == len then return new ASCIIFlatString.full_data(items, byte_length, from, len)
+               return new UnicodeFlatString.full_data(items, byte_length, from, len)
        end
 
        # Low-level creation of a new string with all the data.
        #
        # `_items` will be used as is, without copy, to retrieve the characters of the string.
        # Aliasing issues is the responsibility of the caller.
-       private init full(items: NativeString, bytelen, from, length: Int)
+       private new full(items: NativeString, byte_length, from, length: Int)
        do
-               self._items = items
-               self._length = length
-               self._bytelen = bytelen
-               _first_byte = from
-               _bytepos = from
+               if byte_length == length then return new ASCIIFlatString.full_data(items, byte_length, from, length)
+               return new UnicodeFlatString.full_data(items, byte_length, from, length)
        end
 
        redef fun ==(other)
@@ -526,9 +553,9 @@ class FlatString
 
                if self.object_id == other.object_id then return true
 
-               var my_length = _bytelen
+               var my_length = _byte_length
 
-               if other._bytelen != my_length then return false
+               if other._byte_length != my_length then return false
 
                var my_index = _first_byte
                var its_index = other.first_byte
@@ -556,8 +583,8 @@ class FlatString
                var myits = _items
                var itsits = other._items
 
-               var mbt = _bytelen
-               var obt = other.bytelen
+               var mbt = _byte_length
+               var obt = other.byte_length
 
                var minln = if mbt < obt then mbt else obt
                var mst = _first_byte
@@ -579,8 +606,8 @@ class FlatString
 
        redef fun +(o) do
                var s = o.to_s
-               var slen = s.bytelen
-               var mlen = _bytelen
+               var slen = s.byte_length
+               var mlen = _byte_length
                var nlen = mlen + slen
                var mits = _items
                var mifrom = _first_byte
@@ -597,24 +624,23 @@ class FlatString
        end
 
        redef fun *(i) do
-               var mybtlen = _bytelen
-               var new_bytelen = mybtlen * i
+               var mybtlen = _byte_length
+               var new_byte_length = mybtlen * i
                var mylen = _length
                var newlen = mylen * i
                var its = _items
                var fb = _first_byte
-               var ns = new NativeString(new_bytelen + 1)
-               ns[new_bytelen] = 0u8
+               var ns = new NativeString(new_byte_length + 1)
+               ns[new_byte_length] = 0u8
                var offset = 0
                while i > 0 do
                        its.copy_to(ns, mybtlen, fb, offset)
                        offset += mybtlen
                        i -= 1
                end
-               return new FlatString.full(ns, new_bytelen, 0, newlen)
+               return new FlatString.full(ns, new_byte_length, 0, newlen)
        end
 
-
        redef fun hash
        do
                if hash_cache == null then
@@ -639,6 +665,80 @@ class FlatString
        redef fun substrings do return new FlatSubstringsIter(self)
 end
 
+# Regular Nit UTF-8 strings
+private class UnicodeFlatString
+       super FlatString
+
+       init full_data(items: NativeString, byte_length, from, length: Int) do
+               self._items = items
+               self._length = length
+               self._byte_length = byte_length
+               _first_byte = from
+               _bytepos = from
+       end
+
+       redef fun substring_from(from) do
+               if from >= self._length then return empty
+               if from <= 0 then return self
+               var c = char_to_byte_index(from)
+               var st = c - _first_byte
+               var fln = byte_length - st
+               return new FlatString.full(items, fln, c, _length - from)
+       end
+end
+
+# Special cases of String where all the characters are ASCII-based
+#
+# Optimizes access operations to O(1) complexity.
+private class ASCIIFlatString
+       super FlatString
+
+       init full_data(items: NativeString, byte_length, from, length: Int) do
+               self._items = items
+               self._length = length
+               self._byte_length = byte_length
+               _first_byte = from
+               _bytepos = from
+       end
+
+       redef fun [](idx) do
+               assert idx < _byte_length and idx >= 0
+               return _items[idx + _first_byte].ascii
+       end
+
+       redef fun substring(from, count) do
+               var ln = _length
+               if count <= 0 then return ""
+               if (count + from) > ln then count = ln - from
+               if count <= 0 then return ""
+               if from < 0 then
+                       count += from
+                       if count <= 0 then return ""
+                       from = 0
+               end
+               return new ASCIIFlatString.full_data(_items, count, from + _first_byte, count)
+       end
+
+       redef fun reversed do
+               var b = new FlatBuffer.with_capacity(_byte_length + 1)
+               var i = _length - 1
+               while i >= 0 do
+                       b.add self[i]
+                       i -= 1
+               end
+               var s = b.to_s.as(FlatString)
+               return s
+       end
+
+       redef fun char_to_byte_index(index) do return index + _first_byte
+
+       redef fun substring_impl(from, count, end_index) do
+               return new ASCIIFlatString.full_data(_items, count, from + _first_byte, count)
+       end
+
+       redef fun fetch_char_at(i) do return _items[i + _first_byte].ascii
+end
+
 private class FlatStringCharReverseIterator
        super IndexedIterator[Char]
 
@@ -752,7 +852,7 @@ private class FlatStringByteView
                # Check that the index (+ _first_byte) is not larger than last_byte
                # In other terms, if the index is valid
                var target = _target
-               assert index >= 0 and index < target._bytelen
+               assert index >= 0 and index < target._byte_length
                var ind = index + target._first_byte
                return target._items[ind]
        end
@@ -774,19 +874,12 @@ class FlatBuffer
        super FlatText
        super Buffer
 
-       redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
-
-       redef var bytes = new FlatBufferByteView(self) is lazy
+       redef fun chars do return new FlatBufferCharView(self)
 
-       private var char_cache: Int = -1
-
-       private var byte_cache: Int = -1
+       redef fun bytes do return new FlatBufferByteView(self)
 
        private var capacity = 0
 
-       # Real items, used as cache for when to_cstring is called
-       private var real_items: NativeString is noinit
-
        redef fun fast_cstring do return _items.fast_cstring(0)
 
        redef fun substrings do return new FlatSubstringsIter(self)
@@ -797,18 +890,18 @@ class FlatBuffer
        # the Copy-On-Write flag `written` is set at true.
        private fun reset do
                var nns = new NativeString(capacity)
-               if _bytelen != 0 then _items.copy_to(nns, _bytelen, 0, 0)
+               if _byte_length != 0 then _items.copy_to(nns, _byte_length, 0, 0)
                _items = nns
                written = false
        end
 
        # Shifts the content of the buffer by `len` bytes to the right, starting at byte `from`
        #
-       # Internal only, does not modify _bytelen or length, this is the caller's responsability
+       # Internal only, does not modify _byte_length or length, this is the caller's responsability
        private fun rshift_bytes(from: Int, len: Int) do
                var oit = _items
                var nit = _items
-               var bt = _bytelen
+               var bt = _byte_length
                if bt + len > capacity then
                        capacity = capacity * 2 + 2
                        nit = new NativeString(capacity)
@@ -819,17 +912,16 @@ class FlatBuffer
 
        # Shifts the content of the buffer by `len` bytes to the left, starting at `from`
        #
-       # Internal only, does not modify _bytelen or length, this is the caller's responsability
+       # Internal only, does not modify _byte_length or length, this is the caller's responsability
        private fun lshift_bytes(from: Int, len: Int) do
                var it = _items
-               it.copy_to(it, _bytelen - from, from, from - len)
+               it.copy_to(it, _byte_length - from, from, from - len)
        end
 
        redef fun []=(index, item)
        do
                assert index >= 0 and index <= _length
                if written then reset
-               is_dirty = true
                if index == _length then
                        add item
                        return
@@ -845,27 +937,28 @@ class FlatBuffer
                else if size_diff < 0 then
                        lshift_bytes(ip + clen, -size_diff)
                end
-               _bytelen += size_diff
+               _byte_length += size_diff
                it.set_char_at(ip, item)
        end
 
        redef fun add(c)
        do
                if written then reset
-               is_dirty = true
                var clen = c.u8char_len
-               var bt = _bytelen
+               var bt = _byte_length
                enlarge(bt + clen)
                _items.set_char_at(bt, c)
-               _bytelen += clen
+               _byte_length += clen
                _length += 1
        end
 
        redef fun clear do
-               is_dirty = true
-               if written then reset
-               _bytelen = 0
+               _byte_length = 0
                _length = 0
+               if written then
+                       _capacity = 16
+                       reset
+               end
        end
 
        redef fun empty do return new Buffer
@@ -874,12 +967,13 @@ class FlatBuffer
        do
                var c = capacity
                if cap <= c then return
-               while c <= cap do c = c * 2 + 2
+               if c <= 16 then c = 16
+               while c <= cap do c = c * 2
                # The COW flag can be set at false here, since
                # it does a copy of the current `Buffer`
                written = false
-               var bln = _bytelen
-               var a = new NativeString(c+1)
+               var bln = _byte_length
+               var a = new NativeString(c)
                if bln > 0 then
                        var it = _items
                        if bln > 0 then it.copy_to(a, bln, 0, 0)
@@ -891,22 +985,18 @@ class FlatBuffer
        redef fun to_s
        do
                written = true
-               var bln = _bytelen
+               var bln = _byte_length
                if bln == 0 then _items = new NativeString(1)
                return new FlatString.full(_items, bln, 0, _length)
        end
 
        redef fun to_cstring
        do
-               if is_dirty then
-                       var bln = _bytelen
-                       var new_native = new NativeString(bln + 1)
-                       new_native[bln] = 0u8
-                       if _length > 0 then _items.copy_to(new_native, bln, 0, 0)
-                       real_items = new_native
-                       is_dirty = false
-               end
-               return real_items
+               var bln = _byte_length
+               var new_native = new NativeString(bln + 1)
+               new_native[bln] = 0u8
+               if _length > 0 then _items.copy_to(new_native, bln, 0, 0)
+               return new_native
        end
 
        # Create a new empty string.
@@ -919,52 +1009,46 @@ class FlatBuffer
        #
        # If `_items` is shared, `written` should be set to true after the creation
        # so that a modification will do a copy-on-write.
-       private init with_infos(items: NativeString, capacity, bytelen, length: Int)
+       private init with_infos(items: NativeString, capacity, byte_length, length: Int)
        do
                self._items = items
                self.capacity = capacity
-               self._bytelen = bytelen
+               self._byte_length = byte_length
                self._length = length
        end
 
        # Create a new string copied from `s`.
        init from(s: Text)
        do
-               _items = new NativeString(s.bytelen)
-               if s isa FlatText then
-                       _items = s._items
-               else
-                       for i in substrings do i.as(FlatString)._items.copy_to(_items, i._bytelen, 0, 0)
-               end
-               _bytelen = s.bytelen
+               _items = new NativeString(s.byte_length)
+               for i in s.substrings do i._items.copy_to(_items, i._byte_length, first_byte, 0)
+               _byte_length = s.byte_length
                _length = s.length
-               _capacity = _bytelen
-               written = true
+               _capacity = _byte_length
        end
 
        # Create a new empty string with a given capacity.
        init with_capacity(cap: Int)
        do
                assert cap >= 0
-               _items = new NativeString(cap + 1)
+               _items = new NativeString(cap)
                capacity = cap
-               _bytelen = 0
+               _byte_length = 0
        end
 
        redef fun append(s)
        do
                if s.is_empty then return
-               is_dirty = true
-               var sl = s.bytelen
-               var nln = _bytelen + sl
+               var sl = s.byte_length
+               var nln = _byte_length + sl
                enlarge(nln)
                if s isa FlatText then
-                       s._items.copy_to(_items, sl, s.first_byte, _bytelen)
+                       s._items.copy_to(_items, sl, s.first_byte, _byte_length)
                else
                        for i in s.substrings do append i
                        return
                end
-               _bytelen = nln
+               _byte_length = nln
                _length += s.length
        end
 
@@ -994,6 +1078,21 @@ class FlatBuffer
                return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
        end
 
+       redef fun append_substring_impl(s, from, length) do
+               if length <= 0 then return
+               if not s isa FlatText then
+                       super
+                       return
+               end
+               var bytest = s.char_to_byte_index(from)
+               var bytend = s.char_to_byte_index(from + length - 1)
+               var btln = bytend - bytest + 1
+               enlarge(btln + _byte_length)
+               s._items.copy_to(_items, btln, bytest, _byte_length)
+               _byte_length += btln
+               _length += length
+       end
+
        redef fun reverse
        do
                written = false
@@ -1004,7 +1103,7 @@ class FlatBuffer
 
        redef fun times(repeats)
        do
-               var bln = _bytelen
+               var bln = _byte_length
                var x = new FlatString.full(_items, bln, 0, _length)
                for i in [1 .. repeats[ do
                        append(x)
@@ -1071,7 +1170,7 @@ private class FlatBufferByteIterator
 
        redef fun index do return curr_pos
 
-       redef fun is_ok do return curr_pos < target._bytelen
+       redef fun is_ok do return curr_pos < target._byte_length
 
        redef fun item do return target_items[curr_pos]
 
@@ -1174,8 +1273,8 @@ redef class NativeString
                return clean_utf8(length)
        end
 
-       redef fun to_s_full(bytelen, unilen) do
-               return new FlatString.full(self, bytelen, 0, unilen)
+       redef fun to_s_full(byte_length, unilen) do
+               return new FlatString.full(self, byte_length, 0, unilen)
        end
 
        redef fun to_s_unsafe(len) do
@@ -1194,7 +1293,6 @@ redef class NativeString
                copy_to(new_self, length, 0, 0)
                var str = new FlatString.with_infos(new_self, length, 0)
                new_self[length] = 0u8
-               str.to_cstring = new_self
                return str
        end
 
@@ -1296,37 +1394,26 @@ redef class NativeString
        #
        # Very unsafe, make sure to have room for this char prior to calling this function.
        private fun set_char_at(pos: Int, c: Char) do
-               if c.code_point < 128 then
-                       self[pos] = c.code_point.to_b
+               var cp = c.code_point
+               if cp < 128 then
+                       self[pos] = cp.to_b
                        return
                end
                var ln = c.u8char_len
-               native_set_char(pos, c, ln)
-       end
-
-       private fun native_set_char(pos: Int, c: Char, ln: Int) `{
-               char* dst = self + pos;
-               switch(ln){
-                       case 1:
-                               dst[0] = c;
-                               break;
-                       case 2:
-                               dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
-                               dst[1] = 0x80 | (c & 0x3F);
-                               break;
-                       case 3:
-                               dst[0] = 0xE0 | ((c & 0xF000) >> 12);
-                               dst[1] = 0x80 | ((c & 0xFC0) >> 6);
-                               dst[2] = 0x80 | (c & 0x3F);
-                               break;
-                       case 4:
-                               dst[0] = 0xF0 | ((c & 0x1C0000) >> 18);
-                               dst[1] = 0x80 | ((c & 0x3F000) >> 12);
-                               dst[2] = 0x80 | ((c & 0xFC0) >> 6);
-                               dst[3] = 0x80 | (c & 0x3F);
-                               break;
-               }
-       `}
+               if ln == 2 then
+                       self[pos] = (0xC0 | ((cp & 0x7C0) >> 6)).to_b
+                       self[pos + 1] = (0x80 | (cp & 0x3F)).to_b
+               else if ln == 3 then
+                       self[pos] = (0xE0 | ((cp & 0xF000) >> 12)).to_b
+                       self[pos + 1] = (0x80 | ((cp & 0xFC0) >> 6)).to_b
+                       self[pos + 2] = (0x80 | (cp & 0x3F)).to_b
+               else if ln == 4 then
+                       self[pos] = (0xF0 | ((cp & 0x1C0000) >> 18)).to_b
+                       self[pos + 1] = (0x80 | ((cp & 0x3F000) >> 12)).to_b
+                       self[pos + 2] = (0x80 | ((cp & 0xFC0) >> 6)).to_b
+                       self[pos + 3] = (0x80 | (cp & 0x3F)).to_b
+               end
+       end
 end
 
 redef class Int
@@ -1368,7 +1455,7 @@ redef class Array[E]
                                continue
                        end
                        var tmp = itsi.to_s
-                       sl += tmp.bytelen
+                       sl += tmp.byte_length
                        na[mypos] = tmp
                        i += 1
                        mypos += 1
@@ -1380,13 +1467,13 @@ redef class Array[E]
                while i < mypos do
                        var tmp = na[i]
                        if tmp isa FlatString then
-                               var tpl = tmp._bytelen
+                               var tpl = tmp._byte_length
                                tmp._items.copy_to(ns, tpl, tmp._first_byte, off)
                                off += tpl
                        else
                                for j in tmp.substrings do
                                        var s = j.as(FlatString)
-                                       var slen = s._bytelen
+                                       var slen = s._byte_length
                                        s._items.copy_to(ns, slen, s._first_byte, off)
                                        off += slen
                                end
@@ -1406,7 +1493,7 @@ redef class NativeArray[E]
                var sl = 0
                var mypos = 0
                while i < l do
-                       sl += na[i].bytelen
+                       sl += na[i].byte_length
                        i += 1
                        mypos += 1
                end
@@ -1417,13 +1504,13 @@ redef class NativeArray[E]
                while i < mypos do
                        var tmp = na[i]
                        if tmp isa FlatString then
-                               var tpl = tmp._bytelen
+                               var tpl = tmp._byte_length
                                tmp._items.copy_to(ns, tpl, tmp._first_byte, off)
                                off += tpl
                        else
                                for j in tmp.substrings do
                                        var s = j.as(FlatString)
-                                       var slen = s._bytelen
+                                       var slen = s._byte_length
                                        s._items.copy_to(ns, slen, s._first_byte, off)
                                        off += slen
                                end
index 7902d11..ebb5661 100644 (file)
@@ -268,23 +268,23 @@ extern class NativeString `{ char* `}
                return endpos
        end
 
-       # Number of UTF-8 characters in `self` starting at `from`, for a length of `bytelen`
-       fun utf8_length(from, bytelen: Int): Int is intern do
+       # Number of UTF-8 characters in `self` starting at `from`, for a length of `byte_length`
+       fun utf8_length(from, byte_length: Int): Int is intern do
                var st = from
                var ln = 0
-               while bytelen > 0 do
-                       while bytelen >= 4 do
+               while byte_length > 0 do
+                       while byte_length >= 4 do
                                var i = fetch_4_chars(st)
                                if i & 0x80808080 != 0 then break
-                               bytelen -= 4
+                               byte_length -= 4
                                st += 4
                                ln += 4
                        end
-                       if bytelen == 0 then break
+                       if byte_length == 0 then break
                        var cln = length_of_char_at(st)
                        st += cln
                        ln += 1
-                       bytelen -= cln
+                       byte_length -= cln
                end
                return ln
        end
index 1a91e00..5ca487a 100644 (file)
@@ -58,7 +58,7 @@ intrude import flat
 #
 # Its purpose is to limit the depth of the `Rope` (this
 # improves performance when accessing/iterating).
-fun maxlen: Int do return 64
+fun maxlen: Int do return 512
 
 # String using a tree-based representation with leaves as `FlatStrings`
 private abstract class Rope
@@ -70,13 +70,13 @@ private class Concat
        super Rope
        super String
 
-       redef var chars is lazy do return new RopeChars(self)
+       redef fun chars do return new RopeChars(self)
 
-       redef var bytes is lazy do return new RopeBytes(self)
+       redef fun bytes do return new RopeBytes(self)
 
        redef var length is noinit
 
-       redef var bytelen is noinit
+       redef var byte_length is noinit
 
        redef fun substrings do return new RopeSubstrings.from(self, 0)
 
@@ -86,17 +86,15 @@ private class Concat
        var flat_cache: FlatString is noinit
 
        # Position of the beginning of `flat_cache` in `self`
-       var flat_last_pos_start: Int = -1
+       var flat_last_pos_start: Int is noinit
 
-       var flat_last_pos_end: Int = -1
-
-       redef var to_cstring is lazy do
-               var len = _bytelen
+       redef fun to_cstring do
+               var len = _byte_length
                var ns = new NativeString(len + 1)
                ns[len] = 0u8
                var off = 0
                for i in substrings do
-                       var ilen = i._bytelen
+                       var ilen = i._byte_length
                        i.as(FlatString)._items.copy_to(ns, ilen, i.as(FlatString)._first_byte, off)
                        off += ilen
                end
@@ -111,11 +109,12 @@ private class Concat
        init do
                var l = _left
                var r = _right
-               length = l.length + r.length
-               _bytelen = l.bytelen + r.bytelen
+               _length = l.length + r.length
+               _byte_length = l.byte_length + r.byte_length
+               _flat_last_pos_start = _length
        end
 
-       redef fun is_empty do return _bytelen == 0
+       redef fun is_empty do return _byte_length == 0
 
        redef fun output do
                _left.output
@@ -131,10 +130,11 @@ private class Concat
        end
 
        redef fun [](i) do
-               assert i >= 0 and i <= _length
+               assert i >= 0 and i < _length
                var flps = _flat_last_pos_start
-               if flps != -1 and i >= flps and i <= _flat_last_pos_end then
-                       return _flat_cache.fetch_char_at(i - flps)
+               if i >= flps then
+                       var fc = _flat_cache
+                       if i < flps + fc._length then return fc.fetch_char_at(i - flps)
                end
                var lf = get_leaf_at(i)
                return lf.fetch_char_at(i - _flat_last_pos_start)
@@ -142,8 +142,9 @@ private class Concat
 
        fun get_leaf_at(pos: Int): FlatString do
                var flps = _flat_last_pos_start
-               if flps != -1 and pos >= flps and pos <= _flat_last_pos_end then
-                       return _flat_cache
+               if pos >= flps then
+                       var fc = _flat_cache
+                       if pos < flps + fc._length then return fc
                end
                var s: String = self
                var st = pos
@@ -160,7 +161,6 @@ private class Concat
                        end
                end
                _flat_last_pos_start = st - pos
-               _flat_last_pos_end = st - pos + s.length - 1
                _flat_cache = s
                return s
        end
@@ -172,14 +172,17 @@ private class Concat
                        from = 0
                end
 
-               var ln = length
+               var ln = _length
                if (count + from) > ln then count = ln - from
                if count <= 0 then return ""
                var end_index = from + count - 1
 
                var flps = _flat_last_pos_start
-               if flps != -1 and from >= flps and end_index <= _flat_last_pos_end then
-                       return _flat_cache.substring_impl(from - flps, count, end_index - flps)
+               if from >= flps then
+                       var fc = _flat_cache
+                       if end_index < flps + fc._length then
+                               return fc.substring_impl(from - flps, count, end_index - flps)
+                       end
                end
 
                var lft = _left
@@ -209,35 +212,28 @@ private class Concat
 
        redef fun +(o) do
                var s = o.to_s
-               var slen = s.bytelen
+               var slen = s.byte_length
                if s isa Concat then
                        return new Concat(self, s)
                else
                        var r = _right
-                       var rlen = r.bytelen
+                       var rlen = r.byte_length
                        if rlen + slen > maxlen then return new Concat(self, s)
                        return new Concat(_left, r + s)
                end
        end
 
        redef fun copy_to_native(dest, n, src_offset, dest_offset) do
-               var subs = new RopeSubstrings.from(self, src_offset)
-               var st = src_offset - subs.pos
-               var off = dest_offset
-               while n > 0 do
-                       var it = subs.item
-                       if n > it.length then
-                               var cplen = it.length - st
-                               it._items.copy_to(dest, cplen, st, off)
-                               off += cplen
-                               n -= cplen
-                       else
-                               it._items.copy_to(dest, n, st, off)
-                               n = 0
-                       end
-                       subs.next
-                       st = 0
+               var l = _left
+               if src_offset < l.byte_length then
+                       var lcopy = l.byte_length - src_offset
+                       lcopy = if lcopy > n then n else lcopy
+                       l.copy_to_native(dest, lcopy, src_offset, dest_offset)
+                       dest_offset += lcopy
+                       n -= lcopy
+                       src_offset = 0
                end
+               _right.copy_to_native(dest, n, src_offset, dest_offset)
        end
 
        # Returns a balanced version of `self`
@@ -303,9 +299,9 @@ class RopeBuffer
        super Rope
        super Buffer
 
-       redef var chars: Sequence[Char] is lazy do return new RopeBufferChars(self)
+       redef fun chars do return new RopeBufferChars(self)
 
-       redef var bytes is lazy do return new RopeBufferBytes(self)
+       redef fun bytes do return new RopeBufferBytes(self)
 
        # The final string being built on the fly
        private var str: String = ""
@@ -338,7 +334,7 @@ class RopeBuffer
        end
 
        # Length of the complete rope in bytes
-       redef var bytelen = 0
+       redef var byte_length = 0
 
        # Length of the mutable part (in bytes)
        #
@@ -360,7 +356,7 @@ class RopeBuffer
                self.str = str
                ns = new NativeString(maxlen)
                buf_size = maxlen
-               _bytelen = str.length
+               _byte_length = str.length
                dumped = 0
        end
 
@@ -391,7 +387,7 @@ class RopeBuffer
                assert i >= 0 and i <= length
                if i == length then add c
                if i < str.length then
-                       _bytelen += c.u8char_len - str[i].u8char_len
+                       _byte_length += c.u8char_len - str[i].u8char_len
                        var s = str
                        var l = s.substring(0, i)
                        var r = s.substring_from(i + 1)
@@ -414,7 +410,7 @@ class RopeBuffer
                                else
                                        ns.copy_to(ns, rpos - st_nxt, st_nxt, st_nxt + delta)
                                end
-                               _bytelen += delta
+                               _byte_length += delta
                                rpos += delta
                        end
                        ns.set_char_at(index, c)
@@ -425,7 +421,7 @@ class RopeBuffer
 
        redef fun clear do
                str = ""
-               _bytelen = 0
+               _byte_length = 0
                rpos = 0
                dumped = 0
                if written then
@@ -466,7 +462,7 @@ class RopeBuffer
        end
 
        redef fun append(s) do
-               var slen = s.bytelen
+               var slen = s.byte_length
                if slen >= maxlen then
                        persist_buffer
                        str += s.to_s
@@ -502,7 +498,7 @@ class RopeBuffer
                end
                ns.set_char_at(rp, c)
                rp += cln
-               _bytelen += cln
+               _byte_length += cln
                rpos = rp
        end
 
@@ -581,8 +577,8 @@ redef class FlatString
 
        redef fun +(o) do
                var s = o.to_s
-               var slen = s.bytelen
-               var mlen = _bytelen
+               var slen = s.byte_length
+               var mlen = _byte_length
                if slen == 0 then return self
                if mlen == 0 then return s
                var nlen = slen + mlen
@@ -598,7 +594,7 @@ redef class FlatString
                        return new FlatString.full(ns, nlen, 0, length + s.length)
                else if s isa Concat then
                        var sl = s._left
-                       var sllen = sl.bytelen
+                       var sllen = sl.byte_length
                        if sllen + mlen > maxlen then return new Concat(self, s)
                        return new Concat(self + sl, s._right)
                else
@@ -670,7 +666,7 @@ private class RopeByteIterator
        var ns: NativeString is noautoinit
        # Substrings of the Rope
        var subs: IndexedIterator[FlatString] is noautoinit
-       # Maximum position to iterate on (e.g. Rope.length)
+       # Maximum position to iterate on (e.g. Rope.byte_length)
        var max: Int is noautoinit
        # Position (char) in the Rope (0-indexed)
        var pos: Int is noautoinit
@@ -680,7 +676,7 @@ private class RopeByteIterator
                pns = pos - subs.index
                self.pos = pos
                ns = subs.item._items
-               max = root.length - 1
+               max = root.byte_length - 1
        end
 
        redef fun item do return ns[pns]
@@ -692,7 +688,7 @@ private class RopeByteIterator
        redef fun next do
                pns += 1
                pos += 1
-               if pns < subs.item._bytelen then return
+               if pns < subs.item._byte_length then return
                if not subs.is_ok then return
                subs.next
                if not subs.is_ok then return
@@ -828,7 +824,7 @@ private class ReverseRopeSubstrings
        redef fun next do
                if pos < 0 then return
                var curr = iter.prev
-               var currit = curr.node
+               var currit = curr.as(not null).node
                while curr != null do
                        currit = curr.node
                        if not currit isa Concat then
@@ -935,14 +931,14 @@ private class RopeSubstrings
        redef fun next do
                pos += str.length
                if pos > max then return
-               var it = iter.prev
+               var it = iter.prev.as(not null)
                var rnod = it.node
                loop
                        if not rnod isa Concat then
                                it.ldone = true
                                it.rdone = true
                                str = rnod.as(FlatString)
-                               iter = it.as(not null)
+                               iter = it
                                break
                        end
                        if not it.ldone then
@@ -954,7 +950,7 @@ private class RopeSubstrings
                                rnod = rnod._right
                                it = new RopeCharIteratorPiece(rnod, false, false, it)
                        else
-                               it = it.prev
+                               it = it.prev.as(not null)
                                rnod = it.node
                                continue
                        end
@@ -984,18 +980,45 @@ private class RopeBytes
 
        redef type SELFTYPE: Concat
 
+       var cache: FlatString is noinit
+
+       var cache_start: Int = -1
+
+       var cache_end: Int = -1
+
        redef fun [](i) do
-               var nod: String = target
+               assert i >= 0 and i < target._byte_length
+               var flps = _cache_start
+               if i >= flps and i <= _cache_end then
+                       return _cache.bytes[i - flps]
+               end
+               var lf = get_leaf_at(i)
+               return lf.bytes[i - _cache_start]
+       end
+
+       fun get_leaf_at(pos: Int): FlatString do
+               var flps = _cache_start
+               if pos >= flps and pos <= _cache_end then
+                       return _cache
+               end
+               var s: String = target
+               var st = pos
                loop
-                       if nod isa FlatString then return nod._items[i]
-                       if not nod isa Concat then abort
-                       var lft = nod._left
-                       if lft.bytelen >= i then
-                               nod = nod._right
+                       if s isa FlatString then break
+                       s = s.as(Concat)
+                       var lft = s._left
+                       var llen = lft.byte_length
+                       if pos >= llen then
+                               s = s._right
+                               pos -= llen
                        else
-                               nod = lft
+                               s = lft
                        end
                end
+               _cache_start = st - pos
+               _cache_end = st - pos + s.byte_length - 1
+               _cache = s
+               return s
        end
 
        redef fun iterator_from(i) do return new RopeByteIterator.from(target, i)
@@ -1094,7 +1117,7 @@ class RopeBufferByteIterator
        # Init the iterator from a RopeBuffer starting from `pos`.
        init from(t: RopeBuffer, pos: Int) do
                ns = t.ns
-               maxpos = t._bytelen
+               maxpos = t._byte_length
                sit = t.str.bytes.iterator_from(pos)
                pns = pos - t.str.length
                index = pos
@@ -1135,7 +1158,7 @@ class RopeBufferByteReverseIterator
        # Init the iterator from a RopeBuffer starting from `pos`.
        init from(tgt: RopeBuffer, pos: Int) do
                sit = tgt.str.bytes.reverse_iterator_from(pos - (tgt.rpos - tgt.dumped))
-               pns = pos - tgt.str.bytelen + tgt.rpos
+               pns = pos - tgt.str.byte_length + tgt.rpos
                index = pos
                ns = tgt.ns
        end
@@ -1164,10 +1187,10 @@ class RopeBufferBytes
        redef type SELFTYPE: RopeBuffer
 
        redef fun [](i) do
-               if i < target.str.bytelen then
+               if i < target.str.byte_length then
                        return target.str.bytes[i]
                else
-                       return target.ns[i - target.str.bytelen]
+                       return target.ns[i - target.str.byte_length]
                end
        end
 
index 62f3b5e..edd90db 100644 (file)
@@ -112,12 +112,12 @@ class BM_Pattern
                var j = from
                while j < n - m + 1 do
                        var i = m - 1 # Cursor in the pattern
-                       while i >= 0 and _motif.chars[i] == s.chars[i + j] do i -= 1
+                       while i >= 0 and _motif[i] == s[i + j] do i -= 1
                        if i < 0 then
                                return j
                        else
                                var gs = _gs[i] # Good shift
-                               var bc = bc(s.chars[i+j]) - m + 1 + i # Bad char
+                               var bc = bc(s[i+j]) - m + 1 + i # Bad char
                                # Both are true, do move to the best
                                if gs > bc then
                                        j += gs
@@ -308,7 +308,7 @@ redef class Char
        do
                var stop = s.length
                while from < stop do
-                       if s.chars[from] == self then return from
+                       if s[from] == self then return from
                        from += 1
                end
                return -1
@@ -334,7 +334,7 @@ redef class Text
                var stop = s.length - length + 1
                while from < stop do
                        var i = length - 1
-                       while i >= 0 and self.chars[i] == s.chars[i + from] do i -= 1
+                       while i >= 0 and self[i] == s[i + from] do i -= 1
                        # Test if we found
                        if i < 0 then return from
                        # Not found so try next one
diff --git a/lib/core/text/test_abstract_text.nit b/lib/core/text/test_abstract_text.nit
new file mode 100644 (file)
index 0000000..c67a991
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+module test_abstract_text is test_suite
+
+import test_suite
+import text
+intrude import ropes
+
+class TestText
+       super TestSuite
+
+       private var factories: Collection[TextFactory] = [
+               new ConcatFactory,
+               new RopeBufferFactory,
+               new FlatBufferFactory
+       : TextFactory]
+
+       fun test_escape_to_c do
+               for f in factories do
+                       assert f.create("abAB12<>&").escape_to_c       == "abAB12<>&"
+                       assert f.create("\n\"'\\").escape_to_c         == "\\n\\\"\\'\\\\"
+                       assert f.create("allo???!").escape_to_c        == "allo??\\?!"
+                       assert f.create("??=??/??'??(??)").escape_to_c == "?\\?=?\\?/??\\'?\\?(?\\?)"
+                       assert f.create("??!??<??>??-").escape_to_c    == "?\\?!?\\?<?\\?>?\\?-"
+               end
+       end
+end
+
+# A factory that creates instances of a particular implementation of `Text`
+interface TextFactory
+
+       # Create a `Text` instance from the specified string
+       fun create(s: String): Text is abstract
+end
+
+
+class ConcatFactory
+       super TextFactory
+
+       redef fun create(s) do return new Concat("", s)
+end
+
+class RopeBufferFactory
+       super TextFactory
+
+       redef fun create(s) do return new RopeBuffer.from(s)
+end
+
+class FlatBufferFactory
+       super TextFactory
+
+       redef fun create(s) do return new FlatBuffer.from(s)
+end
diff --git a/lib/crapto/crapto.nit b/lib/crapto/crapto.nit
new file mode 100644 (file)
index 0000000..5f3f017
--- /dev/null
@@ -0,0 +1,19 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Cryptographic attacks and utilities.
+module crapto
+
+import english_utils
+import xor
diff --git a/lib/crapto/english_utils.nit b/lib/crapto/english_utils.nit
new file mode 100644 (file)
index 0000000..9f39e18
--- /dev/null
@@ -0,0 +1,80 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Philippe Pépos Petitclerc <ppeposp@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# English language utilities for cryptographic purposes.
+module english_utils
+
+redef class Sys
+       # English letter frequency map
+       var english_freqs: HashMap[Char, Float] is lazy do
+               var freqs = new HashMap[Char, Float]
+
+               freqs['a'] = 0.0651738
+               freqs['b'] = 0.0124248
+               freqs['c'] = 0.0217339
+               freqs['d'] = 0.0349835
+               freqs['e'] = 0.1041442
+               freqs['f'] = 0.0197881
+               freqs['g'] = 0.0158610
+               freqs['h'] = 0.0492888
+               freqs['i'] = 0.0558094
+               freqs['j'] = 0.0009033
+               freqs['k'] = 0.0050529
+               freqs['l'] = 0.0331490
+               freqs['m'] = 0.0202124
+               freqs['n'] = 0.0564513
+               freqs['o'] = 0.0596302
+               freqs['p'] = 0.0137645
+               freqs['q'] = 0.0008606
+               freqs['r'] = 0.0497563
+               freqs['s'] = 0.0515760
+               freqs['t'] = 0.0729357
+               freqs['u'] = 0.0225134
+               freqs['v'] = 0.0082903
+               freqs['w'] = 0.0171272
+               freqs['x'] = 0.0013692
+               freqs['y'] = 0.0145984
+               freqs['z'] = 0.0007836
+               freqs[' '] = 0.1918182
+
+               return freqs
+       end
+end
+
+redef class Text
+
+       # Score `self` according to english's letter frequency.
+       # This function is useful mainly for cryptography but could happen to be helpful
+       # elsewhere.
+       #
+       #     assert "aaaa".english_scoring > "bbbb".english_scoring
+       fun english_scoring: Float do
+
+               var freqs = english_freqs
+               var score = 0.0
+
+               for c in self do
+                       c = c.to_lower
+                       var points = freqs.get_or_null(c)
+                       if points != null then
+                               score += points
+                       end
+               end
+
+               return score
+
+       end
+end
diff --git a/lib/crapto/examples/repeating_key_xor_cipher.txt b/lib/crapto/examples/repeating_key_xor_cipher.txt
new file mode 100644 (file)
index 0000000..cecdb81
--- /dev/null
@@ -0,0 +1,64 @@
+HUIfTQsPAh9PE048GmllH0kcDk4TAQsHThsBFkU2AB4BSWQgVB0dQzNTTmVS
+BgBHVBwNRU0HBAxTEjwMHghJGgkRTxRMIRpHKwAFHUdZEQQJAGQmB1MANxYG
+DBoXQR0BUlQwXwAgEwoFR08SSAhFTmU+Fgk4RQYFCBpGB08fWXh+amI2DB0P
+QQ1IBlUaGwAdQnQEHgFJGgkRAlJ6f0kASDoAGhNJGk9FSA8dDVMEOgFSGQEL
+QRMGAEwxX1NiFQYHCQdUCxdBFBZJeTM1CxsBBQ9GB08dTnhOSCdSBAcMRVhI
+CEEATyBUCHQLHRlJAgAOFlwAUjBpZR9JAgJUAAELB04CEFMBJhAVTQIHAh9P
+G054MGk2UgoBCVQGBwlTTgIQUwg7EAYFSQ8PEE87ADpfRyscSWQzT1QCEFMa
+TwUWEXQMBk0PAg4DQ1JMPU4ALwtJDQhOFw0VVB1PDhxFXigLTRkBEgcKVVN4
+Tk9iBgELR1MdDAAAFwoFHww6Ql5NLgFBIg4cSTRWQWI1Bk9HKn47CE8BGwFT
+QjcEBx4MThUcDgYHKxpUKhdJGQZZVCFFVwcDBVMHMUV4LAcKQR0JUlk3TwAm
+HQdJEwATARNFTg5JFwQ5C15NHQYEGk94dzBDADsdHE4UVBUaDE5JTwgHRTkA
+Umc6AUETCgYAN1xGYlUKDxJTEUgsAA0ABwcXOwlSGQELQQcbE0c9GioWGgwc
+AgcHSAtPTgsAABY9C1VNCAINGxgXRHgwaWUfSQcJABkRRU8ZAUkDDTUWF01j
+OgkRTxVJKlZJJwFJHQYADUgRSAsWSR8KIgBSAAxOABoLUlQwW1RiGxpOCEtU
+YiROCk8gUwY1C1IJCAACEU8QRSxORTBSHQYGTlQJC1lOBAAXRTpCUh0FDxhU
+ZXhzLFtHJ1JbTkoNVDEAQU4bARZFOwsXTRAPRlQYE042WwAuGxoaAk5UHAoA
+ZCYdVBZ0ChQLSQMYVAcXQTwaUy1SBQsTAAAAAAAMCggHRSQJExRJGgkGAAdH
+MBoqER1JJ0dDFQZFRhsBAlMMIEUHHUkPDxBPH0EzXwArBkkdCFUaDEVHAQAN
+U29lSEBAWk44G09fDXhxTi0RAk4ITlQbCk0LTx4cCjBFeCsGHEETAB1EeFZV
+IRlFTi4AGAEORU4CEFMXPBwfCBpOAAAdHUMxVVUxUmM9ElARGgZBAg4PAQQz
+DB4EGhoIFwoKUDFbTCsWBg0OTwEbRSonSARTBDpFFwsPCwIATxNOPBpUKhMd
+Th5PAUgGQQBPCxYRdG87TQoPD1QbE0s9GkFiFAUXR0cdGgkADwENUwg1DhdN
+AQsTVBgXVHYaKkg7TgNHTB0DAAA9DgQACjpFX0BJPQAZHB1OeE5PYjYMAg5M
+FQBFKjoHDAEAcxZSAwZOBREBC0k2HQxiKwYbR0MVBkVUHBZJBwp0DRMDDk5r
+NhoGACFVVWUeBU4MRREYRVQcFgAdQnQRHU0OCxVUAgsAK05ZLhdJZChWERpF
+QQALSRwTMRdeTRkcABcbG0M9Gk0jGQwdR1ARGgNFDRtJeSchEVIDBhpBHQlS
+WTdPBzAXSQ9HTBsJA0UcQUl5bw0KB0oFAkETCgYANlVXKhcbC0sAGgdFUAIO
+ChZJdAsdTR0HDBFDUk43GkcrAAUdRyonBwpOTkJEUyo8RR8USSkOEENSSDdX
+RSAdDRdLAA0HEAAeHQYRBDYJC00MDxVUZSFQOV1IJwYdB0dXHRwNAA9PGgMK
+OwtTTSoBDBFPHU54W04mUhoPHgAdHEQAZGU/OjV6RSQMBwcNGA5SaTtfADsX
+GUJHWREYSQAnSARTBjsIGwNOTgkVHRYANFNLJ1IIThVIHQYKAGQmBwcKLAwR
+DB0HDxNPAU94Q083UhoaBkcTDRcAAgYCFkU1RQUEBwFBfjwdAChPTikBSR0T
+TwRIEVIXBgcURTULFk0OBxMYTwFUN0oAIQAQBwkHVGIzQQAGBR8EdCwRCEkH
+ElQcF0w0U05lUggAAwANBxAAHgoGAwkxRRMfDE4DARYbTn8aKmUxCBsURVQf
+DVlOGwEWRTIXFwwCHUEVHRcAMlVDKRsHSUdMHQMAAC0dCAkcdCIeGAxOazkA
+BEk2HQAjHA1OAFIbBxNJAEhJBxctDBwKSRoOVBwbTj8aQS4dBwlHKjUECQAa
+BxscEDMNUhkBC0ETBxdULFUAJQAGARFJGk9FVAYGGlMNMRcXTRoBDxNPeG43
+TQA7HRxJFUVUCQhBFAoNUwctRQYFDE43PT9SUDdJUydcSWRtcwANFVAHAU5T
+FjtFGgwbCkEYBhlFeFsABRcbAwZOVCYEWgdPYyARNRcGAQwKQRYWUlQwXwAg
+ExoLFAAcARFUBwFOUwImCgcDDU5rIAcXUj0dU2IcBk4TUh0YFUkASEkcC3QI
+GwMMQkE9SB8AMk9TNlIOCxNUHQZCAAoAHh1FXjYCDBsFABkOBkk7FgALVQRO
+D0EaDwxOSU8dGgI8EVIBAAUEVA5SRjlUQTYbCk5teRsdRVQcDhkDADBFHwhJ
+AQ8XClJBNl4AC1IdBghVEwARABoHCAdFXjwdGEkDCBMHBgAwW1YnUgAaRyon
+B0VTGgoZUwE7EhxNCAAFVAMXTjwaTSdSEAESUlQNBFJOZU5LXHQMHE0EF0EA
+Bh9FeRp5LQdFTkAZREgMU04CEFMcMQQAQ0lkay0ABwcqXwA1FwgFAk4dBkIA
+CA4aB0l0PD1MSQ8PEE87ADtbTmIGDAILAB0cRSo3ABwBRTYKFhROHUETCgZU
+MVQHYhoGGksABwdJAB0ASTpFNwQcTRoDBBgDUkksGioRHUkKCE5THEVCC08E
+EgF0BBwJSQoOGkgGADpfADETDU5tBzcJEFMLTx0bAHQJCx8ADRJUDRdMN1RH
+YgYGTi5jMURFeQEaSRAEOkURDAUCQRkKUmQ5XgBIKwYbQFIRSBVJGgwBGgtz
+RRNNDwcVWE8BT3hJVCcCSQwGQx9IBE4KTwwdASEXF01jIgQATwZIPRpXKwYK
+BkdEGwsRTxxDSToGMUlSCQZOFRwKUkQ5VEMnUh0BR0MBGgAAZDwGUwY7CBdN
+HB5BFwMdUz0aQSwWSQoITlMcRUILTxoCEDUXF01jNw4BTwVBNlRBYhAIGhNM
+EUgIRU5CRFMkOhwGBAQLTVQOHFkvUkUwF0lkbXkbHUVUBgAcFA0gRQYFCBpB
+PU8FQSsaVycTAkJHYhsRSQAXABxUFzFFFggICkEDHR1OPxoqER1JDQhNEUgK
+TkJPDAUAJhwQAg0XQRUBFgArU04lUh0GDlNUGwpOCU9jeTY1HFJARE4xGA4L
+ACxSQTZSDxsJSw1ICFUdBgpTNjUcXk0OAUEDBxtUPRpCLQtFTgBPVB8NSRoK
+SREKLUUVAklkERgOCwAsUkE2Ug8bCUsNSAhVHQYKUyI7RQUFABoEVA0dWXQa
+Ry1SHgYOVBFIB08XQ0kUCnRvPgwQTgUbGBwAOVREYhAGAQBJEUgETgpPGR8E
+LUUGBQgaQRIaHEshGk03AQANR1QdBAkAFwAcUwE9AFxNY2QxGA4LACxSQTZS
+DxsJSw1ICFUdBgpTJjsIF00GAE1ULB1NPRpPLF5JAgJUVAUAAAYKCAFFXjUe
+DBBOFRwOBgA+T04pC0kDElMdC0VXBgYdFkU2CgtNEAEUVBwTWXhTVG5SGg8e
+AB0cRSo+AwgKRSANExlJCBQaBAsANU9TKxFJL0dMHRwRTAtPBRwQMAAATQcB
+FlRlIkw5QwA2GggaR0YBBg5ZTgIcAAw3SVIaAQcVEU8QTyEaYy0fDE4ITlhI
+Jk8DCkkcC3hFMQIEC0EbAVIqCFZBO1IdBgZUVA4QTgUWSR4QJwwRTWM=
diff --git a/lib/crapto/examples/repeating_key_xor_solve.nit b/lib/crapto/examples/repeating_key_xor_solve.nit
new file mode 100644 (file)
index 0000000..f88e2d9
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base64
+import crapto
+
+# Check usage
+if args.length != 1 then
+       print "Usage: repeating_key_xor_solve <cipher_file>"
+       exit 1
+end
+
+# Read the cipher from the file
+var cipher_bytes = args[0].to_path.read_all_bytes.decode_base64
+
+# Create a RepeatingKeyXorCipher object to manipulate your ciphertext
+var xorcipher = new RepeatingKeyXorCipher
+xorcipher.ciphertext = cipher_bytes
+
+# Try to find the best candidate key
+xorcipher.find_key
+
+# Decrypt the cipher according to the found key
+xorcipher.decrypt
+
+# Check the resulting plaintext out...
+print xorcipher.plaintext
diff --git a/lib/crapto/package.ini b/lib/crapto/package.ini
new file mode 100644 (file)
index 0000000..2f9d180
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=crapto
+tags=crypto
+maintainer=Philippe Pépos Petitclerc <ppeposp@gmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/crapto
+git=https://github.com/nitlang/nit.git
+git.directory=lib/crapto/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/crapto/xor.nit b/lib/crapto/xor.nit
new file mode 100644 (file)
index 0000000..8745c1a
--- /dev/null
@@ -0,0 +1,150 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Cryptographic attacks and utilities for XOR-based algorithms.
+module xor
+
+import combinations
+import crypto
+import english_utils
+
+redef class SingleByteXorCipher
+       # Tries to find key using frequency analysis on all possible plaintexts.
+       # Populates `self.key`
+       fun find_key do
+
+               # Accumulate best result
+               var max = 0.0
+               var best = 0.to_b
+
+               # Iterate on possible values for a byte
+               var xor_b = new Bytes.with_capacity(1)
+               for b in [0 .. 255] do
+                       # Need `Bytes` to pass to xor
+                       xor_b[0] = b.to_b
+
+                       # Xor and evaluate result
+                       var xored = ciphertext.xorcipher(xor_b)
+                       var result = xored.to_s.english_scoring
+                       if result > max then
+                               max = result
+                               best = b.to_b
+                       end
+               end
+
+               self.key = best
+
+       end
+end
+
+redef class RepeatingKeyXorCipher
+       # Attempts to find the key by using frequency analysis on the resulting plaintexts.
+       # Best key lengths are estimated using the hamming distance of blocks of keylength bytes.
+       # Ciphertext is then transposed in such a way that it can be solved by sequences of
+       # SingleByteXor (one for each offset in the key).
+       #
+       # `min_keylength` and `max_keylength` are limits as to what key lengths are tested.
+       # `considered_keylength_count` is the number of best key lengths that are kept to be
+       # analysed by the SingleByteXor frequency analysis.
+       fun find_key(min_keylength, max_keylength, considered_keylength_count: nullable Int)  do
+
+               # Set default values
+               if min_keylength == null then min_keylength = 2
+               if max_keylength == null then max_keylength = 40
+               if considered_keylength_count == null then considered_keylength_count = 3
+
+               # Find the best candidate key lengths based on the normalized hamming distances
+               var best_sizes = get_normalized_hamming_distances(min_keylength, max_keylength, considered_keylength_count)
+
+               var best = 0.0
+               var best_key: nullable Bytes = null
+               for ks in best_sizes do
+
+                       # Rearrange ciphertext to be in SingleByteXORable blocks
+                       var transposed = transpose_ciphertext(ks)
+
+                       var key = new Bytes.empty
+                       for slot in transposed do
+                               var sbx = new SingleByteXorCipher
+                               sbx.ciphertext = slot
+                               sbx.find_key
+                               key.add sbx.key
+                       end
+
+                       # Evaluate the resulting plaintext based on english frequency analysis
+                       var eng_score = ciphertext.xorcipher(key).to_s.english_scoring
+                       if eng_score > best then
+                               best_key = key
+                               best = eng_score
+                       end
+
+                       assert best_key != null
+                       self.key = best_key
+
+               end
+       end
+
+       # Computes the normalized hamming distances between blocks of ciphertext of length between `min_length` and `max_length`.
+       # The `considered_keylength_count` smallest results are returned
+       private fun get_normalized_hamming_distances(min_keylength, max_keylength, considered_keylength_count: Int): Array[Int] do
+
+               var normalized_distances = new HashMap[Float, Int]
+
+               # Iterate over all given key lengths
+               for ks in [min_keylength .. max_keylength[ do
+
+                       # Initialize the blocks of size `ks`
+                       var blocks = new Array[Bytes]
+                       while (blocks.length + 1) * ks < ciphertext.length do
+                               blocks.add(ciphertext.slice(blocks.length * ks, ks))
+                       end
+
+                       # Compute the normalized hamming distance with all block combinations
+                       var pairs = new CombinationCollection[Bytes](blocks, 2)
+                       var hamming_dists = new Array[Float]
+                       for p in pairs do
+                               hamming_dists.add(p[0].hamming_distance(p[1]).to_f / ks.to_f)
+                       end
+
+                       # Normalize the results based on the number of blocks
+                       var normalized = 0.0
+                       for dist in hamming_dists do normalized += dist
+                       normalized /= hamming_dists.length.to_f
+                       normalized_distances[normalized] = ks
+
+               end
+
+               # Collect the best candidates
+               var distances = normalized_distances.keys.to_a
+               default_comparator.sort(distances)
+               var best_distances = distances.subarray(0, considered_keylength_count)
+               var best_sizes = [for d in best_distances do normalized_distances[d]]
+
+               return best_sizes
+       end
+
+       # Returns a rearranged format of the ciphertext where every byte of a slot will be XORed with the same offset of a key of length `keylength`.
+       private fun transpose_ciphertext(keylength: Int): Array[Bytes] do
+               var transposed = new Array[Bytes]
+               for i in [0 .. keylength[ do
+                       transposed.add(new Bytes.empty)
+               end
+
+               for byte_idx in [0 .. ciphertext.length[ do
+                       transposed[byte_idx % keylength].add(ciphertext[byte_idx])
+               end
+
+               return transposed
+       end
+end
similarity index 89%
rename from lib/crypto.nit
rename to lib/crypto/basic_ciphers.nit
index 357eb99..e4a133c 100644 (file)
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Mix of all things cryptography-related
-module crypto
+# Basic cryptographic ciphers and utilities.
+module basic_ciphers
 
 redef class Char
        # Rotates self of `x`
@@ -154,32 +154,24 @@ redef class Text
                end
                return arr.to_s
        end
+end
 
-       # Returns `self` xored with `key`
-       #
-       # The shortest of the two is cycled through until the longest has been
-       # completely xored.
-       #
-       #     assert "goodmorning".xor(" ".to_bytes) == "GOODMORNING"
-       fun xor(key: SequenceRead[Byte]): Text do
-               var xored = new Bytes.with_capacity(bytelen.max(key.length))
-
-               var shortest: SequenceRead[Byte]
-               var longest: SequenceRead[Byte]
-
-               if key.length > self.length then
-                       shortest = self.to_bytes
-                       longest = key
-               else
-                       shortest = key
-                       longest = self.to_bytes
-               end
-
-               for i in longest.length.times do
-                       xored.add(longest[i] ^ shortest[i % shortest.length])
+redef class Bytes
+       # Computes the edit/hamming distance of two sequences of bytes.
+       #
+       #     assert "this is a test".to_bytes.hamming_distance("wokka wokka!!!".bytes) == 37
+       #     assert "this is a test".to_bytes.hamming_distance("this is a test".bytes) == 0
+       #
+       fun hamming_distance(other: SequenceRead[Byte]): Int do
+               var diff = 0
+               for idx in self.length.times do
+                       var res_byte = self[idx] ^ other[idx]
+                       for bit in [0..8[ do
+                               if res_byte & 1u8 == 1u8 then diff += 1
+                               res_byte = res_byte >> 1
+                       end
                end
-
-               return xored.to_s
+               return diff
        end
 end
 
diff --git a/lib/crypto/crypto.nit b/lib/crypto/crypto.nit
new file mode 100644 (file)
index 0000000..4709403
--- /dev/null
@@ -0,0 +1,19 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Mix of all things cryptography-related
+module crypto
+
+import basic_ciphers
+import xor_ciphers
similarity index 89%
rename from lib/crypto.ini
rename to lib/crypto/package.ini
index e8b4cf1..24f6b14 100644 (file)
@@ -6,6 +6,6 @@ license=Apache-2.0
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/lib/crypto.nit
 git=https://github.com/nitlang/nit.git
-git.directory=lib/crypto.nit
+git.directory=lib/crypto/crypto.nit
 homepage=http://nitlanguage.org
 issues=https://github.com/nitlang/nit/issues
diff --git a/lib/crypto/xor_ciphers.nit b/lib/crypto/xor_ciphers.nit
new file mode 100644 (file)
index 0000000..1c4807c
--- /dev/null
@@ -0,0 +1,88 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# XOR oriented cryptographic ciphers and utilities.
+module xor_ciphers
+
+redef class Bytes
+       # Returns `self` xored with `key`
+       #
+       # The key is cycled through until the `self` has been completely xored.
+       #
+       #     assert "goodmorning".to_bytes.xorcipher(" ".to_bytes) == "GOODMORNING".bytes
+       fun xorcipher(key: Bytes): Bytes do
+               var xored = new Bytes.with_capacity(self.length)
+
+               for i in self.length.times do
+                       xored.add(self[i] ^ key[i % key.length])
+               end
+
+               return xored
+       end
+end
+
+# Base class to modelize cryptographic ciphers
+abstract class Cipher
+
+       # Encrypted data
+       var ciphertext = new Bytes.empty is writable
+
+       # Unencrypted data
+       var plaintext = new Bytes.empty is writable
+
+       # Encrypt plaintext and populate `self.ciphertext`
+       fun encrypt is abstract
+
+       # Decrypt ciphertext and populate `self.plaintext`
+       fun decrypt is abstract
+
+end
+
+# Simple XOR cipher where the whole plaintext is XORed with a single byte.
+class SingleByteXorCipher
+       super Cipher
+
+       # Cryptographic key used in encryption and decryption.
+       var key: Byte = 0.to_b
+
+       redef fun encrypt do
+               var key_bytes = new Bytes.with_capacity(1)
+               key_bytes.add(key)
+               ciphertext = plaintext.xorcipher(key_bytes)
+       end
+
+       redef fun decrypt do
+               var key_bytes = new Bytes.with_capacity(1)
+               key_bytes.add(key)
+               plaintext = ciphertext.xorcipher(key_bytes)
+       end
+end
+
+# XOR cipher where the key is repeated to match the length of the message.
+class RepeatingKeyXorCipher
+       super Cipher
+
+       # Cryptographic key used in encryption and decryption.
+       var key = new Bytes.empty
+
+       redef fun encrypt do
+               assert key.length > 0
+               ciphertext = plaintext.xorcipher(key)
+       end
+
+       redef fun decrypt do
+               assert key.length > 0
+               plaintext = ciphertext.xorcipher(key)
+       end
+end
index 57590ad..abc4437 100644 (file)
 # CSV document handling.
 module csv
 
-# Specifies a CSV format.
-class CsvFormat
-       # The character that delimits escaped value.
-       #
-       # The delimiter is escaped by doubling it.
-       var delimiter: Char
-
-       # The character that split each cell in a row.
-       var separator: Char
-
-       # The character that ends a row (end of line).
-       var eol: String
+redef class Text
+       # Escape the content of `self` for inclusion in a CSV document
+       private fun escape_to_csv(sep_char, delim_char: Char, eol: String): String do
+               var add_sp = chars_to_escape_csv(sep_char, delim_char, eol)
+               if add_sp == 0 then return to_s
+               var bf = new Buffer.with_cap(add_sp + byte_length)
+               bf.add '"'
+               for i in [0 .. length[ do
+                       var c = self[i]
+                       if c == delim_char then
+                               bf.add c
+                       end
+                       bf.add c
+               end
+               bf.add '"'
+               return bf.to_s
+       end
 
-       # Escape sequence for the delimiter.
-       private var escaping = "{delimiter}{delimiter}" is lazy
+       # How many more bytes should be allocated for CSV escaping ?
+       private fun chars_to_escape_csv(sep_char, delim_char: Char, eol: String): Int do
+               var more_ln = 0
+               var ln = length
+               var need_esc = false
+               var fst_eol = eol.first
+               var i = 0
+               while i < ln do
+                       var c = self[i]
+                       if c == delim_char then more_ln += 1
+                       if c == fst_eol then
+                               need_esc = true
+                               for j in [1 .. eol.length[ do
+                                       i += 1
+                                       c = self[i]
+                                       if c != eol[j] then
+                                               i -= j
+                                               need_esc = false
+                                               break
+                                       end
+                               end
+                       end
+                       if c == sep_char then need_esc = true
+                       i += 1
+               end
+               var more = more_ln * delim_char.u8char_len
+               if need_esc then more += 2
+               return more
+       end
 
-       # Escape the specified cell.
-       private fun escape_cell(cell: String): Text do
-               var result = new RopeBuffer
-               result.add delimiter
-               result.append cell.replace(delimiter, escaping)
-               result.add delimiter
-               return result
+       # Unescape the content of `self` from CSV format to Nit String
+       private fun unescape_csv(delim_char: Char): String do
+               var to_un = chars_to_unescape_csv(delim_char)
+               if to_un == 0 then return to_s
+               var buf = new Buffer.with_cap(byte_length - to_un)
+               var pos = 0
+               var ln = length
+               while pos < ln do
+                       var c = self[pos]
+                       if c == delim_char then pos += 1
+                       buf.add c
+                       pos += 1
+               end
+               return buf.to_s
        end
 
-       # Can the specified value be inserted without any escaping?
-       private fun is_value_clean(value: String): Bool do
-               for c in value.chars do
-                       if c == delimiter then return false
-                       if c == separator then return false
-                       if eol.chars.has(c) then return false
+       # How many bytes should be removed for CSV unescaping ?
+       private fun chars_to_unescape_csv(delim_char: Char): Int do
+               var pos = 0
+               var to_un = 0
+               var ln = length
+               while pos < ln do
+                       var c = self[pos]
+                       if c == delim_char then
+                               pos += 1
+                               to_un += 1
+                       end
+                       pos += 1
                end
-               return true
+               return to_un
        end
 end
 
+# Shared properties by all CSV-related classes
+#
+# This class is basically only here for implementation purposes and should not be used
+# by clients for typing.
+abstract class CsvStream
+       # The character that delimits escaped value.
+       #
+       # The delimiter is escaped by doubling it.
+       var delimiter = '"' is writable
+
+       # The character that split each cell in a record.
+       var separator = ',' is writable
+
+       # The character that ends a record (end of line).
+       var eol = "\n" is writable
+end
+
 # A CSV document representation.
 class CsvDocument
        super Writable
-
-       # The format to use.
-       #
-       # Defaults to `rfc4180`.
-       var format: CsvFormat = rfc4180 is writable
+       super CsvStream
 
        # The header.
        #
        # Contains the name of all fields in this table.
-       var header: Array[String] = new Array[String] is writable
+       var header = new Array[String] is writable, optional
 
        # The list of the records.
        #
        # All records must have the same length than `header`.
-       var records: Array[Array[String]] = new Array[Array[String]]
+       var records = new Array[Array[String]] is writable, optional
 
-       # Replace the header by the specified row.
-       fun set_header(values: Object...) do
-               header.clear
-               for value in values do header.add(value.to_s)
-       end
-
-       # Append the specfied record.
-       fun add_record(values: Object...) do
-               assert values.length == header.length else
-                       sys.stderr.write "CSV error: Header declares {header.length} columns, record contains {values.length} values.\n"
-               end
-               var record = new Array[String]
-               for value in values do record.add(value.to_s)
-               records.add(record)
+       # Adds a new record to document containing the values in `objs`
+       fun add_record(objs: Object...) do
+               var ln = new Array[String].with_capacity(objs.length)
+               for i in objs do ln.add(i.to_s)
+               records.add ln
        end
 
        redef fun write_to(stream) do
-               var writer = new CsvWriter.with_format(stream, format)
-               writer.write_sequence(header)
-               for record in records do writer.write_sequence(record)
+               var s = new CsvWriter(stream)
+               s.separator = separator
+               s.eol = eol
+               s.delimiter = delimiter
+               if not header.is_empty then
+                       s.write_line header
+               end
+               s.write_lines(records)
        end
 
-       # Deprecated alias for `write_to_file`.
-       fun save(file: String) do write_to_file(file)
-
        # Load from the specified stream.
        #
        # Parameters:
        #
        # * `stream`: Input stream.
-       # * `has_header`: Is the first row the header?
-       # * `skip_empty`: Do we skip the empty lines?
-       # For details, see `CsvReader.skip_empty`.
-       fun load_from(stream: Reader, has_header: Bool, skip_empty: Bool) do
-               var reader = new CsvReader.with_format(stream, format)
+       # * `has_header`: Is the first record the header? - defaults to true
+       # * `skip_empty`: Do we skip the empty lines? - defaults to true
+       fun load_from(stream: Reader, has_header: nullable Bool, skip_empty: nullable Bool) do
+               if has_header == null then has_header = true
+               if skip_empty == null then skip_empty = true
+               var reader = new CsvReader(stream)
+               reader.separator = separator
+               reader.eol = eol
+               reader.delimiter = delimiter
                reader.skip_empty = skip_empty
-               if has_header then
-                       if reader.is_ok then
-                               header = reader.item
-                       else
-                               header.clear
-                       end
-               end
-               records.clear
-               for record in reader do records.add(record)
-       end
-
-       # Load from the specified file.
-       #
-       # Parameters:
-       #
-       # * `path`: Path of the file.
-       # * `has_header`: Is the first row the header?
-       # * `skip_empty`: Do we skip the empty lines?
-       fun load(path: String, has_header: Bool, skip_empty: Bool) do
-               var istream = new FileReader.open(path)
-               load_from(istream, has_header, skip_empty)
-               istream.close
        end
 end
 
-# Appends CSV rows to a file.
+# Appends CSV records to a file.
 #
 # By default, uses the format recommended by RFC 4180 (see `rfc4180`).
 #
-# Note: If a row contains only an empty cell, its representation is
+# Note: If a record contains only an empty cell, its representation is
 # undistinguishable from an empty line. This is because the empty values are
 # always written unescaped in order to avoid them to be interpreted as escaped
 # delimiters by some parsers.
@@ -143,240 +176,167 @@ end
 # ~~~nit
 # var out = new StringWriter
 # var writer = new CsvWriter(out)
-# writer.write_row(1, 2.0, "foo\nbar")
-# writer.write_sequence([""])
-# assert out.to_s == """1,2.0,"foo\nbar"\r\n\r\n"""
+# writer.write_elements(1, 2.0, "foo\nbar")
+# writer.write_line([""])
+# assert out.to_s == """1,2.0,"foo\nbar"\n\n"""
 # ~~~
 class CsvWriter
+       super CsvStream
 
        # The output stream.
        var ostream: Writer
 
-       # The format to use.
-       #
-       # Defaults to `rfc4180`.
-       var format: CsvFormat = rfc4180
+       # Write several lines to a stream
+       fun write_lines(lines: Array[Array[Object]]) do for i in lines do write_line i
 
-       # Do we escape all cells (except empty ones)?
-       #
-       # If `false` (the default), escape only cells that contain a metacharacter
-       # of the format. In all cases, empty cells are not escaped. This option
-       # permits to choose between the optimization of the performances (when
-       # `true`) and optimization of the size of the output (when `false`).
-       #
-       # Note: Escaping may not be correctly recognized by some parsers.
-       var always_escape = false is writable
-
-       # Create a new writer with the specified format.
-       init with_format(ostream:Writer, format: CsvFormat) do
-               init(ostream)
-               self.format = format
-       end
-
-       # Append the specified sequence as a row.
+       # Append the elements in `els` as a record.
        #
        # The representation of each cell is determined by `to_s`.
-       fun write_sequence(row: SequenceRead[Object]) do
-               if not row.is_empty then
-                       var i = row.iterator
-                       var separator = format.separator.to_s
-                       write_cell i.item.to_s
-                       i.next
-                       for cell in i do
-                               ostream.write separator
-                               write_cell cell.to_s
-                       end
+       fun write_elements(els: Object...) do
+               var os = ostream
+               var esc = delimiter
+               var sep = separator
+               var eol = eol
+               for i in [0 .. els.length - 1[ do
+                       os.write(els[i].to_s.escape_to_csv(sep, esc, eol))
+                       os.write_char(sep)
                end
-               ostream.write format.eol
+               os.write(els.last.to_s.escape_to_csv(sep, esc, eol))
+               os.write(eol)
        end
 
-       # Append the specified row.
+       # Append the specified record.
        #
        # The representation of each cell is determined by `to_s`.
-       fun write_row(row: Object...) do write_sequence(row)
-
-       # Close the output stream.
-       fun close do ostream.close
-
-       private fun write_cell(cell: String) do
-               if cell.is_empty then return
-               if not always_escape and format.is_value_clean(cell) then
-                       ostream.write cell
-               else
-                       ostream.write format.escape_cell(cell)
+       fun write_line(line: Array[Object]) do
+               var os = ostream
+               var esc = delimiter
+               var sep = separator
+               var eol = eol
+               for i in [0 .. line.length - 1[ do
+                       os.write(line[i].to_s.escape_to_csv(sep, esc, eol))
+                       os.write_char(sep)
                end
+               os.write(line.last.to_s.escape_to_csv(sep, esc, eol))
+               os.write(eol)
        end
 end
 
-# Reads rows from a CSV file.
+# Reads records from a CSV file.
 #
-# By default, uses the format recommended by RFC 4180 (see `rfc4180`).
+# By default, the format recognizes EOLs as `\n`
 #
 # ~~~nit
-# var example = new StringReader("""
-# foo,bar\r
-# "Hello, word!",1234.5 + 42\r
-# "Something\r
-# ""else""\", baz\r
-# """)
-# var reader = new CsvReader(example)
-# var table = new Array[Array[String]]
+# var example = """
+# foo,bar
+# "Hello, word!",1234.5 + 42
+# "Something
+# ""else""\", baz
+# """
+# var reader = new CsvReader.from_string(example)
+# var table = reader.read_all
 #
-# for row in reader do table.add row
-# assert table == [
-#                      ["foo","bar"],
-#                      ["Hello, word!","1234.5 + 42"],
-#                      ["Something\r\n\"else\""," baz"]
-#              ]
+# assert table.header  == ["foo","bar"]
+# assert table.records == [["Hello, word!","1234.5 + 42"],
+#                      ["Something\n\"else\""," baz"]]
 # ~~~
 class CsvReader
-       super Iterator[Array[String]]
+       super CsvStream
 
        # The input stream.
        var istream: Reader
 
-       # The format to use.
-       #
-       # Defaults to `rfc4180`.
-       var format: CsvFormat = rfc4180 is lazy
-
        # Do we skip the empty lines?
        #
        # Note: Even if this attribute is `false`, the presence of an line ending at
-       # end of the last row does not change the number of returned rows.
+       # end of the last record does not change the number of returned record.
        # This is because the line endings are processed as terminators, not as
        # separators. Therefore, when there is more than one line ending at the end
-       # of the file, the additional lines are interpreted as empty rows that
+       # of the file, the additional lines are interpreted as empty records that
        # are skipped only if `skip_empty` is set to `true`.
        #
        # `false` by default.
        var skip_empty: Bool = false is writable
 
-       # The last read row.
-       private var row: nullable Array[String] = null
-
-       # Did we read something?
-       private var started = false
-
-       # Create a new reader with the specified format.
-       init with_format(istream:Reader, format: CsvFormat) do
-               init(istream)
-               self.format = format
-       end
-
-       # Read the first row, if needed.
-       fun prepare do
-               if not started then
-                       row = read_row
-                       started = true
-               end
-       end
-
-       redef fun next do
-               prepare
-               assert is_ok else
-                       sys.stderr.write "Already at the end of the stream.\n"
-               end
-               row = read_row
-       end
-
-       # Return the last read row.
-       redef fun item do
-               prepare
-               return row.as(not null)
-       end
+       # Creates a new CSVReader from a `string` data
+       init from_string(s: String) do init(new StringReader(s))
 
-       redef fun is_ok do
-               prepare
-               return row != null
-       end
-
-       # Free some internal ressources and set `is_ok` to `false`.
+       # Reads the content of the Stream and interprets it as a CSV Document
        #
-       # Do not close the input stream.
-       redef fun finish do row = null
-
-       # Close the input stream.
-       fun close do istream.close
-
-       private fun read_row: nullable Array[String] do
-               if istream.eof then return null
-               var row = new Array[String]
-               var value = new RopeBuffer
-
-               # Number of unescaped characters since the last delimiter or separator.
-               var unescaped = 0
-
-               # Do we read the start of a row?
-               var got_row = false
-
-               # Do we found a delimited string in the current cell?
-               var got_delimiter = false
-
-               loop
-                       var c = istream.read_char
-
-                       if c == null then
-                               if got_row then
-                                       row.add value.to_s
-                                       return row
-                               else
-                                       return null
-                               end
-                       end
-
-                       if c == format.delimiter then
-                               if got_delimiter and unescaped == 0 then
-                                       # Got an escaped delimiter.
-                                       value.add format.delimiter
-                               end
-                               # Read all bytes until the delimiter.
-                               loop
-                                       c = istream.read_char
-                                       assert not_eof: c != null else
-                                               sys.stderr.write "Unexpected end of file before the end of a delimited value.\n"
+       # Optional parameter `has_header` determines whether the first line
+       # of the CSV Document is header data.
+       # Defaults to true
+       fun read_all(has_header: nullable Bool): CsvDocument do
+               var header: nullable Array[String] = null
+               if has_header == null then has_header = true
+               var iss = istream
+               var res_data = new Array[Array[String]]
+               var eol_st = eol.first
+               var line = new Array[String]
+               var esc = delimiter
+               var sep = separator
+               var eol = eol
+               var is_eol = false
+               var eol_buf = new Buffer.with_cap(eol.length)
+               var c = iss.read_char
+               var el = new Buffer
+               while not iss.eof do
+                       if c == null then continue
+                       loop
+                               if c == esc then
+                                       c = iss.read_char
+                                       loop
+                                               if c == esc then
+                                                       c = iss.read_char
+                                                       if c != esc then break
+                                               end
+                                               if c == null then break
+                                               el.add c
+                                               c = iss.read_char
                                        end
-                                       if c == format.delimiter then break
-                                       value.add c
                                end
-                               unescaped = 0
-                               got_row = true
-                               got_delimiter = true
-                       else if c == format.separator then
-                               # Flush the value to the row.
-                               row.add value.to_s
-                               value.clear
-                               unescaped = 0
-                               got_delimiter = false
-                       else
-                               value.add c
-                               unescaped += 1
-                               if unescaped >= format.eol.length and
-                                               value.has_suffix(format.eol) then
-                                       var value_trimed = value.substring(0,
-                                                       value.length - format.eol.length).to_s
-                                       if skip_empty and row.is_empty and
-                                                       value_trimed.is_empty and
-                                                       not got_delimiter then
-                                               # Skip the empty line.
-                                               value.clear
-                                               unescaped = 0
-                                               got_row = false
-                                       else
-                                               row.add value_trimed
-                                               return row
+                               if c == sep then break
+                               if c == eol_st then
+                                       eol_buf.add c.as(not null)
+                                       is_eol = true
+                                       for i in [1 .. eol.length[ do
+                                               c = iss.read_char
+                                               if c == null or c != eol[i] then
+                                                       is_eol = false
+                                                       el.append(eol_buf)
+                                                       eol_buf.clear
+                                                       break
+                                               end
+                                               eol_buf.add c
                                        end
-                               else
-                                       got_row = true
+                                       if not is_eol then continue
+                                       eol_buf.clear
+                                       break
                                end
+                               if c == sep then break
+                               el.add c.as(not null)
+                               c = iss.read_char
+                               if c == null then break
                        end
+                       line.add el.to_s
+                       el.clear
+                       if is_eol or iss.eof then
+                               c = iss.read_char
+                               is_eol = false
+                               if skip_empty and line.is_empty then
+                                       continue
+                               end
+                               if has_header and header == null then
+                                       header = line
+                               else res_data.add line
+                               line = new Array[String]
+                       end
+                       if c == sep then c = iss.read_char
                end
+               if header == null then header = new Array[String]
+               var doc = new CsvDocument
+               doc.header = header
+               doc.records = res_data
+               return doc
        end
 end
-
-# The CSV format recommended by [RFC 4180](https://tools.ietf.org/html/rfc4180).
-#
-# * `delimiter`: `'"'`
-# * `separator`: `','`
-# * `eol`: `"\r\n"`
-fun rfc4180: CsvFormat do return once new CsvFormat('"', ',', "\r\n")
diff --git a/lib/csv/test_csv.nit b/lib/csv/test_csv.nit
deleted file mode 100644 (file)
index d23d007..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# This file is free software, which comes along with NIT. This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. You can modify it is you want, provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You are allowed to redistribute it and sell it, alone or is a part of
-# another product.
-
-# Tests for `csv`.
-module test_csv is test_suite
-
-import test_suite
-import csv
-
-class TestCsvWriter
-       super TestSuite
-
-       # The custom CSV format used in the tests.
-       private var custom_format = new CsvFormat('/', ':', "#")
-
-       # Expect to write `row` as `expected_rfc4180` and as `expected_custom`.
-       #
-       # Parameters:
-       #
-       # * `always_escape`: value of the `always_escape` option.
-       # * `row`: row to write.
-       # * `expected_rfc4180`: expected result in RFC 4180.
-       # * `expected_custom`: expected result in the custom CSV format.
-       private fun expect(always_escape: Bool, row: SequenceRead[String],
-                       expected_rfc4180: String,
-                       expected_custom: String) do
-               var out = new StringWriter
-               var writer = new CsvWriter(out)
-
-               writer.always_escape = always_escape
-               writer.write_sequence(row)
-               assert out.to_s == expected_rfc4180 else
-                       sys.stderr.write "\nFormat: RFC 4180\n"
-                       sys.stderr.write "Expecting: \"{expected_rfc4180.escape_to_nit}\"\n"
-                       sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
-               end
-               writer.close
-
-               out = new StringWriter
-               writer = new CsvWriter.with_format(out, custom_format)
-               writer.always_escape = always_escape
-               writer.write_sequence(row)
-               assert out.to_s == expected_custom else
-                       sys.stderr.write "\nFormat: {custom_format.delimiter}"
-                       sys.stderr.write " {custom_format.separator}"
-                       sys.stderr.write " {custom_format.eol.escape_to_nit}\n"
-                       sys.stderr.write "Expecting: \"{expected_custom.escape_to_nit}\"\n"
-                       sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
-               end
-               writer.close
-       end
-
-       fun test_empty do expect(true, new Array[String], "\r\n", "#")
-
-       fun test_one_cell do expect(true, ["foo/\"\r\n,"],
-                       "\"foo/\"\"\r\n,\"\r\n",
-                       "/foo//\"\r\n,/#")
-
-       fun test_optimize_size_escaped do expect(false, ["foo/\"\r\n,"],
-                       "\"foo/\"\"\r\n,\"\r\n",
-                       "/foo//\"\r\n,/#")
-
-       fun test_optimize_size_eol do expect(false, ["foo\r#\n"],
-                       "\"foo\r#\n\"\r\n",
-                       "/foo\r#\n/#")
-
-       fun test_optimize_size_unescaped do expect(false, ["foo"],
-                       "foo\r\n",
-                       "foo#")
-
-       fun test_multiple_cells do expect(true, ["1", "", "/"],
-                       "\"1\",,\"/\"\r\n",
-                       "/1/::////#")
-
-       fun test_multiple_cells_optimize_size do expect(false, ["1", "", "/"],
-                       "1,,/\r\n",
-                       "1::////#")
-end
-
-class TestCsvReader
-       super TestSuite
-
-       # The custom CSV format used in the tests.
-       private var custom_format = new CsvFormat('/', ':', "#")
-
-       # Expect to read `expected`.
-       #
-       # Parameters:
-       #
-       # * `skip_empty`: value of the `skip_empty` option.
-       # * `modal_escaping`: value of the `modal_escaping` option.
-       # * `input_rfc4180`: input in the RFC 4180 format.
-       # * `input_custom`: input in the custom CSV format.
-       # * `expected`: expected resulting table.
-       private fun expect(skip_empty: Bool,
-                       input_rfc4180: String,
-                       input_custom: String,
-                       expected: SequenceRead[SequenceRead[String]]) do
-               var istream: Reader
-               var reader: CsvReader
-
-               istream = new StringReader(input_rfc4180)
-               reader = new CsvReader(istream)
-               reader.skip_empty = skip_empty
-               assert_table_equals("RFC 4180", reader, expected.iterator)
-
-               istream = new StringReader(input_custom)
-               reader = new CsvReader.with_format(istream, custom_format)
-               reader.skip_empty = skip_empty
-               assert_table_equals("{custom_format.delimiter} " +
-                               "{custom_format.separator} " +
-                               "{custom_format.eol.escape_to_nit}", reader, expected.iterator)
-       end
-
-       # Check if tables are equal.
-       private fun assert_table_equals(format: String,
-                       actual: Iterator[SequenceRead[String]],
-                       expected: Iterator[SequenceRead[String]]) do
-               var i = 0
-
-               for actual_row in actual do
-                       assert expected.is_ok else fail(format,"Too many rows.")
-                       var expected_row = expected.item
-                       assert_row_equals(format, i, actual_row, expected_row)
-                       expected.next
-                       i += 1
-               end
-               assert not expected.is_ok else fail(format, "Not enough rows.")
-               expected.finish
-       end
-
-       # Check if rows are equal.
-       private fun assert_row_equals(format: String,
-                       row_index: Int,
-                       actual: SequenceRead[String],
-                       expected: SequenceRead[String]) do
-               assert actual == expected else
-                       fail(format, """
-At row {{{row_index}}}.
-Expecting: {{{expected.join("|")}}}
-Got: {{{actual.join("|")}}}""")
-               end
-       end
-
-       # Output an error message with an indication of the format used.
-       private fun fail(format: Text, message: Text) do
-               sys.stderr.write "\nFormat: {format}\n"
-               sys.stderr.write message
-               sys.stderr.write "\n"
-       end
-
-       fun test_empty do expect(false, "", "", new Array[Array[String]])
-
-       fun test_empty_eol do expect(false, "\r\n", "#", [[""]])
-
-       fun test_empty_skip do expect(true, "", "", new Array[Array[String]])
-
-       fun test_empty_skip1 do expect(true, "\r\n", "#", new Array[Array[String]])
-
-       fun test_empty_skip2 do expect(true, "\r\n\r\n", "##", new Array[Array[String]])
-
-       fun test_escaped do expect(false, "\"foo/\"\"\r\n,\"\r\n",
-                       "/foo//\"\r\n,/#", [["foo/\"\r\n,"]])
-
-       fun test_unescaped do expect(false, "foo bar\r\n",
-                       "foo bar#", [["foo bar"]])
-
-       fun test_escaped_no_eol do expect(false, "\"foo/\"\"\r\n,\"",
-                       "/foo//\"\r\n,/", [["foo/\"\r\n,"]])
-
-       fun test_unescaped_no_eol do expect(false, "foo bar",
-                       "foo bar", [["foo bar"]])
-
-       fun test_multiple_cells do expect(false, "\"1\",,\"/\"\r\n",
-                       "/1/::////#", [["1", "", "/"]])
-
-       fun test_multiple_cells_unescaped do expect(false, "1,,/\r\n",
-                       "1::////#", [["1", "", "/"]])
-
-       fun test_modal_escaping do expect(false, """a"b""/c","d"e""",
-                       """/ab"///c:d/e/""", [["""ab"/c""", "de"]])
-
-       fun test_skip_start do expect(true, "\r\n1,,/\r\n",
-                       "#1::////#", [["1", "", "/"]])
-
-       fun test_dont_skip_empty_delimited do expect(true, "\"\"\r\n",
-                       "//#", [[""]])
-
-       fun test_dont_skip_multiple_empty_cells do expect(true, ",\r\n",
-                       ":#", [["",""]])
-
-       fun test_mutiple_rows do expect(false, "\"a\r\nb#\",c\r\nd,\r\n,e\r\n",
-                       "/a\r\nb#/:c#d:#:e#", [["a\r\nb#", "c"], ["d", ""], ["", "e"]])
-end
diff --git a/lib/dot/dot.nit b/lib/dot/dot.nit
new file mode 100644 (file)
index 0000000..5f70ed3
--- /dev/null
@@ -0,0 +1,242 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Dot rendering library
+#
+# Example:
+# ~~~
+# import dot
+#
+# var graph = new DotGraph("G", "digraph")
+#
+# var hello = graph.add_node("hello")
+# var world = graph.add_node("world")
+#
+# graph.add_edge(hello, world)
+#
+# print graph.to_dot
+# ~~~
+module dot
+
+# Something that can be rendered in dot format.
+abstract class DotElement
+
+       # Element ID
+       var id: String
+
+       # Element attributes
+       var attrs = new AttributeMap
+
+       # Get attribute value for `key`
+       fun [](key: String): Object do return attrs[key]
+
+       # Set attribute `value` for `key`
+       fun []=(key: String, value: Object) do attrs[key] = value
+
+       # Render `self` to dot format
+       fun to_dot: Text do
+               var res = new Buffer
+               res.append "\"{escape_id}\" "
+               if attrs.not_empty then res.append "[{attrs.to_dot(",")}]"
+               return res.write_to_string
+       end
+
+       # Return `id.escape_to_dot`
+       fun escape_id: String do return id.escape_to_dot
+end
+
+# Map of graph/node/edge attribute that can be rendered to dot.
+class AttributeMap
+       super HashMap[String, Object]
+
+       # Render `self` to dot.
+       #
+       # Use `;` for graph attributes `separator` or `,` for node and edge attributes.
+       fun to_dot(separator: String): Text do
+               var dot = new Buffer
+               for key, value in self do
+                       dot.append "{key}=\"{value.to_s}\"{separator}"
+               end
+               return dot
+       end
+end
+
+# A Graph representation suited for dot format.
+#
+# Creating a new graph
+# ~~~
+# var graph = new DotGraph("G", "digraph")
+# graph["rankdir"] = "BT"
+# graph["ranksep"] = 0.3
+# graph["nodesep"] = 0.3
+# graph.nodes_attrs["fontname"] = "helvetica"
+# graph.edges_attrs["color"] = "gray"
+# ~~~
+#
+# Creating subgraphs
+# ~~~
+# var sub = new DotGraph("cluster_sub", "subgraph")
+# sub["label"] = "process #1"
+#
+# var a0 = sub.add_node("a0")
+# var a1 = sub.add_node("a1")
+# sub.add_edge(a0, a1)
+#
+# graph.add sub
+# ~~~
+class DotGraph
+       super DotElement
+
+       # Graph kind like `graph`, `digraph`, `subgraph`...
+       var kind: String is writable
+
+       # Nodes attributes
+       var nodes_attrs = new AttributeMap
+
+       # Edges attributes
+       var edges_attrs = new AttributeMap
+
+       # Node list by id
+       var nodes = new HashMap[String, DotElement]
+
+       # Add a node to the graph
+       #
+       # If the graph already contains a node with that ID, it will be replaced.
+       fun add(element: DotElement) do
+               nodes[element.id] = element
+       end
+
+       # Edge list
+       #
+       # There can be multiple edges between the same couple of nodes.
+       var edges = new Array[DotEdge]
+
+       # Add a new node to the graph
+       fun add_node(id: String): DotNode do
+               var node = new DotNode(id)
+               add node
+               return node
+       end
+
+       # Add an edge to the graph
+       fun add_edge(from, to: DotElement): DotEdge do
+               var edge = new DotEdge(from, to)
+               add edge
+               return edge
+       end
+
+       redef fun to_dot do
+               var dot = new Buffer
+               dot.append "{kind} \"{id}\" \{\n"
+               if attrs.not_empty then dot.append attrs.to_dot(";\n")
+               if nodes_attrs.not_empty then dot.append "node[{nodes_attrs.to_dot(",")}];\n"
+               if edges_attrs.not_empty then dot.append "edge[{edges_attrs.to_dot(",")}];\n"
+               for id, node in nodes do
+                       dot.append "{node.to_dot};\n"
+               end
+               for edge in edges do
+                       dot.append("{edge.to_dot};\n")
+               end
+               dot.append("\}")
+               return dot
+       end
+
+       # Render `self` as an SVG image
+       fun to_svg: Text do
+               var proc = new ProcessDuplex("dot", "-Tsvg")
+               var svg = proc.write_and_read(to_dot)
+               proc.close
+               proc.wait
+               return svg
+       end
+
+       # Show dot in graphviz (blocking)
+       fun show do
+               var f = new ProcessWriter("dot", "-Txlib")
+               f.write to_dot
+               f.close
+               f.wait
+       end
+end
+
+# A dot node
+#
+# Nodes can be created from scratch
+# ~~~
+# var node = new DotNode("id")
+# node["label"] = "ID"
+# ~~~
+# Then added to a graph
+# ~~~
+# var graph = new DotGraph("G", "digraph")
+# graph.add node
+# ~~~
+# Or can be created directly from an existing graph
+# ~~~
+# var node2 = graph.add_node("id2")
+# node2["label"] = "ID2"
+# ~~~
+class DotNode
+       super DotElement
+end
+
+# A dot edge that links two nodes
+#
+# Edges can be created from scratch
+# ~~~
+# var a1 = new DotNode("a1")
+# var b1 = new DotNode("b1")
+# var edge = new DotEdge(a1, b1)
+# edge["color"] = "blue"
+# ~~~
+# Then added to a graph
+# ~~~
+# var graph = new DotGraph("G", "digraph")
+# graph.add edge
+# ~~~
+# Or can be created directly from an existing graph
+# ~~~
+# var a2 = graph.add_node("a2")
+# var b2 = graph.add_node("b2")
+# var edge2 = graph.add_edge(a2, b2)
+# edge2["color"] = "red"
+# ~~~
+class DotEdge
+       super DotElement
+       autoinit from, to
+
+       # Node this edge is from
+       var from: DotElement
+
+       # Node this edge goes to
+       var to: DotElement
+
+       # Is this edge directed?
+       var directed = true is writable
+
+       redef fun id do return "{from.id}--{to.id}"
+
+       redef fun to_dot do
+               var res = new Buffer
+               res.append "\"{from.escape_id}\" "
+               if directed then
+                       res.append "->"
+               else
+                       res.append "--"
+               end
+               res.append " \"{to.escape_id}\" "
+               if attrs.not_empty then res.append "[{attrs.to_dot(",")}]"
+               return res.write_to_string
+       end
+end
diff --git a/lib/dot/exemples/clusters.nit b/lib/dot/exemples/clusters.nit
new file mode 100644 (file)
index 0000000..993d3ca
--- /dev/null
@@ -0,0 +1,72 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example from http://www.graphviz.org/content/cluster
+
+import dot
+
+var graph = new DotGraph("G", "digraph")
+
+var cl0 = new DotGraph("cluster_0", "subgraph")
+cl0["label"] = "process #1"
+cl0["style"] = "filled"
+cl0["color"] = "lightgrey"
+cl0.nodes_attrs["style"] = "filled"
+cl0.nodes_attrs["color"] = "white"
+
+var a0 = cl0.add_node("a0")
+var a1 = cl0.add_node("a1")
+var a2 = cl0.add_node("a2")
+var a3 = cl0.add_node("a3")
+cl0.add_edge(a0, a1)
+cl0.add_edge(a1, a2)
+cl0.add_edge(a2, a3)
+
+graph.add cl0
+
+var cl1 = new DotGraph("cluster_1", "subgraph")
+cl1["label"] = "process #2"
+cl1["color"] = "blue"
+cl1.nodes_attrs["style"] = "filled"
+
+var b0 = cl1.add_node("b0")
+var b1 = cl1.add_node("b1")
+var b2 = cl1.add_node("b2")
+var b3 = cl1.add_node("b3")
+cl1.add_edge(b0, b1)
+cl1.add_edge(b1, b2)
+cl1.add_edge(b2, b3)
+
+graph.add cl1
+
+var start = graph.add_node("start")
+start["shape"] = "Mdiamond"
+
+var nend = graph.add_node("end")
+nend["shape"] = "Msquare"
+
+graph.add_edge(start, a0)
+graph.add_edge(start, b0)
+graph.add_edge(a1, b3)
+graph.add_edge(b2, a3)
+graph.add_edge(a3, a0)
+graph.add_edge(a3, nend)
+graph.add_edge(b3, nend)
+
+if args.is_empty then
+       print graph.to_dot
+       graph.show
+else
+       graph.to_dot.write_to_file args.first
+end
diff --git a/lib/dot/exemples/hello.nit b/lib/dot/exemples/hello.nit
new file mode 100644 (file)
index 0000000..645620e
--- /dev/null
@@ -0,0 +1,29 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example from http://www.graphviz.org/content/hello
+
+import dot
+
+var graph = new DotGraph("G", "digraph")
+
+var hello = graph.add_node("hello")
+var world = graph.add_node("world")
+graph.add_edge(hello, world)
+
+if args.is_empty then
+       print graph.to_dot
+else
+       graph.to_dot.write_to_file args.first
+end
diff --git a/lib/dot/exemples/undirected_clusters.nit b/lib/dot/exemples/undirected_clusters.nit
new file mode 100644 (file)
index 0000000..4b42567
--- /dev/null
@@ -0,0 +1,48 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example from http://www.graphviz.org/Gallery/undirected/fdpclust.html
+
+import dot
+
+var graph = new DotGraph("G", "graph")
+
+var a = new DotNode("a")
+var b = new DotNode("b")
+var c = new DotNode("c")
+var d = new DotNode("d")
+var e = new DotNode("e")
+var f = new DotNode("f")
+var g = new DotNode("g")
+
+var clA = new DotGraph("clusterA", "subgraph")
+clA.add_edge(a, b).directed = false
+graph.add clA
+
+var clB = new DotGraph("clusterB", "subgraph")
+clB.add_edge(c, d).directed = false
+clA.add clB
+
+var clC = new DotGraph("clusterC", "subgraph")
+clC.add_edge(e, g).directed = false
+graph.add clC
+
+graph.add_edge(e, d).directed = false
+graph.add_edge(f, g).directed = false
+
+if args.is_empty then
+       print graph.to_dot
+else
+       graph.to_dot.write_to_file args.first
+end
diff --git a/lib/dot/package.ini b/lib/dot/package.ini
new file mode 100644 (file)
index 0000000..6824bd6
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=dot
+tags=dot,graphviz,visualization,graph
+maintainer=Alexandre Terrasa <alexandre@moz-code.org>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/dot/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/dot/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
index a1eed2f..245d071 100644 (file)
@@ -162,6 +162,35 @@ class EulerCamera
                yaw = 0.0
                roll = 0.0
        end
+
+       # Convert the position `x, y` on screen, to world coordinates on the plane at `target_z`
+       #
+       # `target_z` defaults to `0.0` and specifies the Z coordinates of the plane
+       # on which to project the screen position `x, y`.
+       #
+       # This method assumes that the camera is looking along the Z axis towards higher values.
+       # Using it in a different orientation can be useful, but won't result in valid
+       # world coordinates.
+       fun camera_to_world(x, y: Numeric, target_z: nullable Float): Point[Float]
+       do
+               # TODO, this method could be tweaked to support projecting the 2D point,
+               # on the near plane (x,y) onto a given distance no matter to orientation
+               # of the camera.
+
+               target_z = target_z or else 0.0
+
+               # Convert from pixel units / window resolution to
+               # units on the near clipping wall to
+               # units on the target wall at Z = 0
+               var near_height = (field_of_view_y/2.0).tan * near
+               var cross_screen_to_near = near_height / (display.height.to_f/2.0)
+               var cross_near_to_target = (position.z - target_z) / near
+               var mod = cross_screen_to_near * cross_near_to_target * 1.72 # FIXME drop the magic number
+
+               var wx = position.x + (x.to_f-display.width.to_f/2.0) * mod
+               var wy = position.y - (y.to_f-display.height.to_f/2.0) * mod
+               return new Point[Float](wx, wy)
+       end
 end
 
 # Orthogonal camera to draw UI objects with services to work with screens of different sizes
index 12d0f15..9fc0ba7 100644 (file)
@@ -17,7 +17,9 @@ module depth
 
 intrude import more_materials
 import more_models
+import model_dimensions
 import particles
+import selection
 
 redef class App
 
@@ -30,7 +32,7 @@ redef class App
                world_camera.near = 0.1
 
                # Prepare programs
-               var programs = [versatile_program, normals_program, explosion_program, smoke_program, static_program: GamnitProgram]
+               var programs = [versatile_program, normals_program, explosion_program, smoke_program, static_program, selection_program: GamnitProgram]
                for program in programs do
                        program.compile_and_link
                        var gamnit_error = program.error
index 03a3970..45863dc 100644 (file)
@@ -103,6 +103,9 @@ class Mesh
        # Coordinates on the texture per vertex
        var texture_coords = new Array[Float] is lazy, writable
 
+       # `GLDrawMode` used to display this mesh, defaults to `gl_TRIANGLES`
+       fun draw_mode: GLDrawMode do return gl_TRIANGLES
+
        # Create an UV sphere of `radius` with `n_meridians` and `n_parallels`
        init uv_sphere(radius: Float, n_meridians, n_parallels: Int)
        do
@@ -154,74 +157,6 @@ class Mesh
                        end
                end
        end
-
-       # Dimensions of this geometry using the min and max of all points on each axis
-       var dimensions: Point3d[Float] is lazy, writable do
-               assert vertices.length % 3 == 0
-
-               var minx = inf
-               var miny = inf
-               var minz = inf
-               var maxx = -inf
-               var maxy = -inf
-               var maxz = -inf
-
-               var i = 0
-               while i < vertices.length do
-                       var x = vertices[i]
-                       i += 1
-                       var y = vertices[i]
-                       i += 1
-                       var z = vertices[i]
-                       i += 1
-
-                       minx = minx.min(x)
-                       miny = miny.min(y)
-                       minz = minz.min(z)
-
-                       maxx = maxx.max(x)
-                       maxy = maxy.max(y)
-                       maxz = maxz.max(z)
-               end
-
-               return new Point3d[Float](maxx-minx, maxy-miny, maxz-minz)
-       end
-
-       # Center of the geometry
-       var center: Point3d[Float] is lazy, writable do
-               assert vertices.length % 3 == 0
-
-               var minx = inf
-               var miny = inf
-               var minz = inf
-               var maxx = -inf
-               var maxy = -inf
-               var maxz = -inf
-
-               var i = 0
-               while i < vertices.length do
-                       var x = vertices[i]
-                       i += 1
-                       var y = vertices[i]
-                       i += 1
-                       var z = vertices[i]
-                       i += 1
-
-                       minx = minx.min(x)
-                       miny = miny.min(y)
-                       minz = minz.min(z)
-
-                       maxx = maxx.max(x)
-                       maxy = maxy.max(y)
-                       maxz = maxz.max(z)
-               end
-
-               var center = new Point3d[Float](
-                       (minx+maxx)/2.0,
-                       (miny+maxy)/2.0,
-                       (minz+maxz)/2.0)
-               return center
-       end
 end
 
 # Source of light
diff --git a/lib/gamnit/depth/model_dimensions.nit b/lib/gamnit/depth/model_dimensions.nit
new file mode 100644 (file)
index 0000000..1e6a8ba
--- /dev/null
@@ -0,0 +1,109 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Dimensions related services for `Model` and `Mesh`
+module model_dimensions
+
+import depth_core
+
+redef class Model
+
+       # Dimensions of the bounding box containing all vertices
+       var dimensions = new Point3d[Float](max.x-min.x, max.y-min.y, max.z-min.z) is lazy, writable
+
+       # Center coordinates of all the vertices
+       var center = new Point3d[Float]((min.x+max.x)/2.0, (min.y+max.y)/2.0, (min.z+max.z)/2.0) is lazy, writable
+
+       # Minimum coordinates of all vertices on each axes
+       #
+       # This is a corner of the bounding box.
+       var min: Point3d[Float] is lazy do
+               var mx = inf
+               var my = inf
+               var mz = inf
+               for leaf in leaves do
+                       var lm = leaf.mesh.min
+                       mx = mx.min(lm.x)
+                       my = my.min(lm.y)
+                       mz = mz.min(lm.z)
+               end
+               return new Point3d[Float](mx, my, mz)
+       end
+
+       # Maximum coordinates of all vertices on each axes
+       #
+       # This is a corner of the bounding box.
+       var max: Point3d[Float] is lazy do
+               var mx = -inf
+               var my = -inf
+               var mz = -inf
+               for leaf in leaves do
+                       var lm = leaf.mesh.max
+                       mx = mx.max(lm.x)
+                       my = my.max(lm.y)
+                       mz = mz.max(lm.z)
+               end
+               return new Point3d[Float](mx, my, mz)
+       end
+end
+
+redef class LeafModel
+
+       redef fun dimensions do return mesh.dimensions
+
+       redef fun center do return mesh.center
+end
+
+redef class Mesh
+
+       # Dimensions of the bounding box containing all vertices
+       var dimensions = new Point3d[Float](max.x-min.x, max.y-min.y, max.z-min.z) is lazy, writable
+
+       # Center coordinates of all the vertices
+       var center = new Point3d[Float]((min.x+max.x)/2.0, (min.y+max.y)/2.0, (min.z+max.z)/2.0) is lazy, writable
+
+       # Minimum coordinates of all vertices on each axes
+       #
+       # This is a corner of the bounding box.
+       var min: Point3d[Float] is lazy do
+               var mx = inf
+               var my = inf
+               var mz = inf
+               var i = 0
+               while i < vertices.length do
+                       mx = mx.min(vertices[i])
+                       my = my.min(vertices[i+1])
+                       mz = mz.min(vertices[i+2])
+                       i += 3
+               end
+               return new Point3d[Float](mx, my, mz)
+       end
+
+       # Maximum coordinates of all vertices on each axes
+       #
+       # This is a corner of the bounding box.
+        var max: Point3d[Float] is lazy do
+               var mx = -inf
+               var my = -inf
+               var mz = -inf
+               var i = 0
+               while i < vertices.length do
+                       mx = mx.max(vertices[i])
+                       my = my.max(vertices[i+1])
+                       mz = mz.max(vertices[i+2])
+                       i += 3
+               end
+               return new Point3d[Float](mx, my, mz)
+       end
+end
index 613ff1a..b77c85e 100644 (file)
@@ -75,9 +75,9 @@ class SmoothMaterial
 
                # Execute draw
                if mesh.indices.is_empty then
-                       glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
                else
-                       glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
                end
        end
 end
@@ -154,11 +154,13 @@ class TexturedMaterial
                                var xd = sample_used_texture.offset_right - xa
                                var ya = sample_used_texture.offset_top
                                var yd = sample_used_texture.offset_bottom - ya
+                               xd *= 0.999
+                               yd *= 0.999
 
                                var tex_coords = new Array[Float].with_capacity(mesh.texture_coords.length)
                                for i in [0..mesh.texture_coords.length/2[ do
                                        tex_coords[i*2]   = xa + xd * mesh.texture_coords[i*2]
-                                       tex_coords[i*2+1] = ya + yd * mesh.texture_coords[i*2+1]
+                                       tex_coords[i*2+1] = 1.0 - (ya + yd * mesh.texture_coords[i*2+1])
                                end
 
                                program.tex_coord.array(tex_coords, 2)
@@ -180,9 +182,9 @@ class TexturedMaterial
                program.camera.uniform(app.world_camera.position.x, app.world_camera.position.y, app.world_camera.position.z)
 
                if mesh.indices.is_empty then
-                       glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
                else
-                       glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
                end
        end
 end
@@ -218,9 +220,9 @@ class NormalsMaterial
                program.normal.array(mesh.normals, 3)
 
                if mesh.indices.is_empty then
-                       glDrawArrays(gl_TRIANGLES, 0, mesh.vertices.length/3)
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
                else
-                       glDrawElements(gl_TRIANGLES, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
                end
        end
 end
index 9f2318a..eeeb258 100644 (file)
@@ -17,6 +17,7 @@ module more_meshes
 
 import geometry
 import depth_core
+import model_dimensions
 
 # Simple flat mesh, sits on the axes X and Z, normal on Y
 class Plane
@@ -124,7 +125,8 @@ class Cube
                var d = [1.0, 0.0]
 
                var texture_coords = new Array[Float]
-               for v in [c, d, a, a, d, b] do for i in 6.times do texture_coords.add_all v
+               var face = [a, c, d, a, d, b]
+               for i in 6.times do for v in face do texture_coords.add_all v
                return texture_coords
        end
 
index a126223..c2b9664 100644 (file)
@@ -50,7 +50,7 @@ class ModelAsset
 
                if leaves.is_empty then
                        # Nothing was loaded, use a cube with the default material
-                       var leaf = new LeafModel(new Cube, new SmoothMaterial.default)
+                       var leaf = placeholder_model
                        leaves.add leaf
                end
        end
@@ -168,7 +168,7 @@ private class ModelFromObj
                # Load textures need for these materials
                for name in texture_names do
                        if not asset_textures_by_name.keys.has(name) then
-                               var tex = new GamnitAssetTexture(name)
+                               var tex = new TextureAsset(name)
                                asset_textures_by_name[name] = tex
                        end
                end
@@ -379,8 +379,13 @@ end
 
 redef class Sys
        # Textures loaded from .mtl files for models
-       var asset_textures_by_name = new Map[String, GamnitAssetTexture]
+       var asset_textures_by_name = new Map[String, TextureAsset]
 
        # All instantiated asset models
        var models = new Set[ModelAsset]
+
+       # Blue cube of 1 unit on each side, acting as placeholder for models failing to load
+       #
+       # This model can be freely used by any `Actor` as placeholder or for debugging.
+       var placeholder_model = new LeafModel(new Cube, new SmoothMaterial.default) is lazy
 end
diff --git a/lib/gamnit/depth/selection.nit b/lib/gamnit/depth/selection.nit
new file mode 100644 (file)
index 0000000..18744ee
--- /dev/null
@@ -0,0 +1,321 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Select `Actor` from a screen coordinate
+#
+# The two main services are `App::visible_at` and ; App::visible_in_center`.
+#
+# This is implemented with simple pixel picking.
+# This algorithm draws each actor in a unique color to the display buffer,
+# using the color as an ID to detect which actor is visible at each pixel.
+#
+# It is implemented at the level of the material,
+# so it can be applied to any _gamnit_ programs.
+# However it is not optimal performance wise,
+# so client programs should implement a more efficient algorithm.
+#
+# By default, the actors are drawn as opaque objects.
+# This behavior can be refined, as does `TexturedMaterial` to use its
+# `diffuse_texture` for partial opacity.
+module selection
+
+# TODO support `sprites` and `ui_sprites`
+
+import more_materials
+intrude import depth_core
+
+redef class App
+
+       # Which `Actor` is at the center of the screen?
+       fun visible_in_center: nullable Actor
+       do
+               var display = display
+               assert display != null
+               return visible_at(display.width/2, display.height/2)
+       end
+
+       # Which `Actor` is on screen at `x, y`?
+       fun visible_at(x, y: Numeric): nullable Actor
+       do
+               var display = display
+               assert display != null
+
+               if not selection_calculated then draw_selection_screen
+
+               x = x.to_i
+               y = y.to_i
+               y = display.height - y
+
+               # Read selection values
+               var data = once new NativeCByteArray(4)
+               glReadPixels(x, y, 1, 1, gl_RGBA, gl_UNSIGNED_BYTE, data)
+               assert_no_gl_error
+
+               var r = display.red_bits
+               var g = display.green_bits
+               var b = display.blue_bits
+
+               # Rebuild ID from pixel color
+               var rv = data[0].to_i >> (8-r)
+               var gv = data[1].to_i >> (8-g) << (r)
+               var bv = data[2].to_i >> (8-b) << (r+g)
+               if data[0].to_i & (2**(8-r)-1) > (2**(8-r-1)) then rv += 1
+               if data[1].to_i & (2**(8-g)-1) > (2**(8-g-1)) then gv += 1 << r
+               if data[2].to_i & (2**(8-b)-1) > (2**(8-b-1)) then bv += 1 << (r+g)
+               var id = rv + gv + bv
+
+               # ID 0 is the background
+               if id == 0 then return null
+
+               # Wrongful selection? This should not happen.
+               if not selection_map.keys.has(id) then
+                       print_error "Gamnit Warning: Invalid selection {id}"
+                       return null
+               end
+
+               return selection_map[id]
+       end
+
+       # Program drawing selection values to the buffer
+       var selection_program = new SelectionProgram
+
+       # Map IDs to actors
+       private var selection_map = new Map[Int, Actor]
+
+       # Is there a valid selection draw in the buffer?
+       private var selection_calculated = false
+
+       # Draw the selection values to the buffer
+       private fun draw_selection_screen
+       do
+               selection_calculated = true
+
+               app.selection_program.use
+               app.selection_program.mvp.uniform app.world_camera.mvp_matrix
+
+               # Set aside previous buffer clear color
+               var user_r = glGetFloatv(gl_COLOR_CLEAR_VALUE, 0)
+               var user_g = glGetFloatv(gl_COLOR_CLEAR_VALUE, 1)
+               var user_b = glGetFloatv(gl_COLOR_CLEAR_VALUE, 2)
+               var user_a = glGetFloatv(gl_COLOR_CLEAR_VALUE, 3)
+
+               glClearColor(0.0, 0.0, 0.0, 1.0)
+               glClear(gl_DEPTH_BUFFER_BIT | gl_COLOR_BUFFER_BIT)
+
+               # TODO restrict the list of actors with a valid ID, maybe with an `active_actors` list?
+
+               var id = 1
+               for actor in actors do
+                       selection_map[id] = actor
+                       for leaf in actor.model.leaves do
+                               leaf.material.draw_selection(actor, leaf, id)
+                       end
+
+                       id += 1
+                       #id += 100 # Debug
+               end
+
+               # Debug, show the selection values for half a second
+               #display.flip
+               #0.5.sleep
+
+               glClearColor(user_r, user_g, user_b, user_a)
+       end
+
+       redef fun frame_core(display)
+       do
+               super
+
+               # Invalidate the selection values
+               selection_calculated = false
+       end
+end
+
+redef class Material
+
+       # Draw `actor` to selection values
+       protected fun draw_selection(actor: Actor, model: LeafModel, id: Int)
+       do
+               var program = app.selection_program
+               var mesh = model.mesh
+
+               draw_selection_texture(actor, model)
+
+               program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
+               program.scale.uniform actor.scale
+
+               program.coord.array_enabled = true
+               program.coord.array(mesh.vertices, 3)
+               program.rotation.uniform new Matrix.rotation(actor.rotation, 0.0, 1.0, 0.0)
+
+               var display = app.display
+               assert display != null
+               var r = display.red_bits
+               var g = display.green_bits
+               var b = display.blue_bits
+
+               # Build ID as a color
+               var p1 = id & ((2**r)-1)
+               var p2 = id >> r & ((2**g)-1)
+               var p3 = id >> (r+g) & ((2**b)-1)
+               program.color_id.uniform(
+                       p1.to_f/((2**r)-1).to_f,
+                       p2.to_f/((2**g)-1).to_f,
+                       p3.to_f/((2**b)-1).to_f, 1.0)
+
+               if mesh.indices.is_empty then
+                       glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
+               else
+                       glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+               end
+       end
+
+       private fun draw_selection_texture(actor: Actor, model: LeafModel)
+       do
+               var program = app.selection_program
+               program.use_map_diffuse.uniform false
+       end
+end
+
+redef class TexturedMaterial
+       redef fun draw_selection_texture(actor, model)
+       do
+               var program = app.selection_program
+               var mesh = model.mesh
+
+               # One of the textures used, if any
+               var sample_used_texture = null
+               var texture = diffuse_texture
+               if texture != null then
+                       glActiveTexture gl_TEXTURE1
+                       glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
+                       program.use_map_diffuse.uniform true
+                       program.map_diffuse.uniform 1
+                       sample_used_texture = texture
+               else
+                       program.use_map_diffuse.uniform false
+               end
+
+               # If using a texture, set `texture_coords`
+               program.tex_coord.array_enabled = sample_used_texture != null
+               if sample_used_texture != null then
+                       if sample_used_texture isa GamnitRootTexture then
+                               # Coordinates are directly valid
+                               program.tex_coord.array(mesh.texture_coords, 2)
+                       else
+                               # Correlate texture coordinates from the subtexture sand the mesh.
+                               # This is slow, but should be cached on the GPU.
+                               var xa = sample_used_texture.offset_left
+                               var xd = sample_used_texture.offset_right - xa
+                               var ya = sample_used_texture.offset_top
+                               var yd = sample_used_texture.offset_bottom - ya
+
+                               var tex_coords = new Array[Float].with_capacity(mesh.texture_coords.length)
+                               for i in [0..mesh.texture_coords.length/2[ do
+                                       tex_coords[i*2]   = xa + xd * mesh.texture_coords[i*2]
+                                       tex_coords[i*2+1] = ya + yd * mesh.texture_coords[i*2+1]
+                               end
+
+                               program.tex_coord.array(tex_coords, 2)
+                       end
+               end
+       end
+end
+
+# Program to draw selection values
+class SelectionProgram
+       super GamnitProgramFromSource
+
+       redef var vertex_shader_source = """
+               // Vertex coordinates
+               attribute vec4 coord;
+
+               // Vertex translation
+               uniform vec4 translation;
+
+               // Vertex scaling
+               uniform float scale;
+
+               // Vertex coordinates on textures
+               attribute vec2 tex_coord;
+
+               // Model view projection matrix
+               uniform mat4 mvp;
+
+               // Model rotation
+               uniform mat4 rotation;
+
+               // Output for the fragment shader
+               varying vec2 v_tex_coord;
+
+               void main()
+               {
+                       v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
+
+                       gl_Position = (vec4(coord.xyz * scale, 1.0) * rotation + translation) * mvp;
+               }
+               """ @ glsl_vertex_shader
+
+       #
+       redef var fragment_shader_source = """
+               precision highp float;
+
+               varying vec2 v_tex_coord;
+
+               // Map used as reference for opacity
+               uniform sampler2D map_diffuse;
+
+               // Should `map_diffuse` be used?
+               uniform bool use_map_diffuse;
+
+               // Color ID
+               uniform vec4 color;
+
+               void main()
+               {
+                       gl_FragColor = vec4(color.rgb, 1.0);
+
+                       if (use_map_diffuse && texture2D(map_diffuse, v_tex_coord).a < 0.1) {
+                               gl_FragColor.a = 0.0;
+                       }
+               }
+               """ @ glsl_fragment_shader
+
+       # Vertices coordinates
+       var coord = attributes["coord"].as(AttributeVec4) is lazy
+
+       # Should this program use the texture `map_diffuse`?
+       var use_map_diffuse = uniforms["use_map_diffuse"].as(UniformBool) is lazy
+
+       # Diffuse texture unit
+       var map_diffuse = uniforms["map_diffuse"].as(UniformSampler2D) is lazy
+
+       # Coordinates on the textures, per vertex
+       var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
+
+       # Translation applied to each vertex
+       var translation = uniforms["translation"].as(UniformVec4) is lazy
+
+       # Rotation matrix
+       var rotation = uniforms["rotation"].as(UniformMat4) is lazy
+
+       # Scaling per vertex
+       var scale = uniforms["scale"].as(UniformFloat) is lazy
+
+       # Model view projection matrix
+       var mvp = uniforms["mvp"].as(UniformMat4) is lazy
+
+       # ID as a color
+       var color_id = uniforms["color"].as(UniformVec4) is lazy
+end
index 69ddb41..6e7c58c 100644 (file)
@@ -44,6 +44,15 @@ class GamnitDisplay
        # Only affects the desktop implementations.
        var show_cursor: Bool = true is writable
 
+       # Number of bits used for the red value in the color buffer
+       fun red_bits: Int do return 8
+
+       # Number of bits used for the green value in the color buffer
+       fun green_bits: Int do return 8
+
+       # Number of bits used for the blue value in the color buffer
+       fun blue_bits: Int do return 8
+
        # Prepare this display
        #
        # The implementation varies per platform.
index 3997c4a..e9d1b4a 100644 (file)
@@ -21,7 +21,7 @@ module display_android is
        android_manifest """<uses-feature android:glEsVersion="0x00020000"/>"""
 end
 
-import ::android
+import ::android::game
 intrude import android::load_image
 
 private import gamnit::egl
@@ -37,7 +37,7 @@ redef class GamnitDisplay
                setup_egl_display native_display
 
                # We need 8 bits per color for selection by color
-               select_egl_config(8, 8, 8, 0, 8, 0, 0)
+               select_egl_config(red_bits, green_bits, blue_bits, 0, 8, 0, 0)
 
                var format = egl_config.attribs(egl_display).native_visual_id
                native_window.set_buffers_geometry(0, 0, format)
@@ -48,7 +48,7 @@ redef class GamnitDisplay
        redef fun close do close_egl
 end
 
-redef class GamnitAssetTexture
+redef class TextureAsset
 
        redef fun load_from_platform
        do
index 8f115e7..1dbee21 100644 (file)
@@ -48,7 +48,7 @@ redef class GamnitDisplay
                setup_egl_display x11_display
 
                if debug_gamnit then print "Setting up EGL context"
-               select_egl_config(8, 8, 8, 8, 8, 0, 0)
+               select_egl_config(red_bits, green_bits, blue_bits, 8, 8, 0, 0)
                setup_egl_context window_handle
        end
 
@@ -95,7 +95,7 @@ redef class GamnitDisplay
        end
 end
 
-redef class GamnitAssetTexture
+redef class TextureAsset
 
        redef fun load_from_platform
        do
index 3fcb8c8..b451362 100644 (file)
@@ -46,14 +46,14 @@ redef class GamnitDisplay
        end
 
        # Select an EGL config
-       protected fun select_egl_config(blue, green, red, alpha, depth, stencil, sample: Int)
+       protected fun select_egl_config(red, green, blue, alpha, depth, stencil, sample: Int)
        do
                var config_chooser = new EGLConfigChooser
                config_chooser.renderable_type_egl
                config_chooser.surface_type_egl
-               config_chooser.blue_size = blue
-               config_chooser.green_size = green
                config_chooser.red_size = red
+               config_chooser.green_size = green
+               config_chooser.blue_size = blue
                if alpha > 0 then config_chooser.alpha_size = alpha
                if depth > 0 then config_chooser.depth_size = depth
                if stencil > 0 then config_chooser.stencil_size = stencil
index b74da82..45d1ce9 100644 (file)
@@ -166,12 +166,11 @@ redef class App
                # Prepare to draw
                for tex in all_root_textures do
                        tex.load
+                       gamnit_error = tex.error
+                       if gamnit_error != null then print_error gamnit_error
 
                        glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
                        glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
-
-                       gamnit_error = tex.error
-                       assert gamnit_error == null else print_error gamnit_error
                end
        end
 
index a300b54..896f07f 100644 (file)
@@ -29,8 +29,8 @@ redef class App
 
        # Current frame-rate
        #
-       # Updated each 5 seconds.
-       var current_fps = 0.0
+       # Updated each 5 seconds, initialized at the value of `maximum_fps`.
+       var current_fps: Float = maximum_fps is lazy
 
        redef fun frame_full
        do
@@ -47,7 +47,7 @@ redef class App
        private var frame_count = 0
 
        # Deadline used to compute `current_fps`
-       private var frame_count_deadline = 0
+       private var frame_count_deadline = 5.0
 
        # Check and sleep to maintain a frame-rate bellow `maximum_fps`
        #
@@ -55,12 +55,12 @@ redef class App
        # Is automatically called at the end of `full_frame`.
        fun limit_fps
        do
-               var t = clock.total.sec
+               var t = clock.total
                if t >= frame_count_deadline then
                        var cfps = frame_count.to_f / 5.0
                        self.current_fps = cfps
                        frame_count = 0
-                       frame_count_deadline = t + 5
+                       frame_count_deadline = t + 5.0
                end
                frame_count += 1
 
index 1a995ef..6859c76 100644 (file)
@@ -428,6 +428,23 @@ abstract class GamnitProgram
                end
        end
 
+       # Diagnose possible problems with the shaders of the program
+       #
+       # Lists to the console inactive uniforms and attributes.
+       # These may not be problematic but they can help to debug the program.
+       fun diagnose
+       do
+               if gl_program == null then compile_and_link
+
+               print "# Diagnose {class_name}"
+               for k,v in uniforms do
+                       if not v.is_active then print "* Uniform {v.name} is inactive"
+               end
+               for k,v in attributes do
+                       if not v.is_active then print "* Attribute {v.name} is inactive"
+               end
+       end
+
        # Attributes of this program organized by name
        #
        # Active attributes are gathered at `compile_and_link`.
similarity index 98%
rename from contrib/asteronits/src/texture_atlas_parser.nit
rename to lib/gamnit/texture_atlas_parser.nit
index 113b887..a0e4d5f 100644 (file)
@@ -44,7 +44,7 @@ var attributes = new Array[String]
 # Insert the first attribute, to load the root texture
 var png_file = "images" / xml_file.basename("xml") + "png"
 attributes.add """
-       var root_texture = new Texture("{{{png_file}}}")"""
+       var root_texture = new TextureAsset("{{{png_file}}}")"""
 
 # Read XML file
 var content = xml_file.to_path.read_all
index 415aa90..3b72a51 100644 (file)
@@ -21,7 +21,7 @@ import display
 abstract class Texture
 
        # Prepare a texture located at `path` within the `assets` folder
-       new (path: Text) do return new GamnitAssetTexture(path.to_s)
+       new (path: Text) do return new TextureAsset(path.to_s)
 
        # Root texture of which `self` is derived
        fun root: GamnitRootTexture is abstract
@@ -41,7 +41,7 @@ abstract class Texture
        # OpenGL handle to this texture
        fun gl_texture: Int do return root.gl_texture
 
-       # Prepare a subtexture from this texture
+       # Prepare a subtexture from this texture, from the given pixel offsets
        fun subtexture(left, top, width, height: Numeric): GamnitSubtexture
        do
                # Setup the subtexture
@@ -98,7 +98,7 @@ class GamnitRootTexture
 
        private fun load_from_pixels(pixels: Pointer, width, height: Int, format: GLPixelFormat)
        do
-               var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE)
+               var max_texture_size = glGetIntegerv(gl_MAX_TEXTURE_SIZE, 0)
                if width > max_texture_size or height > max_texture_size then
                        error = new Error("Texture {self} width or height is over the GL_MAX_TEXTURE_SIZE of {max_texture_size}")
                        return
@@ -143,7 +143,7 @@ class GamnitRootTexture
 end
 
 # Texture loaded from the assets folder
-class GamnitAssetTexture
+class TextureAsset
        super GamnitRootTexture
 
        # Path to this texture within the `assets` folder
index 94ad1c9..1be832e 100644 (file)
@@ -441,31 +441,40 @@ fun turn_left(p1, p2, p3: Point[Float]): Bool do
 end
 
 # Split a polygon into triangles
-# Useful for converting a concave polygon into multiple convex ones
-fun triangulate(pts: Array[Point[Float]], results: Array[ConvexPolygon]) do
-       var poly = new Polygon(pts)
-       pts = poly.points
-       recursive_triangulate(pts, results)
+#
+# Useful for converting a concave polygon into multiple convex ones.
+#
+# See: the alternative `triangulate_recursive` uses arrays in-place.
+fun triangulate(points: Array[Point[Float]]): Array[ConvexPolygon]
+do
+       var results = new Array[ConvexPolygon]
+       triangulate_recursive(points.clone, results)
+       return results
 end
 
-private fun recursive_triangulate(pts: Array[Point[Float]], results: Array[ConvexPolygon]) do
-       if pts.length == 3 then
-               results.add(new ConvexPolygon(pts))
+# Split a polygon into triangles using arrays in-place
+#
+# Consumes the content of `points` and add the triangles to `results`.
+#
+# See: the alternative `triangulate` which does not modify `points` and returns a new array.
+fun triangulate_recursive(points: Array[Point[Float]], results: Array[ConvexPolygon]) do
+       if points.length == 3 then
+               results.add(new ConvexPolygon(points))
                return
        end
-       var prev = pts[pts.length - 1]
-       var curr = pts[0]
-       var next = pts[1]
-       for i in [1..pts.length[ do
+       var prev = points[points.length - 1]
+       var curr = points[0]
+       var next = points[1]
+       for i in [1..points.length[ do
                if turn_left(prev, curr, next) then
-                       prev = pts[i-1]
+                       prev = points[i-1]
                        curr = next
-                       if i+1 == pts.length then next = pts[pts.length - 1] else next = pts[i+1]
+                       if i+1 == points.length then next = points[points.length - 1] else next = points[i+1]
                        continue
                end
                var contains = false
                var triangle = new ConvexPolygon(new Array[Point[Float]].with_items(prev, curr, next))
-               for p in pts do
+               for p in points do
                        if p != prev and p != curr and p != next then
                                if triangle.contain(p) then
                                        contains = true
@@ -475,12 +484,12 @@ private fun recursive_triangulate(pts: Array[Point[Float]], results: Array[Conve
                end
                if not contains then
                        results.add(triangle)
-                       pts.remove(curr)
-                       recursive_triangulate(pts, results)
+                       points.remove(curr)
+                       triangulate_recursive(points, results)
                        break
                end
-               prev = pts[i-1]
+               prev = points[i-1]
                curr = next
-               if i+1 == pts.length then next = pts[pts.length - 1] else next = pts[i+1]
+               if i+1 == points.length then next = points[points.length - 1] else next = points[i+1]
        end
 end
index e6d2287..7ad1e0b 100644 (file)
@@ -1195,27 +1195,29 @@ fun glPolygonOffset(factor, units: Float) `{ glPolygonOffset(factor, units); `}
 # Specify the width of rasterized lines
 fun glLineWidth(width: Float) `{ glLineWidth(width); `}
 
-# Get the value of the parameter `pname`
-fun glGetBooleanv(pname: GLGetParameterName): Bool `{
-       GLboolean v;
-       glGetBooleanv(pname, &v);
-       return v;
+# Get the value of the parameter `pname` at `offset`
+fun glGetBooleanv(pname: GLGetParameterName, offset: Int): Bool `{
+       GLboolean v[4];
+       glGetBooleanv(pname, v);
+       return v[offset];
 `}
 
-# Get the value of the parameter `pname`
-fun glGetFloatv(pname: GLGetParameterName): Float `{
-       GLfloat v;
-       glGetFloatv(pname, &v);
-       return v;
+# Get the value of the parameter `pname` at `offset`
+fun glGetFloatv(pname: GLGetParameterName, offset: Int): Float `{
+       GLfloat v[4];
+       glGetFloatv(pname, v);
+       return v[offset];
 `}
 
-# Get the value of the parameter `pname`
-fun glGetIntegerv(pname: GLGetParameterName): Int `{
-       GLint v;
-       glGetIntegerv(pname, &v);
-       return v;
+# Get the value of the parameter `pname` at `offset`
+fun glGetIntegerv(pname: GLGetParameterName, offset: Int): Int `{
+       GLint v[4];
+       glGetIntegerv(pname, v);
+       return v[offset];
 `}
 
+fun gl_COLOR_CLEAR_VALUE: GLGetParameterName `{ return GL_COLOR_CLEAR_VALUE; `}
+
 fun gl_MAX_TEXTURE_SIZE: GLGetParameterName `{ return GL_MAX_TEXTURE_SIZE; `}
 fun gl_MAX_VIEWPORT_DIMS: GLGetParameterName `{ return GL_MAX_VIEWPORT_DIMS; `}
 fun gl_MAX_VERTEX_ATTRIBS: GLGetParameterName `{ return GL_MAX_VERTEX_ATTRIBS; `}
index 0d43784..e472143 100644 (file)
@@ -72,8 +72,8 @@ extern class GtkAssistant `{GtkAssistant *`}
                gtk_assistant_set_page_type(self, page, t);
        `}
 
-       fun get_page_title(page: GtkWidget): String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_assistant_get_page_title(self, page));
+       fun get_page_title(page: GtkWidget): String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_assistant_get_page_title(self, page));
        `}
 
        fun set_page_title(page: GtkWidget, title: String) import String.to_cstring `{
index ae6b915..b6fec7c 100644 (file)
@@ -331,8 +331,8 @@ extern class GtkFrame `{GtkFrame *`}
                return (GtkFrame *)gtk_frame_new(String_to_cstring(lbl));
        `}
 
-       fun frame_label: String `{
-               return NativeString_to_s((char *)gtk_frame_get_label(self));
+       fun frame_label: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_frame_get_label(self));
        `}
 
        fun frame_label=(lbl: String) import String.to_cstring `{
@@ -682,8 +682,8 @@ extern class GtkLabel `{GtkLabel *`}
        `}
 
        # Returns the text of the label
-       fun text: String import NativeString.to_s `{
-               return NativeString_to_s((char*)gtk_label_get_text(self));
+       fun text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char*)gtk_label_get_text(self));
        `}
 
        # Sets the angle of rotation for the label.
@@ -803,8 +803,8 @@ extern class GtkButton `{GtkButton *`}
                return (GtkButton *)gtk_button_new_from_stock(String_to_cstring(stock_id));
        `}
 
-       fun text: String `{
-               return NativeString_to_s((char *)gtk_button_get_label(self));
+       fun text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_button_get_label(self));
        `}
 
        fun text=(value: String) import String.to_cstring `{
@@ -876,8 +876,8 @@ extern class GtkExpander `{GtkExpander *`}
                gtk_expander_set_spacing(self, pixels);
        `}
 
-       fun label_text: String `{
-               return NativeString_to_s((char *)gtk_expander_get_label(self));
+       fun label_text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_expander_get_label(self));
        `}
 
        fun label_text=(lbl: String) import String.to_cstring `{
@@ -995,8 +995,8 @@ extern class GtkComboBox `{GtkComboBox *`}
                gtk_combo_box_set_id_column(self, id_column);
        `}
 
-       fun active_id: String `{
-               return NativeString_to_s((char *)gtk_combo_box_get_active_id(self));
+       fun active_id: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_combo_box_get_active_id(self));
        `}
 
        fun active_id=(id_active: String) import String.to_cstring `{
@@ -1019,8 +1019,8 @@ extern class GtkComboBox `{GtkComboBox *`}
                gtk_combo_box_popdown(self);
        `}
 
-       fun title: String `{
-               return NativeString_to_s((char *)gtk_combo_box_get_title(self));
+       fun title: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_combo_box_get_title(self));
        `}
 
        fun title=(t: String) import String.to_cstring `{
index d4ff95f..f28a91b 100644 (file)
@@ -52,40 +52,40 @@ extern class GtkAboutDialog `{GtkAboutDialog *`}
                return (GtkAboutDialog *)gtk_about_dialog_new();
        `}
 
-       fun program_name: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_program_name(self));
+       fun program_name: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_program_name(self));
        `}
 
        fun program_name=(name: String) import String.to_cstring `{
                gtk_about_dialog_set_program_name(self, String_to_cstring(name));
        `}
 
-       fun version: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_version(self));
+       fun version: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_version(self));
        `}
 
        fun version=(v: String) import String.to_cstring `{
                gtk_about_dialog_set_version(self, String_to_cstring(v));
        `}
 
-       fun copyright: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_copyright(self));
+       fun copyright: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_copyright(self));
        `}
 
        fun copyright=(c: String) import String.to_cstring `{
                gtk_about_dialog_set_copyright(self, String_to_cstring(c));
        `}
 
-       fun comments: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_comments(self));
+       fun comments: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_comments(self));
        `}
 
        fun comments=(com: String) import String.to_cstring `{
                gtk_about_dialog_set_comments(self, String_to_cstring(com));
        `}
 
-       fun license: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_license(self));
+       fun license: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_license(self));
        `}
 
        fun license=(li: String) import String.to_cstring `{
@@ -94,16 +94,16 @@ extern class GtkAboutDialog `{GtkAboutDialog *`}
 
        # license_type
 
-       fun website: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_about_dialog_get_website(self));
+       fun website: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_about_dialog_get_website(self));
        `}
 
        fun website=(link: String) import String.to_cstring `{
                gtk_about_dialog_set_website(self, String_to_cstring(link));
        `}
 
-       fun website_label: String import NativeString.to_s `{
-               return NativeString_to_s((char *) gtk_about_dialog_get_website_label(self));
+       fun website_label: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *) gtk_about_dialog_get_website_label(self));
        `}
 
        fun website_label=(link_label: String) import String.to_cstring `{
@@ -111,8 +111,8 @@ extern class GtkAboutDialog `{GtkAboutDialog *`}
        `}
 
        # TODO
-       # fun authors: String`{
-       #               return NativeString_to_s(gtk_about_dialog_get_authors(self));
+       # fun authors: String  import NativeString.to_s_with_copy `{
+       #               return NativeString_to_s_with_copy(gtk_about_dialog_get_authors(self));
        # `}
 
        # TODO
@@ -144,8 +144,8 @@ extern class GtkAppChooserDialog `{GtkAppChooserDialog *`}
 
        fun widget: GtkWidget `{ return gtk_app_chooser_dialog_get_widget(self); `}
 
-       fun heading: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_app_chooser_dialog_get_heading(self));
+       fun heading: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_app_chooser_dialog_get_heading(self));
        `}
 
        fun heading=(text: String) import String.to_cstring `{
index 25265c4..1cf98ee 100644 (file)
@@ -131,8 +131,8 @@ extern class GtkProgressBar `{GtkProgressBar *`}
                gtk_progress_bar_set_show_text(self, show);
        `}
 
-       fun text: String import NativeString.to_s `{
-               return NativeString_to_s((char *)gtk_progress_bar_get_text(self));
+       fun text: String import NativeString.to_s_with_copy `{
+               return NativeString_to_s_with_copy((char *)gtk_progress_bar_get_text(self));
        `}
 
        fun text=(value: String) import String.to_cstring `{
index a4a0000..d6ea633 100644 (file)
@@ -25,16 +25,16 @@ in "ObjC" `{
 @interface NitCallbackReference: NSObject
 
        // Nit object target of the callbacks from UI events
-       @property (nonatomic) Button nit_button;
+       @property (nonatomic) View nit_view;
 
        // Actual callback method
-       -(void) nitOnEvent: (UIButton*) sender;
+       -(void) nitOnEvent: (UIView*) sender;
 @end
 
 @implementation NitCallbackReference
 
-       -(void) nitOnEvent: (UIButton*) sender {
-               Button_on_click(self.nit_button);
+       -(void) nitOnEvent: (UIView*) sender {
+               View_on_ios_event(self.nit_view);
        }
 @end
 
@@ -112,6 +112,19 @@ redef class App
                set_view_controller(app_delegate.window, window.native)
                super
        end
+
+       # Use iOS ` popViewControllerAnimated`
+       redef fun pop_window
+       do
+               window_stack.pop
+               pop_view_controller app_delegate.window
+               window.on_resume
+       end
+
+       private fun pop_view_controller(window: UIWindow) in "ObjC" `{
+               UINavigationController *navController = (UINavigationController*)window.rootViewController;
+               [navController popViewControllerAnimated: YES];
+       `}
 end
 
 redef class AppDelegate
@@ -136,6 +149,8 @@ redef class View
        redef type NATIVE: UIView
 
        redef var enabled = null is lazy
+
+       private fun on_ios_event do end
 end
 
 redef class CompositeControl
@@ -180,7 +195,6 @@ redef class Layout
        init
        do
                native.alignment = new UIStackViewAlignment.fill
-               native.distribution = new UIStackViewDistribution.fill_equally
 
                # TODO make customizable
                native.spacing = 4.0
@@ -197,11 +211,19 @@ redef class Layout
 end
 
 redef class HorizontalLayout
-       redef init do native.axis = new UILayoutConstraintAxis.horizontal
+       redef init
+       do
+               native.axis = new UILayoutConstraintAxis.horizontal
+               native.distribution = new UIStackViewDistribution.fill_equally
+       end
 end
 
 redef class VerticalLayout
-       redef init do native.axis = new UILayoutConstraintAxis.vertical
+       redef init
+       do
+               native.axis = new UILayoutConstraintAxis.vertical
+               native.distribution = new UIStackViewDistribution.equal_spacing
+       end
 end
 
 redef class Label
@@ -253,15 +275,20 @@ redef class CheckBox
        # `UISwitch` acting as the real check box
        var ui_switch: UISwitch is noautoinit
 
-       init do
+       redef fun on_ios_event do notify_observers new ToggleEvent(self)
+
+       init
+       do
                # Tweak the layout so it is centered
-               layout.native.distribution = new UIStackViewDistribution.fill_proportionally
-               layout.native.alignment = new UIStackViewAlignment.center
+               layout.native.distribution = new UIStackViewDistribution.equal_spacing
+               layout.native.alignment = new UIStackViewAlignment.fill
                layout.native.layout_margins_relative_arrangement = true
 
                var s = new UISwitch
                native.add_arranged_subview s
                ui_switch = s
+
+               ui_switch.set_callback self
        end
 
        redef fun text=(text) do lbl.text = text
@@ -271,6 +298,23 @@ redef class CheckBox
        redef fun is_checked=(value) do ui_switch.set_on_animated(value, true)
 end
 
+redef class UISwitch
+       # Register callbacks on this switch to be relayed to `sender`
+       private fun set_callback(sender: View)
+       import View.on_ios_event in "ObjC" `{
+
+               NitCallbackReference *ncr = [[NitCallbackReference alloc] init];
+               ncr.nit_view = sender;
+
+               // Pin the objects in both Objective-C and Nit GC
+               View_incr_ref(sender);
+               ncr = (__bridge NitCallbackReference*)CFBridgingRetain(ncr);
+
+               [self addTarget:ncr action:@selector(nitOnEvent:)
+                       forControlEvents:UIControlEventValueChanged];
+       `}
+end
+
 redef class TextInput
 
        redef type NATIVE: UITextField
@@ -293,25 +337,25 @@ redef class Button
 
        init do native.set_callback self
 
+       redef fun on_ios_event do notify_observers new ButtonPressEvent(self)
+
        redef fun text=(text) do if text != null then native.title = text.to_nsstring
        redef fun text do return native.current_title.to_s
 
-       private fun on_click do notify_observers new ButtonPressEvent(self)
-
        redef fun enabled=(enabled) do native.enabled = enabled or else true
        redef fun enabled do return native.enabled
 end
 
 redef class UIButton
        # Register callbacks on this button to be relayed to `sender`
-       private fun set_callback(sender: Button)
-       import Button.on_click in "ObjC" `{
+       private fun set_callback(sender: View)
+       import View.on_ios_event in "ObjC" `{
 
                NitCallbackReference *ncr = [[NitCallbackReference alloc] init];
-               ncr.nit_button = sender;
+               ncr.nit_view = sender;
 
                // Pin the objects in both Objective-C and Nit GC
-               Button_incr_ref(sender);
+               View_incr_ref(sender);
                ncr = (__bridge NitCallbackReference*)CFBridgingRetain(ncr);
 
                [self addTarget:ncr action:@selector(nitOnEvent:)
@@ -333,7 +377,7 @@ redef class ListLayout
                native_stack_view.translates_autoresizing_mask_into_constraits = false
                native_stack_view.axis = new UILayoutConstraintAxis.vertical
                native_stack_view.alignment = new UIStackViewAlignment.fill
-               native_stack_view.distribution = new UIStackViewDistribution.fill_equally
+               native_stack_view.distribution = new UIStackViewDistribution.equal_spacing
                native_stack_view.spacing = 4.0
 
                native.add_subview native_stack_view
@@ -479,3 +523,15 @@ redef class UITableView
                self.dataSource = objc_delegate;
        `}
 end
+
+redef class Text
+       redef fun open_in_browser do to_nsstring.native_open_in_browser
+end
+
+redef class NSString
+       private fun native_open_in_browser
+       in "ObjC" `{
+               NSURL *nsurl = [NSURL URLWithString: self];
+               [[UIApplication sharedApplication] openURL: nsurl];
+       `}
+end
index 097158f..bd46a42 100644 (file)
@@ -28,7 +28,7 @@
 # ~~~
 module collections
 
-import base
+import ffi_support
 
 # Java primitive array
 #
similarity index 94%
rename from lib/java/base.nit
rename to lib/java/ffi_support.nit
index 10950a2..0a1e4bf 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Supporting services for the FFI with Java
+# Core supporting services for the FFI with Java
 #
-# This modules relies on `Sys::jvm`, `Sys::jni_env` and
-# `Sys::create_default_jvm` to get a handle on a JVM. You can adapt the
-# behavior of the FFI and services in this module by redefing
-# `Sys::create_default_jvm` and supply your own JVM object. You can manage
-# multiple java thread by switching the current environment in a redef
-# of `Sys::jni_env`, and multiple JVM using `Sys::jvm`.
-module base is
+# This module *must* be imported by modules using the Java FFI.
+# Some might prefer to import the whole `java` package as it provides
+# other useful services.
+module ffi_support is
        cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
        ldflags "-L $(JNI_LIB_PATH) -ljvm"
        new_annotation extra_java_files
@@ -66,7 +63,9 @@ redef class Sys
                assert jvm != null else print "JVM creation failed"
 
                self.jvm = jvm
-               self.jni_env = builder.jni_env.as(not null)
+               assert not jvm.address_is_null
+               self.jni_env = jvm.env
+               assert not jni_env.address_is_null
        end
 
        # Get a Java class by its name from the current `jni_env`
index c13abe0..3299a9e 100644 (file)
@@ -19,7 +19,7 @@
 # This module is used by `android::assets_and_resources` and `android::audio`.
 module io
 
-import base
+import ffi_support
 
 in "Java" `{
        import java.io.File;
index 116f40d..0714c3c 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Supporting services for the FFI with Java
+# Supporting services for the FFI with Java and to access Java libraries
 #
 # This modules relies on `Sys::jvm`, `Sys::jni_env` and
 # `Sys::create_default_jvm` to get a handle on a JVM. You can adapt the
 # multiple java thread by switching the current environment in a redef
 # of `Sys::jni_env`, and multiple JVM using `Sys::jvm`.
 #
-# The module `jvm` gives more control over the JVM instances and wraps
-# most of JNI functions. You can use it to further customize the behavior
-# of your code.
+# See also, the module `jvm` to control the JVM instances and access JNI functions.
+# You can use it to further customize the behavior of your code.
 module java
 
-import base
+import ffi_support
 import collections
index 4f9df3c..45578f2 100644 (file)
 
 # Handles serialization and deserialization of objects to/from JSON
 #
-# ## Nity JSON
+# ## Writing JSON with metadata
 #
 # `JsonSerializer` write Nit objects that subclass `Serializable` to JSON,
-# and `JsonDeserializer` can read them. They both use meta-data added to the
+# and `JsonDeserializer` can read them. They both use metadata added to the
 # generated JSON to recreate the Nit instances with the exact original type.
 #
 # For more information on Nit serialization, see: ../serialization/README.md
 #
-# ## Plain JSON
+# ## Writing plain JSON
 #
 # The attribute `JsonSerializer::plain_json` triggers generating plain and
 # clean JSON. This format is easier to read for an human and a non-Nit program,
 # but it cannot be fully deserialized. It can still be read by services from
 # `json::static` and `json::dynamic`.
 #
-# A shortcut to this service is provided by `Serializable::to_plain_json`.
+# A shortcut to these writing services is provided by `Serializable::serialize_to_json`.
 #
 # ### Usage Example
 #
 # var bob = new Person("Bob", 1986)
 # var alice = new Person("Alice", 1978, bob)
 #
-# assert bob.to_plain_json == """
-# {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}"""
+# assert bob.serialize_to_json(pretty=true, plain=true) == """
+#{
+#      "name": "Bob",
+#      "year_of_birth": 1986,
+#      "next_of_kin": null
+#}"""
 #
-# assert alice.to_plain_json == """
-# {"name": "Alice", "year_of_birth": 1978, "next_of_kin": {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}}"""
+# assert alice.serialize_to_json(pretty=true, plain=true) == """
+#{
+#      "name": "Alice",
+#      "year_of_birth": 1978,
+#      "next_of_kin": {
+#              "name": "Bob",
+#              "year_of_birth": 1986,
+#              "next_of_kin": null
+#      }
+#}"""
 # ~~~
 #
 # ## JSON to Nit objects
 #
-# The `JsonDeserializer` support reading JSON code with minimal meta-data
+# The `JsonDeserializer` support reading JSON code with minimal metadata
 # to easily create Nit object from client-side code or configuration files.
 # Each JSON object must define the `__class` attribute with the corresponding
 # Nit class and the expected attributes with its name in Nit followed by its value.
 # var deserializer = new JsonDeserializer(json_code)
 #
 # var meet = deserializer.deserialize
+#
+# # Check for errors
+# assert deserializer.errors.is_empty
+#
 # assert meet isa MeetupConfig
 # assert meet.description == "My Awesome Meetup"
 # assert meet.max_participants == null
@@ -90,7 +106,8 @@ module serialization
 
 import ::serialization::caching
 private import ::serialization::engine_tools
-import static
+private import static
+private import string_parser
 
 # Serializer of Nit objects to Json string.
 class JsonSerializer
@@ -99,18 +116,18 @@ class JsonSerializer
        # Target writing stream
        var stream: Writer
 
-       # Write plain JSON? easier to read but does not support Nit deserialization
+       # Write plain JSON? Standard JSON without metadata for deserialization
        #
        # If `false`, the default, serialize to support deserialization:
        #
-       # * Write meta-data, including the types of the serialized objects so they can
+       # * Write metadata, including the types of the serialized objects so they can
        #   be deserialized to their original form using `JsonDeserializer`.
        # * Use references when an object has already been serialized so to not duplicate it.
        # * Support cycles in references.
        # * Preserve the Nit `Char` type as an object because it does not exist in JSON.
        # * The generated JSON is standard and can be read by non-Nit programs.
        #   However, some Nit types are not represented by the simplest possible JSON representation.
-       #   With the added meta-data, it can be complex to read.
+       #   With the added metadata, it can be complex to read.
        #
        # If `true`, serialize for other programs:
        #
@@ -119,11 +136,20 @@ class JsonSerializer
        # * Nit objects are serialized for every references, so they can be duplicated.
        #   It is easier to read but it creates a larger output.
        # * Does not support cycles, will replace the problematic references by `null`.
-       # * Does not serialize the meta-data needed to deserialize the objects
+       # * Does not serialize the metadata needed to deserialize the objects
        #   back to regular Nit objects.
-       # * Keys of Nit `HashMap` are converted to their string reprensentation using `to_s`.
+       # * Keys of Nit `HashMap` are converted to their string representation using `to_s`.
        var plain_json = false is writable
 
+       # Write pretty JSON for human eyes?
+       #
+       # Toggles skipping lines between attributes of an object and
+       # properly indent the written JSON.
+       var pretty_json = false is writable
+
+       # Current indentation level used for writing `pretty_json`
+       private var indent_level = 0
+
        # List of the current open objects, the first is the main target of the serialization
        #
        # Used only when `plain_json == true` to detect cycles in serialization.
@@ -142,7 +168,8 @@ class JsonSerializer
                        if plain_json then
                                for o in open_objects do
                                        if object.is_same_serialized(o) then
-                                               # Cycle detected
+                                               # Cycle, can't be managed in plain json
+                                               warn "Cycle detected in serialized object, replacing reference with 'null'."
                                                stream.write "null"
                                                return
                                        end
@@ -152,7 +179,7 @@ class JsonSerializer
                        end
 
                        first_attribute = true
-                       object.serialize_to_json self
+                       object.accept_json_serializer self
                        first_attribute = false
 
                        if plain_json then open_objects.pop
@@ -162,10 +189,11 @@ class JsonSerializer
        redef fun serialize_attribute(name, value)
        do
                if not plain_json or not first_attribute then
-                       stream.write ", "
+                       stream.write ","
                        first_attribute = false
                end
 
+               new_line_and_indent
                stream.write "\""
                stream.write name
                stream.write "\": "
@@ -177,14 +205,28 @@ class JsonSerializer
                if not plain_json and cache.has_object(object) then
                        # if already serialized, add local reference
                        var id = cache.id_for(object)
-                       stream.write "\{\"__kind\": \"ref\", \"__id\": "
+                       stream.write "\{"
+                       indent_level += 1
+                       new_line_and_indent
+                       stream.write "\"__kind\": \"ref\", \"__id\": "
                        stream.write id.to_s
+                       indent_level -= 1
+                       new_line_and_indent
                        stream.write "\}"
                else
                        # serialize here
                        serialize object
                end
        end
+
+       # Write a new line and indent it, only if `pretty_json`
+       private fun new_line_and_indent
+       do
+               if pretty_json then
+                       stream.write "\n"
+                       for i in indent_level.times do stream.write "\t"
+               end
+       end
 end
 
 # Deserializer from a Json string.
@@ -195,10 +237,10 @@ class JsonDeserializer
        private var text: Text
 
        # Root json object parsed from input text.
-       private var root: nullable Jsonable is noinit
+       private var root: nullable Object is noinit
 
        # Depth-first path in the serialized object tree.
-       private var path = new Array[JsonObject]
+       private var path = new Array[Map[String, nullable Object]]
 
        # Last encountered object reference id.
        #
@@ -207,7 +249,7 @@ class JsonDeserializer
 
        init do
                var root = text.parse_json
-               if root isa JsonObject then path.add(root)
+               if root isa Map[String, nullable Object] then path.add(root)
                self.root = root
        end
 
@@ -243,7 +285,7 @@ class JsonDeserializer
                        return null
                end
 
-               if object isa JsonObject then
+               if object isa Map[String, nullable Object] then
                        var kind = null
                        if object.keys.has("__kind") then
                                kind = object["__kind"]
@@ -355,7 +397,7 @@ class JsonDeserializer
                                var array_type = types.first
 
                                var typed_array
-                               if array_type == "FlatString" then
+                               if array_type == "ASCIIFlatString" or array_type == "UnicodeFlatString" then
                                        if has_nullable then
                                                typed_array = new Array[nullable FlatString]
                                        else typed_array = new Array[FlatString]
@@ -442,7 +484,7 @@ class JsonDeserializer
        # deserialized = deserializer.deserialize
        # assert deserialized isa MyError
        # ~~~
-       protected fun class_name_heuristic(json_object: JsonObject): nullable String
+       protected fun class_name_heuristic(json_object: Map[String, nullable Object]): nullable String
        do
                return null
        end
@@ -467,15 +509,50 @@ redef class Text
                return res
        end
 
-       redef fun serialize_to_json(v) do v.stream.write(to_json)
+       redef fun accept_json_serializer(v) do v.stream.write(to_json)
 end
 
 redef class Serializable
-       private fun serialize_to_json(v: JsonSerializer)
+
+       # Serialize `self` to JSON
+       #
+       # Set `plain = true` to generate standard JSON, without deserialization metadata.
+       # Use this option if the generated JSON will be read by other programs or humans.
+       # Use the default, `plain = false`, if the JSON is to be deserialized by a Nit program.
+       #
+       # Set `pretty = true` to generate pretty JSON for human eyes.
+       # Use the default, `pretty = false`, to generate minified JSON.
+       #
+       # This method should not be refined by subclasses,
+       # instead `accept_json_serializer` can customize the serialization of an object.
+       #
+       # See: `JsonSerializer`
+       fun serialize_to_json(plain, pretty: nullable Bool): String
+       do
+               var stream = new StringWriter
+               var serializer = new JsonSerializer(stream)
+               serializer.plain_json = plain or else false
+               serializer.pretty_json = pretty or else false
+               serializer.serialize self
+               stream.close
+               return stream.to_s
+       end
+
+       # Refinable service to customize the serialization of this class to JSON
+       #
+       # This method can be refined to customize the serialization by either
+       # writing pure JSON directly on the stream `v.stream` or
+       # by using other services of `JsonSerializer`.
+       #
+       # Most of the time, it is preferable to refine the method `core_serialize_to`
+       # which is used by all the serialization engines, not just JSON.
+       protected fun accept_json_serializer(v: JsonSerializer)
        do
                var id = v.cache.new_id_for(self)
                v.stream.write "\{"
+               v.indent_level += 1
                if not v.plain_json then
+                       v.new_line_and_indent
                        v.stream.write "\"__kind\": \"obj\", \"__id\": "
                        v.stream.write id.to_s
                        v.stream.write ", \"__class\": \""
@@ -483,61 +560,40 @@ redef class Serializable
                        v.stream.write "\""
                end
                core_serialize_to(v)
-               v.stream.write "\}"
-       end
 
-       # Serialize this object to a JSON string with metadata for deserialization
-       fun to_json_string: String
-       do
-               var stream = new StringWriter
-               var serializer = new JsonSerializer(stream)
-               serializer.serialize self
-               stream.close
-               return stream.to_s
-       end
-
-       # Serialize this object to plain JSON
-       #
-       # This is a shortcut using `JsonSerializer::plain_json`,
-       # see its documentation for more information.
-       fun to_plain_json: String
-       do
-               var stream = new StringWriter
-               var serializer = new JsonSerializer(stream)
-               serializer.plain_json = true
-               serializer.serialize self
-               stream.close
-               return stream.to_s
+               v.indent_level -= 1
+               v.new_line_and_indent
+               v.stream.write "\}"
        end
 end
 
 redef class Int
-       redef fun serialize_to_json(v) do v.stream.write(to_s)
+       redef fun accept_json_serializer(v) do v.stream.write to_s
 end
 
 redef class Float
-       redef fun serialize_to_json(v) do v.stream.write(to_s)
+       redef fun accept_json_serializer(v) do v.stream.write to_s
 end
 
 redef class Bool
-       redef fun serialize_to_json(v) do v.stream.write(to_s)
+       redef fun accept_json_serializer(v) do v.stream.write to_s
 end
 
 redef class Char
-       redef fun serialize_to_json(v)
+       redef fun accept_json_serializer(v)
        do
                if v.plain_json then
-                       v.stream.write to_s.to_json
+                       to_s.accept_json_serializer v
                else
                        v.stream.write "\{\"__kind\": \"char\", \"__val\": "
-                       v.stream.write to_s.to_json
+                       to_s.accept_json_serializer v
                        v.stream.write "\}"
                end
        end
 end
 
 redef class NativeString
-       redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
+       redef fun accept_json_serializer(v) do to_s.accept_json_serializer(v)
 end
 
 redef class Collection[E]
@@ -545,36 +601,48 @@ redef class Collection[E]
        private fun serialize_to_pure_json(v: JsonSerializer)
        do
                        v.stream.write "["
+                       v.indent_level += 1
                        var is_first = true
                        for e in self do
                                if is_first then
                                        is_first = false
-                               else v.stream.write ", "
+                               else v.stream.write ","
+                               v.new_line_and_indent
 
                                if not v.try_to_serialize(e) then
+                                       assert e != null # null would have been serialized
                                        v.warn("element of type {e.class_name} is not serializable.")
                                end
                        end
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "]"
        end
 end
 
 redef class SimpleCollection[E]
-       redef fun serialize_to_json(v)
+       redef fun accept_json_serializer(v)
        do
                # Register as pseudo object
                if not v.plain_json then
                        var id = v.cache.new_id_for(self)
-                       v.stream.write """{"__kind": "obj", "__id": """
+                       v.stream.write """{"""
+                       v.indent_level += 1
+                       v.new_line_and_indent
+                       v.stream.write """"__kind": "obj", "__id": """
                        v.stream.write id.to_s
                        v.stream.write """, "__class": """"
                        v.stream.write class_name
-                       v.stream.write """", "__items": """
+                       v.stream.write """","""
+                       v.new_line_and_indent
+                       v.stream.write """"__items": """
                end
 
                serialize_to_pure_json v
 
                if not v.plain_json then
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "\}"
                end
        end
@@ -596,42 +664,56 @@ redef class SimpleCollection[E]
 end
 
 redef class Map[K, V]
-       redef fun serialize_to_json(v)
+       redef fun accept_json_serializer(v)
        do
                # Register as pseudo object
                var id = v.cache.new_id_for(self)
 
                if v.plain_json then
                        v.stream.write "\{"
+                       v.indent_level += 1
                        var first = true
                        for key, val in self do
                                if not first then
-                                       v.stream.write ", "
+                                       v.stream.write ","
                                else first = false
+                               v.new_line_and_indent
 
                                var k = key or else "null"
-                               v.stream.write k.to_s.to_json
+                               k.to_s.accept_json_serializer v
                                v.stream.write ": "
                                if not v.try_to_serialize(val) then
+                                       assert val != null # null would have been serialized
                                        v.warn("element of type {val.class_name} is not serializable.")
                                        v.stream.write "null"
                                end
                        end
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "\}"
                else
-                       v.stream.write """{"__kind": "obj", "__id": """
+                       v.stream.write "\{"
+                       v.indent_level += 1
+                       v.new_line_and_indent
+                       v.stream.write """"__kind": "obj", "__id": """
                        v.stream.write id.to_s
                        v.stream.write """, "__class": """"
                        v.stream.write class_name
                        v.stream.write """", "__length": """
                        v.stream.write length.to_s
 
-                       v.stream.write """, "__keys": """
+                       v.stream.write ","
+                       v.new_line_and_indent
+                       v.stream.write """"__keys": """
                        keys.serialize_to_pure_json v
 
-                       v.stream.write """, "__values": """
+                       v.stream.write ","
+                       v.new_line_and_indent
+                       v.stream.write """"__values": """
                        values.serialize_to_pure_json v
 
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "\}"
                end
        end
index 7c0e2cf..dd1cc85 100644 (file)
@@ -31,6 +31,9 @@ private import json_lexer
 interface Jsonable
        # Encode `self` in JSON.
        #
+       # This is a recursive method which can be refined by any subclasses.
+       # To write any `Serializable` object to JSON, see `serialize_to_json`.
+       #
        # SEE: `append_json`
        fun to_json: String is abstract
 
@@ -136,7 +139,7 @@ redef class Text
        #     assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n"
        #     assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓"
        protected fun json_to_nit_string: String do
-               var res = new FlatBuffer.with_capacity(bytelen)
+               var res = new FlatBuffer.with_capacity(byte_length)
                var i = 0
                var ln = self.length
                while i < ln do
@@ -185,7 +188,7 @@ redef class Text
        #     "\"\\t\\\"http://example.com\\\"\\r\\n\\u0000\\\\\""
        # ~~~
        redef fun to_json do
-               var b = new FlatBuffer.with_capacity(bytelen)
+               var b = new FlatBuffer.with_capacity(byte_length)
                append_json(b)
                return b.to_s
        end
@@ -439,6 +442,11 @@ redef class JsonParseError
                                "\"position\":{position.to_json}," +
                                "\"message\":{message.to_json}\}"
        end
+
+       redef fun pretty_json_visit(buf, indents) do
+               buf.clear
+               buf.append(to_json)
+       end
 end
 
 redef class Position
index d429697..51d302e 100644 (file)
@@ -231,32 +231,103 @@ class JSONStringParser
                return val
        end
 
+       private var parse_str_buf = new FlatBuffer
+
        # Parses and returns a Nit string from a JSON String
        fun parse_json_string: Jsonable do
+               var src = src
                var ln = src.length
                var p = pos
                p += 1
                if p > ln then return make_parse_error("Malformed JSON String")
                var c = src[p]
-               var st = p
+               var ret = parse_str_buf
+               var chunk_st = p
                while c != '"' do
-                       if c == '\\' then
-                               if p + 1 >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
+                       if c != '\\' then
                                p += 1
+                               if p >= ln then return make_parse_error("Malformed JSON string")
                                c = src[p]
-                               if c == 'u' then
+                               continue
+                       end
+                       ret.append_substring_impl(src, chunk_st, p - chunk_st)
+                       p += 1
+                       if p >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
+                       c = src[p]
+                       if c == 'r' then
+                               ret.add '\r'
+                               p += 1
+                       else if c == 'n' then
+                               ret.add '\n'
+                               p += 1
+                       else if c == 't' then
+                               ret.add '\t'
+                               p += 1
+                       else if c == 'u' then
+                               var cp = 0
+                               p += 1
+                               for i in [0 .. 4[ do
+                                       cp <<= 4
+                                       if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       c = src[p]
+                                       if c >= '0' and c <= '9' then
+                                               cp += c.code_point - '0'.code_point
+                                       else if c >= 'a' and c <= 'f' then
+                                               cp += c.code_point - 'a'.code_point + 10
+                                       else if c >= 'A' and c <= 'F' then
+                                               cp += c.code_point - 'A'.code_point + 10
+                                       else
+                                               make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       end
                                        p += 1
-                                       if p + 3 >= ln then return make_parse_error("Bad Unicode escape sequence in string")
-                                       for i in [0 .. 4[ do if not src[p + i].is_hexdigit then return make_parse_error("Bad Unicode escape sequence in string")
-                                       p += 3
                                end
+                               c = cp.code_point
+                               if cp >= 0xD800 and cp <= 0xDBFF then
+                                       if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       c = src[p]
+                                       if c != '\\' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       p += 1
+                                       c = src[p]
+                                       if c != 'u' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                       var locp = 0
+                                       p += 1
+                                       for i in [0 .. 4[ do
+                                               locp <<= 4
+                                               if p > ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                               c = src[p]
+                                               if c >= '0' and c <= '9' then
+                                                       locp += c.code_point - '0'.code_point
+                                               else if c >= 'a' and c <= 'f' then
+                                                       locp += c.code_point - 'a'.code_point + 10
+                                               else if c >= 'A' and c <= 'F' then
+                                                       locp += c.code_point - 'A'.code_point + 10
+                                               else
+                                                       make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+                                               end
+                                               p += 1
+                                       end
+                                       c = (((locp & 0x3FF) | ((cp & 0x3FF) << 10)) + 0x10000).code_point
+                               end
+                               ret.add c
+                       else if c == 'b' then
+                               ret.add 8.code_point
+                               p += 1
+                       else if c == 'f' then
+                               ret.add '\f'
+                               p += 1
+                       else
+                               p += 1
+                               ret.add c
                        end
-                       p += 1
-                       if p >= ln then return make_parse_error("Malformed JSON String")
+                       chunk_st = p
                        c = src[p]
                end
                pos = p + 1
-               return src.substring(st, p - st).unescape_json
+               if ret.is_empty then return src.substring(chunk_st, p - chunk_st)
+               ret.append_substring_impl(src, chunk_st, p - chunk_st)
+               var rets = ret.to_s
+               ret.clear
+               return rets
        end
 
        # Ignores any character until a JSON separator is encountered
index 379e6ec..874510c 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Java Virtual Machine services
+# Java Virtual Machine invocation API and others services from the JNI C API
+#
+# Users of this module and the Java FFI, on desktop computers, must define three environment variables:
+# * `JAVA_HOME` points to the installation folder of the target Java VM.
+#   This folder should contain the JNI header file `include/jni.h`.
+#   e.g. `/usr/lib/jvm/default-java` on Debian Jessie.
+# * `JNI_LIB_PATH` points to the folder with `libjvm.so`.
+#   e.g. `/usr/lib/jvm/default-java/jre/lib/amd64/server/` on Debian Jessie.
+# * `LD_LIBRARY_PATH` has the path to the folder with `libjvm.so`.
+#   It's the same value as `JNI_LIB_PATH` but `LD_LIBRARY_PATH` is a colon separated list
+#   which may contain other paths.
 #
 # See: http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
 module jvm is
@@ -38,17 +48,16 @@ in "C Header" `{
 # var env = builder.jni_env
 # ~~~~
 class JavaVMBuilder
-       super JniEnvRef
 
        # Version code of the JVM requested by `create_jvm`
        #
-       # Default at 0x00010002
+       # Default at 0x00010002 for `JNI_VERSION_1_2`.
        var version = 0x00010002 is writable
 
        # Additional option strings
        var options = new Array[String]
 
-       # Create the JVM and return it on success
+       # Create a JVM instance, or return `null` on error
        fun create_jvm: nullable JavaVM
        do
                var args = new JavaVMInitArgs
@@ -65,7 +74,7 @@ class JavaVMBuilder
 
                args.options = c_options
 
-               var jvm = new JavaVM(args, self)
+               var jvm = new JavaVM(args)
 
                args.free
                c_options.free
@@ -122,11 +131,12 @@ end
 
 # Represents a jni JavaVM
 extern class JavaVM `{JavaVM *`}
-       # Create the JVM, returns its handle and store the a pointer to JniEnv in `env_ref`
+       # Create the JVM
        #
-       # Unavailable on Android, where you cannot instanciate a new JVM.
-       private new(args: JavaVMInitArgs, env_ref: JniEnvRef)
-       import jni_error, JniEnvRef.jni_env=, JniEnv.as nullable `{
+       # The corresponding `JniEnv` can be obtained by calling `env`.
+       #
+       # Unavailable on some platforms, including Android where you cannot instanciate a new JVM.
+       private new(args: JavaVMInitArgs) import jni_error `{
 
        #ifdef ANDROID
                JavaVM_jni_error(NULL, "JVM creation not supported on Android", 0);
@@ -139,14 +149,12 @@ extern class JavaVM `{JavaVM *`}
 
                res = JNI_CreateJavaVM(&jvm, (void**)&env, args);
 
-               if (res != 0) {
+               if (res != JNI_OK) {
                        JavaVM_jni_error(NULL, "Could not create Java VM", res);
                        return NULL;
                }
-               else {
-                       JniEnvRef_jni_env__assign(env_ref, JniEnv_as_nullable_JniEnv(env));
-                       return jvm;
-               }
+
+               return jvm;
        `}
 
        private fun jni_error(msg: NativeString, v: Int)
@@ -155,20 +163,29 @@ extern class JavaVM `{JavaVM *`}
                abort
        end
 
+       # Unload the Java VM when the calling thread is the only remaining non-daemon attached user thread
        fun destroy `{
                (*self)->DestroyJavaVM(self);
        `}
 
+       # `JniEnv` attached to the calling thread
+       #
+       # A null pointer is returned if the calling thread is not attached to the JVM.
        fun env: JniEnv import jni_error `{
                JNIEnv *env;
                int res = (*self)->GetEnv(self, (void **)&env, JNI_VERSION_1_6);
-               if (res != JNI_OK) {
+               if (res == JNI_EDETACHED) {
+                       JavaVM_jni_error(NULL, "Requesting JNIEnv from an unattached thread", res);
+                       return NULL;
+               }
+               else if (res != JNI_OK) {
                        JavaVM_jni_error(NULL, "Could not get JNIEnv from Java VM", res);
                        return NULL;
                }
                return env;
        `}
 
+       # Attach the calling thread to the JVM and return its `JniEnv`
        fun attach_current_thread: JniEnv import jni_error `{
                JNIEnv *env;
        #ifdef ANDROID
@@ -410,11 +427,6 @@ extern class JniEnv `{JNIEnv *`}
        `}
 end
 
-# used to initialize a JavaVM
-class JniEnvRef
-       var jni_env: nullable JniEnv = null
-end
-
 # Represents a jni jclass
 extern class JClass `{jclass`}
 end
index 36de40f..8c65323 100644 (file)
@@ -116,9 +116,23 @@ class Connection
        redef fun close
        do
                if closed then return
-               var success = native_buffer_event.destroy
-               close_requested = true
-               closed = success
+
+               var i = native_buffer_event.input_buffer
+               var o = native_buffer_event.output_buffer
+               if i.length > 0 or o.length > 0 then
+                       close_requested = true
+               else
+                       force_close
+               end
+       end
+
+       # Force closing this connection and freeing `native_buffer_event`
+       fun force_close
+       do
+               if closed then return
+
+               native_buffer_event.free
+               closed = true
        end
 
        # Callback method on a write event
@@ -149,8 +163,18 @@ class Connection
        fun event_callback(events: Int): Bool
        do
                if events & bev_event_error != 0 or events & bev_event_eof != 0 then
-                       if events & bev_event_error != 0 then print_error "Error from bufferevent"
-                       close
+                       if events & bev_event_error != 0 then
+                               var sock_err = evutil_socket_error
+                               # Ignore some normal errors and print the others for debugging
+                               if sock_err == 110 then
+                                       # Connection timed out (ETIMEDOUT)
+                               else if sock_err == 104 then
+                                       # Connection reset by peer (ECONNRESET)
+                               else
+                                       print_error "libevent error event: {evutil_socket_error_to_string(sock_err)} ({sock_err})"
+                               end
+                       end
+                       force_close
                        return true
                end
 
@@ -161,7 +185,7 @@ class Connection
        redef fun write(str)
        do
                if close_requested then return
-               native_buffer_event.write(str.to_cstring, str.bytelen)
+               native_buffer_event.write(str.to_cstring, str.byte_length)
        end
 
        redef fun write_byte(byte)
@@ -228,6 +252,18 @@ fun bev_event_timeout: Int `{ return BEV_EVENT_TIMEOUT; `}
 # connect operation finished.
 fun bev_event_connected: Int `{ return BEV_EVENT_CONNECTED; `}
 
+# Global error code for the last socket operation on the calling thread
+#
+# Not idempotent on all platforms.
+fun evutil_socket_error: Int `{
+       return EVUTIL_SOCKET_ERROR();
+`}
+
+# Convert an error code from `evutil_socket_error` to a string
+fun evutil_socket_error_to_string(error_code: Int): NativeString `{
+       return evutil_socket_error_to_string(error_code);
+`}
+
 # ---
 # Options that can be specified when creating a `NativeBufferEvent`
 
@@ -288,18 +324,7 @@ extern class NativeBufferEvent `{ struct bufferevent * `}
                return bufferevent_write(self, &byt, 1);
        `}
 
-       # Check if we have anything left in our buffers. If so, we set our connection to be closed
-       # on a callback. Otherwise we close it and free it right away.
-       fun destroy: Bool `{
-               struct evbuffer* out = bufferevent_get_output(self);
-               struct evbuffer* in = bufferevent_get_input(self);
-               if(evbuffer_get_length(in) > 0 || evbuffer_get_length(out) > 0) {
-                       return 0;
-               } else {
-                       bufferevent_free(self);
-                       return 1;
-               }
-       `}
+       redef fun free `{ bufferevent_free(self); `}
 
        # The output buffer associated to `self`
        fun output_buffer: OutputNativeEvBuffer `{ return bufferevent_get_output(self); `}
@@ -381,16 +406,9 @@ extern class ConnectionListener `{ struct evconnlistener * `}
 
        # Callback method on listening error
        fun error_callback do
-               var cstr = socket_error
-               sys.stderr.write "libevent error: '{cstr}'"
+               var cstr = evutil_socket_error_to_string(evutil_socket_error)
+               print_error "libevent error: '{cstr}'"
        end
-
-       # Error with sockets
-       fun socket_error: NativeString `{
-               // TODO move to Nit and maybe NativeEventBase
-               int err = EVUTIL_SOCKET_ERROR();
-               return evutil_socket_error_to_string(err);
-       `}
 end
 
 # Factory to listen on sockets and create new `Connection`
index ca004a0..2c0eec1 100644 (file)
@@ -43,7 +43,7 @@ redef class App
                bar.title = "app.nit" # TODO offer a portable API to name windows
                bar.show_close_button = true
 
-               # TODO add back button
+               bar.add back_button.native
 
                return bar
        end
@@ -55,6 +55,9 @@ redef class App
                return stack
        end
 
+       # Button on the header bar to go back
+       var back_button = new BackButton is lazy
+
        # On GNU/Linux, we go through all the callbacks once,
        # there is no complex life-cycle.
        redef fun run
@@ -64,7 +67,6 @@ redef class App
                app.on_start
                app.on_resume
 
-               native_window.show_all
                gtk_main
 
                app.on_pause
@@ -88,7 +90,13 @@ redef class App
                # improved with GTK 3.18 and interpolate_size.
                native_window.resizable = false
 
+               native_window.show_all
+
                super
+
+               if window.enable_back_button then
+                       back_button.native.show
+               else back_button.native.hide
        end
 end
 
@@ -243,6 +251,21 @@ redef class Button
        init do native.signal_connect("clicked", self, null)
 end
 
+# Button to go back between windows
+class BackButton
+       super Button
+
+       # TODO i18n
+       redef fun text=(value) do super(value or else "Back")
+
+       redef fun signal(sender, data)
+       do
+               super
+
+               app.window.on_back_button
+       end
+end
+
 redef class Label
        redef type NATIVE: GtkLabel
        redef var native = new GtkLabel("")
@@ -294,6 +317,9 @@ redef class CheckBox
        redef type NATIVE: GtkCheckButton
        redef var native = new GtkCheckButton
 
+       redef fun signal(sender, data) do notify_observers new ToggleEvent(self)
+       init do native.signal_connect("toggled", self, null)
+
        redef fun text do return native.text
        redef fun text=(value) do native.text = (value or else "").to_s
 
@@ -317,3 +343,7 @@ redef class TextInput
                super
        end
 end
+
+redef class Text
+       redef fun open_in_browser do system("xdg-open '{self.escape_to_sh}' &")
+end
diff --git a/lib/logic/lexpr.nit b/lib/logic/lexpr.nit
new file mode 100644 (file)
index 0000000..b139de3
--- /dev/null
@@ -0,0 +1,379 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Logical expression.
+#
+# This module provides a simple data structure for first order logic.
+# Basic logical operators and algorithms are provided.
+#
+# ~~~
+# var a = new LVar("a")
+# var b = new LVar("b")
+# var c = new LVar("c")
+#
+# assert a.to_s == "a"
+# assert (a|b).to_s == "(a | b)"
+# assert (~(a|b)).to_s == "~(a | b)"
+# ~~~
+#
+# Compute a negative normal form:
+#
+# ~~~
+# var ex = a | (b & c)
+# var nex = ~ex
+# assert ex.nnf.to_s == "(a | (b & c))"
+# assert nex.nnf.to_s == "(~a & (~b | ~c))"
+# ~~~
+#
+# Compute a conjunctive normal form:
+#
+# ~~~
+# assert ex.cnf.simplify.to_s == "(a|b) & (a|c)"
+# assert nex.cnf.simplify.to_s == "(~a) & (~b|~c)"
+# ~~~
+module lexpr
+
+# A logical expression
+#
+# The basic (primitive) operators are: `&` (and), `|` (or) and `~` (not).
+# Composed operators are `impl` and `^`, they implemented using the basic operators.
+class LExpr
+       # It the logical expression `ltrue`?
+       #
+       # ~~~
+       # assert     ltrue.is_t
+       # assert not lfalse.is_t
+       # assert not (new LVar("a")).is_t
+       # ~~~
+       fun is_t: Bool do return false
+
+       # It the logical expression `lfalse`?
+       #
+       # ~~~
+       # assert     lfalse.is_f
+       # assert not ltrue.is_f
+       # assert not (new LVar("a")).is_f
+       # ~~~
+       fun is_f: Bool do return false
+
+       # The negation of `self`
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # assert (~a).to_s == "~a"
+       # assert (~~a).to_s == "a"
+       # ~~~
+       fun ~:LExpr do return lnot
+
+       private var lnot: LExpr is lazy do return new LNot(self)
+
+       # Disjunction with `e` (and).
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # assert (a|b).to_s == "(a | b)"
+       # ~~~
+       #
+       # `true` and `false` operands are optimized.
+       #
+       # ~~~
+       # assert (a|ltrue).is_t
+       # assert (a|lfalse) == a
+       # ~~~
+       fun |(e: LExpr): LExpr do
+               if self.is_f then return e
+               if self.is_t then return ltrue
+               if e.is_f then return self
+               if e.is_t then return ltrue
+               return new LOr(self, e)
+       end
+
+       # Conjunction with `e` (or).
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # assert (a&b).to_s == "(a & b)"
+       # ~~~
+       #
+       # `true` and `false` operands are optimized.
+       #
+       # ~~~
+       # assert (a&ltrue) == a
+       # assert (a&lfalse).is_f
+       # ~~~
+       fun &(e: LExpr): LExpr do
+               if self.is_f then return lfalse
+               if self.is_t then return e
+               if e.is_f then return lfalse
+               if e.is_t then return self
+               return new LAnd(self, e)
+       end
+
+       # Implication with `e` (implies).
+       #
+       # Note: it is transformed with conjunctions and disjunctions.
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # assert a.imp(b).to_s == "(~a | b)"
+       # ~~~
+       fun imp(e: LExpr): LExpr
+       do
+               return (~self) | e
+       end
+
+       # Exclusive disjunction with `e` (xor).
+       #
+       # Note: it is transformed with conjunctions and disjunctions.
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # assert (a^b).to_s == "((a & ~b) | (~a & b))"
+       # ~~~
+       fun ^(e: LExpr): LExpr
+       do
+               return (self & ~e) | (~self & e)
+       end
+
+       # The negation normal form (NNF).
+       #
+       # In NNF, the negation operator is only applied to variables.
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # assert (a&b).nnf.to_s == "(a & b)"
+       # assert (~(a&b)).nnf.to_s == "(~a | ~b)"
+       # ~~~
+       #
+       # Subclasses implement it recursively.
+       fun nnf: LExpr do return self
+
+       # The negative negation normal form (NNF).
+       #
+       # This method is used to implement `nnf`.
+       # Basically, it returns the NNF of `~self`.
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # assert (a&b).nnnf.to_s == "(~a | ~b)"
+       # ~~~
+       #
+       # Subclasses implements it by using De Morgan's laws to push negation inwards.
+       fun nnnf: LExpr do return ~self
+
+       # Compute a conjunctive normal form.
+       #
+       # By default, a unique *equivalent* formula is computed (but its size might be exponential).
+       #
+       # ~~~
+       # var a = new LVar("a")
+       # var b = new LVar("b")
+       # var c = new LVar("c")
+       #
+       # var ex = a | (b & c)
+       # assert ex.cnf.to_s == "(a|b) & (a|c)"
+       # assert (~ex).cnf.to_s == "(~a) & (~b|~c)"
+       # ~~~
+       #
+       # If a parameter `vars` is given, an *equisatisfiable* formula is computed.
+       # Additional variables, named `zzsomething`, are created by the transformation and stored in `vars`.
+       fun cnf(vars: nullable Array[LVar]): CNF do
+               var ss = new CNF
+               var s = new Set[LExpr]
+               ss.data.add s
+               s.add self
+               return ss
+       end
+
+       # The size of the logical expression.
+       fun size: Int do return 1
+end
+
+# A logical conjunction operation
+class LAnd
+       super LExpr
+
+       # The first operand
+       var e1: LExpr
+
+       # The second operand
+       var e2: LExpr
+
+       redef fun to_s do return "({e1} & {e2})"
+
+       redef fun nnf do return e1.nnf & e2.nnf
+       redef fun nnnf do return e1.nnnf | e2.nnnf
+       redef fun cnf(v) do return e1.cnf(v) & e2.cnf(v)
+
+       redef fun size do return e1.size + e2.size + 1
+end
+
+# A logical disjunction operation
+class LOr
+       super LExpr
+
+       # The first operand
+       var e1: LExpr
+
+       # The second operand
+       var e2: LExpr
+
+       redef fun to_s do return "({e1} | {e2})"
+       redef fun nnf do return e1.nnf | e2.nnf
+       redef fun nnnf do return e1.nnnf & e2.nnnf
+
+       redef fun cnf(v) do
+               var c1 = e1.cnf(v)
+               var c2 = e2.cnf(v)
+
+               if c1.data.length > 1 and c2.data.length > 1 and v != null then
+                       var z = new LVar("zz{v.length}")
+                       v.add z
+                       var nz = ~z
+
+                       var res = new CNF
+                       for c in c1.data do
+                               var set = c.clone
+                               set.add z
+                               res.data.add(set)
+                       end
+                       for c in c2.data do
+                               var set = c.clone
+                               set.add nz
+                               res.data.add(set)
+                       end
+
+                       return res
+               end
+
+               var res = new CNF
+               for i in c1.data do for j in c2.data do
+                       res.data.add i.union(j)
+               end
+               return res
+       end
+
+       redef fun size do return e1.size + e2.size + 1
+end
+
+# A logical negation operation
+class LNot
+       super LExpr
+
+       # The argument
+       var e: LExpr
+
+       redef fun ~ do return e
+       redef fun to_s do return "~{e}"
+       redef fun nnf do return e.nnnf
+       redef fun nnnf do return e.nnf
+       redef fun cnf(v) do
+               if e isa LVar then return super
+               return e.nnnf.cnf(v)
+       end
+       redef fun size do return e.size + 1
+end
+
+# The logical *true* variable.
+fun ltrue: LTrue do return once new LTrue
+
+# The class of the singleton `ltrue`
+class LTrue
+       super LExpr
+       redef fun is_t do return true
+       redef fun ~ do return lfalse
+       redef fun to_s do return "true"
+end
+
+# The logical *false* variable.
+fun lfalse: LFalse do return once new LFalse
+
+# The class of the singleton `lfalse`
+class LFalse
+       super LExpr
+       redef fun is_f do return true
+       redef fun ~ do return ltrue
+       redef fun to_s do return "false"
+end
+
+
+# A variable of a logical expression.
+class LVar
+       super LExpr
+
+       # The name of the variable (used for representation)
+       #
+       # Internally, two variables with the same name are not merged
+       var name: String
+
+       redef fun to_s do return name
+end
+
+# A conjunctive normal form of a logical expression.
+class CNF
+       # The conjunction of disjunction of units.
+       var data = new Set[Set[LExpr]]
+
+       # Simplify `self` by removing some useless clauses.
+       #
+       # * trivially clauses (with both a variable and its negation)
+       # * subsumed clauses
+       fun simplify: CNF
+       do
+               var cs2 = new Set[Set[LExpr]]
+
+               # First pass to remove clauses with a variable and its negation.
+               # These clauses become trivially `true`, thus useless in the conjunction.
+               for c in data do
+                       for i in c do
+                               var nn = ~i
+                               if c.has(nn) then continue label
+                       end
+                       cs2.add c
+               end label
+
+               # Second pass to remove clauses subsumed by an other one.
+               var cs3 = new Set[Set[LExpr]]
+               for c in cs2 do
+                       for c2 in cs2 do
+                               if c2 != c and c2.has_all(c) then continue label
+                       end
+                       cs3.add c
+               end label
+
+               var res = new CNF
+               res.data = cs3
+               return res
+       end
+
+       # Merge two CNF
+       fun &(o: CNF): CNF
+       do
+               var res = new CNF
+               res.data.add_all self.data
+               res.data.add_all o.data
+               return res
+       end
+
+       redef fun to_s do return [for c in data do "(" + c.join("|") + ")"].join(" & ")
+
+       redef fun ==(o) do return o isa CNF and data == o.data
+       redef fun hash do return data.hash
+end
diff --git a/lib/logic/logic.nit b/lib/logic/logic.nit
new file mode 100644 (file)
index 0000000..26def77
--- /dev/null
@@ -0,0 +1,18 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# First-order logic data structure and algorithm.
+module logic
+
+import lexpr
diff --git a/lib/logic/package.ini b/lib/logic/package.ini
new file mode 100644 (file)
index 0000000..4394b65
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=logic
+tags=algo,lib
+maintainer=Jean Privat <jean@pryen.org>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/logic/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/logic/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
index 24ea4bb..3dcc1cc 100644 (file)
@@ -1,4 +1,4 @@
-NITC=../../bin/nitc
+NITC=nitc
 
 all: nitmd
 
index c8f02e3..e78da16 100644 (file)
@@ -22,7 +22,7 @@ module android_app is android_manifest_activity """
 
 import mnit
 import mnit::opengles1
-import ::android
+import ::android::game
 intrude import ::android::input_events
 
 in "C" `{
index 3841bc6..f4abd92 100644 (file)
@@ -44,19 +44,19 @@ redef class App
        private var frame_count = 0
 
        # Deadline used to compute `current_fps`
-       private var frame_count_deadline = 0
+       private var frame_count_deadline = 0.0
 
        # Check and sleep to maitain a frame-rate bellow `maximum_fps`
        # Also periodically uptate `current_fps`
        # Is automatically called at the end of `full_frame`.
        fun limit_fps
        do
-               var t = clock.total.sec
+               var t = clock.total
                if t >= frame_count_deadline then
                        var cfps = frame_count.to_f / 5.0
                        self.current_fps = cfps
                        frame_count = 0
-                       frame_count_deadline = t + 5
+                       frame_count_deadline = t + 5.0
                end
                frame_count += 1
 
index 9fb05bb..3062d5e 100644 (file)
@@ -540,3 +540,44 @@ private class UnrolledIterator[E]
                end
        end
 end
+
+# Keep track of the best elements according to a distance value.
+#
+# ~~~
+# var bests = new BestDistance[String](5)
+# bests.update(10, "Too big")
+# assert bests.best_items.is_empty
+# bests.update(5, "Just fine")
+# bests.update(5, "Another one")
+# assert bests.best_items.has_exactly(["Just fine", "Another one"])
+# bests.update(2, "A better one")
+# bests.update(4, "Not good enough")
+# assert bests.best_distance == 2
+# assert bests.best_items.has_exactly(["A better one"])
+# ~~~
+class BestDistance[E]
+       # Current smallest distance
+       var best_distance: Int is writable
+
+       # Known elements with the smallest distance
+       var best_items = new Set[E] is writable
+
+       # Register a `candidate` with a `distance`
+       #
+       # * To high, it is ignored.
+       # * Equal to the current best, it is added
+       # * Better that them, is is the new best element
+       #
+       # Return `true` if the candidate is kept (alone or with other)
+       # returns `false` if the candidate is ignored.
+       fun update(distance: Int, candidate: E): Bool
+       do
+               if distance > best_distance then return false
+               if distance < best_distance then
+                       best_distance = distance
+                       best_items.clear
+               end
+               best_items.add candidate
+               return true
+       end
+end
index 727ac60..b0d25d8 100644 (file)
@@ -130,7 +130,7 @@ class MPDConnection
                        var vol = status.volume
                        if vol != null then
                                var new_vol = vol + diff
-                               new_vol = new_vol.max(0).min(100)
+                               new_vol = new_vol.clamp(0, 100)
                                volume = new_vol
                                return
                        end
index a9e22d8..bc47997 100644 (file)
@@ -450,7 +450,6 @@ redef class FlatBuffer
                        source, tag, new Comm.world, new Status.ignore)
 
                length = capacity
-               is_dirty = true
        end
 
        redef fun recv_fill(mpi, dest, tag, comm) do recv(mpi, 0, capacity, dest, tag, comm)
index 6bc8b36..2db66b8 100644 (file)
@@ -26,7 +26,8 @@ and the FFI enables calling services in different languages.
 A minimal example follows with a custom `Action` and using `FileServer`.
 
 More general examples are available at `lib/nitcorn/examples/`.
-It includes the configuration of `http://xymus.net/` which merges many other _nitcorn_ applications.
+For an example of a larger project merging many _nitcorn_ applications into one server,
+take a look at the configuration of `http://xymus.net/` at `../contrib/xymus_net/xymus_net.nit`.
 
 Larger projects using _nitcorn_ can be found in the `contrib/` folder:
 * _opportunity_ is a meetup planner heavily based on _nitcorn_.
index d1bdeae..b1bbed9 100644 (file)
@@ -2,10 +2,6 @@ all: bin/restful_annot
        mkdir -p bin/
        ../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit
 
-xymus.net:
-       mkdir -p bin/
-       ../../../bin/nitc --dir bin/ src/xymus_net.nit
-
 pre-build: src/restful_annot_gen.nit
 src/restful_annot_gen.nit:
        ../../../bin/nitrestful -o $@ src/restful_annot.nit
index 37fa87e..315d76f 100644 (file)
@@ -21,15 +21,19 @@ module htcpcp_server
 
 import nitcorn
 
+# Nitcorn Action used to answer requests.
 class HTCPCPAction
        super Action
+
+       # Brewing status.
        var brewing = false
-  var is_teapot = false
+
+       # Teapot status.
+       var is_teapot = false
 
        redef fun answer(http_request, turi) do
                var message: String
                var method = http_request.method
-               var headers = http_request.header
                var response: HttpResponse
 
                if is_teapot == true then
@@ -77,10 +81,13 @@ class HTCPCPAction
        end
 end
 
-
+# Nitcorn server.
 class HTCPCServer
+
+       # Port to listen to.
        var port: Int
 
+       # Start listening.
        fun run do
                var vh = new VirtualHost("localhost:{port}")
                vh.routes.add new Route("/", new HTCPCPAction)
diff --git a/lib/nitcorn/examples/www/hello_world/dir/binary_file.png b/lib/nitcorn/examples/www/hello_world/dir/binary_file.png
new file mode 100644 (file)
index 0000000..d522fcc
Binary files /dev/null and b/lib/nitcorn/examples/www/hello_world/dir/binary_file.png differ
index 80f64d2..a9ef5f6 100644 (file)
@@ -71,6 +71,15 @@ class FileServer
        # Caching attributes of served files, used as the `cache-control` field in response headers
        var cache_control = "public, max-age=360" is writable
 
+       # Show directory listing?
+       var show_directory_listing = true is writable
+
+       # Default file returned when no static file matches the requested URI.
+       #
+       # If no `default_file` is provided, the FileServer responds 404 error to
+       # unmatched queries.
+       var default_file: nullable String = null is writable
+
        redef fun answer(request, turi)
        do
                var response
@@ -83,15 +92,14 @@ class FileServer
                # This make sure that the requested file is within the root folder.
                if (local_file + "/").has_prefix(root) then
                        # Does it exists?
-                       if local_file.file_exists then
-                               if local_file.file_stat.is_dir then
+                       var file_stat = local_file.file_stat
+                       if file_stat != null then
+                               if file_stat.is_dir then
                                        # If we target a directory without an ending `/`,
                                        # redirect to the directory ending with `/`.
-                                       if not request.uri.is_empty and
-                                          request.uri.chars.last != '/' then
-                                               response = new HttpResponse(303)
-                                               response.header["Location"] = request.uri + "/"
-                                               return response
+                                       var uri = request.uri
+                                       if not uri.is_empty and uri.chars.last != '/' then
+                                               return answer_redirection(request.uri + "/")
                                        end
 
                                        # Show index file instead of the directory listing
@@ -105,31 +113,94 @@ class FileServer
                                        end
                                end
 
-                               response = new HttpResponse(200)
-                               if local_file.file_stat.is_dir then
-                                       # Show the directory listing
-                                       var title = turi
-                                       var files = local_file.files
+                               file_stat = local_file.file_stat
+                               if show_directory_listing and file_stat != null and file_stat.is_dir then
+                                       response = answer_directory_listing(request, turi, local_file)
+                               else if file_stat != null and not file_stat.is_dir then # It's a single file
+                                       response = answer_file(local_file)
+                               else response = answer_default
+                       else response = answer_default
+               else response = new HttpResponse(403)
 
-                                       var links = new Array[String]
-                                       if turi.length > 1 then
-                                               var path = (request.uri + "/..").simplify_path
-                                               links.add "<a href=\"{path}/\">..</a>"
-                                       end
-                                       for file in files do
-                                               var local_path = local_file.join_path(file).simplify_path
-                                               var web_path = file.simplify_path
-                                               if local_path.file_stat.is_dir then web_path = web_path + "/"
-                                               links.add "<a href=\"{web_path}\">{file}</a>"
-                                       end
+               if response.status_code != 200 then
+                       var tmpl = error_page(response.status_code)
+                       if header != null and tmpl isa ErrorTemplate then tmpl.header = header
+                       response.body = tmpl.to_s
+               end
+
+               return response
+       end
+
+       # Answer the `default_file` if any.
+       fun answer_default: HttpResponse do
+               var default_file = self.default_file
+               if default_file == null then
+                       return new HttpResponse(404)
+               end
+
+               var local_file = (root / default_file).simplify_path
+               return answer_file(local_file)
+       end
+
+       # Answer a 303 redirection to `location`.
+       fun answer_redirection(location: String): HttpResponse do
+               var response = new HttpResponse(303)
+               response.header["Location"] = location
+               return response
+       end
+
+       # Build a reponse containing a single `local_file`.
+       #
+       # Returns a 404 error if local_file does not exists.
+       fun answer_file(local_file: String): HttpResponse do
+               if not local_file.file_exists then return new HttpResponse(404)
+
+               var response = new HttpResponse(200)
+               response.files.add local_file
+
+               # Set Content-Type depending on the file extension
+               var ext = local_file.file_extension
+               if ext != null then
+                       var media_type = media_types[ext]
+                       if media_type != null then
+                               response.header["Content-Type"] = media_type
+                       else response.header["Content-Type"] = "application/octet-stream"
+               end
+
+               # Cache control
+               response.header["cache-control"] = cache_control
+               return response
+       end
+
+       # Answer with a directory listing for files within `local_files`.
+       fun answer_directory_listing(request: HttpRequest, turi, local_file: String): HttpResponse do
+               # Show the directory listing
+               var title = turi
+               var files = local_file.files
 
-                                       var header = self.header
-                                       var header_code
-                                       if header != null then
-                                               header_code = header.write_to_string
-                                       else header_code = ""
+               alpha_comparator.sort files
 
-                                       response.body = """
+               var links = new Array[String]
+               if turi.length > 1 then
+                       var path = (request.uri + "/..").simplify_path
+                       links.add "<a href=\"{path}/\">..</a>"
+               end
+               for file in files do
+                       var local_path = local_file.join_path(file).simplify_path
+                       var web_path = file.simplify_path
+                       var file_stat = local_path.file_stat
+                       if file_stat != null and file_stat.is_dir then web_path = web_path + "/"
+                       links.add "<a href=\"{web_path}\">{file}</a>"
+               end
+
+               var header = self.header
+               var header_code
+               if header != null then
+                       header_code = header.write_to_string
+               else header_code = ""
+
+               var response = new HttpResponse(200)
+               response.body = """
 <!DOCTYPE html>
 <head>
        <meta charset="utf-8">
@@ -151,32 +222,7 @@ class FileServer
 </body>
 </html>"""
 
-                                       response.header["Content-Type"] = media_types["html"].as(not null)
-                               else
-                                       # It's a single file
-                                       response.files.add local_file
-
-                                       var ext = local_file.file_extension
-                                       if ext != null then
-                                               var media_type = media_types[ext]
-                                               if media_type != null then
-                                                       response.header["Content-Type"] = media_type
-                                               else response.header["Content-Type"] = "application/octet-stream"
-                                       end
-
-                                       # Cache control
-                                       response.header["cache-control"] = cache_control
-                               end
-
-                       else response = new HttpResponse(404)
-               else response = new HttpResponse(403)
-
-               if response.status_code != 200 then
-                       var tmpl = error_page(response.status_code)
-                       if header != null and tmpl isa ErrorTemplate then tmpl.header = header
-                       response.body = tmpl.to_s
-               end
-
+               response.header["Content-Type"] = media_types["html"].as(not null)
                return response
        end
 end
index 0d12c04..1833fca 100644 (file)
@@ -114,6 +114,7 @@ class HttpRequestParser
        # Words of the first line
        private var first_line = new Array[String]
 
+       # Parse the `first_line`, `header_fields` and `body` of `full_request`.
        fun parse_http_request(full_request: String): nullable HttpRequest
        do
                clear_data
index 3d0fd33..dd3e03b 100644 (file)
@@ -46,7 +46,7 @@ class HttpResponse
                # Set the content length if not already set
                if not header.keys.has("Content-Length") then
                        # Size of the body
-                       var len = body.bytelen
+                       var len = body.byte_length
 
                        # Size of included files
                        for path in files do
@@ -97,7 +97,8 @@ class HttpStatusCodes
        # All know code and their message
        var codes = new HashMap[Int, String]
 
-       protected init do insert_status_codes
+       # Init the status `codes` list.
+       protected init is old_style_init do insert_status_codes
 
        # Get the message associated to the status `code`, return `null` in unknown
        fun [](code: Int): nullable String
index 73c9626..305f314 100644 (file)
@@ -20,6 +20,8 @@ module media_types
 
 # Map of known MIME types
 class MediaTypes
+
+       # MIME types by extensions.
        protected var types = new HashMap[String, String]
 
        # Get the type/subtype associated to a file extension `ext`
@@ -51,6 +53,7 @@ class MediaTypes
                types["jar"]        = "application/java-archive"
                types["war"]        = "application/java-archive"
                types["ear"]        = "application/java-archive"
+               types["json"]       = "application/json"
                types["hqx"]        = "application/mac-binhex40"
                types["pdf"]        = "application/pdf"
                types["cco"]        = "application/x-cocoa"
@@ -105,4 +108,5 @@ class MediaTypes
        end
 end
 
+# MIME types list.
 fun media_types: MediaTypes do return once new MediaTypes
index d475444..de9ddc0 100644 (file)
@@ -81,8 +81,8 @@ class HttpServer
                                # Delegate the responsibility to respond to the `Action`
                                handler.prepare_respond_and_close(request, turi, self)
                                return
-                       else response = new HttpResponse(405)
-               else response = new HttpResponse(405)
+                       else response = new HttpResponse(404)
+               else response = new HttpResponse(404)
 
                respond response
                close
index bccfd2c..158643a 100644 (file)
@@ -35,11 +35,13 @@ class RestfulAction
                if val == null then return null
 
                var deserializer = new JsonDeserializer(val)
+               var obj = deserializer.deserialize
+
                if deserializer.errors.not_empty then
                        print_error deserializer.errors.join("\n")
                        return null
                end
 
-               return deserializer.deserialize
+               return obj
        end
 end
index c2bd614..e61caad 100644 (file)
@@ -61,6 +61,12 @@ class Route
        # Path to this action present in the URI
        var path: nullable String
 
+       init
+       do
+               var path = path
+               if path != null then self.path = "/" / path
+       end
+
        # `Action` to activate when this route is traveled
        var handler: Action
 end
index 4d54aa9..005a4dd 100644 (file)
@@ -72,7 +72,7 @@ end
 
 redef class HttpRequest
        # The `Session` associated to this request
-       var session: nullable Session = null
+       var session: nullable Session = null is writable
 end
 
 redef class HttpRequestParser
index 07664f6..3ec5dc1 100644 (file)
@@ -226,6 +226,8 @@ private class UriParam
 
        # Parameters match everything.
        redef fun match(part) do return true
+
+       redef fun to_s do return name
 end
 
 # A static uri string like `users`.
@@ -237,6 +239,8 @@ private class UriString
 
        # Empty strings match everything otherwise matching is based on string equality.
        redef fun match(part) do return string.is_empty or string == part
+
+       redef fun to_s do return string
 end
 
 redef class Routes
index ba09bc1..cf4d0fa 100644 (file)
@@ -359,3 +359,72 @@ redef universal Int
                return self & 0x3FFF_FFFF
        end
 end
+
+redef universal Float
+       # Smoothened `self`, used by `ImprovedNoise`
+       private fun fade: Float do return self*self*self*(self*(self*6.0-15.0)+10.0)
+end
+
+# Direct translation of Ken Perlin's improved noise Java implementation
+#
+# This implementation differs from `PerlinNoise` on two main points.
+# This noise is calculated for a 3D point, vs 2D in `PerlinNoise`.
+# `PerlinNoise` is based off a customizable seed, while this noise has a static data source.
+class ImprovedNoise
+
+       # Permutations
+       private var p: Array[Int] = [151,160,137,91,90,15,
+               131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
+               190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
+               88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
+               77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
+               102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
+               135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
+               5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
+               223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
+               129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
+               251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
+               49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
+               138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180] * 2
+
+       # Noise value in [-1..1] at 3D coordinates `x, y, z`
+       fun noise(x, y, z: Float): Float
+       do
+               var xx = x.floor.to_i & 255
+               var yy = y.floor.to_i & 255
+               var zz = z.floor.to_i & 255
+
+               x -= x.floor
+               y -= y.floor
+               z -= z.floor
+
+               var u = x.fade
+               var v = y.fade
+               var w = z.fade
+
+               var a  = p[xx  ] + yy
+               var aa = p[a   ] + zz
+               var ab = p[a+1 ] + zz
+               var b  = p[xx+1] + yy
+               var ba = p[b   ] + zz
+               var bb = p[b+1 ] + zz
+
+               return w.lerp(v.lerp(u.lerp(grad(p[aa  ], x,     y,     z    ),
+                                           grad(p[ba  ], x-1.0, y,     z    )),
+                                    u.lerp(grad(p[ab  ], x,     y-1.0, z    ),
+                                           grad(p[bb  ], x-1.0, y-1.0, z    ))),
+                      v.lerp(u.lerp(grad(p[aa+1], x,     y,     z-1.0),
+                                           grad(p[ba+1], x-1.0, y,     z-1.0)),
+                                    u.lerp(grad(p[ab+1], x,     y-1.0, z-1.0),
+                                           grad(p[bb+1], x-1.0, y-1.0, z-1.0))))
+       end
+
+       # Value at a corner of the grid
+       private fun grad(hash: Int, x, y, z: Float): Float
+       do
+               var h = hash & 15
+               var u = if h < 8 then x else y
+               var v = if h < 4 then y else if h == 12 or h == 14 then x else z
+               return (if h.is_even then u else -u) + (if h & 2 == 0 then v else -v)
+       end
+end
index 4763061..b9e436a 100644 (file)
@@ -122,11 +122,8 @@ class PerfEntry
        # Total execution time of this event
        var sum = 0.0
 
-       # Register a new event execution time with a `Timespec`
-       fun add(lapse: Timespec) do add_float lapse.to_f
-
-       # Register a new event execution time in seconds using a `Float`
-       fun add_float(time: Float)
+       # Register a new event execution time in seconds
+       fun add(time: Float)
        do
                if time.to_f < min.to_f or count == 0 then min = time
                if time.to_f > max.to_f then max = time
diff --git a/lib/popcorn/.gitignore b/lib/popcorn/.gitignore
new file mode 100644 (file)
index 0000000..c32211a
--- /dev/null
@@ -0,0 +1 @@
+tests/out
diff --git a/lib/popcorn/Makefile b/lib/popcorn/Makefile
new file mode 100644 (file)
index 0000000..ad89fec
--- /dev/null
@@ -0,0 +1,22 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+NITUNIT=nitunit
+
+all:
+
+check:
+       $(NITUNIT) .
diff --git a/lib/popcorn/README.md b/lib/popcorn/README.md
new file mode 100644 (file)
index 0000000..2bb6646
--- /dev/null
@@ -0,0 +1,855 @@
+# Popcorn
+
+**Why endure plain corn when you can pop it?!**
+
+Popcorn is a minimal yet powerful nit web application framework that provides cool
+features for lazy developpers.
+
+Popcorn is built over nitcorn to provide a clean and user friendly interface
+without all the boiler plate code.
+
+## What does it taste like?
+
+Set up is quick and easy as 10 lines of code.
+Create a file `app.nit` and add the following code:
+
+~~~
+import popcorn
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.html "<h1>Hello World!</h1>"
+end
+
+var app = new App
+app.use("/", new HelloHandler)
+app.listen("localhost", 3000)
+~~~
+
+The Popcorn app listens on port 3000 for connections.
+The app responds with "Hello World!" for requests to the root URL (`/`) or **route**.
+For every other path, it will respond with a **404 Not Found**.
+
+The `req` (request) and `res` (response) parameters are the same that nitcorn provides
+so you can do anything else you would do in your route without Popcorn involved.
+
+Run the app with the following command:
+
+~~~bash
+$ nitc app.nit && ./app
+~~~
+
+Then, load [http://localhost:3000](http://localhost:3000) in a browser to see the output.
+
+Here the output using the `curl` command:
+
+~~~bash
+$ curl localhost:3000
+<h1>Hello World!</h1>
+
+$ curl localhost:3000/wrong_uri
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Not Found</title>
+</head>
+<body>
+<h1>404 Not Found</h1>
+</body>
+</html>
+~~~
+
+This is why we love popcorn!
+
+## Basic routing
+
+**Routing** refers to determining how an application responds to a client request
+to a particular endpoint, which is a URI (or path) and a specific HTTP request
+method GET, POST, PUT or DELETE (other methods are not suported yet).
+
+Each route can have one or more handler methods, which are executed when the route is matched.
+
+Route handlers definition takes the following form:
+
+~~~nitish
+import popcorn
+
+class MyHandler
+       super Handler
+
+       redef fun METHOD(req, res) do end
+end
+~~~
+
+Where:
+* `MyHandler` is the name of the handler you will add to the app.
+* `METHOD` can be replaced by `get`, `post`, `put` or `delete`.
+
+The following example responds to GET and POST requests:
+
+~~~
+import popcorn
+
+class MyHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Got a GET request"
+       redef fun post(req, res) do res.send "Got a POST request"
+end
+~~~
+
+To make your handler responds to a specific route, you have to add it to the app.
+
+Respond to POST request on the root route (`/`), the application's home page:
+
+~~~
+var app = new App
+app.use("/", new MyHandler)
+~~~
+
+Respond to a request to the `/user` route:
+
+~~~
+app.use("/user", new MyHandler)
+~~~
+
+For more details about routing, see the routing section.
+
+## Serving static files with Popcorn
+
+To serve static files such as images, CSS files, and JavaScript files, use the
+Popcorn built-in handler `StaticHandler`.
+
+Pass the name of the directory that contains the static assets to the StaticHandler
+init method to start serving the files directly.
+For example, use the following code to serve images, CSS files, and JavaScript files
+in a directory named `public`:
+
+~~~
+app.use("/", new StaticHandler("public/"))
+~~~
+
+Now, you can load the files that are in the `public` directory:
+
+~~~raw
+http://localhost:3000/images/trollface.jpg
+http://localhost:3000/css/style.css
+http://localhost:3000/js/app.js
+http://localhost:3000/hello.html
+~~~
+
+Popcorn looks up the files relative to the static directory, so the name of the
+static directory is not part of the URL.
+To use multiple static assets directories, add the `StaticHandler` multiple times:
+
+~~~
+app.use("/", new StaticHandler("public/"))
+app.use("/", new StaticHandler("files/"))
+~~~
+
+Popcorn looks up the files in the order in which you set the static directories
+with the `use` method.
+
+To create a virtual path prefix (where the path does not actually exist in the file system)
+for files that are served by the `StaticHandler`, specify a mount path for the
+static directory, as shown below:
+
+~~~
+app.use("/static/", new StaticHandler("public/"))
+~~~
+
+Now, you can load the files that are in the public directory from the `/static`
+path prefix.
+
+~~~raw
+http://localhost:3000/static/images/trollface.jpg
+http://localhost:3000/static/css/style.css
+http://localhost:3000/static/js/app.js
+http://localhost:3000/static/hello.html
+~~~
+
+However, the path that you provide to the `StaticHandler` is relative to the
+directory from where you launch your app.
+If you run the app from another directory, it’s safer to use the absolute path of
+the directory that you want to serve.
+
+In some cases, you can want to redirect request to static files to a default file
+instead of returning a 404 error.
+This can be achieved by specifying a default file in the StaticHandler:
+
+~~~
+app.use("/static/", new StaticHandler("public/", "default.html"))
+~~~
+
+This way all non-matched queries to the StaticHandler will be answered with the
+`default.html` file.
+
+## Advanced Routing
+
+**Routing** refers to the definition of application end points (URIs) and how
+they respond to client requests. For an introduction to routing, see the Basic routing
+section.
+
+The following code is an example of a very basic route.
+
+~~~
+import popcorn
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Hello World!"
+end
+
+var app = new App
+app.use("/", new HelloHandler)
+~~~
+
+### Route methods
+
+A **route method** is derived from one of the HTTP methods, and is attached to an
+instance of the Handler class.
+
+The following code is an example of routes that are defined for the GET and the POST
+methods to the root of the app.
+
+~~~
+import popcorn
+
+class GetPostHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "GET request to the homepage"
+       redef fun post(req, res) do res.send "POST request to the homepage"
+end
+
+var app = new App
+app.use("/", new GetPostHandler)
+~~~
+
+Popcorn supports the following routing methods that correspond to HTTP methods:
+get, post, put, and delete.
+
+The request query string is accessed through the `req` parameter:
+
+~~~
+import popcorn
+import template
+
+class QueryStringHandler
+       super Handler
+
+       redef fun get(req, res) do
+               var tpl = new Template
+               tpl.addn "URI: {req.uri}"
+               tpl.addn "Query string: {req.query_string}"
+               for name, arg in req.get_args do
+                       tpl.addn "{name}: {arg}"
+               end
+        res.send tpl
+       end
+end
+
+var app = new App
+app.use("/", new QueryStringHandler)
+app.listen("localhost", 3000)
+~~~
+
+Post parameters can also be accessed through the `req` parameter:
+
+~~~
+import popcorn
+import template
+
+class PostHandler
+       super Handler
+
+       redef fun post(req, res) do
+               var tpl = new Template
+               tpl.addn "URI: {req.uri}"
+               tpl.addn "Body: {req.body}"
+               for name, arg in req.post_args do
+                       tpl.addn "{name}: {arg}"
+               end
+        res.send tpl
+       end
+end
+
+var app = new App
+app.use("/", new PostHandler)
+app.listen("localhost", 3000)
+~~~
+
+There is a special routing method, `all(res, req)`, which is not derived from any
+HTTP method. This method is used to respond at a path for all request methods.
+
+In the following example, the handler will be executed for requests to "/user"
+whether you are using GET, POST, PUT, DELETE, or any other HTTP request method.
+
+~~~
+import popcorn
+
+class AllHandler
+       super Handler
+
+       redef fun all(req, res) do res.send "Every request to the homepage"
+end
+~~~
+
+Using the `all` method you can also implement other HTTP request methods.
+
+~~~
+import popcorn
+
+class MergeHandler
+       super Handler
+
+       redef fun all(req, res) do
+               if req.method == "MERGE" then
+                       # handle that method
+               else super # keep handle GET, POST, PUT and DELETE methods
+       end
+end
+~~~
+
+### Route paths
+
+**Route paths**, in combination with a request handlers, define the endpoints at
+which requests can be made.
+Route paths can be strings, parameterized strings or glob patterns.
+Query strings such as `?q=foo`are not part of the route path.
+
+Popcorn uses the `Handler::match(uri)` method to match the route paths.
+
+Here are some examples of route paths based on strings.
+
+This route path will match requests to the root route, `/`.
+
+~~~
+import popcorn
+
+class MyHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Got a GET request"
+end
+
+var app = new App
+app.use("/", new MyHandler)
+~~~
+
+This route path will match requests to `/about`.
+
+~~~
+app.use("/about", new MyHandler)
+~~~
+
+This route path will match requests to `/random.text`.
+
+~~~
+app.use("/random.text", new MyHandler)
+~~~
+
+During the query/response process, routes are matched by order of declaration
+through the `App::use` method.
+
+The app declared in this example will try to match the routes in this order:
+
+1. `/`
+2. `/about`
+3. `/random.text`
+
+### Route parameters
+
+**Route parameters** are variable parts of a route path. They can be used to path
+arguments within the URI.\13
+Parameters in a route are prefixed with a colon `:` like in `:userId`, `:year`.
+
+The following example declares a handler `UserHome` that responds with the `user`
+name.
+
+~~~
+import popcorn
+
+class UserHome
+       super Handler
+
+       redef fun get(req, res) do
+               var user = req.param("user")
+               if user != null then
+                       res.send "Hello {user}"
+               else
+                       res.send("Nothing received", 400)
+               end
+       end
+end
+
+var app = new App
+app.use("/:user", new UserHome)
+app.listen("localhost", 3000)
+~~~
+
+The `UserHome` handler listen to every path matching `/:user`. This can be `/Morriar`,
+`/10`, ... but not `/Morriar/profile` since route follow the strict matching rule.
+
+### Glob routes
+
+**Glob routes** are routes that match only on a prefix, thus accepting a wider range
+of URI.
+Glob routes end with the symbol `*`.
+
+Here we define a `UserItem` handler that will respond to any URI matching the prefix
+`/user/:user/item/:item`.
+Note that glob route are compatible with route parameters.
+
+~~~
+import popcorn
+
+class UserItem
+       super Handler
+
+       redef fun get(req, res) do
+               var user = req.param("user")
+               var item = req.param("item")
+               if user == null or item == null then
+                       res.send("Nothing received", 400)
+               else
+                       res.send "Here the item {item} of the use {user}."
+               end
+       end
+end
+
+var app = new App
+app.use("/user/:user/item/:item/*", new UserItem)
+app.listen("localhost", 3000)
+~~~
+
+## Response methods
+
+The methods on the response object (`res`), can is used to manipulate the
+request-response cycle.
+If none of these methods are called from a route handler, the client request will
+receive a `404 Not found` error.
+
+* `res.html()` Send a HTML response.
+* `res.json()` Send a JSON response.
+* `res.redirect()` Redirect a request.
+* `res.send()` Send a response of various types.
+* `res.error()` Set the response status code and send its message as the response body.
+
+## Response cycle
+
+When the popcorn `App` receives a request, the response cycle is the following:
+
+1. `pre-middlewares` lookup matching middlewares registered with `use_before(pre_middleware)`:
+       1. execute matching middleware by registration order
+       2. if a middleware send a response then let the `pre-middlewares` loop continue
+          with the next middleware
+2. `response-handlers` lookup matching handlers registered with `use(handler)`:
+       1. execute matching middleware by registration order
+       2. if a middleware send a response then stop the `response-handlers` loop
+       3. if no hander matches or sends a response, generate a 404 response
+3. `post-middlewares` lookup matching handlers registered with `use_after(post_handler)`:
+       1. execute matching middleware by registration order
+       2. if a middleware send a response then let the `post-middlewares` loop continue
+          with the next middleware
+
+## Middlewares
+
+### Overview
+
+**Middleware** handlers are handlers that typically do not send `HttpResponse` responses.
+Middleware handlers can perform the following tasks:
+
+* Execute any code.
+* Make changes to the request and the response objects.
+* End its action and pass to the next handler in the stack.
+
+If a middleware handler makes a call to `res.send()`, it provoques the end the
+request-response cycle and the response is sent to the client.
+
+### Ultra simple logger example
+
+Here is an example of a simple “Hello World” Popcorn application.
+We add a middleware handler to the application called MyLogger that prints a simple
+log message in the app stdout.
+
+~~~
+import popcorn
+
+class MyLogger
+       super Handler
+
+       redef fun all(req, res) do print "Request Logged!"
+end
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Hello World!"
+end
+
+
+var app = new App
+app.use_before("/*", new MyLogger)
+app.use("/", new HelloHandler)
+app.listen("localhost", 3000)
+~~~
+
+By using the `MyLogger` handler to the route `/*` we ensure that every requests
+(even 404 ones) pass through the middleware handler.
+This handler just prints “Request Logged!” when a request is received.
+
+Be default, the order of middleware execution is that are loaded first are also executed first.
+To ensure our middleware `MyLogger` will be executed before all the other, we add it
+with the `use_before` method.
+
+### Ultra cool, more advanced logger example
+
+Next, we’ll create a middleware handler called “LogHandler” that prints the requested
+uri, the response status and the time it took to Popcorn to process the request.
+
+This example gives a simplified version of the `RequestClock` and `ConsoleLog` middlewares.
+
+~~~
+import popcorn
+import realtime
+
+redef class HttpRequest
+       # Time that request was received by the Popcorn app.
+       var timer: nullable Clock = null
+end
+
+class RequestTimeHandler
+       super Handler
+
+       redef fun all(req, res) do req.timer = new Clock
+end
+
+class LogHandler
+       super Handler
+
+       redef fun all(req, res) do
+               var timer = req.timer
+               if timer != null then
+                       print "{req.method} {req.uri} {res.color_status} ({timer.total}s)"
+               else
+                       print "{req.method} {req.uri} {res.color_status}"
+               end
+       end
+end
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Hello World!"
+end
+
+var app = new App
+app.use_before("/*", new RequestTimeHandler)
+app.use("/", new HelloHandler)
+app.use_after("/*", new LogHandler)
+app.listen("localhost", 3000)
+~~~
+
+First, we attach a new attribute `timer` to every `HttpRequest`.
+Doing so we can access our data from all handlers that import our module, directly
+from the `req` parameter.
+
+We use the new middleware called `RequestTimeHandler` to initialize the request timer.
+Because of the `use_before` method, the `RequestTimeHandler` middleware will be executed
+before all the others.
+
+We then let the `HelloHandler` produce the response.
+
+Finally, our `LogHandler` will display a bunch of data and use the request `timer`
+to display the time it took to process the request.
+Because of the `use_after` method, the `LogHandler` middleware will be executed after
+all the others.
+
+The app now uses the `RequestTimeHandler` middleware for every requests received
+by the Popcorn app.
+The page is processed the `HelloHandler` to display the index page.
+And, before every response is sent, the `LogHandler` is activated to display the
+log line.
+
+Because you have access to the request object, the response object, and all the
+Popcorn API, the possibilities with middleware functions are endless.
+
+### Built-in middlewares
+
+Starting with version 0.1, Popcorn provide a set of built-in middleware that can
+be used to develop your app faster.
+
+* `RequestClock`: initializes requests clock.
+* `ConsoleLog`: displays resquest and response status in console (can be used with `RequestClock`).
+* `SessionInit`: initializes requests session (see the `Sessions` section).
+* `StaticServer`: serves static files (see the `Serving static files with Popcorn` section).
+* `Router`: a mountable mini-app (see the `Mountable routers` section).
+
+## Mountable routers
+
+Use the `Router` class to create modular, mountable route handlers.
+A Router instance is a complete middleware and routing system; for this reason,
+it is often referred to as a “mini-app”.
+
+The following example creates a router as a module, loads a middleware handler in it,
+defines some routes, and mounts the router module on a path in the main app.
+
+~~~
+import popcorn
+
+class AppHome
+       super Handler
+
+       redef fun get(req, res) do res.send "Site Home"
+end
+
+class UserLogger
+       super Handler
+
+       redef fun all(req, res) do print "User logged"
+end
+
+class UserHome
+       super Handler
+
+       redef fun get(req, res) do res.send "User Home"
+end
+
+class UserProfile
+       super Handler
+
+       redef fun get(req, res) do res.send "User Profile"
+end
+
+var user_router = new Router
+user_router.use("/*", new UserLogger)
+user_router.use("/", new UserHome)
+user_router.use("/profile", new UserProfile)
+
+var app = new App
+app.use("/", new AppHome)
+app.use("/user", user_router)
+app.listen("localhost", 3000)
+~~~
+
+The app will now be able to handle requests to /user and /user/profile, as well
+as call the `Time` middleware handler that is specific to the route.
+
+## Error handling
+
+**Error handling** is based on middleware handlers.
+
+Define error-handling middlewares in the same way as other middleware handlers:
+
+~~~
+import popcorn
+
+class SimpleErrorHandler
+       super Handler
+
+       redef fun all(req, res) do
+               if res.status_code != 200 then
+                       print "An error occurred! {res.status_code})"
+               end
+       end
+end
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Hello World!"
+end
+
+var app = new App
+app.use("/", new HelloHandler)
+app.use("/*", new SimpleErrorHandler)
+app.listen("localhost", 3000)
+~~~
+
+In this example, every non-200 response is caught by the `SimpleErrorHandler`
+that print an error in stdout.
+
+By defining multiple middleware error handlers, you can take multiple action depending
+on the kind of error or the kind of interface you provide (HTML, XML, JSON...).
+
+Here an example of the 404 custom error page in HTML:
+
+~~~
+import popcorn
+import template
+
+class HtmlErrorTemplate
+       super Template
+
+       var status: Int
+       var message: nullable String
+
+       redef fun rendering do add """
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>{{{message or else status}}}</title>
+               </head>
+               <body>
+               <h1>{{{status}}} {{{message or else ""}}}</h1>
+               </body>
+               </html>"""
+end
+
+class HtmlErrorHandler
+       super Handler
+
+       redef fun all(req, res) do
+               if res.status_code != 200 then
+                       res.send(new HtmlErrorTemplate(res.status_code, "An error occurred!"))
+               end
+       end
+end
+
+var app = new App
+app.use("/*", new HtmlErrorHandler)
+app.listen("localhost", 3000)
+~~~
+
+## Sessions
+
+**Sessions** can be used thanks to the built-in `SessionMiddleware`.
+
+Here a simple example of login button that define a value in the `req` session.
+
+~~~
+import popcorn
+
+redef class Session
+       var is_logged = false
+end
+
+class AppLogin
+       super Handler
+
+       redef fun get(req, res) do
+               res.html """
+               <p>Is logged: {{{req.session.as(not null).is_logged}}}</p>
+               <form action="/" method="POST">
+                       <input type="submit" value="Login" />
+               </form>"""
+       end
+
+       redef fun post(req, res) do
+               req.session.as(not null).is_logged = true
+               res.redirect("/")
+       end
+end
+
+var app = new App
+app.use("/*", new SessionInit)
+app.use("/", new AppLogin)
+app.listen("localhost", 3000)
+~~~
+
+Notice the use of the `SessionInit` on the `/*` route. You must use the
+`SessionInit` first to initialize the request session.
+Without that, your request session will be set to `null`.
+If you don't use sessions in your app, you do not need to include that middleware.
+
+## Database integration
+
+### Mongo DB
+
+If you want to persist your data, Popcorn works well with MongoDB.
+
+In this example, we will show how to store and list user with a Mongo database.
+
+First let's define a handler that access the database to list all the user.
+The mongo database reference is passed to the UserList handler through the `db` attribute.
+
+Then we define a handler that displays the user creation form on GET requests.
+POST requests are used to save the user data.
+
+~~~
+import popcorn
+import mongodb
+import template
+
+class UserList
+       super Handler
+
+       var db: MongoDb
+
+       redef fun get(req, res) do
+               var users = db.collection("users").find_all(new JsonObject)
+
+               var tpl = new Template
+               tpl.add "<h1>Users</h1>"
+               tpl.add "<table>"
+               for user in users do
+                       tpl.add """<tr>
+                               <td>{{{user["login"] or else "null"}}}</td>
+                               <td>{{{user["password"] or else "null"}}}</td>
+                       </tr>"""
+               end
+               tpl.add "</table>"
+               res.html tpl
+       end
+end
+
+class UserForm
+       super Handler
+
+       var db: MongoDb
+
+       redef fun get(req, res) do
+               var tpl = new Template
+               tpl.add """<h1>Add a new user</h1>
+               <form action="/new" method="POST">
+                       <input type="text" name="login" />
+                       <input type="password" name="password" />
+                       <input type="submit" value="save" />
+               </form>"""
+               res.html tpl
+       end
+
+       redef fun post(req, res) do
+               var json = new JsonObject
+               json["login"] = req.post_args["login"]
+               json["password"] = req.post_args["password"]
+               db.collection("users").insert(json)
+               res.redirect "/"
+       end
+end
+
+var mongo = new MongoClient("mongodb://localhost:27017/")
+var db = mongo.database("mongo_example")
+
+var app = new App
+app.use("/", new UserList(db))
+app.use("/new", new UserForm(db))
+app.listen("localhost", 3000)
+~~~
+
+## Angular.JS integration
+
+Loving [AngularJS](https://angularjs.org/)? Popcorn is made for Angular and for you!
+
+Using the StaticHandler with a glob route, you can easily redirect all HTTP requests
+to your angular controller:
+
+~~~
+import popcorn
+
+var app = new App
+app.use("/*", new StaticHandler("my-ng-app/", "index.html"))
+app.listen("localhost", 3000)
+~~~
+
+Because the StaticHandler will not find the angular routes as static files,
+you must specify the path to the default angular controller.
+In this example, the StaticHandler will redirect any unknown requests to the `index.html`
+angular controller.
+
+See the examples for a more detailed use case working with a JSON API.
diff --git a/lib/popcorn/examples/angular/example_angular.nit b/lib/popcorn/examples/angular/example_angular.nit
new file mode 100644 (file)
index 0000000..9212a8e
--- /dev/null
@@ -0,0 +1,44 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014-2015 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+class CounterAPI
+       super Handler
+
+       var counter = 0
+
+       fun json_counter: JsonObject do
+               var json = new JsonObject
+               json["label"] = "Visitors"
+               json["value"] = counter
+               return json
+       end
+
+       redef fun get(req, res) do
+               res.json(json_counter)
+       end
+
+       redef fun post(req, res) do
+               counter += 1
+               res.json(json_counter)
+       end
+end
+
+var app = new App
+app.use("/counter", new CounterAPI)
+app.use("/*", new StaticHandler("www/", "index.html"))
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/angular/test_example_angular.nit b/lib/popcorn/examples/angular/test_example_angular.nit
new file mode 100644 (file)
index 0000000..b61f1b0
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_angular is test_suite
+
+import pop_tests
+import example_angular
+
+class TestExampleAngular
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/counter"
+               system "curl -s {host}:{port}/counter -X POST"
+               system "curl -s {host}:{port}/counter"
+               system "curl -s {host}:{port}/not_found" # handled by angular controller
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/counter", new CounterAPI)
+               app.use("/*", new StaticHandler("../examples/angular/www/", "index.html"))
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/angular/test_example_angular.sav/test_example_angular.res b/lib/popcorn/examples/angular/test_example_angular.sav/test_example_angular.res
new file mode 100644 (file)
index 0000000..bb94961
--- /dev/null
@@ -0,0 +1,22 @@
+
+[Client] curl -s localhost:*****/counter
+{"label":"Visitors","value":0}
+[Client] curl -s localhost:*****/counter -X POST
+{"label":"Visitors","value":1}
+[Client] curl -s localhost:*****/counter
+{"label":"Visitors","value":1}
+[Client] curl -s localhost:*****/not_found
+<!DOCTYPE html>
+<html lang='en' ng-app='ng-example'>
+       <head>
+               <base href='/'>
+               <title>ng-example</title>
+       </head>
+       <body>
+               <div ng-view></div>
+
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'></script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'></script>
+               <script src='/javascripts/ng-example.js'></script>
+       </body>
+</html>
diff --git a/lib/popcorn/examples/angular/www/index.html b/lib/popcorn/examples/angular/www/index.html
new file mode 100644 (file)
index 0000000..59bc4d1
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang='en' ng-app='ng-example'>
+       <head>
+               <base href='/'>
+               <title>ng-example</title>
+       </head>
+       <body>
+               <div ng-view></div>
+
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'></script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'></script>
+               <script src='/javascripts/ng-example.js'></script>
+       </body>
+</html>
diff --git a/lib/popcorn/examples/angular/www/javascripts/ng-example.js b/lib/popcorn/examples/angular/www/javascripts/ng-example.js
new file mode 100644 (file)
index 0000000..f9270d2
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular
+
+       .module('ng-example', ['ngRoute'])
+
+       .factory('CounterModel', ['$http', function($http) {
+               return {
+                       load: function(cb, cbErr) {
+                               $http.get('/counter')
+                                       .success(cb)
+                                       .error(cbErr);
+                       },
+                       increment: function(cb, cbErr) {
+                               $http.post('/counter')
+                                       .success(cb)
+                                       .error(cbErr);
+                       }
+               };
+       }])
+
+       .controller('CounterCtrl', ['CounterModel', function(CounterModel) {
+               var $this = this;
+
+               this.loadCounter = function() {
+                       CounterModel.load(
+                               function(data) {
+                                       $this.counter = data;
+                               }, function(err) {
+                                       $this.error = err;
+                               });
+               };
+
+               this.incrementCounter = function() {
+                       CounterModel.increment(
+                               function(data) {
+                                       $this.counter = data;
+                               }, function(err) {
+                                       $this.error = err;
+                               });
+               };
+
+               this.loadCounter();
+       }])
+
+       .config(function($routeProvider, $locationProvider) {
+               $routeProvider
+                       .when('/', {
+                               templateUrl: 'views/index.html',
+                               controller: 'CounterCtrl',
+                               controllerAs: 'counterCtrl'
+                       })
+                       .otherwise({
+                               redirectTo: '/'
+                       });
+               $locationProvider.html5Mode(true);
+       });
+})();
diff --git a/lib/popcorn/examples/angular/www/views/index.html b/lib/popcorn/examples/angular/www/views/index.html
new file mode 100644 (file)
index 0000000..058e799
--- /dev/null
@@ -0,0 +1,9 @@
+<h1>Nit &hearts; Angular.JS</h1>
+
+<p>Click the button to increment the counter.</p>
+
+<form>
+       <label>{{counterCtrl.counter.label}}</label>
+       <input type="number" ng-model="counterCtrl.counter.value" readonly />
+       <button ng-click="counterCtrl.incrementCounter()">Increment!</button>
+</form>
diff --git a/lib/popcorn/examples/handlers/example_post_handler.nit b/lib/popcorn/examples/handlers/example_post_handler.nit
new file mode 100644 (file)
index 0000000..826c165
--- /dev/null
@@ -0,0 +1,36 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+import template
+
+class PostHandler
+       super Handler
+
+       redef fun post(req, res) do
+               var tpl = new Template
+               tpl.addn "URI: {req.uri}"
+               tpl.addn "Body: {req.body}"
+               for name, arg in req.post_args do
+                       tpl.addn "{name}: {arg}"
+               end
+        res.send tpl
+       end
+end
+
+var app = new App
+app.use("/", new PostHandler)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/handlers/example_query_string.nit b/lib/popcorn/examples/handlers/example_query_string.nit
new file mode 100644 (file)
index 0000000..e8c6187
--- /dev/null
@@ -0,0 +1,36 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+import template
+
+class QueryStringHandler
+       super Handler
+
+       redef fun get(req, res) do
+               var tpl = new Template
+               tpl.addn "URI: {req.uri}"
+               tpl.addn "Query string: {req.query_string}"
+               for name, arg in req.get_args do
+                       tpl.addn "{name}: {arg}"
+               end
+        res.send tpl
+       end
+end
+
+var app = new App
+app.use("/", new QueryStringHandler)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/handlers/test_example_post_handler.nit b/lib/popcorn/examples/handlers/test_example_post_handler.nit
new file mode 100644 (file)
index 0000000..45728ef
--- /dev/null
@@ -0,0 +1,39 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_post_handler is test_suite
+
+import pop_tests
+import example_post_handler
+
+class TestExampleQueryString
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/ -X POST"
+               system "curl -s {host}:{port}/ --data 'user'"
+               system "curl -s {host}:{port}/ --data 'user=Morriar'"
+               system "curl -s {host}:{port}/ --data 'user=\&order=desc'"
+               system "curl -s {host}:{port}/ --data 'user=Morriar\&order=desc'"
+               system "curl -s {host}:{port}/"
+       end
+
+       fun test_example_glob_route do
+               var app = new App
+               app.use("/", new PostHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/handlers/test_example_post_handler.sav/test_example_post_handler.res b/lib/popcorn/examples/handlers/test_example_post_handler.sav/test_example_post_handler.res
new file mode 100644 (file)
index 0000000..72589e2
--- /dev/null
@@ -0,0 +1,38 @@
+
+[Client] curl -s localhost:*****/ -X POST
+URI: /
+Body: 
+
+[Client] curl -s localhost:*****/ --data 'user'
+POST Error: user format error on user
+URI: /
+Body: user
+
+[Client] curl -s localhost:*****/ --data 'user=Morriar'
+URI: /
+Body: user=Morriar
+user: Morriar
+
+[Client] curl -s localhost:*****/ --data 'user=&order=desc'
+URI: /
+Body: user=&order=desc
+user: 
+order: desc
+
+[Client] curl -s localhost:*****/ --data 'user=Morriar&order=desc'
+URI: /
+Body: user=Morriar&order=desc
+user: Morriar
+order: desc
+
+[Client] curl -s localhost:*****/
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/examples/handlers/test_example_query_string.nit b/lib/popcorn/examples/handlers/test_example_query_string.nit
new file mode 100644 (file)
index 0000000..b584126
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_query_string is test_suite
+
+import pop_tests
+import example_query_string
+
+class TestExampleQueryString
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/?user=Morriar"
+               system "curl -s {host}:{port}/?reload"
+               system "curl -s {host}:{port}/?foo\\&bar=baz"
+               system "curl -s {host}:{port}/?items=10\\&order=asc"
+       end
+
+       fun test_example_glob_route do
+               var app = new App
+               app.use("/", new QueryStringHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/handlers/test_example_query_string.sav/test_example_query_string.res b/lib/popcorn/examples/handlers/test_example_query_string.sav/test_example_query_string.res
new file mode 100644 (file)
index 0000000..699b0db
--- /dev/null
@@ -0,0 +1,24 @@
+
+[Client] curl -s localhost:*****/
+URI: /
+Query string: 
+
+[Client] curl -s localhost:*****/?user=Morriar
+URI: /
+Query string: user=Morriar
+user: Morriar
+
+[Client] curl -s localhost:*****/?reload
+URI: /
+Query string: reload
+
+[Client] curl -s localhost:*****/?foo\&bar=baz
+URI: /
+Query string: foo&bar=baz
+bar: baz
+
+[Client] curl -s localhost:*****/?items=10\&order=asc
+URI: /
+Query string: items=10&order=asc
+items: 10
+order: asc
diff --git a/lib/popcorn/examples/hello_world/example_hello.nit b/lib/popcorn/examples/hello_world/example_hello.nit
new file mode 100644 (file)
index 0000000..fc9f3e5
--- /dev/null
@@ -0,0 +1,27 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.html "<h1>Hello World!</h1>"
+end
+
+var app = new App
+app.use("/", new HelloHandler)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/hello_world/test_example_hello.nit b/lib/popcorn/examples/hello_world/test_example_hello.nit
new file mode 100644 (file)
index 0000000..163e624
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_hello is test_suite
+
+import pop_tests
+import example_hello
+
+class TestExampleHello
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}///////////"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/not_found/not_found"
+       end
+
+       fun test_example_hello do
+               var app = new App
+               app.use("/", new HelloHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/hello_world/test_example_hello.sav/test_example_hello.res b/lib/popcorn/examples/hello_world/test_example_hello.sav/test_example_hello.res
new file mode 100644 (file)
index 0000000..f5b3c10
--- /dev/null
@@ -0,0 +1,29 @@
+
+[Client] curl -s localhost:*****
+<h1>Hello World!</h1>
+[Client] curl -s localhost:*****/
+<h1>Hello World!</h1>
+[Client] curl -s localhost:*****///////////
+<h1>Hello World!</h1>
+[Client] curl -s localhost:*****/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/not_found/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
diff --git a/lib/popcorn/examples/middlewares/example_advanced_logger.nit b/lib/popcorn/examples/middlewares/example_advanced_logger.nit
new file mode 100644 (file)
index 0000000..4ca9120
--- /dev/null
@@ -0,0 +1,54 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+import realtime
+
+redef class HttpRequest
+       # Time that request was received by the Popcorn app.
+       var timer: nullable Clock = null
+end
+
+class RequestTimeHandler
+       super Handler
+
+       redef fun all(req, res) do req.timer = new Clock
+end
+
+class LogHandler
+       super Handler
+
+       redef fun all(req, res) do
+               var timer = req.timer
+               if timer != null then
+                       print "{req.method} {req.uri} {res.color_status} ({timer.total}s)"
+               else
+                       print "{req.method} {req.uri} {res.color_status}"
+               end
+       end
+end
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Hello World!"
+end
+
+var app = new App
+app.use_before("/*", new RequestTimeHandler)
+app.use("/", new HelloHandler)
+app.use_after("/*", new LogHandler)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/middlewares/example_html_error_handler.nit b/lib/popcorn/examples/middlewares/example_html_error_handler.nit
new file mode 100644 (file)
index 0000000..5bd399c
--- /dev/null
@@ -0,0 +1,51 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+import template
+
+class HtmlErrorTemplate
+       super Template
+
+       var status: Int
+       var message: nullable String
+
+       redef fun rendering do add """
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>{{{message or else status}}}</title>
+               </head>
+               <body>
+               <h1>{{{status}}} {{{message or else ""}}}</h1>
+               </body>
+               </html>"""
+end
+
+class HtmlErrorHandler
+       super Handler
+
+       redef fun all(req, res) do
+               if res.status_code != 200 then
+                       res.send(new HtmlErrorTemplate(res.status_code, "An error occurred!"))
+               end
+       end
+end
+
+var app = new App
+app.use("/*", new HtmlErrorHandler)
+app.listen("localhost", 3000)
similarity index 58%
rename from tests/test_realtime.nit
rename to lib/popcorn/examples/middlewares/example_simple_error_handler.nit
index 6101a3d..16a5977 100644 (file)
@@ -1,6 +1,6 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import realtime
+import popcorn
 
-redef extern class Timespec
-       fun simplify : Int
-       do
-               return sec*1000000 + nanosec/1000
+class SimpleErrorHandler
+       super Handler
+
+       redef fun all(req, res) do
+               if res.status_code != 200 then
+                       res.send("An error occurred!", res.status_code)
+               end
        end
 end
 
-var c = new Clock
-var t0 = c.total.simplify
-
-print "sleeping 1s"
-nanosleep(1, 0)
-print c.total.sec >= 1
-print c.lapse.sec >= 1
+class HelloHandler
+       super Handler
 
-var t1 = c.total.simplify
-
-print "sleeping 5000ns"
-nanosleep(0, 5000)
-print c.lapse.nanosec >= 5000
+       redef fun get(req, res) do res.send "Hello World!"
+end
 
-var t2 = c.total.simplify
 
-print t0 <= t1
-print t1 <= t2
+var app = new App
+app.use("/", new HelloHandler)
+app.use("/*", new SimpleErrorHandler)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/middlewares/example_simple_logger.nit b/lib/popcorn/examples/middlewares/example_simple_logger.nit
new file mode 100644 (file)
index 0000000..4052169
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+class LogHandler
+       super Handler
+
+       redef fun all(req, res) do print "Request Logged"
+end
+
+class HelloHandler
+       super Handler
+
+       redef fun get(req, res) do res.send "Hello World!"
+end
+
+
+var app = new App
+app.use_before("/*", new LogHandler)
+app.use("/", new HelloHandler)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/middlewares/test_example_advanced_logger.nit b/lib/popcorn/examples/middlewares/test_example_advanced_logger.nit
new file mode 100644 (file)
index 0000000..0cd8030
--- /dev/null
@@ -0,0 +1,37 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_advanced_logger is test_suite
+
+import pop_tests
+import example_advanced_logger
+
+class TestExampleAdvancedLogger
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/about"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use_before("/*", new RequestTimeHandler)
+               app.use("/", new HelloHandler)
+               app.use_after("/*", new LogHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/middlewares/test_example_advanced_logger.sav/test_example_advanced_logger.res b/lib/popcorn/examples/middlewares/test_example_advanced_logger.sav/test_example_advanced_logger.res
new file mode 100644 (file)
index 0000000..7e56183
--- /dev/null
@@ -0,0 +1,16 @@
+
+[Client] curl -s localhost:*****/
+GET / \e[32m200\e[m (0.0s)
+Hello World!
+[Client] curl -s localhost:*****/about
+GET /about \e[33m404\e[m (0.0s)
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/examples/middlewares/test_example_html_error_handler.nit b/lib/popcorn/examples/middlewares/test_example_html_error_handler.nit
new file mode 100644 (file)
index 0000000..b870dd4
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_html_error_handler is test_suite
+
+import pop_tests
+import example_html_error_handler
+
+class TestExampleHtmlErrorHandler
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/about"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/*", new HtmlErrorHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/middlewares/test_example_html_error_handler.sav/test_example_html_error_handler.res b/lib/popcorn/examples/middlewares/test_example_html_error_handler.sav/test_example_html_error_handler.res
new file mode 100644 (file)
index 0000000..3015280
--- /dev/null
@@ -0,0 +1,23 @@
+
+[Client] curl -s localhost:*****/
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>An error occurred!</title>
+               </head>
+               <body>
+               <h1>404 An error occurred!</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/about
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>An error occurred!</title>
+               </head>
+               <body>
+               <h1>404 An error occurred!</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/examples/middlewares/test_example_simple_error_handler.nit b/lib/popcorn/examples/middlewares/test_example_simple_error_handler.nit
new file mode 100644 (file)
index 0000000..1b1f080
--- /dev/null
@@ -0,0 +1,36 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_simple_error_handler is test_suite
+
+import pop_tests
+import example_simple_error_handler
+
+class TestExampleSimpleErrorHandler
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/about"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/", new HelloHandler)
+               app.use("/*", new SimpleErrorHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/middlewares/test_example_simple_error_handler.sav/test_example_simple_error_handler.res b/lib/popcorn/examples/middlewares/test_example_simple_error_handler.sav/test_example_simple_error_handler.res
new file mode 100644 (file)
index 0000000..67e5a72
--- /dev/null
@@ -0,0 +1,5 @@
+
+[Client] curl -s localhost:*****/
+Hello World!
+[Client] curl -s localhost:*****/about
+An error occurred!
\ No newline at end of file
diff --git a/lib/popcorn/examples/middlewares/test_example_simple_logger.nit b/lib/popcorn/examples/middlewares/test_example_simple_logger.nit
new file mode 100644 (file)
index 0000000..80a5edf
--- /dev/null
@@ -0,0 +1,36 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_simple_logger is test_suite
+
+import pop_tests
+import example_simple_logger
+
+class TestExampleSimpleLogger
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/about"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use_before("/*", new LogHandler)
+               app.use("/", new HelloHandler)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/middlewares/test_example_simple_logger.sav/test_example_simple_logger.res b/lib/popcorn/examples/middlewares/test_example_simple_logger.sav/test_example_simple_logger.res
new file mode 100644 (file)
index 0000000..9bc50e3
--- /dev/null
@@ -0,0 +1,16 @@
+
+[Client] curl -s localhost:*****/
+Request Logged
+Hello World!
+[Client] curl -s localhost:*****/about
+Request Logged
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/examples/mongodb/example_mongodb.nit b/lib/popcorn/examples/mongodb/example_mongodb.nit
new file mode 100644 (file)
index 0000000..4f89f5b
--- /dev/null
@@ -0,0 +1,66 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014-2015 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+import mongodb
+import template
+
+class UserList
+       super Handler
+
+       var db: MongoDb
+
+       redef fun get(req, res) do
+               var users = db.collection("users").find_all(new JsonObject)
+
+               var tpl = new Template
+               tpl.add """
+               <h1>Users</h1>
+
+               <h2>Add a new user</h2>
+               <form action="/" method="POST">
+                       <input type="text" name="login" />
+                       <input type="password" name="password" />
+                       <input type="submit" value="save" />
+               </form>
+
+               <h2>All users</h2>
+               <table>"""
+               for user in users do
+                       tpl.add """<tr>
+                       <td>{{{user["login"] or else "null"}}}</td>
+                       <td>{{{user["password"] or else "null"}}}</td>
+                       </tr>"""
+               end
+               tpl.add "</table>"
+               res.html(tpl)
+       end
+
+       redef fun post(req, res) do
+               var json = new JsonObject
+               json["login"] = req.post_args["login"]
+               json["password"] = req.post_args["password"]
+               db.collection("users").insert(json)
+               res.redirect("/")
+       end
+end
+
+var mongo = new MongoClient("mongodb://localhost:27017/")
+var db = mongo.database("mongo_example")
+
+var app = new App
+app.use("/", new UserList(db))
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/routing/example_glob_route.nit b/lib/popcorn/examples/routing/example_glob_route.nit
new file mode 100644 (file)
index 0000000..b33caec
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+class UserItem
+       super Handler
+
+       redef fun get(req, res) do
+               var user = req.param("user")
+               var item = req.param("item")
+               if user == null or item == null then
+                       res.send("Nothing received", 400)
+               else
+                       res.send "Here the item {item} of the use {user}."
+               end
+       end
+end
+
+var app = new App
+app.use("/user/:user/item/:item/*", new UserItem)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/routing/example_param_route.nit b/lib/popcorn/examples/routing/example_param_route.nit
new file mode 100644 (file)
index 0000000..f536771
--- /dev/null
@@ -0,0 +1,34 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+class UserHome
+       super Handler
+
+       redef fun get(req, res) do
+               var user = req.param("user")
+               if user != null then
+                       res.send "Hello {user}"
+               else
+                       res.send("Nothing received", 400)
+               end
+       end
+end
+
+var app = new App
+app.use("/:user", new UserHome)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/routing/example_router.nit b/lib/popcorn/examples/routing/example_router.nit
new file mode 100644 (file)
index 0000000..91aa3ab
--- /dev/null
@@ -0,0 +1,51 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+class AppHome
+       super Handler
+
+       redef fun get(req, res) do res.send "Site Home"
+end
+
+class UserLogger
+       super Handler
+
+       redef fun all(req, res) do print "User logged"
+end
+
+class UserHome
+       super Handler
+
+       redef fun get(req, res) do res.send "User Home"
+end
+
+class UserProfile
+       super Handler
+
+       redef fun get(req, res) do res.send "User Profile"
+end
+
+var user_router = new Router
+user_router.use("/*", new UserLogger)
+user_router.use("/", new UserHome)
+user_router.use("/profile", new UserProfile)
+
+var app = new App
+app.use("/", new AppHome)
+app.use("/user", user_router)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/routing/test_example_glob_route.nit b/lib/popcorn/examples/routing/test_example_glob_route.nit
new file mode 100644 (file)
index 0000000..c258260
--- /dev/null
@@ -0,0 +1,40 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_glob_route is test_suite
+
+import pop_tests
+import example_glob_route
+
+class TestExampleGlobRoute
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/user/Morriar/item/10"
+               system "curl -s {host}:{port}/user/Morriar/item/10/"
+               system "curl -s {host}:{port}/user/Morriar/item/10/profile"
+               system "curl -s {host}:{port}/user/Morriar/item/10/profile/settings"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/not_found/not_found"
+       end
+
+       fun test_example_glob_route do
+               var app = new App
+               app.use("/user/:user/item/:item/*", new UserItem)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/routing/test_example_glob_route.sav/test_example_glob_route.res b/lib/popcorn/examples/routing/test_example_glob_route.sav/test_example_glob_route.res
new file mode 100644 (file)
index 0000000..ad68ca3
--- /dev/null
@@ -0,0 +1,42 @@
+
+[Client] curl -s localhost:*****/user/Morriar/item/10
+Here the item 10 of the use Morriar.
+[Client] curl -s localhost:*****/user/Morriar/item/10/
+Here the item 10 of the use Morriar.
+[Client] curl -s localhost:*****/user/Morriar/item/10/profile
+Here the item 10 of the use Morriar.
+[Client] curl -s localhost:*****/user/Morriar/item/10/profile/settings
+Here the item 10 of the use Morriar.
+[Client] curl -s localhost:*****/
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/not_found/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
diff --git a/lib/popcorn/examples/routing/test_example_param_route.nit b/lib/popcorn/examples/routing/test_example_param_route.nit
new file mode 100644 (file)
index 0000000..52f818a
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_param_route is test_suite
+
+import pop_tests
+import example_param_route
+
+class TestExampleParamRoute
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/Morriar"
+               system "curl -s {host}:{port}//"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/not_found/not_found"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/:user", new UserHome)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/routing/test_example_param_route.sav/test_example_param_route.res b/lib/popcorn/examples/routing/test_example_param_route.sav/test_example_param_route.res
new file mode 100644 (file)
index 0000000..87c2dea
--- /dev/null
@@ -0,0 +1,38 @@
+
+[Client] curl -s localhost:*****/Morriar
+Hello Morriar
+[Client] curl -s localhost:*****//
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/not_found
+Hello not_found
+[Client] curl -s localhost:*****/not_found/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
diff --git a/lib/popcorn/examples/routing/test_example_router.nit b/lib/popcorn/examples/routing/test_example_router.nit
new file mode 100644 (file)
index 0000000..94cc78c
--- /dev/null
@@ -0,0 +1,46 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_router is test_suite
+
+import pop_tests
+import example_router
+
+class TestExampleRouter
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/user"
+               system "curl -s {host}:{port}/user/"
+               system "curl -s {host}:{port}/user/profile"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/user/not_found"
+               system "curl -s {host}:{port}/products/not_found"
+       end
+
+       fun test_example_router do
+               var user_router = new Router
+               user_router.use("/*", new UserLogger)
+               user_router.use("/", new UserHome)
+               user_router.use("/profile", new UserProfile)
+               var app = new App
+               app.use("/", new AppHome)
+               app.use("/user", user_router)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/routing/test_example_router.sav/test_example_router.res b/lib/popcorn/examples/routing/test_example_router.sav/test_example_router.res
new file mode 100644 (file)
index 0000000..d47aeb4
--- /dev/null
@@ -0,0 +1,48 @@
+
+[Client] curl -s localhost:*****
+Site Home
+[Client] curl -s localhost:*****/
+Site Home
+[Client] curl -s localhost:*****/user
+User logged
+User Home
+[Client] curl -s localhost:*****/user/
+User logged
+User Home
+[Client] curl -s localhost:*****/user/profile
+User logged
+User Profile
+[Client] curl -s localhost:*****/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/user/not_found
+User logged
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/products/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
diff --git a/lib/popcorn/examples/sessions/example_session.nit b/lib/popcorn/examples/sessions/example_session.nit
new file mode 100644 (file)
index 0000000..be79fb1
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+redef class Session
+       var is_logged = false
+end
+
+class AppLogin
+       super Handler
+
+       redef fun get(req, res) do
+               res.html """
+               <p>Is logged: {{{req.session.as(not null).is_logged}}}</p>
+               <form action="/" method="POST">
+                       <input type="submit" value="Login" />
+               </form>"""
+       end
+
+       redef fun post(req, res) do
+               req.session.as(not null).is_logged = true
+               res.redirect("/")
+       end
+end
+
+var app = new App
+app.use("/*", new SessionInit)
+app.use("/", new AppLogin)
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/sessions/test_example_session.nit b/lib/popcorn/examples/sessions/test_example_session.nit
new file mode 100644 (file)
index 0000000..ec6ee71
--- /dev/null
@@ -0,0 +1,39 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_session is test_suite
+
+import pop_tests
+import example_session
+
+class TestExampleSession
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/ -X POST"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/user/not_found"
+               system "curl -s {host}:{port}/products/not_found"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/*", new SessionInit)
+               app.use("/", new AppLogin)
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/sessions/test_example_session.sav/test_example_session.res b/lib/popcorn/examples/sessions/test_example_session.sav/test_example_session.res
new file mode 100644 (file)
index 0000000..4ca758e
--- /dev/null
@@ -0,0 +1,41 @@
+
+[Client] curl -s localhost:*****/
+               <p>Is logged: false</p>
+               <form action="/" method="POST">
+                       <input type="submit" value="Login" />
+               </form>
+[Client] curl -s localhost:*****/ -X POST
+
+[Client] curl -s localhost:*****/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/user/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/products/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/examples/static_files/example_static.nit b/lib/popcorn/examples/static_files/example_static.nit
new file mode 100644 (file)
index 0000000..c3da6d0
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+var app = new App
+app.use("/", new StaticHandler("public/"))
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/static_files/example_static_default.nit b/lib/popcorn/examples/static_files/example_static_default.nit
new file mode 100644 (file)
index 0000000..6aa5b34
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+var app = new App
+app.use("/", new StaticHandler("public/", "default.html"))
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/static_files/example_static_multiple.nit b/lib/popcorn/examples/static_files/example_static_multiple.nit
new file mode 100644 (file)
index 0000000..1c730c6
--- /dev/null
@@ -0,0 +1,24 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import popcorn
+
+var app = new App
+app.use("/", new StaticHandler("public/"))
+app.use("/", new StaticHandler("files/"))
+app.use("/static", new StaticHandler("public/"))
+app.use("/static", new StaticHandler("files/"))
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/static_files/files/index.html b/lib/popcorn/examples/static_files/files/index.html
new file mode 100644 (file)
index 0000000..bb850bd
--- /dev/null
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+       <body>
+               <h1>Another Index</h1>
+       </body>
+</html>
diff --git a/lib/popcorn/examples/static_files/public/css/style.css b/lib/popcorn/examples/static_files/public/css/style.css
new file mode 100644 (file)
index 0000000..5ba0171
--- /dev/null
@@ -0,0 +1,4 @@
+body {
+       color: blue;
+       padding: 20px;
+}
diff --git a/lib/popcorn/examples/static_files/public/default.html b/lib/popcorn/examples/static_files/public/default.html
new file mode 100644 (file)
index 0000000..2abb789
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
diff --git a/lib/popcorn/examples/static_files/public/hello.html b/lib/popcorn/examples/static_files/public/hello.html
new file mode 100644 (file)
index 0000000..61521ac
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Hello Popcorn!</h1>
+
+               <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+               <script src="/js/app.js"></script>
+       </body>
+</html>
diff --git a/lib/popcorn/examples/static_files/public/images/trollface.jpg b/lib/popcorn/examples/static_files/public/images/trollface.jpg
new file mode 100644 (file)
index 0000000..0c733bc
Binary files /dev/null and b/lib/popcorn/examples/static_files/public/images/trollface.jpg differ
diff --git a/lib/popcorn/examples/static_files/public/js/app.js b/lib/popcorn/examples/static_files/public/js/app.js
new file mode 100644 (file)
index 0000000..4a1510d
--- /dev/null
@@ -0,0 +1 @@
+alert("Hello World!");
diff --git a/lib/popcorn/examples/static_files/test_example_static.nit b/lib/popcorn/examples/static_files/test_example_static.nit
new file mode 100644 (file)
index 0000000..1b39dcd
--- /dev/null
@@ -0,0 +1,40 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_static is test_suite
+
+import pop_tests
+import example_static
+
+class TestExampleStatic
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/css/style.css"
+               system "curl -s {host}:{port}/js/app.js"
+               system "curl -s {host}:{port}/hello.html"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/css/not_found.nit"
+               system "curl -s {host}:{port}/static/css/not_found.nit"
+               system "curl -s {host}:{port}/not_found.nit"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/", new StaticHandler("../examples/static_files/public/"))
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/static_files/test_example_static.sav/test_example_static.res b/lib/popcorn/examples/static_files/test_example_static.sav/test_example_static.res
new file mode 100644 (file)
index 0000000..7f07ed8
--- /dev/null
@@ -0,0 +1,73 @@
+
+[Client] curl -s localhost:*****/css/style.css
+body {
+       color: blue;
+       padding: 20px;
+}
+
+[Client] curl -s localhost:*****/js/app.js
+alert("Hello World!");
+
+[Client] curl -s localhost:*****/hello.html
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Hello Popcorn!</h1>
+
+               <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+               <script src="/js/app.js"></script>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/css/not_found.nit
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/static/css/not_found.nit
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/not_found.nit
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/examples/static_files/test_example_static_default.nit b/lib/popcorn/examples/static_files/test_example_static_default.nit
new file mode 100644 (file)
index 0000000..af2a068
--- /dev/null
@@ -0,0 +1,40 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_static_default is test_suite
+
+import pop_tests
+import example_static_default
+
+class TestExampleStaticDefault
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/css/style.css"
+               system "curl -s {host}:{port}/js/app.js"
+               system "curl -s {host}:{port}/hello.html"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/css/not_found.nit"
+               system "curl -s {host}:{port}/static/css/not_found.nit"
+               system "curl -s {host}:{port}/not_found.nit"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/", new StaticHandler("../examples/static_files/public/", "default.html"))
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/static_files/test_example_static_default.sav/test_example_static_default.res b/lib/popcorn/examples/static_files/test_example_static_default.sav/test_example_static_default.res
new file mode 100644 (file)
index 0000000..59392f6
--- /dev/null
@@ -0,0 +1,88 @@
+
+[Client] curl -s localhost:*****/css/style.css
+body {
+       color: blue;
+       padding: 20px;
+}
+
+[Client] curl -s localhost:*****/js/app.js
+alert("Hello World!");
+
+[Client] curl -s localhost:*****/hello.html
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Hello Popcorn!</h1>
+
+               <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+               <script src="/js/app.js"></script>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/css/not_found.nit
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/static/css/not_found.nit
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/not_found.nit
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
diff --git a/lib/popcorn/examples/static_files/test_example_static_multiple.nit b/lib/popcorn/examples/static_files/test_example_static_multiple.nit
new file mode 100644 (file)
index 0000000..eddfa4c
--- /dev/null
@@ -0,0 +1,47 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_example_static_multiple is test_suite
+
+import pop_tests
+import example_static_multiple
+
+class TestExampleStaticMultiple
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}/css/style.css"
+               system "curl -s {host}:{port}/js/app.js"
+               system "curl -s {host}:{port}/hello.html"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/static/css/style.css"
+               system "curl -s {host}:{port}/static/js/app.js"
+               system "curl -s {host}:{port}/static/hello.html"
+               system "curl -s {host}:{port}/static/"
+               system "curl -s {host}:{port}/css/not_found.nit"
+               system "curl -s {host}:{port}/static/css/not_found.nit"
+               system "curl -s {host}:{port}/not_found.nit"
+       end
+
+       fun test_example_param_route do
+               var app = new App
+               app.use("/", new StaticHandler("../examples/static_files/public/"))
+               app.use("/", new StaticHandler("../examples/static_files/files/"))
+               app.use("/static", new StaticHandler("../examples/static_files/public/"))
+               app.use("/static", new StaticHandler("../examples/static_files/files/"))
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/examples/static_files/test_example_static_multiple.sav/test_example_static_multiple.res b/lib/popcorn/examples/static_files/test_example_static_multiple.sav/test_example_static_multiple.res
new file mode 100644 (file)
index 0000000..3a6692f
--- /dev/null
@@ -0,0 +1,106 @@
+
+[Client] curl -s localhost:*****/css/style.css
+body {
+       color: blue;
+       padding: 20px;
+}
+
+[Client] curl -s localhost:*****/js/app.js
+alert("Hello World!");
+
+[Client] curl -s localhost:*****/hello.html
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Hello Popcorn!</h1>
+
+               <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+               <script src="/js/app.js"></script>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/
+<!DOCTYPE html>
+<html>
+       <body>
+               <h1>Another Index</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/static/css/style.css
+body {
+       color: blue;
+       padding: 20px;
+}
+
+[Client] curl -s localhost:*****/static/js/app.js
+alert("Hello World!");
+
+[Client] curl -s localhost:*****/static/hello.html
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Hello Popcorn!</h1>
+
+               <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+               <script src="/js/app.js"></script>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/static/
+<!DOCTYPE html>
+<html>
+       <body>
+               <h1>Another Index</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/css/not_found.nit
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/static/css/not_found.nit
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/not_found.nit
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
\ No newline at end of file
diff --git a/lib/popcorn/package.ini b/lib/popcorn/package.ini
new file mode 100644 (file)
index 0000000..8f99b9c
--- /dev/null
@@ -0,0 +1,12 @@
+[package]
+name=popcorn
+tags=web,lib
+maintainer=Alexandre Terrasa <alexandre@moz-code.org>
+license=Apache-2.0
+version=1.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/popcorn/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/popcorn/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/popcorn/pop_handlers.nit b/lib/popcorn/pop_handlers.nit
new file mode 100644 (file)
index 0000000..5d84961
--- /dev/null
@@ -0,0 +1,477 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Route handlers.
+module pop_handlers
+
+import pop_routes
+import json
+
+# Class handler for a route.
+#
+# **Routing** refers to determining how an application responds to a client request
+# to a particular endpoint, which is a URI (or path) and a specific HTTP request
+# method GET, POST, PUT or DELETE (other methods are not suported yet).
+#
+# Each route can have one or more handler methods, which are executed when the route is matched.
+#
+# Route handlers definition takes the following form:
+#
+# ~~~nitish
+# class MyHandler
+#      super Handler
+#
+#      redef fun METHOD(req, res) do end
+# end
+# ~~~
+#
+# Where:
+# * `MyHandler` is the name of the handler you will add to the app.
+# * `METHOD` can be replaced by `get`, `post`, `put` or `delete`.
+#
+# The following example responds with `Hello World!` to GET and POST requests:
+#
+# ~~~
+# class MyHandler
+#      super Handler
+#
+#      redef fun get(req, res) do res.send "Got a GET request"
+#      redef fun post(req, res) do res.send "Got a POST request"
+# end
+# ~~~
+#
+# To make your handler responds to a specific route, you have to add it to the app.
+#
+# Respond to POST request on the root route (`/`), the application's home page:
+#
+# ~~~
+# var app = new App
+# app.use("/", new MyHandler)
+# ~~~
+#
+# Respond to a request to the `/user` route:
+#
+# ~~~
+# app.use("/user", new MyHandler)
+# ~~~
+abstract class Handler
+
+       # Call `all(req, res)` if `route` matches `uri`.
+       private fun handle(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               if route.match(uri) then
+                       if route isa AppParamRoute then
+                               req.uri_params = route.parse_uri_parameters(uri)
+                       end
+                       all(req, res)
+               end
+       end
+
+       # Handler to all kind of HTTP request methods.
+       #
+       # `all` is a special request handler, which is not derived from any
+       # HTTP method. This method is used to respond at a path for all request methods.
+       #
+       # In the following example, the handler will be executed for requests to "/user"
+       # whether you are using GET, POST, PUT, DELETE, or any other HTTP request method.
+       #
+       # ~~~
+       # class AllHandler
+       #       super Handler
+       #
+       #       redef fun all(req, res) do res.send "Every request to the homepage"
+       # end
+       # ~~~
+       #
+       # Using the `all` method you can also implement other HTTP request methods.
+       #
+       # ~~~
+       # class MergeHandler
+       #       super Handler
+       #
+       #       redef fun all(req, res) do
+       #               if req.method == "MERGE" then
+       #                       # handle that method
+       #               else super # keep handle GET, POST, PUT and DELETE methods
+       #       end
+       # end
+       # ~~~
+       fun all(req: HttpRequest, res: HttpResponse) do
+               if req.method == "GET" then
+                       get(req, res)
+               else if req.method == "POST" then
+                       post(req, res)
+               else if req.method == "PUT" then
+                       put(req, res)
+               else if req.method == "DELETE" then
+                       delete(req, res)
+               else
+                       res.status_code = 405
+               end
+       end
+
+       # GET handler.
+       #
+       # Exemple of route responding to GET requests.
+       # ~~~
+       # class GetHandler
+       #       super Handler
+       #
+       #       redef fun get(req, res) do res.send "GETrequest received"
+       # end
+       # ~~~
+       fun get(req: HttpRequest, res: HttpResponse) do end
+
+       # POST handler.
+       #
+       # Exemple of route responding to POST requests.
+       # ~~~
+       # class PostHandler
+       #       super Handler
+       #
+       #       redef fun post(req, res) do res.send "POST request received"
+       # end
+       # ~~~
+       fun post(req: HttpRequest, res: HttpResponse) do end
+
+       # PUT handler.
+       #
+       # Exemple of route responding to PUT requests.
+       # ~~~
+       # class PutHandler
+       #       super Handler
+       #
+       #       redef fun put(req, res) do res.send "PUT request received"
+       # end
+       # ~~~
+       fun put(req: HttpRequest, res: HttpResponse) do end
+
+       # DELETE handler.
+       #
+       # Exemple of route responding to PUT requests.
+       # ~~~
+       # class DeleteHandler
+       #       super Handler
+       #
+       #       redef fun delete(req, res) do res.send "DELETE request received"
+       # end
+       # ~~~
+       fun delete(req: HttpRequest, res: HttpResponse) do end
+end
+
+# Static files server.
+#
+# To serve static files such as images, CSS files, and JavaScript files, use the
+# Popcorn built-in handler `StaticHandler`.
+#
+# Pass the name of the directory that contains the static assets to the StaticHandler
+# init method to start serving the files directly.
+# For example, use the following code to serve images, CSS files, and JavaScript files
+# in a directory named `public`:
+#
+# ~~~
+# var app = new App
+# app.use("/", new StaticHandler("public/"))
+# ~~~
+#
+# Now, you can load the files that are in the `public` directory:
+#
+# ~~~raw
+# http://localhost:3000/images/trollface.jpg
+# http://localhost:3000/css/style.css
+# http://localhost:3000/js/app.js
+# http://localhost:3000/hello.html
+# ~~~
+#
+# Popcorn looks up the files relative to the static directory, so the name of the
+# static directory is not part of the URL.
+# To use multiple static assets directories, add the `StaticHandler` multiple times:
+#
+# ~~~
+# app.use("/", new StaticHandler("public/"))
+# app.use("/", new StaticHandler("files/"))
+# ~~~
+#
+# Popcorn looks up the files in the order in which you set the static directories
+# with the `use` method.
+#
+# To create a virtual path prefix (where the path does not actually exist in the file system)
+# for files that are served by the `StaticHandler`, specify a mount path for the
+# static directory, as shown below:
+#
+# ~~~
+# app.use("/static/", new StaticHandler("public/"))
+# ~~~
+#
+# Now, you can load the files that are in the public directory from the `/static`
+# path prefix.
+#
+# ~~~raw
+# http://localhost:3000/static/images/trollface.jpg
+# http://localhost:3000/static/css/style.css
+# http://localhost:3000/static/js/app.js
+# http://localhost:3000/static/hello.html
+# ~~~
+#
+# However, the path that you provide to the `StaticHandler` is relative to the
+# directory from where you launch your app.
+# If you run the app from another directory, it’s safer to use the absolute path of
+# the directory that you want to serve.
+class StaticHandler
+       super Handler
+
+       # Static files directory to serve.
+       var static_dir: String
+
+       # Default file to serve if nothing matches the request.
+       #
+       # `null` for no default file.
+       var default_file: nullable String
+
+       # Internal file server used to lookup and render files.
+       var file_server: FileServer is lazy do
+               var srv = new FileServer(static_dir)
+               srv.show_directory_listing = false
+               srv.default_file = default_file
+               return srv
+       end
+
+       redef fun handle(route, uri, req, res) do
+               var answer = file_server.answer(req, route.uri_root(uri))
+               if answer.status_code == 200 then
+                       res.status_code = answer.status_code
+                       res.header.add_all answer.header
+                       res.files.add_all answer.files
+                       res.send
+               else if answer.status_code != 404 then
+                       res.status_code = answer.status_code
+               end
+       end
+end
+
+# Mountable routers
+#
+# Use the `Router` class to create modular, mountable route handlers.
+# A Router instance is a complete middleware and routing system; for this reason,
+# it is often referred to as a “mini-app”.
+#
+# The following example creates a router as a module, loads a middleware handler in it,
+# defines some routes, and mounts the router module on a path in the main app.
+#
+# ~~~
+# class AppHome
+#      super Handler
+#
+#      redef fun get(req, res) do res.send "Site Home"
+# end
+#
+# class UserLogger
+#      super Handler
+#
+#      redef fun all(req, res) do print "User logged"
+# end
+#
+# class UserHome
+#      super Handler
+#
+#      redef fun get(req, res) do res.send "User Home"
+# end
+#
+# class UserProfile
+#      super Handler
+#
+#      redef fun get(req, res) do res.send "User Profile"
+# end
+#
+# var user_router = new Router
+# user_router.use("/*", new UserLogger)
+# user_router.use("/", new UserHome)
+# user_router.use("/profile", new UserProfile)
+#
+# var app = new App
+# app.use("/", new AppHome)
+# app.use("/user", user_router)
+# ~~~
+#
+# The app will now be able to handle requests to /user and /user/profile, as well
+# as call the `Time` middleware handler that is specific to the route.
+class Router
+       super Handler
+
+       # List of handlers to match with requests.
+       private var handlers = new Map[AppRoute, Handler]
+
+       # List of handlers to match before every other.
+       private var pre_handlers = new Map[AppRoute, Handler]
+
+       # List of handlers to match after every other.
+       private var post_handlers = new Map[AppRoute, Handler]
+
+       # Register a `handler` for a route `path`.
+       #
+       # Route paths are matched in registration order.
+       fun use(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               handlers[route] = handler
+       end
+
+       # Register a pre-handler for a route `path`.
+       #
+       # Prehandlers are matched before every other handlers in registrastion order.
+       fun use_before(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               pre_handlers[route] = handler
+       end
+
+       # Register a post-handler for a route `path`.
+       #
+       # Posthandlers are matched after every other handlers in registrastion order.
+       fun use_after(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               post_handlers[route] = handler
+       end
+
+       redef fun handle(route, uri, req, res) do
+               if not route.match(uri) then return
+               handle_pre(route, uri, req, res)
+               handle_in(route, uri, req, res)
+               handle_post(route, uri, req, res)
+       end
+
+       private fun handle_pre(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in pre_handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+               end
+       end
+
+       private fun handle_in(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+                       if res.sent then break
+               end
+       end
+
+       private fun handle_post(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in post_handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+               end
+       end
+
+       private fun build_route(handler: Handler, path: String): AppRoute do
+               if handler isa Router or handler isa StaticHandler then
+                       return new AppGlobRoute(path)
+               else if path.has_suffix("*") then
+                       return new AppGlobRoute(path)
+               else
+                       return new AppParamRoute(path)
+               end
+       end
+end
+
+# Popcorn application.
+#
+# The `App`  is the main point of the application.
+# It acts as a `Router` that holds the top level route handlers.
+#
+# Here an example to create a simple web app with Popcorn:
+#
+# ~~~
+# import popcorn
+#
+# class HelloHandler
+#      super Handler
+#
+#      redef fun get(req, res) do res.html "<h1>Hello World!</h1>"
+# end
+#
+# var app = new App
+# app.use("/", new HelloHandler)
+# # app.listen("localhost", 3000)
+# ~~~
+#
+# The Popcorn app listens on port 3000 for connections.
+# The app responds with "Hello World!" for request to the root URL (`/`) or **route**.
+# For every other path, it will respond with a **404 Not Found**.
+#
+# The `req` (request) and `res` (response) parameters are the same that nitcorn provides
+# so you can do anything else you would do in your route without Popcorn involved.
+#
+# Run the app with the following command:
+#
+# ~~~bash
+# nitc app.nit && ./app
+# ~~~
+#
+# Then, load [http://localhost:3000](http://localhost:3000) in a browser to see the output.
+class App
+       super Router
+end
+
+redef class HttpResponse
+
+       # Was this request sent by a handler?
+       var sent = false
+
+       private fun check_sent do
+               if sent then print "Warning: Headers already sent!"
+       end
+
+       # Write data in body response and send it.
+       fun send(raw_data: nullable Writable, status: nullable Int) do
+               if raw_data != null then
+                       body += raw_data.write_to_string
+               end
+               if status != null then
+                       status_code = status
+               else
+                       status_code = 200
+               end
+               check_sent
+               sent = true
+       end
+
+       # Write data as HTML and set the right content type header.
+       fun html(html: nullable Writable, status: nullable Int) do
+               header["Content-Type"] = media_types["html"].as(not null)
+               send(html, status)
+       end
+
+       # Write data as JSON and set the right content type header.
+       fun json(json: nullable Jsonable, status: nullable Int) do
+               header["Content-Type"] = media_types["json"].as(not null)
+               if json == null then
+                       send(null, status)
+               else
+                       send(json.to_json, status)
+               end
+       end
+
+       # Redirect response to `location`
+       fun redirect(location: String, status: nullable Int) do
+               header["Location"] = location
+               if status != null then
+                       status_code = status
+               else
+                       status_code = 302
+               end
+               check_sent
+               sent = true
+       end
+
+       # TODO The error message should be parameterizable.
+       fun error(status: Int) do
+               html("Error", status)
+       end
+end
diff --git a/lib/popcorn/pop_middlewares.nit b/lib/popcorn/pop_middlewares.nit
new file mode 100644 (file)
index 0000000..518d9dc
--- /dev/null
@@ -0,0 +1,77 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module pop_middlewares
+
+import pop_handlers
+import console
+import realtime
+
+# Initialize session in request if non existent.
+#
+# Should be called before any use of the session.
+class SessionInit
+       super Handler
+
+       redef fun all(req, res) do if req.session == null then req.session = new Session
+end
+
+# Initialize a clock for the resquest.
+#
+# Can be used to compute the time passed to respond that request.
+class RequestClock
+       super Handler
+
+       redef fun all(req, res) do req.clock = new Clock
+end
+
+# Display log info about request processing.
+class ConsoleLog
+       super Handler
+
+       # Do we want colors in the console output?
+       var colors = true
+
+       redef fun all(req, res) do
+               var clock = req.clock
+               if clock != null then
+                       print "{req.method} {req.uri} {status(res)} ({clock.total}s)"
+               else
+                       print "{req.method} {req.uri} {status(res)}"
+               end
+       end
+
+       # Colorize the request status.
+       private fun status(res: HttpResponse): String do
+               if colors then return res.color_status
+               return res.status_code.to_s
+       end
+end
+
+redef class HttpRequest
+       # Time that request was received by the Popcorn app.
+       var clock: nullable Clock = null
+end
+
+redef class HttpResponse
+       # Return `self` status colored for console.
+       fun color_status: String do
+               if status_code == 200 then return status_code.to_s.green
+               if status_code == 304 then return status_code.to_s.blue
+               if status_code == 404 then return status_code.to_s.yellow
+               return status_code.to_s.red
+       end
+end
diff --git a/lib/popcorn/pop_routes.nit b/lib/popcorn/pop_routes.nit
new file mode 100644 (file)
index 0000000..68c3d37
--- /dev/null
@@ -0,0 +1,263 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Internal routes representation.
+module pop_routes
+
+import nitcorn
+
+# AppRoute provide services for path and uri manipulation and matching..
+#
+# Default strict routes like `/` or `/user` match the same URI string.
+# An exception is done for the trailing `/`, which is always omitted during the
+# parsing.
+#
+# ~~~
+# var route = new AppRoute("/")
+# assert route.match("")
+# assert route.match("/")
+# assert not route.match("/user")
+# assert not route.match("user")
+#
+# route = new AppRoute("/user")
+# assert not route.match("/")
+# assert route.match("/user")
+# assert route.match("/user/")
+# assert not route.match("/user/10")
+# assert not route.match("/foo")
+# assert not route.match("user")
+# assert not route.match("/username")
+# ~~~
+class AppRoute
+
+       # Route relative path from server root.
+       var path: String
+
+       # Does self match the `req`?
+       fun match(uri: String): Bool do
+               uri = uri.simplify_path
+               var path = resolve_path(uri)
+               if uri.is_empty and path == "/" then return true
+               return uri == path
+       end
+
+       # Replace path parameters with concrete values from the `uri`.
+       #
+       # For strict routes, it returns the path unchanged:
+       # ~~~
+       # var route = new AppRoute("/")
+       # assert route.resolve_path("/user/10/profile") == "/"
+       #
+       # route = new AppRoute("/user")
+       # assert route.resolve_path("/user/10/profile") == "/user"
+       # ~~~
+       fun resolve_path(uri: String): String do return path.simplify_path
+
+       # Remove `resolved_path` prefix from `uri`.
+       #
+       # Mainly used to resolve and match mountable routes.
+       #
+       # ~~~
+       # var route = new AppRoute("/")
+       # assert route.uri_root("/user/10/profile") == "/user/10/profile"
+       #
+       # route = new AppRoute("/user")
+       # assert route.uri_root("/user/10/profile") == "/10/profile"
+       # ~~~
+       fun uri_root(uri: String): String do
+               var path = resolve_path(uri)
+               if path == "/" then return uri
+               return uri.substring(path.length, uri.length).simplify_path
+       end
+end
+
+# Parameterizable routes.
+#
+# Routes that can contains variables parts that will be resolved during the
+# matching process.
+#
+# Route parameters are marked with a colon `:`
+# ~~~
+# var route = new AppParamRoute("/:id")
+# assert not route.match("/")
+# assert route.match("/user")
+# assert route.match("/user/")
+# assert not route.match("/user/10")
+# ~~~
+#
+# It is possible to use more than one parameter in the same route:
+# ~~~
+# route = new AppParamRoute("/user/:userId/items/:itemId")
+# assert not route.match("/user/10/items/")
+# assert route.match("/user/10/items/e895346")
+# assert route.match("/user/USER/items/0/")
+# assert not route.match("/user/10/items/10/profile")
+# ~~~
+class AppParamRoute
+       super AppRoute
+
+       init do parse_path_parameters(path)
+
+       # Cut `path` into `UriParts`.
+       fun parse_path_parameters(path: String) do
+               for part in path.split("/") do
+                       if not part.is_empty and part.first == ':' then
+                               # is an uri param
+                               path_parts.add new UriParam(part.substring(1, part.length))
+                       else
+                               # is a standard string
+                               path_parts.add new UriString(part)
+                       end
+               end
+       end
+
+       # For parameterized routes, parameter names are replaced by their value in the URI.
+       # ~~~
+       # var route = new AppParamRoute("/user/:id")
+       # assert route.resolve_path("/user/10/profile") == "/user/10"
+       #
+       # route = new AppParamRoute("/user/:userId/items/:itemId")
+       # assert route.resolve_path("/user/Morriar/items/i156/desc") == "/user/Morriar/items/i156"
+       # ~~~
+       redef fun resolve_path(uri) do
+               var uri_params = parse_uri_parameters(uri)
+               var path = "/"
+               for part in path_parts do
+                       if part isa UriString then
+                               path /= part.string
+                       else if part isa UriParam then
+                               path /= uri_params.get_or_default(part.name, part.name)
+                       end
+               end
+               return path.simplify_path
+       end
+
+       # Extract parameter values from `uri`.
+       # ~~~
+       # var route = new AppParamRoute("/user/:userId/items/:itemId")
+       # var params = route.parse_uri_parameters("/user/10/items/i125/desc")
+       # assert params["userId"] == "10"
+       # assert params["itemId"] == "i125"
+       # assert params.length == 2
+       #
+       # params = route.parse_uri_parameters("/")
+       # assert params.is_empty
+       # ~~~
+       fun parse_uri_parameters(uri: String): Map[String, String] do
+               var res = new HashMap[String, String]
+               if path_parts.is_empty then return res
+               var parts = uri.split("/")
+               for i in [0 .. path_parts.length[ do
+                       if i >= parts.length then return res
+                       var ppart = path_parts[i]
+                       var part = parts[i]
+                       if not ppart.match(part) then return res
+                       if ppart isa UriParam then
+                               res[ppart.name] = part
+                       end
+               end
+               return res
+       end
+
+       private var path_parts = new Array[UriPart]
+end
+
+# Route with glob.
+#
+# Route variable part is suffixed with a star `*`:
+# ~~~
+# var route = new AppGlobRoute("/*")
+# assert route.match("/")
+# assert route.match("/user")
+# assert route.match("/user/10")
+# ~~~
+#
+# Glob routes can be combined with parameters:
+# ~~~
+# route = new AppGlobRoute("/user/:id/*")
+# assert not route.match("/user")
+# assert route.match("/user/10")
+# assert route.match("/user/10/profile")
+# ~~~
+#
+# Note that the star can be used directly on the end of an URI part:
+# ~~~
+# route = new AppGlobRoute("/user*")
+# assert route.match("/user")
+# assert route.match("/username")
+# assert route.match("/user/10/profile")
+# assert not route.match("/foo")
+# ~~~
+#
+# For now, stars cannot be used inside a route, use URI parameters instead.
+class AppGlobRoute
+       super AppParamRoute
+
+       # Path without the trailing `*`.
+       # ~~~
+       # var route = new AppGlobRoute("/user/:id/*")
+       # assert route.resolve_path("/user/10/profile") == "/user/10"
+       #
+       # route = new AppGlobRoute("/user/:userId/items/:itemId*")
+       # assert route.resolve_path("/user/Morriar/items/i156/desc") == "/user/Morriar/items/i156"
+       # ~~~
+       redef fun resolve_path(uri) do
+               var path = super
+               if path.has_suffix("*") then
+                       return path.substring(0, path.length - 1).simplify_path
+               end
+               return path.simplify_path
+       end
+
+       redef fun match(uri) do
+               var path = resolve_path(uri)
+               return uri.has_prefix(path.substring(0, path.length - 1))
+       end
+end
+
+# A String that compose an URI.
+#
+# In practice, UriPart can be parameters or static strings.
+private interface UriPart
+       # Does `self` matches a part of the uri?
+       fun match(uri_part: String): Bool is abstract
+end
+
+# An uri parameter string like `:id`.
+private class UriParam
+       super UriPart
+
+       # Param `name` in the route uri.
+       var name: String
+
+       # Parameters match everything.
+       redef fun match(part) do return not part.is_empty
+
+       redef fun to_s do return name
+end
+
+# A static uri string like `users`.
+private class UriString
+       super UriPart
+
+       # Uri part string.
+       var string: String
+
+       # Empty strings match everything otherwise matching is based on string equality.
+       redef fun match(part) do return string.is_empty or string == part
+
+       redef fun to_s do return string
+end
diff --git a/lib/popcorn/pop_tests.nit b/lib/popcorn/pop_tests.nit
new file mode 100644 (file)
index 0000000..1deaeab
--- /dev/null
@@ -0,0 +1,162 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Popcorn testing services
+#
+# ## Blackbox testing
+#
+# Popcorn allows you to test your apps using nitunit blackbox testing.
+#
+# With blackbox testing you compare the output of your program with a result file.
+#
+# To get started with blackbox testing, create a nitunit test suite and imports
+# the `pop_tests` module.
+#
+# You then need to build the app that will be tested by nitunit as shown in the
+# `test_example_hello` method.
+# Calling `run_test` will automatically set the `host` and `port` used for testing.
+#
+# Redefine the `client_test` method to write your scenario.
+# Here we use `curl` to access some URI on the app.
+#
+# ~~~nitish
+# module test_example_hello is test_suite
+#
+# import pop_tests
+# import example_hello
+#
+# class TestExampleHello
+#      super TestPopcorn
+#
+#      fun test_example_hello do
+#              var app = new App
+#              app.use("/", new HelloHandler)
+#              run_test(app)
+#      end
+#
+#      redef fun client_test do
+#              system "curl -s {host}:{port}"
+#              system "curl -s {host}:{port}/"
+#              system "curl -s {host}:{port}///////////"
+#              system "curl -s {host}:{port}/not_found"
+#              system "curl -s {host}:{port}/not_found/not_found"
+#      end
+# end
+# ~~~
+#
+# The blackbox testing needs a reference result file against wich the test output
+# will be compared.
+# Create your expected result file in `test_example_hello.sav/test_example_hello.res`.
+#
+# Test your app by running nitunit:
+#
+# ~~~bash
+# nitunit ./example_hello.nit
+# ~~~
+#
+# See `examples/hello_world` for the complete example.
+module pop_tests
+
+import test_suite
+import popcorn
+import pthreads
+
+redef class Sys
+
+       # Use localhost for testing
+       var test_host = "localhost"
+
+       # Return a new port for each instance
+       fun test_port: Int do
+               srand
+               return 10000+20000.rand
+       end
+end
+
+# Thread running the App to test.
+class AppThread
+       super Thread
+
+       # Host used by tested App.
+       var host: String
+
+       # Port used by tested App.
+       var port: Int
+
+       # App to test.
+       var app: App
+
+       redef fun main
+       do
+               # Hide testing concept to force nitcorn to actually run
+               "NIT_TESTING".setenv("false")
+               app.quiet = true
+               app.listen(host, port)
+               return null
+       end
+end
+
+# Thread running the test client.
+class ClientThread
+       super Thread
+
+       # Test suite to execute.
+       var test_suite: TestPopcorn
+
+       redef fun main do
+               test_suite.client_test
+               print ""
+               return null
+       end
+end
+
+# TestSuite for Popcorn blackbox testing.
+class TestPopcorn
+       super TestSuite
+
+       # Host used to run App.
+       var host: String = test_host
+
+       # Port used to run App.
+       var port: Int = test_port
+
+       # Run the test suite on the App.
+       fun run_test(app: App) do
+               var server = new AppThread(host, port, app)
+               server.start
+               0.1.sleep
+
+               var client = new ClientThread(self)
+               client.start
+               client.join
+               0.1.sleep
+
+               exit 0
+       end
+
+       # Redefine this method to implement your test scenario.
+       fun client_test do end
+
+       # Regex to catch and hide the port from the output to get consistent results
+       var host_re: Regex = "localhost:\[0-9\]+".to_re
+
+       # Execute a System function.
+       fun system(cmd: String, title: nullable String)
+       do
+               title = title or else cmd
+               title = title.replace(host_re, "localhost:*****")
+               print "\n[Client] {title}"
+               sys.system cmd
+       end
+end
diff --git a/lib/popcorn/popcorn.nit b/lib/popcorn/popcorn.nit
new file mode 100644 (file)
index 0000000..64e4459
--- /dev/null
@@ -0,0 +1,96 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Application server abstraction on top of nitcorn.
+module popcorn
+
+import nitcorn
+import pop_middlewares
+intrude import pop_handlers
+
+# App acts like a wrapper around a nitcorn `Action`.
+redef class App
+       super Action
+
+       # Do not show anything on console
+       var quiet = false is writable
+
+       # Start listening on `host:port`.
+       fun listen(host: String, port: Int) do
+               var iface = "{host}:{port}"
+               var vh = new VirtualHost(iface)
+
+               vh.routes.add new Route("/", self)
+
+               var fac = new HttpFactory.and_libevent
+               fac.config.virtual_hosts.add vh
+
+               if not quiet then
+                       print "Launching server on http://{iface}/"
+               end
+               fac.run
+       end
+
+       # Handle request from nitcorn
+       redef fun answer(req, uri) do
+               uri = uri.simplify_path
+               var res = new HttpResponse(404)
+               for route, handler in pre_handlers do
+                       handler.handle(route, uri, req, res)
+               end
+               for route, handler in handlers do
+                       handler.handle(route, uri, req, res)
+                       if res.sent then break
+               end
+               if not res.sent then
+                       res.send(error_tpl(res.status_code, res.status_message), 404)
+               end
+               for route, handler in post_handlers do
+                       handler.handle(route, uri, req, res)
+               end
+               res.session = req.session
+               return res
+       end
+
+       #
+       fun error_tpl(status: Int, message: nullable String): Template do
+               return new ErrorTpl(status, message)
+       end
+end
+
+#
+class ErrorTpl
+       super Template
+
+       #
+       var status: Int
+
+       #
+       var message: nullable String
+
+       redef fun rendering do add """
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>{{{message or else status}}}</title>
+               </head>
+               <body>
+               <h1>{{{status}}} {{{message or else ""}}}</h1>
+               </body>
+               </html>"""
+
+end
diff --git a/lib/popcorn/test_pop_routes.nit b/lib/popcorn/test_pop_routes.nit
new file mode 100644 (file)
index 0000000..9ad37a5
--- /dev/null
@@ -0,0 +1,166 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_pop_routes is test_suite
+
+import pop_routes
+import test_suite
+
+class TestAppRoute
+       super TestSuite
+
+       fun test_root_match_only_one_uri do
+               var r = new AppRoute("/")
+               assert r.match("")
+               assert r.match("/")
+               assert not r.match("/user")
+       end
+
+       fun test_strict_route_match_only_one_uri do
+               var r = new AppRoute("/user")
+               assert not r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/")
+               assert not r.match("/user/10")
+               assert not r.match("/foo")
+       end
+end
+
+class TestAppParamRoute
+       super TestSuite
+
+       fun test_param_route_match_good_uri_params_1 do
+               var r = new AppParamRoute("/:id")
+               assert not r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/")
+               assert not r.match("/user/10")
+       end
+
+       fun test_param_route_match_good_uri_params_2 do
+               var r = new AppParamRoute("/user/:id")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert not r.match("/user/")
+               assert r.match("/user/10")
+               assert r.match("/user/10/")
+               assert not r.match("/user/10/profile")
+       end
+
+       fun test_param_route_match_good_uri_params_3 do
+               var r = new AppParamRoute("/user/:id/profile")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert not r.match("/user/")
+               assert not r.match("/user/10")
+               assert not r.match("/user/10/")
+               assert r.match("/user/10/profile")
+               assert r.match("/user/10/profile/")
+               assert not r.match("/user/10/profile/settings")
+               assert not r.match("/user/10/foo")
+       end
+
+       fun test_param_route_match_good_uri_params_4 do
+               var r = new AppParamRoute("/:id/:foo")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert not r.match("/user/")
+               assert r.match("/user/10")
+               assert r.match("/user/10/")
+               assert not r.match("/user/10/10")
+       end
+
+       fun test_param_route_match_good_uri_params_5 do
+               var r = new AppParamRoute("/user/:id/:foo")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert not r.match("/foo")
+               assert not r.match("/user/10")
+               assert r.match("/user/10/10")
+               assert r.match("/user/10/10/")
+               assert not r.match("/user/10/10/profile")
+       end
+
+       fun test_param_route_match_good_uri_params_6 do
+               var r = new AppParamRoute("/user/:id/settings/:foo")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert not r.match("/foo")
+               assert not r.match("/user/10")
+               assert not r.match("/user/10/10")
+               assert not r.match("/user/10/10/")
+               assert not r.match("/user/10/10/profile")
+               assert r.match("/user/10/settings/profile")
+               assert r.match("/user/10/settings/profile/")
+               assert not r.match("/user/10/settings/profile/10")
+       end
+end
+
+class TestRouteMatching
+       super TestSuite
+
+       fun test_glob_route_match_good_uri_prefix1 do
+               var r = new AppGlobRoute("/*")
+               assert r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/10")
+       end
+
+       fun test_glob_route_match_good_uri_prefix2 do
+               var r = new AppGlobRoute("/user/*")
+               assert not r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/10")
+       end
+
+       fun test_glob_route_match_good_uri_prefix3 do
+               var r = new AppGlobRoute("/user*")
+               assert not r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/10")
+       end
+
+       fun test_glob_route_work_with_parameters_1 do
+               var r = new AppGlobRoute("/:id/*")
+               assert not r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/10")
+               assert r.match("/user/10/profile")
+       end
+
+       fun test_glob_route_work_with_parameters_2 do
+               var r = new AppGlobRoute("/:id*")
+               assert not r.match("/")
+               assert r.match("/user")
+               assert r.match("/user/10")
+       end
+
+       fun test_glob_route_work_with_parameters_3 do
+               var r = new AppGlobRoute("/user/:id/*")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert r.match("/user/10")
+               assert r.match("/user/10/profile")
+       end
+
+       fun test_glob_route_work_with_parameters_4 do
+               var r = new AppGlobRoute("/user/:id*")
+               assert not r.match("/")
+               assert not r.match("/user")
+               assert r.match("/user/10")
+               assert r.match("/user/10/profile")
+       end
+end
diff --git a/lib/popcorn/test_popcorn.nit b/lib/popcorn/test_popcorn.nit
new file mode 100644 (file)
index 0000000..108eb7a
--- /dev/null
@@ -0,0 +1,97 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_popcorn is test_suite
+
+import pop_tests
+import popcorn
+
+class TestHandler
+       super Handler
+
+       var marker: String
+
+       redef fun get(req, res) do res.send marker
+end
+
+class TestPopcornRouter
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/user"
+               system "curl -s {host}:{port}/user/"
+               system "curl -s {host}:{port}/user/settings"
+               system "curl -s {host}:{port}/products"
+               system "curl -s {host}:{port}/products/"
+               system "curl -s {host}:{port}/products/list"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/user/not_found"
+               system "curl -s {host}:{port}/products/not_found"
+       end
+
+       fun test_router do
+               var app = new App
+               app.use("/", new TestHandler("/"))
+               app.use("/about", new TestHandler("/about"))
+
+               var router1 = new App
+               router1.use("/", new TestHandler("/user"))
+               router1.use("/settings", new TestHandler("/user/settings"))
+               app.use("/user", router1)
+
+               var router2 = new App
+               router2.use("/", new TestHandler("/products"))
+               router2.use("/list", new TestHandler("/products/list"))
+               app.use("/products", router2)
+
+               run_test(app)
+       end
+end
+
+class TestPopcornRoutes
+       super TestPopcorn
+
+       redef fun client_test do
+               system "curl -s {host}:{port}"
+               system "curl -s {host}:{port}/"
+               system "curl -s {host}:{port}/misc"
+               system "curl -s {host}:{port}/misc/foo"
+               system "curl -s {host}:{port}/misc/foo/bar"
+               system "curl -s {host}:{port}/misc/foo/baz"
+               system "curl -s {host}:{port}/user"
+               system "curl -s {host}:{port}/user/"
+               system "curl -s {host}:{port}/user/id"
+               system "curl -s {host}:{port}/user/id/profile"
+               system "curl -s {host}:{port}/user/id/misc/foo"
+               system "curl -s {host}:{port}/user/id/misc/foo/bar"
+               system "curl -s {host}:{port}/user/id/misc/foo/bar/baz"
+               system "curl -s {host}:{port}/not_found"
+               system "curl -s {host}:{port}/user/id/not_found"
+       end
+
+       fun test_routes do
+               var app = new App
+               app.use("/", new TestHandler("/"))
+               app.use("/user", new TestHandler("/user"))
+               app.use("/misc/*", new TestHandler("/misc/everything"))
+               app.use("/user/:id", new TestHandler("/user/id"))
+               app.use("/user/:id/profile", new TestHandler("/user/id/profile"))
+               app.use("/user/:id/misc/*", new TestHandler("/user/id/misc/everything"))
+               run_test(app)
+       end
+end
diff --git a/lib/popcorn/test_popcorn.sav/test_router.res b/lib/popcorn/test_popcorn.sav/test_router.res
new file mode 100644 (file)
index 0000000..1022411
--- /dev/null
@@ -0,0 +1,50 @@
+
+[Client] curl -s localhost:*****
+/
+[Client] curl -s localhost:*****/
+/
+[Client] curl -s localhost:*****/user
+/user
+[Client] curl -s localhost:*****/user/
+/user
+[Client] curl -s localhost:*****/user/settings
+/user/settings
+[Client] curl -s localhost:*****/products
+/products
+[Client] curl -s localhost:*****/products/
+/products
+[Client] curl -s localhost:*****/products/list
+/products/list
+[Client] curl -s localhost:*****/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/user/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/products/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
diff --git a/lib/popcorn/test_popcorn.sav/test_routes.res b/lib/popcorn/test_popcorn.sav/test_routes.res
new file mode 100644 (file)
index 0000000..58e47d0
--- /dev/null
@@ -0,0 +1,49 @@
+
+[Client] curl -s localhost:*****
+/
+[Client] curl -s localhost:*****/
+/
+[Client] curl -s localhost:*****/misc
+/misc/everything
+[Client] curl -s localhost:*****/misc/foo
+/misc/everything
+[Client] curl -s localhost:*****/misc/foo/bar
+/misc/everything
+[Client] curl -s localhost:*****/misc/foo/baz
+/misc/everything
+[Client] curl -s localhost:*****/user
+/user
+[Client] curl -s localhost:*****/user/
+/user
+[Client] curl -s localhost:*****/user/id
+/user/id
+[Client] curl -s localhost:*****/user/id/profile
+/user/id/profile
+[Client] curl -s localhost:*****/user/id/misc/foo
+/user/id/misc/everything
+[Client] curl -s localhost:*****/user/id/misc/foo/bar
+/user/id/misc/everything
+[Client] curl -s localhost:*****/user/id/misc/foo/bar/baz
+/user/id/misc/everything
+[Client] curl -s localhost:*****/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
+[Client] curl -s localhost:*****/user/id/not_found
+               <!DOCTYPE html>
+               <html>
+               <head>
+                       <meta charset="utf-8">
+                       <title>Not Found</title>
+               </head>
+               <body>
+               <h1>404 Not Found</h1>
+               </body>
+               </html>
diff --git a/lib/postgresql/native_postgres.nit b/lib/postgresql/native_postgres.nit
new file mode 100644 (file)
index 0000000..d32c908
--- /dev/null
@@ -0,0 +1,145 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A native wrapper ove the postgres c api
+module native_postgres is pkgconfig("libpq")
+
+in "C header" `{
+  #include <libpq-fe.h>
+`}
+
+extern class ExecStatusType `{int`}
+  new empty           `{ return PGRES_EMPTY_QUERY; `}
+  new command_ok      `{ return PGRES_COMMAND_OK; `}
+  new tuples_ok       `{ return PGRES_TUPLES_OK; `}
+  new copy_out        `{ return PGRES_COPY_OUT; `}
+  new copy_in         `{ return PGRES_COPY_IN; `}
+  new bad_response    `{ return PGRES_BAD_RESPONSE; `}
+  new nonfatal_error  `{ return PGRES_NONFATAL_ERROR; `}
+  new fatal_error     `{ return PGRES_FATAL_ERROR; `}
+
+  fun is_ok: Bool `{
+    return !(self == PGRES_BAD_RESPONSE || self == PGRES_NONFATAL_ERROR || self == PGRES_FATAL_ERROR);
+  `}
+
+  redef fun to_s import NativeString.to_s `{
+    char * err = PQresStatus(self);
+    if(err == NULL) err = "";
+    return NativeString_to_s(err);
+  `}
+end
+
+extern class ConnStatusType `{int`}
+  new connection_ok `{ return CONNECTION_OK; `}
+  new connection_bad `{ return CONNECTION_BAD; `}
+
+  fun is_ok: Bool `{return self == CONNECTION_OK; `}
+end
+
+extern class NativePGResult `{PGresult *`}
+  # Frees the memory block associated with the result
+  fun clear `{PQclear(self); `}
+
+  # Returns the number of rows in the query result
+  fun ntuples:Int `{ return PQntuples(self); `}
+
+  # Returns the number of columns in each row of the query result
+  fun nfields:Int `{return PQnfields(self); `}
+
+  # Returns the ExecStatusType of a result
+  fun status: ExecStatusType `{ return PQresultStatus(self); `}
+
+  # Returns the field name of a given column_number
+  fun fname(column_number:Int):String import NativeString.to_s `{
+    return NativeString_to_s( PQfname(self, column_number));
+  `}
+
+  # Returns the column number associated with the column name
+  fun fnumber(column_name:String):Int import String.to_cstring `{
+    return PQfnumber(self, String_to_cstring(column_name));
+  `}
+
+  # Returns a single field value of one row of the result at row_number, column_number
+  fun value(row_number:Int, column_number:Int):String import NativeString.to_s `{
+    return NativeString_to_s(PQgetvalue(self, row_number, column_number));
+  `}
+
+  # Tests wether a field is a null value
+  fun is_null(row_number:Int, column_number: Int): Bool `{
+    return PQgetisnull(self, row_number, column_number);
+  `}
+
+end
+extern class NativePostgres `{PGconn *`}
+
+  # Connect to a new database using the conninfo string as a parameter
+  new connectdb(conninfo: Text) import Text.to_cstring `{
+    PGconn * self = NULL;
+    self = PQconnectdb(Text_to_cstring(conninfo));
+    return self;
+  `}
+
+  # Submits a query to the server and waits for the result returns the ExecStatustype of the query
+  fun exec(query: Text): NativePGResult import Text.to_cstring `{
+    PGresult *res = PQexec(self, Text_to_cstring(query));
+    return res;
+  `}
+
+  # Prepares a statement with the given parameters
+  fun prepare(stmt: String, query: String, nParams: Int): NativePGResult import String.to_cstring `{
+    const char * stmtName = String_to_cstring(stmt);
+    const char * queryStr = String_to_cstring(query);
+    PGresult * res = PQprepare(self, stmtName, queryStr, nParams, NULL);
+    return res;
+  `}
+
+  fun exec_prepared(stmt: String, nParams: Int, values: Array[String], pLengths: Array[Int], pFormats: Array[Int], resultFormat: Int): NativePGResult import String.to_cstring, Array[String].[], Array[Int].[] `{
+    const char * stmtName = String_to_cstring(stmt);
+    const char * paramValues[nParams];
+    int paramLengths[nParams];
+    int paramFormats[nParams];
+    int i;
+    for(i = 0; i < nParams; i++)
+      paramValues[i] = String_to_cstring(Array_of_String__index(values, i));
+    for(i = 0; i < nParams; i++)
+      paramLengths[i] = Array_of_Int__index(pLengths, i);
+    for(i = 0; i < nParams; i++)
+      paramFormats[i] = Array_of_Int__index(pFormats, i);
+    PGresult * res = PQexecPrepared(self, stmtName, nParams, paramValues, paramLengths, paramFormats, resultFormat);
+    return res;
+  `}
+
+  # Returns the error message of the last operation on the connection
+  fun error: String import NativeString.to_s `{
+    char * error = PQerrorMessage(self);
+    return NativeString_to_s(error);
+  `}
+
+  # Returns the status of this connection
+  fun status: ConnStatusType `{
+    return PQstatus(self);
+  `}
+
+  # Closes the connection to the server
+  fun finish  `{
+    PQfinish(self);
+  `}
+
+  # Closes the connection to the server and attempts to reconnect with the previously used params
+  fun reset `{
+    PQreset(self);
+  `}
+end
diff --git a/lib/postgresql/package.ini b/lib/postgresql/package.ini
new file mode 100644 (file)
index 0000000..dc82d85
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=postgresql
+tags=database,lib
+maintainer=Guilherme Mansur <guilhermerpmansur@gmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/postgresql/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/postgresql/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
\ No newline at end of file
diff --git a/lib/postgresql/postgres.nit b/lib/postgresql/postgres.nit
new file mode 100644 (file)
index 0000000..22e498f
--- /dev/null
@@ -0,0 +1,144 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Guilherme Mansur<guilhermerpmansur@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to manipulate a Postgres database
+#
+# For more information, refer to the documentation of http://www.postgresql.org/docs/manuals/
+#
+# ### Usage example
+#
+# ~~~
+# class Animal
+#   var name: String
+#   var kind: String
+#   var age: Int
+# end
+#
+# var animals = new Array[Animal]
+# var dog = new Animal("Lassy", "dog", 10)
+# var cat = new Animal("Garfield", "cat", 3)
+# var turtle = new Animal("George", "turtle", 123)
+#
+# animals.add(dog)
+# animals.add(cat)
+# animals.add(turtle)
+#
+# var db = new Postgres.open("dbname=postgres")
+#
+# assert db_is_open: not db.is_closed
+# assert create_table: db.create_table("IF NOT EXISTS animals (aname TEXT PRIMARY KEY, kind TEXT NOT NULL, age INT NOT NULL)") else print db.error
+#
+# for animal in animals do
+#   assert insert: db.insert("INTO animals VALUES('{animal.name}', '{animal.kind}', {animal.age})") else print db.error
+# end
+#
+# var result = db.raw_execute("SELECT * FROM animals")
+# assert  result.is_ok
+# assert drop_table: db.execute("DROP TABLE animals")
+# db.finish
+# assert db_is_closed: db.is_closed
+# ~~~
+module postgres
+
+private import native_postgres
+
+# A connection to a Postgres database
+class Postgres
+  private var native_connection: NativePostgres
+
+  var is_closed = true
+
+  # Open the connnection with the database using the `conninfo`
+  init open(conninfo: Text)
+  do
+    init(new NativePostgres.connectdb(conninfo))
+    if native_connection.status.is_ok then is_closed = false
+  end
+
+  # Close this connection with the database
+  fun finish
+  do
+    if is_closed then return
+
+    is_closed = true
+
+    native_connection.finish
+  end
+
+  fun prepare(stmt_name:String, query:String, num_params: Int):PGResult do return new PGResult(native_connection.prepare(stmt_name, query, num_params))
+
+  fun exec_prepared(stmt_name: String, num_params: Int, values: Array[String], param_lengths: Array[Int], param_formats: Array[Int], result_format: Int):PGResult do
+    return new PGResult(native_connection.exec_prepared(stmt_name, num_params, values, param_lengths, param_formats, result_format))
+  end
+
+  # Executes a `query` and returns the raw `PGResult`
+  fun raw_execute(query: Text): PGResult do return new PGResult(native_connection.exec(query))
+
+  # Execute the `sql` statement and returns `true` on success
+  fun execute(query: Text): Bool do return native_connection.exec(query).status.is_ok
+
+  # Create a table on the DB with a statement beginning with "CREATE TABLE ", followed by `rest`
+  #
+  # This method does not escape special characters.
+  fun create_table(rest: Text): Bool do return execute("CREATE TABLE " + rest)
+
+  # Insert in the DB with a statement beginning with "INSERT ", followed by `rest`
+  #
+  # This method does not escape special characters.
+  fun insert(rest: Text): Bool do return execute("INSERT " + rest)
+
+  # Replace in the DB with a statement beginning with "REPLACE", followed by `rest`
+  #
+  # This method does not escape special characters.
+  fun replace(rest: Text): Bool do return execute("REPLACE " + rest)
+
+  # The latest error message on the connection an empty string if none
+  fun error: String do return native_connection.error
+
+  # The status of this connection
+  fun is_valid: Bool do return native_connection.status.is_ok
+
+  # Resets the connection to the database
+  fun reset do native_connection.reset
+end
+
+# The result of a given query
+class PGResult
+  private var pg_result: NativePGResult
+
+  fun clear do pg_result.clear
+
+  # Returns the number of rows in the query result
+  fun ntuples:Int do return pg_result.ntuples
+
+  # Returns the number of columns in each row of the query result
+  fun nfields:Int do return pg_result.nfields
+
+  # Returns the ExecStatusType of a result
+  fun is_ok:Bool do return pg_result.status.is_ok
+
+  # Returns the field name of a given `column_number`
+  fun fname(column_number:Int):String do return pg_result.fname(column_number)
+
+  # Returns the column number associated with the `column_name`
+  fun fnumber(column_name:String):Int do return pg_result.fnumber(column_name)
+
+  # Returns a single field value of one row of the result at `row_number`, `column_number`
+  fun value(row_number:Int, column_number:Int):String  do return pg_result.value(row_number, column_number)
+
+  # Tests wether a field specified by the `row_number` and `column_number` is null.
+  fun is_null(row_number:Int, column_number: Int): Bool do return pg_result.is_null(row_number, column_number)
+end
index a1c43a2..1db10b2 100644 (file)
@@ -16,7 +16,7 @@
 
 # Main POSIX threads support and intro the classes `Thread`, `Mutex` and `Barrier`
 module pthreads is
-       cflags "-pthread"
+       cflags "-pthread -Wno-unknown-attributes"
        ldflags "-pthread"
        pkgconfig "bdw-gc"
        new_annotation threaded
@@ -33,6 +33,8 @@ in "C Header" `{
 `}
 
 in "C" `{
+       #include <string.h>
+
        // TODO protect with: #ifdef WITH_LIBGC
        // We might have to add the next line to gc_chooser.c too, especially
        // if we get an error like "thread not registered with GC".
diff --git a/lib/readline.ini b/lib/readline.ini
new file mode 100644 (file)
index 0000000..c7e2ee9
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=readline
+tags=lib
+maintainer=Frédéric Vachon <fredvac@gmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/readline.nit
+git=https://github.com/nitlang/nit.git
+git.directory=lib/readline.nit
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/readline.nit b/lib/readline.nit
new file mode 100644 (file)
index 0000000..783e4e4
--- /dev/null
@@ -0,0 +1,58 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2016 Frédéric Vachon <fredvac@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# GNU readline library wrapper
+module readline is ldflags "-lreadline"
+
+in "C" `{
+       #include <readline/readline.h>
+       #include <readline/history.h>
+`}
+
+private fun native_readline(prompt: NativeString): NativeString `{
+       return readline(prompt);
+`}
+
+private fun native_add_history(data: NativeString) `{
+       if (data == NULL) return;
+       add_history(data);
+`}
+
+# Set emacs keybindings mode
+fun set_vi_mode `{ rl_editing_mode = 0; `}
+
+# Set emacs keybindings mode
+fun set_emacs_mode `{ rl_editing_mode = 1; `}
+
+# Use the GNU Library readline function
+# Returns `null` if EOF is read
+# If `with_history` is true, it will save all commands in the history except
+# empty strings and white characters strings
+fun readline(message: String, with_history: nullable Bool): nullable String do
+       var line = native_readline(message.to_cstring)
+       if line.address_is_null then return null
+
+       var nit_str = line.to_s
+
+       if with_history != null and with_history then
+               if nit_str.trim != "" then native_add_history(line)
+       end
+
+       return nit_str
+end
+
+# Adds the data String to the history no matter what it contains
+fun add_history(data: String) do native_add_history data.to_cstring
index 8cfa4da..243b5d8 100644 (file)
@@ -73,13 +73,16 @@ extern class Timespec `{struct timespec*`}
                clock_gettime(CLOCK_MONOTONIC, self);
        `}
 
-       # Substract a Timespec from `self`.
-       fun - ( o : Timespec ) : Timespec
+       # Subtract `other` from `self`
+       fun -(other: Timespec): Timespec
        do
-               var s = sec - o.sec
-               var ns = nanosec - o.nanosec
-               if ns > nanosec then s += 1
-               return new Timespec( s, ns )
+               var s = sec - other.sec
+               var ns = nanosec - other.nanosec
+               if ns < 0 then
+                       s -= 1
+                       ns += 1000000000
+               end
+               return new Timespec(s, ns)
        end
 
        # Number of whole seconds of elapsed time.
@@ -117,15 +120,33 @@ extern class Timespec `{struct timespec*`}
 end
 
 # Keeps track of real time
+#
+# ~~~
+# var clock = new Clock
+#
+# # sleeping at least 1s
+# 1.0.sleep
+# assert clock.total >= 1.0
+# assert clock.lapse >= 1.0
+#
+# # sleeping at least 5ms
+# 0.005.sleep
+# assert clock.total >= 1.005
+# assert clock.lapse >= 0.005
+# ~~~
 class Clock
-       # Time at instanciation
+       super FinalizableOnce
+
+       # TODO use less mallocs
+
+       # Time at creation
        protected var time_at_beginning = new Timespec.monotonic_now
 
        # Time at last time a lapse method was called
        protected var time_at_last_lapse = new Timespec.monotonic_now
 
        # Smallest time frame reported by clock
-       fun resolution : Timespec `{
+       fun resolution: Timespec `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
 #ifdef __MACH__
                clock_serv_t cclock;
@@ -142,18 +163,43 @@ class Clock
                return tv;
        `}
 
-       # Return timelapse since instanciation of this instance
-       fun total : Timespec
+       # Seconds since the creation of this instance
+       fun total: Float
        do
-               return new Timespec.monotonic_now - time_at_beginning
+               var now = new Timespec.monotonic_now
+               var diff = now - time_at_beginning
+               var r = diff.to_f
+               diff.free
+               now.free
+               return r
        end
 
-       # Return timelapse since last call to lapse
-       fun lapse : Timespec
+       # Seconds since the last call to `lapse`
+       fun lapse: Float
        do
                var nt = new Timespec.monotonic_now
                var dt = nt - time_at_last_lapse
+               var r = dt.to_f
+               dt.free
+               time_at_last_lapse.free
                time_at_last_lapse = nt
-               return dt
+               return r
+       end
+
+       # Seconds since the last call to `lapse`, without resetting the lapse counter
+       fun peek_lapse: Float
+       do
+               var nt = new Timespec.monotonic_now
+               var dt = nt - time_at_last_lapse
+               var r = dt.to_f
+               nt.free
+               dt.free
+               return r
+       end
+
+       redef fun finalize_once
+       do
+               time_at_beginning.free
+               time_at_last_lapse.free
        end
 end
index 874961f..1fa3638 100644 (file)
@@ -53,14 +53,14 @@ end
 redef class FlatString
        redef fun internal_to_dot: String
        do
-               return "n{object_id} [label=\"FlatString\\nlength = {length}\\nbytelen = {bytelen}\\nfirst_byte = {first_byte}\\nlast_byte = {last_byte}\\nText = {self.escape_to_dot}\"];\n"
+               return "n{object_id} [label=\"FlatString\\nlength = {length}\\nbyte_length = {byte_length}\\nfirst_byte = {first_byte}\\nlast_byte = {last_byte}\\nText = {self.escape_to_dot}\"];\n"
        end
 end
 
 redef class FlatBuffer
        redef fun internal_to_dot: String
        do
-               return "n{object_id} [label=\"FlatBuffer\\nbytelen = {bytelen}\\nlength = {length}\\ncapacity = {capacity}\\nText = {escape_to_dot}\"];\n"
+               return "n{object_id} [label=\"FlatBuffer\\nbyte_length = {byte_length}\\nlength = {length}\\ncapacity = {capacity}\\nText = {escape_to_dot}\"];\n"
        end
 end
 
diff --git a/lib/rubix.ini b/lib/rubix.ini
new file mode 100644 (file)
index 0000000..c644c2e
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=rubix
+tags=algo,lib
+maintainer=Lucas Bajolet<r4pass@hotmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/rubix.nit
+git=https://github.com/nitlang/nit.git
+git.directory=lib/rubix.nit
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/rubix.nit b/lib/rubix.nit
new file mode 100644 (file)
index 0000000..8e63784
--- /dev/null
@@ -0,0 +1,666 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+# Rubix-cube modelization library
+#
+# As for now the library supports basic representation of a Rubix-cube
+# The library is built for speed, most operations cost no allocations to perform.
+# This does however mean that the library is NOT thread-safe and should be handled
+# with the appropriate mutual-exclusion mechanisms to avoid memory corruption
+# or unintended side-effects on a single cube.
+#
+# The library supports the singmaster notation as a way to perform operations
+# on a Rubix cube.
+#
+# No solver is (yet) provided along with the library.
+module rubix
+
+import console
+
+private fun array1d_copy_to(fromarr: Array[Int], oarr: Array[Int]) do
+       while oarr.length > fromarr.length do oarr.pop
+       while oarr.length < fromarr.length do oarr.push 0
+       fromarr.copy_to(0, fromarr.length, oarr, 0)
+end
+
+private fun front_face: Int do return 0
+private fun left_face: Int do return 1
+private fun top_face: Int do return 2
+private fun right_face: Int do return 3
+private fun bottom_face: Int do return 4
+private fun back_face: Int do return 5
+
+private fun top_ln: Int do return 0
+private fun mid_ln: Int do return 1
+private fun bottom_ln: Int do return 2
+
+private fun left_col: Int do return 0
+private fun mid_col: Int do return 1
+private fun right_col: Int do return 2
+
+private fun clock: Int do return 0
+private fun counterclock: Int do return 1
+
+private fun square: String do return once "■"
+
+redef class Int
+
+       # Returns a coloured square for a defined colour id
+       #
+       # Assume colours are:
+       #
+       # * Green -> 0
+       # * White (replaced with light gray) -> 1
+       # * Red -> 2
+       # * Yellow -> 3
+       # * Orange (replaced with purple) -> 4
+       # * Blue -> 5
+       #
+       private fun rubix_colour: String do
+               if self == 0 then return square.green
+               if self == 1 then return square.light_gray
+               if self == 2 then return square.red
+               if self == 3 then return square.yellow
+               if self == 4 then return square.purple
+               if self == 5 then return square.blue
+               abort
+       end
+end
+
+# In-memory representation of a Rubix Cube
+#
+# Faces are represented in memory as if they were a flattened cube like:
+#
+# ~~~raw
+#       B B B
+#       B B B
+#       B B B
+#       U U U
+#       U U U
+#       U U U
+# L L L F F F R R R
+# L L L F F F R R R
+# L L L F F F R R R
+#       D D D
+#       D D D
+#       D D D
+# ~~~
+#
+# All the commands detailed in the class respect the Singmaster notation
+class RubixCube
+       # Faces of the Cube
+       #
+       # 0 - Front
+       # 1 - Left
+       # 2 - Up
+       # 3 - Right
+       # 4 - Down
+       # 5 - Back
+       #
+       # Each line is:
+       #
+       # 0 - Top line
+       # 1 - Middle line
+       # 2 - Down line
+       var faces: Array[Array[Array[Int]]]
+
+       # Build a new Rubix Cube with a solved layout
+       init solved do
+               var faces = new Array[Array[Array[Int]]]
+               var colour = 0
+               for i in [0 .. 6[ do
+                       var face = new Array[Array[Int]]
+                       faces.add face
+                       for j in [0 .. 3[ do
+                               var line = new Array[Int]
+                               for k in [0 .. 3[ do
+                                       line.add colour
+                               end
+                               face.add line
+                       end
+                       colour += 1
+               end
+               init faces
+       end
+
+       # Build a new Rubix Cube with a scrambled layout
+       #
+       # NOTE: The layout is not random, but scrambled nonetheless
+       init scrambled do
+               var colours = once [0, 1, 2, 3, 4, 5]
+               var colour_pos = 0
+               var faces = new Array[Array[Array[Int]]]
+               var increment = 1
+               for i in [0 .. 6[ do
+                       var face = new Array[Array[Int]]
+                       faces.add face
+                       for j in [0 .. 3[ do
+                               var line = new Array[Int]
+                               for k in [0 .. 3[ do
+                                       line.add colours[colour_pos]
+                                       colour_pos += increment
+                                       if colour_pos > 5 then
+                                               increment = -1
+                                               colour_pos = 5
+                                       end
+                                       if colour_pos < 0 then
+                                               increment = 1
+                                               colour_pos = 0
+                                       end
+                               end
+                               face.add line
+                       end
+               end
+               init faces
+       end
+
+       # Reset the Rubix Cube to a solved position
+       fun reset do
+               for i in [0 .. 6[ do
+                       var face = faces[i]
+                       for j in [0 .. 3[ do
+                               var line = face[j]
+                               for k in [0 .. 3[ do
+                                       line[k] = i
+                               end
+                       end
+               end
+       end
+
+       # Checks if both objects are Rubix cubes and their content is equivalent
+       #
+       # NOTE: Rotationed versions are not yet considered equal
+       redef fun ==(o) do
+               if not o isa RubixCube then return false
+               for mf in faces, tf in o.faces do
+                       for ml in mf, tl in tf do
+                               for mc in ml, tc in tl do if mc != tc then return false
+                       end
+               end
+               return true
+       end
+
+       # Is `self` a solved Rubix Cube ?
+       fun is_solved: Bool do
+               for face_id in [0 .. 6[ do
+                       var i = faces[face_id]
+                       var colour = i.first.first
+                       for j in [0 .. 3[ do
+                               var ln = i[j]
+                               for k in [0 .. 3[ do
+                                       if ln[k] != colour then return false
+                               end
+                       end
+               end
+               return true
+       end
+
+       redef fun to_s do
+               var buf = new Buffer
+               buf.append(single_face(back_face))
+               buf.append(single_face(top_face))
+               buf.append(three_faces(left_face, front_face, right_face))
+               buf.append(single_face(bottom_face))
+               return buf.to_s
+       end
+
+       private fun single_face(face_id: Int): Text do
+               var b = new Buffer
+               var face = faces[face_id]
+               for i in [0 .. 3[ do
+                       var ln = face[i]
+                       b.append("{" " * 6}{ln[0].rubix_colour} {ln[1].rubix_colour} {ln[2].rubix_colour}{" " * 6}\n")
+               end
+               return b
+       end
+
+       private fun three_faces(face1, face2, face3: Int): Text do
+               var b = new Buffer
+               var face_ids = [face1, face2, face3]
+               for i in [0 .. 3[ do
+                       for j in face_ids do
+                               var face = faces[j]
+                               var ln = face[i]
+                               b.append("{ln[0].rubix_colour} {ln[1].rubix_colour} {ln[2].rubix_colour} ")
+                       end
+                       b.add '\n'
+               end
+               return b
+       end
+
+       private var rot_ln_buffer = new Array[Array[Int]].with_capacity(4)
+       private fun rotate_line(ln_id: Int, direction: Int) do
+               var line_data = rot_ln_buffer
+               if line_data.is_empty then for i in [0 .. 4[ do line_data.add(new Array[Int])
+               array1d_copy_to(faces[front_face][ln_id], line_data[0])
+               array1d_copy_to(faces[left_face][ln_id], line_data[1])
+               array1d_copy_to(faces[back_face][2 - ln_id], line_data[2])
+               array1d_copy_to(faces[right_face][ln_id], line_data[3])
+               if direction == counterclock then
+                       line_data[3].swap_at(0, 2)
+                       line_data[2].swap_at(0, 2)
+                       rot_ln_buffer.rotate_left
+               else if direction == clock then
+                       line_data[1].swap_at(0, 2)
+                       line_data[2].swap_at(0, 2)
+                       rot_ln_buffer.rotate_right
+               else
+                       abort
+               end
+               array1d_copy_to(line_data[0], faces[front_face][ln_id])
+               array1d_copy_to(line_data[1], faces[left_face][ln_id])
+               array1d_copy_to(line_data[2], faces[back_face][2 - ln_id])
+               array1d_copy_to(line_data[3], faces[right_face][ln_id])
+       end
+
+       private var colbuf = new Array[Int].with_capacity(3)
+       private fun coldata(face_id: Int, col_id: Int): Array[Int] do
+               if colbuf.is_empty then for i in [0 .. 3[ do colbuf.add 0
+               var face = faces[face_id]
+               for i in [0 .. 3[ do colbuf[i] = face[i][col_id]
+               return colbuf
+       end
+
+       private fun set_coldata(face_id, col_id: Int, coldata: Array[Int]) do
+               var face = faces[face_id]
+               for i in [0 .. 3[ do face[i][col_id] = coldata[i]
+       end
+
+       private var rot_col_buffer = new Array[Array[Int]].with_capacity(4)
+       private fun rotate_column(col_id: Int, direction: Int) do
+               var col_data = rot_col_buffer
+               if col_data.is_empty then for i in [0 .. 4[ do col_data.add(new Array[Int])
+               array1d_copy_to(coldata(front_face, col_id), rot_col_buffer[0])
+               array1d_copy_to(coldata(top_face, col_id), rot_col_buffer[1])
+               array1d_copy_to(coldata(back_face, col_id), rot_col_buffer[2])
+               array1d_copy_to(coldata(bottom_face, col_id), rot_col_buffer[3])
+               if direction == clock then
+                       rot_col_buffer.rotate_left
+               else if direction == counterclock then
+                       rot_col_buffer.rotate_right
+               else
+                       abort
+               end
+               set_coldata(front_face, col_id, rot_col_buffer[0])
+               set_coldata(top_face, col_id, rot_col_buffer[1])
+               set_coldata(back_face, col_id, rot_col_buffer[2])
+               set_coldata(bottom_face, col_id, rot_col_buffer[3])
+       end
+
+       private var r90_cache = new Array[Array[Int]]
+       private fun rotate_l90_face(face_id: Int) do
+               var lines = r90_cache
+               if lines.is_empty then for i in [0 .. 3[ do lines.add(new Array[Int])
+               array1d_copy_to(faces[face_id][top_ln], lines[0])
+               array1d_copy_to(faces[face_id][mid_ln], lines[1])
+               array1d_copy_to(faces[face_id][bottom_ln], lines[2])
+               for i in [0 .. 3[ do lines[i].swap_at(0, 2)
+               set_coldata(face_id, left_col, lines[0])
+               set_coldata(face_id, mid_col, lines[1])
+               set_coldata(face_id, right_col, lines[2])
+       end
+
+       private fun rotate_r90_face(face_id: Int) do
+               var lines = r90_cache
+               if lines.is_empty then for i in [0 .. 3[ do lines.add(new Array[Int])
+               array1d_copy_to(faces[face_id][top_ln], lines[0])
+               array1d_copy_to(faces[face_id][mid_ln], lines[1])
+               array1d_copy_to(faces[face_id][bottom_ln], lines[2])
+               set_coldata(face_id, right_col, lines[0])
+               set_coldata(face_id, mid_col, lines[1])
+               set_coldata(face_id, left_col, lines[2])
+       end
+
+       # U command
+       fun clock_U do
+               rotate_line(top_ln, clock)
+               rotate_r90_face(top_face)
+       end
+
+       # U' command
+       fun cclock_U do
+               rotate_line(top_ln, counterclock)
+               rotate_l90_face(top_face)
+       end
+
+       # D command
+       fun clock_D do
+               rotate_line(bottom_ln, counterclock)
+               rotate_r90_face(bottom_face)
+       end
+
+       # D' command
+       fun cclock_D do
+               rotate_line(bottom_ln, clock)
+               rotate_l90_face(bottom_face)
+       end
+
+       # L command
+       fun clock_L do
+               rotate_column(left_col, clock)
+               rotate_r90_face(left_face)
+       end
+
+       # L' command
+       fun cclock_L do
+               rotate_column(left_col, counterclock)
+               rotate_l90_face(left_face)
+       end
+
+       # R command
+       fun clock_R do
+               rotate_column(right_col, counterclock)
+               rotate_r90_face(right_face)
+       end
+
+       # R' command
+       fun cclock_R do
+               rotate_column(right_col, clock)
+               rotate_l90_face(right_face)
+       end
+
+       # M command
+       fun clock_M do rotate_column(mid_col, clock)
+
+       # M' command
+       fun cclock_M do rotate_column(mid_col, counterclock)
+
+       # E command
+       fun clock_E do rotate_line(mid_ln, counterclock)
+
+       # E' command
+       fun cclock_E do rotate_line(mid_ln, clock)
+
+       # F command
+       fun clock_F do
+               cube_Y_rotation
+               clock_L
+               ccube_Y_rotation
+       end
+
+       # F' command
+       fun cclock_F do
+               cube_Y_rotation
+               cclock_L
+               ccube_Y_rotation
+       end
+
+       # B command
+       fun clock_B do
+               ccube_Y_rotation
+               clock_L
+               cube_Y_rotation
+       end
+
+       # B' command
+       fun cclock_B do
+               ccube_Y_rotation
+               cclock_L
+               cube_Y_rotation
+       end
+
+       # S command
+       fun clock_S do
+               cube_Y_rotation
+               clock_M
+               ccube_Y_rotation
+       end
+
+       # S' command
+       fun cclock_S do
+               cube_Y_rotation
+               cclock_M
+               ccube_Y_rotation
+       end
+
+       # u command
+       fun clock_u do
+               clock_U
+               cclock_E
+       end
+
+       # u' command
+       fun cclock_u do
+               cclock_U
+               clock_E
+       end
+
+       # l command
+       fun clock_l do
+               clock_L
+               clock_M
+       end
+
+       # l' command
+       fun cclock_l do
+               cclock_L
+               cclock_M
+       end
+
+       # f command
+       fun clock_f do
+               clock_F
+               clock_S
+       end
+
+       # f' command
+       fun cclock_f do
+               cclock_F
+               cclock_S
+       end
+
+       # r command
+       fun clock_r do
+               clock_R
+               cclock_M
+       end
+
+       # r' command
+       fun cclock_r do
+               cclock_R
+               clock_M
+       end
+
+       # b command
+       fun clock_b do
+               clock_B
+               cclock_S
+       end
+
+       # b' command
+       fun cclock_b do
+               cclock_B
+               clock_S
+       end
+
+       # d command
+       fun clock_d do
+               clock_D
+               clock_E
+       end
+
+       # d' command
+       fun cclock_d do
+               cclock_D
+               cclock_E
+       end
+
+       # Y command
+       fun cube_Y_rotation do
+               clock_U
+               cclock_E
+               cclock_D
+       end
+
+       # Y' command
+       fun ccube_Y_rotation do
+               cclock_U
+               clock_E
+               clock_D
+       end
+
+       # X command
+       fun cube_X_rotation do
+               cclock_L
+               cclock_M
+               clock_R
+       end
+
+       # X' command
+       fun ccube_X_rotation do
+               clock_L
+               clock_M
+               cclock_R
+       end
+
+       # Z command
+       fun cube_Z_rotation do
+               ccube_Y_rotation
+               cube_X_rotation
+               cube_Y_rotation
+       end
+
+       # Z' command
+       fun ccube_Z_rotation do
+               cube_Y_rotation
+               cube_X_rotation
+               ccube_Y_rotation
+       end
+
+       # Applies a command `cmd` to `self`, returns the number of operations performed during the command
+       fun do_cmd(cmd: String): Int do
+               if cmd == "" then return 0
+               var iters = 1
+               var cmdln = cmd.length
+               if cmd[cmdln - 1] == '2' then
+                       iters = 2
+               end
+               var cmd1 = cmd[0]
+               var cmd2 = '\0'
+               if cmdln > 1 then
+                       cmd2 = cmd[1]
+                       if cmd2 == '2' then cmd2 = '\0'
+               end
+               for i in [1 .. iters] do
+                       if cmd1 == 'U' then
+                               if cmd2 == '\'' then
+                                       cclock_U
+                               else
+                                       clock_U
+                               end
+                       else if cmd1 == 'L' then
+                               if cmd2 == '\'' then
+                                       cclock_L
+                               else
+                                       clock_L
+                               end
+                       else if cmd1 == 'F' then
+                               if cmd2 == '\'' then
+                                       cclock_F
+                               else
+                                       clock_F
+                               end
+                       else if cmd1 == 'R' then
+                               if cmd2 == '\'' then
+                                       cclock_R
+                               else
+                                       clock_R
+                               end
+                       else if cmd1 == 'B' then
+                               if cmd2 == '\'' then
+                                       cclock_B
+                               else
+                                       clock_B
+                               end
+                       else if cmd1 == 'D' then
+                               if cmd2 == '\'' then
+                                       cclock_D
+                               else
+                                       clock_D
+                               end
+                       else if cmd1 == 'M' then
+                               if cmd2 == '\'' then
+                                       cclock_M
+                               else
+                                       clock_M
+                               end
+                       else if cmd1 == 'E' then
+                               if cmd2 == '\'' then
+                                       cclock_E
+                               else
+                                       clock_E
+                               end
+                       else if cmd1 == 'S' then
+                               if cmd2 == '\'' then
+                                       cclock_S
+                               else
+                                       clock_S
+                               end
+                       else if cmd1 == 'u' then
+                               if cmd2 == '\'' then
+                                       cclock_u
+                               else
+                                       clock_u
+                               end
+                       else if cmd1 == 'l' then
+                               if cmd2 == '\'' then
+                                       cclock_l
+                               else
+                                       clock_l
+                               end
+                       else if cmd1 == 'f' then
+                               if cmd2 == '\'' then
+                                       cclock_f
+                               else
+                                       clock_f
+                               end
+                       else if cmd1 == 'r' then
+                               if cmd2 == '\'' then
+                                       cclock_r
+                               else
+                                       clock_r
+                               end
+                       else if cmd1 == 'b' then
+                               if cmd2 == '\'' then
+                                       cclock_b
+                               else
+                                       clock_b
+                               end
+                       else if cmd1 == 'd' then
+                               if cmd2 == '\'' then
+                                       cclock_d
+                               else
+                                       clock_d
+                               end
+                       else if cmd1 == 'X' then
+                               if cmd2 == '\'' then
+                                       ccube_X_rotation
+                               else
+                                       cube_X_rotation
+                               end
+                       else if cmd1 == 'Y' then
+                               if cmd2 == '\'' then
+                                       ccube_Y_rotation
+                               else
+                                       cube_Y_rotation
+                               end
+                       else if cmd1 == 'Z' then
+                               if cmd2 == '\'' then
+                                       ccube_Z_rotation
+                               else
+                                       cube_Z_rotation
+                               end
+                       else
+                               abort
+                       end
+               end
+               return iters
+       end
+end
index 4541428..3554d22 100644 (file)
@@ -55,7 +55,7 @@ end
 # then using a reference.
 class SerializerCache
        # Map of already serialized objects to the reference id
-       private var sent: Map[Serializable, Int] = new StrictHashMap[Serializable, Int]
+       protected var sent: Map[Serializable, Int] = new StrictHashMap[Serializable, Int]
 
        # Is `object` known?
        fun has_object(object: Serializable): Bool do return sent.keys.has(object)
@@ -88,7 +88,7 @@ end
 # Used by `Deserializer` to find already deserialized objects by their reference.
 class DeserializerCache
        # Map of references to already deserialized objects.
-       private var received: Map[Int, Object] = new StrictHashMap[Int, Object]
+       protected var received: Map[Int, Object] = new StrictHashMap[Int, Object]
 
        # Is there an object associated to `id`?
        fun has_id(id: Int): Bool do return received.keys.has(id)
index 2bd8fbe..e00745c 100644 (file)
@@ -247,7 +247,7 @@ redef class String
        #     import base64
        #     assert "The quick brown fox jumps over the lazy dog".sha1 == [0x2Fu8, 0xD4u8, 0xE1u8, 0xC6u8, 0x7Au8, 0x2Du8, 0x28u8, 0xFCu8, 0xEDu8, 0x84u8, 0x9Eu8, 0xE1u8, 0xBBu8, 0x76u8, 0xE7u8, 0x39u8, 0x1Bu8, 0x93u8, 0xEBu8, 0x12u8]
        fun sha1: Bytes do
-               return new Bytes(to_cstring.sha1_intern(bytelen), 20, 20)
+               return new Bytes(to_cstring.sha1_intern(byte_length), 20, 20)
        end
 
        # Computes the SHA1 of the receiver.
index f441545..4b92786 100644 (file)
@@ -22,9 +22,14 @@ in "C header" `{
        #include <sqlite3.h>
 `}
 
+in "C" `{
+       // Return code of the last call to the constructor of `NativeSqlite3`
+       static int nit_sqlite_open_error = SQLITE_OK;
+`}
+
 redef class Sys
        # Last error raised when calling `Sqlite3::open`
-       var sqlite_open_error: nullable Sqlite3Code = null
+       fun sqlite_open_error: Sqlite3Code `{ return nit_sqlite_open_error; `}
 end
 
 extern class Sqlite3Code `{int`}
@@ -75,11 +80,10 @@ extern class Sqlite3Code `{int`}
 
        private fun native_to_s: NativeString `{
 #if SQLITE_VERSION_NUMBER >= 3007015
-               char *err = (char *)sqlite3_errstr(self);
+               return (char *)sqlite3_errstr(self);
 #else
-               char *err = "sqlite3_errstr supported only by version >= 3.7.15";
+               return "sqlite3_errstr is not supported in version < 3.7.15";
 #endif
-               return err;
        `}
 end
 
@@ -91,13 +95,8 @@ extern class NativeStatement `{sqlite3_stmt*`}
                return sqlite3_step(self);
        `}
 
-       fun column_name(i: Int) : String import NativeString.to_s `{
-               const char * name = (sqlite3_column_name(self, i));
-               if(name == NULL){
-                       name = "";
-               }
-               char * ret = (char *) name;
-               return NativeString_to_s(ret);
+       fun column_name(i: Int): NativeString `{
+               return (char *)sqlite3_column_name(self, i);
        `}
 
        # Number of bytes in the blob or string at row `i`
@@ -139,25 +138,18 @@ end
 extern class NativeSqlite3 `{sqlite3 *`}
 
        # Open a connection to a database in UTF-8
-       new open(filename: NativeString) import set_sys_sqlite_open_error `{
+       new open(filename: NativeString) `{
                sqlite3 *self = NULL;
                int err = sqlite3_open(filename, &self);
-               NativeSqlite3_set_sys_sqlite_open_error(self, (void*)(long)err);
-               // The previous cast is a hack, using non pointers in extern classes is not
-               // yet in the spec of the FFI.
+               nit_sqlite_open_error = err;
                return self;
        `}
 
-       # Utility method to set `Sys.sqlite_open_error`
-       private fun set_sys_sqlite_open_error(err: Sqlite3Code) do sys.sqlite_open_error = err
-
        # Has this DB been correctly opened?
        #
        # To know if it has been closed or interrupted, you must check for errors with `error`.
        fun is_valid: Bool do return not address_is_null
 
-       fun destroy do close
-
        # Close this connection
        fun close `{
 #if SQLITE_VERSION_NUMBER >= 3007014
@@ -171,18 +163,18 @@ extern class NativeSqlite3 `{sqlite3 *`}
        `}
 
        # Execute a SQL statement
-       fun exec(sql: String): Sqlite3Code import String.to_cstring `{
-               return sqlite3_exec(self, String_to_cstring(sql), 0, 0, 0);
+       fun exec(sql: NativeString): Sqlite3Code `{
+               return sqlite3_exec(self, sql, NULL, NULL, NULL);
        `}
 
        # Prepare a SQL statement
-       fun prepare(sql: String): nullable NativeStatement import String.to_cstring, NativeStatement.as nullable `{
+       fun prepare(sql: NativeString): NativeStatement `{
                sqlite3_stmt *stmt;
-               int res = sqlite3_prepare_v2(self, String_to_cstring(sql), -1, &stmt, 0);
+               int res = sqlite3_prepare_v2(self, sql, -1, &stmt, 0);
                if (res == SQLITE_OK)
-                       return NativeStatement_as_nullable(stmt);
+                       return stmt;
                else
-                       return null_NativeStatement();
+                       return NULL;
        `}
 
        fun last_insert_rowid: Int `{
index 62fe8e5..322e7d7 100644 (file)
@@ -57,8 +57,8 @@ class Sqlite3DB
        # Prepare and return a `Statement`, return `null` on error
        fun prepare(sql: Text): nullable Statement
        do
-               var native_stmt = native_connection.prepare(sql.to_s)
-               if native_stmt == null then return null
+               var native_stmt = native_connection.prepare(sql.to_cstring)
+               if native_stmt.address_is_null then return null
 
                var stmt = new Statement(native_stmt)
                open_statements.add stmt
@@ -68,7 +68,7 @@ class Sqlite3DB
        # Execute the `sql` statement and return `true` on success
        fun execute(sql: Text): Bool
        do
-               var err = native_connection.exec(sql.to_s)
+               var err = native_connection.exec(sql.to_cstring)
                return err.is_ok
        end
 
@@ -99,7 +99,7 @@ class Sqlite3DB
        do
                if not native_connection.is_valid then
                        var err = sys.sqlite_open_error
-                       if err == null then return null
+                       if err.is_ok then return null
                        return err.to_s
                end
 
@@ -182,7 +182,9 @@ class StatementEntry
        var name: String is lazy do
                assert statement_closed: statement.is_open
 
-               return statement.native_statement.column_name(index)
+               var cname = statement.native_statement.column_name(index)
+               assert not cname.address_is_null
+               return cname.to_s
        end
 
        # Get the value of this entry according to its Sqlite type
@@ -305,8 +307,9 @@ redef universal Int super Sqlite3Data end
 
 redef universal Float super Sqlite3Data end
 
-redef class String
-       super Sqlite3Data
+redef class String super Sqlite3Data end
+
+redef class Text
 
        # Return `self` between `'`s, escaping `\` and `'`
        #
@@ -315,6 +318,24 @@ redef class String
        do
                return "'{self.replace('\\', "\\\\").replace('\'', "''")}'"
        end
+
+       # Format the date represented by `self` into an escaped string for SQLite
+       #
+       # `self` must be composed of 1 to 3 integers separated by '-'.
+       # An incompatible format will result in an invalid date string.
+       #
+       #     assert "2016-5-1".to_sql_date_string == "'2016-05-01'"
+       #     assert "2016".to_sql_date_string == "'2016-01-01'"
+       fun to_sql_date_string: String
+       do
+               var parts = self.split("-")
+               for i in [parts.length .. 3[ do parts[i] = "1"
+
+               var year = parts[0].justify(4, 1.0, '0')
+               var month = parts[1].justify(2, 1.0, '0')
+               var day = parts[2].justify(2, 1.0, '0')
+               return "{year}-{month}-{day}".to_sql_string
+       end
 end
 
 # A Sqlite3 blob
index d0bc77d..615c45f 100644 (file)
@@ -37,7 +37,7 @@ import template
 # A macro identifier is valid if:
 #
 # * starts with an uppercase letter
-# * contains only numers, uppercase letters or '_'
+# * contains only numbers, uppercase letters or '_'
 #
 # See `String::is_valid_macro_name` for more details.
 #
index ed0971d..e4dab5f 100644 (file)
@@ -21,8 +21,11 @@ import counter
 
 redef class Sys
 
-       # Counts the number of allocations of FlatString
-       var flatstr_allocations = 0
+       # Counts the number of allocations of UnicodeFlatString
+       var uniflatstr_allocations = 0
+
+       # Counts the number of allocations of ASCIIFlatString
+       var asciiflatstr_allocations = 0
 
        # Counts the number of allocations of FlatBuffer
        var flatbuf_allocations = 0
@@ -50,10 +53,10 @@ redef class Sys
        var index_len = new Counter[Int]
 
        # Length (bytes) of the FlatString created by lib
-       var str_bytelen = new Counter[Int]
+       var str_byte_length = new Counter[Int]
 
-       # Counter of the times `bytelen` is called on FlatString
-       var bytelen_call = new Counter[String]
+       # Counter of the times `byte_length` is called on FlatString
+       var byte_length_call = new Counter[String]
 
        # Counter of the times `bytepos` is called on each type of receiver
        var position_call = new Counter[String]
@@ -76,7 +79,8 @@ Usage of Strings:
 
 Allocations, by type:
                """
-               print "\t-FlatString = {flatstr_allocations}"
+               print "\t-UnicodeFlatString = {uniflatstr_allocations}"
+               print "\t-ASCIIFlatString = {asciiflatstr_allocations}"
                print "\t-FlatBuffer = {flatbuf_allocations}"
                print "\t-Concat = {concat_allocations}"
                print "\t-RopeBuffer = {ropebuf_allocations}"
@@ -84,7 +88,7 @@ Allocations, by type:
                print "Calls to length, by type:"
                for k, v in length_calls do
                        printn "\t{k} = {v}"
-                       if k == "FlatString" then printn " (cache misses {length_cache_miss[k]}, {div(length_cache_miss[k] * 100, v)}%)"
+                       if k == "UnicodeFlatString" then printn " (cache misses {length_cache_miss[k]}, {div(length_cache_miss[k] * 100, v)}%)"
                        printn "\n"
                end
                print "Indexed accesses, by type:"
@@ -94,8 +98,8 @@ Allocations, by type:
                        printn "\n"
                end
 
-               print "Calls to bytelen for each type:"
-               for k, v in bytelen_call do
+               print "Calls to byte_length for each type:"
+               for k, v in byte_length_call do
                        print "\t{k} = {v}"
                end
 
@@ -112,13 +116,11 @@ Allocations, by type:
                print "Calls to first_byte on FlatString {first_byte_call}"
                print "Calls to last_byte on FlatString {last_byte_call}"
 
-               print "FlatStrings allocated with length {str_full_created} ({str_full_created.to_f/flatstr_allocations.to_f * 100.0 }%)"
-
                print "Length of travel for index distribution:"
                index_len.print_content
 
                print "Byte length of the FlatStrings created:"
-               str_bytelen.print_content
+               str_byte_length.print_content
        end
 
        redef fun run do
@@ -137,8 +139,8 @@ redef class Concat
                sys.concat_allocations += 1
        end
 
-       redef fun bytelen do
-               sys.bytelen_call.inc "Concat"
+       redef fun byte_length do
+               sys.byte_length_call.inc "Concat"
                return super
        end
 
@@ -219,8 +221,8 @@ redef class RopeBuffer
                sys.ropebuf_allocations += 1
        end
 
-       redef fun bytelen do
-               sys.bytelen_call.inc "RopeBuffer"
+       redef fun byte_length do
+               sys.byte_length_call.inc "RopeBuffer"
                return super
        end
 
@@ -256,8 +258,8 @@ redef class FlatBuffer
                super
        end
 
-       redef fun bytelen do
-               sys.bytelen_call.inc "FlatBuffer"
+       redef fun byte_length do
+               sys.byte_length_call.inc "FlatBuffer"
                return super
        end
 
@@ -299,8 +301,8 @@ redef class FlatString
                super
        end
 
-       redef fun bytelen do
-               sys.bytelen_call.inc "FlatString"
+       redef fun byte_length do
+               sys.byte_length_call.inc "FlatString"
                return super
        end
 
@@ -319,29 +321,6 @@ redef class FlatString
                return super
        end
 
-       init do
-               sys.flatstr_allocations += 1
-       end
-
-       redef init with_infos(items, bytelen, from)
-       do
-               self.items = items
-               self.bytelen = bytelen
-               sys.str_bytelen.inc bytelen
-               first_byte = from
-               length = items.utf8_length(from, bytelen)
-       end
-
-       redef init full(items, bytelen, from, length)
-       do
-               self.items = items
-               self.length = length
-               self.bytelen = bytelen
-               sys.str_bytelen.inc bytelen
-               sys.str_full_created += 1
-               first_byte = from
-       end
-
        private var length_cache: nullable Int = null
 
        redef fun length do
@@ -349,7 +328,7 @@ redef class FlatString
                var l = length_cache
                if l != null then return l
                sys.length_cache_miss.inc "FlatString"
-               if bytelen == 0 then return 0
+               if byte_length == 0 then return 0
                var st = first_byte
                var its = items
                var ln = 0
@@ -367,3 +346,21 @@ redef class FlatString
                return super
        end
 end
+
+redef class ASCIIFlatString
+       redef init full_data(items, byte_length, from, length)
+       do
+               super
+               sys.asciiflatstr_allocations += 1
+               sys.str_full_created += 1
+       end
+end
+
+redef class UnicodeFlatString
+       redef init full_data(items, byte_length, from, length)
+       do
+               super
+               sys.uniflatstr_allocations += 1
+               sys.str_full_created += 1
+       end
+end
diff --git a/misc/docker/Dockerfile b/misc/docker/Dockerfile
new file mode 100644 (file)
index 0000000..95995bf
--- /dev/null
@@ -0,0 +1,35 @@
+# This is a basic install of Nit on a debian base.
+
+FROM debian:jessie
+MAINTAINER Jean Privat <jean@pryen.org>
+
+# Install dependencies
+RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+               # Recomanded builds pakages
+               build-essential \
+               ccache \
+               libgc-dev \
+               graphviz \
+               libunwind-dev \
+               pkg-config \
+               # Get the code!
+               git \
+               ca-certificates \
+               curl \
+               # For nit manpages :)
+               man \
+       && rm -rf /var/lib/apt/lists/*
+
+# Clone and compile
+RUN git clone https://github.com/nitlang/nit.git /root/nit \
+       && cd /root/nit \
+       && make \
+       && . misc/nit_env.sh install \
+       # Clean and reduce size
+       && strip c_src/nitc bin/nit* \
+       && ccache -C \
+       && rm -rf .git
+
+ENV NIT_DIR /root/nit
+ENV PATH $NIT_DIR/bin:$PATH
+WORKDIR $NIT_DIR
diff --git a/misc/docker/README.md b/misc/docker/README.md
new file mode 100644 (file)
index 0000000..d255285
--- /dev/null
@@ -0,0 +1,64 @@
+# Supported tags and respective Dockerfile links
+
+* [latest](https://github.com/nitlang/nit/blob/master/misc/docker/Dockerfile)
+* [full](https://github.com/nitlang/nit/blob/master/misc/docker/full/Dockerfile)
+
+## What is Nit?
+
+Nit is an expressive language with a script-like syntax, a friendly type-system and aims at elegance, simplicity and intuitiveness.
+
+Nit has a simple straightforward style and can usually be picked up quickly, particularly by anyone who has programmed before.
+While object-oriented, it allows procedural styles.
+
+More information on
+
+* Website <http://www.nitlanguage.org>
+* Github <https://github.com/nitlang/nit>
+* Chatroom <https://gitter.im/nitlang/nit>
+
+## How to use this image
+
+You can use these images to build then run your programs.
+
+### Experimenting with Nit
+
+~~~
+host$ docker run -ti nitlang/nit
+root@ce9b671dd9fc:/root/nit# nitc examples/hello_world.nit
+root@ce9b671dd9fc:/root/nit# ./hello_world
+hello world
+~~~
+
+### Build and Run Programs
+
+In your Dockerfile, write something like:
+
+~~~Dockerfile
+FROM nitlang/nit
+
+# Create a workdir
+RUN mkdir -p /root/work
+WORKDIR /root/work
+
+# Copy the source code in /root/work/
+COPY . /root/work/
+
+# Compile
+RUN nitc src/hello.nit --dir . \
+       # Clear disk space
+       && ccache -C
+# You can also use a Makefile or any build system you want.
+
+# Run
+CMD ["./hello"]
+~~~
+
+Then, build and execute
+
+~~~
+host$ docker build -t nithello .
+host$ docker run --rm nithello
+hello!
+~~~
+
+See the full example at <https://github.com/nitlang/nit/blob/master/misc/docker/hello/Dockerfile>
diff --git a/misc/docker/full/Dockerfile b/misc/docker/full/Dockerfile
new file mode 100644 (file)
index 0000000..df0d214
--- /dev/null
@@ -0,0 +1,76 @@
+# This is a full install of Nit on a debian base.
+# Full because most dependencies are installed so that most tests can be run
+
+FROM nitlang/nit:latest
+MAINTAINER Jean Privat <jean@pryen.org>
+
+# Dependencies for more libs and tests
+RUN dpkg --add-architecture i386 \
+       && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+               # Packages needed for lib/
+               libcurl4-openssl-dev \
+               libegl1-mesa-dev \
+               libevent-dev \
+               libgles1-mesa-dev \
+               libgles2-mesa-dev \
+               libgtk-3-dev \
+               libncurses5-dev \
+               libpq-dev \
+               libsdl-image1.2-dev \
+               libsdl-ttf2.0-dev \
+               libsdl1.2-dev \
+               libsdl2-dev \
+               libsqlite3-dev \
+               libx11-dev \
+               libxdg-basedir-dev \
+               postgresql \
+               # Packages needed for contrib, platforms and FFI
+               ant \
+               clang \
+               default-jdk \
+               file \
+               inkscape \
+               libopenmpi-dev \
+               unzip \
+               # Android
+               libc6:i386 \
+               libstdc++6:i386 \
+               zlib1g:i386 \
+               # TODO neo4j emscripten test_glsl_validation
+       && rm -rf /var/lib/apt/lists/*
+
+# Install android sdk/ndk
+RUN mkdir -p /opt \
+       && cd /opt \
+       # Android SDK
+       && curl https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz -o android-sdk-linux.tgz \
+       && tar xzf android-sdk-linux.tgz \
+       && rm android-sdk-linux.tgz \
+       && echo y | android-sdk-linux/tools/android update sdk -a --no-ui --filter \
+               # Hardcode minimal known working things
+               platform-tools,build-tools-22.0.1,android-22,android-10 \
+       # Android NDK
+       && curl http://dl.google.com/android/repository/android-ndk-r11c-linux-x86_64.zip -o android-ndk.zip \
+       && unzip -q android-ndk.zip \
+       && ln -s android-ndk-r11c android-ndk \
+       && rm android-ndk.zip \
+       && printf "PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_NDK\nexport PATH\n" >> "/etc/profile.d/android.sh"
+
+# Setup environment variables
+
+ENV ANDROID_HOME /opt/android-sdk-linux
+ENV ANDROID_NDK /opt/android-ndk
+ENV PATH $PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_NDK
+
+# Run tests
+RUN cd /root/nit/tests \
+       # Basic tests
+       && ./testfull.sh || true \
+       && rm -rf out/ alt/*.nit \
+       # Nitunits
+       && ../bin/nitunit ../lib ../contrib || true \
+       && rm -rf .nitunit \
+       && ccache -C
+
+WORKDIR /root/nit
+ENTRYPOINT [ "bash" ]
diff --git a/misc/docker/hello/Dockerfile b/misc/docker/hello/Dockerfile
new file mode 100644 (file)
index 0000000..c0a018b
--- /dev/null
@@ -0,0 +1,17 @@
+FROM nitlang/nit
+
+# Create a workdir
+RUN mkdir -p /root/work
+WORKDIR /root/work
+
+# Copy the source code in /root/work/
+COPY . /root/work/
+
+# Compile
+RUN nitc src/hello.nit --dir . \
+        # Clear disk space
+        && ccache -C
+# You can also use a Makefile or what you want
+
+# Say what to run
+CMD ["./hello"]
diff --git a/misc/docker/hello/src/hello.nit b/misc/docker/hello/src/hello.nit
new file mode 100644 (file)
index 0000000..b732142
--- /dev/null
@@ -0,0 +1 @@
+print "hello"
index 9503883..fcdec6c 100644 (file)
@@ -6,6 +6,8 @@ nit - interprets and debugs Nit programs.
 
 nit [*options*] FILE [ARG]...
 
+nit [*options*] - [ARG]...
+
 nit [*options*] -e COMMAND [ARG]...
 
 # DESCRIPTION
@@ -16,6 +18,9 @@ It takes the main module of a program as the first argument then the options and
     $ nit examples/hello_world.nit
     hello world
 
+If `-` is used instead of a module, then the program is read from the standard input.
+The whole program is read before its interpretation starts.
+
 The Nit interpreter is usable and valid as a *shebang* interpreted directive.
 It is however recommended to use with `/usr/bin/env` because the location of the executable is not standardized.
 
index 2927a24..77c8aa8 100644 (file)
@@ -374,9 +374,6 @@ Disable implicit casts on unsafe return with erasure-typing policy (dangerous).
 These options are used to debug or to bench the compilation results.
 Usually you do not need them since they make the generated code slower.
 
-### `--hardening`
-Generate contracts in the C code against bugs in the compiler.
-
 ### `--no-shortcut-range`
 Always instantiate a range and its iterator on 'for' loops.
 
@@ -386,7 +383,7 @@ Put primitive attributes in a box instead of an union.
 ### `--no-shortcut-equal`
 Always call == in a polymorphic way.
 
-### `--no-tag-primitive`
+### `--no-tag-primitives`
 Use only boxes for primitive types.
 
 The separate compiler uses tagged values to encode common primitive types like Int, Bool and Char.
@@ -408,10 +405,35 @@ Disable advanced gcc directives for optimization.
 ### `--trampoline-call`
 Use an indirection when calling.
 
-Just add the trampolines of `--substitute-monomorph` without doing any additionnal optimizations.
+Just add the trampolines of `--substitute-monomorph` without doing any additional optimizations.
+
+
+### DEBUGGING
+
+### `--no-stacktrace`
+Disable the generation of stack traces.
+
+With this option, the compiled program will not display stack traces on runtime errors.
+
+Because stack traces rely on libunwind, this option might be useful in order to generate more portable binaries
+since libunwind might be non available on the runtime system (or available with an ABI incompatible version).
+
+The generated C is API-portable and can be reused, distributed and compiled on any supported system.
+If the option `--no-stacktrace` is not used but the development files of the library `libunwind` are not available, then a warning will be displayed
+and stack trace will be disabled.
+
+Note that the `--no-stacktrace` option (or this absence) can be toggled manually in the generated Makefile (search `NO_STACKTRACE` in the Makefile).
+Moreover, the environment variable `NIT_NO_STACK` (see bellow) can also be used at runtime to disable stack traces.
+
+### `--trace-memory`
+Enable dynamic measure of memory usage.
+
+Compiled programs will generate a large `memory.log` file that logs all memory allocations.
+This logs file can then be analyzed with the tool `memplot` from contrib.
+
+### `--hardening`
+Generate contracts in the C code against bugs in the compiler.
 
-### `--no-tag-primitives`
-Use only boxes for primitive types.
 
 ## INTERNAL OPTIONS
 
@@ -433,21 +455,6 @@ Do not check, and produce errors, on visibility issues.
 ### `--no-main`
 Do not generate main entry point.
 
-### `--no-stacktrace`
-Disable the generation of stack traces.
-
-With this option, the compiled program will not display stack traces on runtime errors.
-
-Because stack traces rely on libunwind, this option might be useful in order to generate more portable binaries
-since libunwind might be non available on the runtime system (or available with an ABI incompatible version).
-
-The generated C is API-portable and can be reused, distributed and compiled on any supported system.
-If the option `--no-stacktrace` is not used but the development files of the library `libunwind` are not available, then a warning will be displayed
-and stack trace will be disabled.
-
-Note that the `--no-stacktrace` option (or this absence) can be toggled manually in the generated Makefile (search `NO_STACKTRACE` in the Makefile).
-Moreover, the environment variable `NIT_NO_STACK` (see bellow) can also be used at runtime to disable stack traces.
-
 ### `--max-c-lines`
 Maximum number of lines in generated C files. Use 0 for unlimited.
 
index cd5472a..0b42d2f 100644 (file)
@@ -66,8 +66,8 @@ Also generate private API.
 
 ## CUSTOMIZATION
 
-### `--sharedir`
-Directory containing nitdoc assets.
+### `--share-dir`
+Directory containing tools assets.
 
 By default `$NIT_DIR/share/nitdoc/` is used.
 
index e01dbd2..73c5b3e 100644 (file)
@@ -118,20 +118,36 @@ Finally, standard markdown documents can be checked with:
 
     $ nitunit foo.md
 
+When testing, the environment variable `NIT_TESTING` is set to `true`.
+This flag can be used by libraries and program to prevent (or limit) the execution of dangerous pieces of code.
+
+~~~~~
+# NIT_TESTING is automatically set.
+#
+#     assert "NIT_TESTING".environ == "true"
+~~~~
+
 ## Working with `TestSuites`
 
-TestSuites are Nit files that define a set of TestCases for a particular module.
+TestSuites are Nit modules that define a set of TestCases.
 
-The test suite must be called `test_` followed by the name of the module to test.
-So for the module `foo.nit` the test suite will be called `test_foo.nit`.
+A test suite is a module that uses the annotation `is test_suite`.
+
+It is common that a test suite focuses on testing a single module.
+In this case, the name of the test_suite is often `test_foo.nit` where `foo.nit` is the tested module.
 
 The structure of a test suite is the following:
 
 ~~~~
 # test suite for module `foo`
-module test_foo
+module test_foo is test_suite
+
+import test_suite
 import foo # can be intrude to test private things
+
 class TestFoo
+       super TestSuite
+
     # test case for `foo::Foo::baz`
     fun test_baz do
         var subject = new Foo
@@ -144,11 +160,13 @@ Test suite can be executed using the same `nitunit` command:
 
     $ nitunit foo.nit
 
-`nitunit` will execute a test for each method named `test_*` in a class named `Test*`
-so multiple tests can be executed for a single method:
+`nitunit` will execute a test for each method named `test_*` in a class
+subclassing `TestSuite` so multiple tests can be executed for a single method:
 
 ~~~~
 class TestFoo
+       super TestSuite
+
     fun test_baz_1 do
         var subject = new Foo
         assert subject.baz(1, 2) == 3
@@ -160,13 +178,64 @@ class TestFoo
 end
 ~~~~
 
-`TestSuites` also provide methods to configure the test run:
+## Black Box Testing
+
+Sometimes, it is easier to validate a `TestCase` by comparing its output with a text file containing the expected result.
+
+For each TestCase `test_bar` of a TestSuite `test_mod.nit`, a corresponding file with the expected output is looked for:
+
+* "test_mod.sav/test_bar.res". I.e. test-cases grouped by test-suites.
+
+       This is the default and is useful if there is a lot of test-suites and test-cases in a directory
+
+* "sav/test_bar.res". I.e. all test-cases grouped in a common sub-directory.
+
+       Useful if there is a lot of test-suites OR test-cases in a directory.
+
+* "test_bar.res" raw in the directory.
+
+       Useful is there is a few test-suites and test-cases in a directory.
+
+All 3 are exclusive. If more than one exists, the test-case is failed.
+
+If a corresponding file then the output of the test-case is compared with the file.
+
+The `diff(1)` command is used to perform the comparison.
+The test is failed if non-zero is returned by `diff`.
+
+~~~
+module test_mod is test_suite
+
+class TestFoo
+       super TestSuite
+
+       fun test_bar do
+               print "Hello!"
+       end
+end
+~~~
+
+Where `test_mod.sav/test_bar.res` contains
+
+~~~raw
+Hello!
+~~~
+
+If no corresponding `.res` file exists, then the output of the TestCase is ignored.
+
+To helps the management of the expected results, the option `--autosav` can be used to automatically create and update them.
+
+
+## Configuring TestSuites
+
+`TestSuite`s also provide methods to configure the test run:
 
 `before_test` and `after_test`: methods called before/after each test case.
 They can be used to factorize repetitive tasks:
 
 ~~~~
 class TestFoo
+       super TestSuite
     var subject: Foo
     # Mandatory empty init
     init do end
@@ -228,13 +297,22 @@ With the `--full` option, all imported modules (even those in standard) are also
 ### `-o`, `--output`
 Output name (default is 'nitunit.xml').
 
-### `nitunit` produces a XML file comatible with JUnit.
+`nitunit` produces a XML file compatible with JUnit.
 
 ### `--dir`
-Working directory (default is '.nitunit').
+Working directory (default is 'nitunit.out').
 
 In order to execute the tests, nit files are generated then compiled and executed in the giver working directory.
 
+In case of success, the directory is removed.
+In case of failure, it is kept as is so files can be investigated.
+
+### `--nitc`
+nitc compiler to use.
+
+By default, nitunit tries to locate the `nitc` program with the environment variable `NITC` or heuristics.
+The option is used to indicate a specific nitc binary.
+
 ### `--no-act`
 Does not compile and run tests.
 
@@ -243,8 +321,22 @@ Only run test case with name that match pattern.
 
 Examples: `TestFoo`, `TestFoo*`, `TestFoo::test_foo`, `TestFoo::test_foo*`, `test_foo`, `test_foo*`
 
-### `-t`, `--target-file`
-Specify test suite location.
+### `--autosav`
+Automatically create/update .res files for black box testing.
+
+If a black block test fails because a difference between the expected result and the current result then the expected result file is updated (and the test is passed).
+
+If a test-case of a test-suite passes but that some output is generated, then an expected result file is created.
+
+It is expected that the created/updated files are checked since the tests are considered passed.
+A VCS like `git` is often a good tool to check the creation and modification of those files.
+
+### `--no-time`
+Disable time information in XML.
+
+This is used to have reproducible XML results.
+
+This option is automatically activated if `NIT_TESTING` is set.
 
 ## SUITE GENERATION
 
@@ -262,6 +354,36 @@ Also generate test case for private methods.
 ### `--only-show`
 Only display the skeleton, do not write any file.
 
+
+# ENVIRONMENT VARIABLES
+
+### `NITC`
+
+Indicate the specific Nit compiler executable to use. See `--nitc`.
+
+### `NIT_TESTING`
+
+The environment variable `NIT_TESTING` is set to `true` during the execution of program tests.
+Some libraries of programs can use it to produce specific reproducible results; or just to exit their executions.
+
+Unit-tests may unset this environment variable to retrieve the original behavior of such piece of software.
+
+### `SRAND`
+
+In order to maximize reproducibility, `SRAND` is set to 0.
+This make the pseudo-random generator no random at all.
+See `Sys::srand` for details.
+
+To retrieve the randomness, unit-tests may unset this environment variable then call `srand`.
+
+### `NIT_TESTING_ID`
+
+Parallel executions can cause some race collisions on named resources (e.g. DB table names).
+To solve this issue, `NIT_TESTING_ID` is initialized with a distinct integer identifier that can be used to give unique names to resources.
+
+Note: `rand` is not a recommended way to get a distinct identifier because its randomness is disabled by default. See `SRAND`.
+
+
 # SEE ALSO
 
 The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/nitweb/directives/contributor-list.html b/share/nitweb/directives/contributor-list.html
new file mode 100644 (file)
index 0000000..a46cce9
--- /dev/null
@@ -0,0 +1,11 @@
+<div ng-if='listContributors.length > 0'>
+       <h3 id={{listId}}>
+               <span>{{listTitle}}</span>
+       </h3>
+       <ul class='list-unstyled user-list'>
+               <li ng-repeat='contributor in listContributors'>
+                       <img class='avatar' src="https://secure.gravatar.com/avatar/{{contributor.hash}}?size=20&amp;default=retro">
+                       {{contributor.name}}
+               </li>
+       </ul>
+</div>
diff --git a/share/nitweb/directives/entity/card.html b/share/nitweb/directives/entity/card.html
new file mode 100644 (file)
index 0000000..aa9ccb3
--- /dev/null
@@ -0,0 +1,11 @@
+<div class='card'>
+       <div class='card-left text-center'>
+               <entity-tag mentity='mentity' />
+       </div>
+       <div class='card-body'>
+               <h5 class='card-heading'>
+                       <entity-signature mentity='mentity'/>
+               </h5>
+               <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/defcard.html b/share/nitweb/directives/entity/defcard.html
new file mode 100644 (file)
index 0000000..5a024fe
--- /dev/null
@@ -0,0 +1,37 @@
+<div class='card card-xl' ng-class='{active: focus.full_name == definition.full_name}'>
+       <div class='card-body'>
+               <h5 class='text-muted'>
+                       <span ng-if='definition.is_intro'>
+                               <span class='glyphicon glyphicon-plus' /> Introduction</span>
+                       </span>
+                       <span ng-if='!definition.is_intro'>
+                               <span class='glyphicon glyphicon-asterisk' /> Redefinition</span>
+                       </span>
+                       <span ng-if='definition.mclass'>
+                               of <entity-link mentity='definition.mclass' />
+                       </span>
+                       <span ng-if='definition.mproperty'>
+                               of <entity-link mentity='definition.mproperty' />
+                       </span>
+                       <span ng-if='definition.mclassdef'>
+                               in <entity-link mentity='definition.mmodule' />
+                               :: <entity-link mentity='definition.mclassdef' />
+                       </span>
+                       <span ng-if='!definition.mclassdef'>
+                               in <entity-link mentity='definition.mmodule' />
+                       </span>
+                       <div class='btn-bar'>
+                               <button class='btn btn-link' aria-expanded='false'
+                                       data-target='#{{codeId}}' ng-click='loadCardCode()'
+                                       aria-controls='{{codeId}}'>
+                                       <span class='glyphicon glyphicon-console'
+                                       title='Show code' />
+                               </button>
+                       </div>
+               </h5>
+               <div id='{{codeId}}' class='collapse'>
+                       <pre ng-bind-html='code' />
+               </div>
+               <entity-location mentity='definition' />
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/doc.html b/share/nitweb/directives/entity/doc.html
new file mode 100644 (file)
index 0000000..9d0ffb0
--- /dev/null
@@ -0,0 +1,5 @@
+<div class='card' ng-if='mentity.mdoc'>
+       <div class='card-body'>
+               <div ng-bind-html='mentity.mdoc.html_documentation'></div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/linearization.html b/share/nitweb/directives/entity/linearization.html
new file mode 100644 (file)
index 0000000..afdde43
--- /dev/null
@@ -0,0 +1,11 @@
+<div class='entity-list' ng-if='listEntities.length'>
+       <h3>{{listTitle}}</h3>
+       <div class='card-list'>
+               <div ng-repeat='def in listEntities'>
+                       <entity-def definition='def' focus='listFocus' />
+                       <h4 ng-if='!$last' class='text-muted text-center'>
+                               <span class='glyphicon glyphicon-chevron-up'></span>
+                       </h4>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/link.html b/share/nitweb/directives/entity/link.html
new file mode 100644 (file)
index 0000000..740ea8b
--- /dev/null
@@ -0,0 +1,3 @@
+<span>
+       <a ng-href='{{mentity.web_url}}'>{{mentity.name}}</a>
+</span>
diff --git a/share/nitweb/directives/entity/list.html b/share/nitweb/directives/entity/list.html
new file mode 100644 (file)
index 0000000..cc026a4
--- /dev/null
@@ -0,0 +1,19 @@
+<div class='entity-list'
+       ng-if='(listEntities | filter:listObjectFilter).length > 0'>
+       <h3 id={{listId}}>
+               <span>{{listTitle}}</span>
+               <button class='btn btn-link btn-xs pull-right btn-filter' ng-click='toggleFilters()'>
+                       <span class='glyphicon glyphicon-filter text-muted' />
+               </button>
+       </h3>
+               <div ng-if='showFilters'>
+                       <ui-filter-form
+                               search-filter='listObjectFilter'
+                               visibility-filter='visibilityFilter'>
+               </div>
+               <div class='card-list'>
+                       <entity-card mentity='mentity'
+                               ng-repeat='mentity in listEntities | filter:listObjectFilter | visibility:visibilityFilter' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/location.html b/share/nitweb/directives/entity/location.html
new file mode 100644 (file)
index 0000000..7e817c6
--- /dev/null
@@ -0,0 +1,5 @@
+<span ng-if='mentity.location'>
+       <a ng-href="{{mentity.web_url}}">{{mentity.location.file}}
+               <span ng-if='mentity.location.line_start'>:{{mentity.location.line_start}}</span>
+       </a>
+</span>
diff --git a/share/nitweb/directives/entity/namespace.html b/share/nitweb/directives/entity/namespace.html
new file mode 100644 (file)
index 0000000..6e39abf
--- /dev/null
@@ -0,0 +1,13 @@
+<span ng-if='mentity.mpackage'>
+       <entity-link mentity='mentity.mpackage' /> ::
+</span>
+<span ng-if='mentity.mmodule'>
+       <entity-link mentity='mentity.mmodule' /> ::
+</span>
+<span ng-if='mentity.intro_mclassdef'>
+       <entity-link mentity='mentity.intro_mclassdef' /> ::
+</span>
+<span ng-if='mentity.mclassdef'>
+       <entity-link mentity='mentity.mclassdef' /> ::
+</span>
+{{mentity.name}}
diff --git a/share/nitweb/directives/entity/signature.html b/share/nitweb/directives/entity/signature.html
new file mode 100644 (file)
index 0000000..f3f2dc6
--- /dev/null
@@ -0,0 +1,51 @@
+<span class='signature'>
+       <span ng-repeat='modifier in mentity.modifiers'>
+               <span ng-if='modifier != "public"' class='modifier'>{{modifier}}</span>
+       </span>
+       <span class='name'>
+               <entity-link mentity='mentity' />
+       </span>
+       <span ng-if='mentity.mparameters'>
+               <span ng-if='mentity.mparameters.length > 0'>
+                       <span>[</span>
+                       <span ng-repeat='mparam in mentity.mparameters'>
+                               <span>
+                                       <span>{{mparam.name}}</span>
+                                       <span>: </span>
+                                       <entity-signature mentity='mparam.mtype' />
+                               </span>
+                               <span ng-if='$middle'>, </span>
+                       </span>
+                       <span>]</span>
+               </span>
+       </span>
+       <span ng-if='mentity.msignature'>
+               <span ng-if='mentity.msignature.arity > 0'>
+                       <span>(</span>
+                       <span ng-repeat='mparam in mentity.msignature.mparams'>
+                               <span>
+                                       <span>{{mparam.name}}</span>
+                                       <span ng-if='mentity.is_intro !== false'>
+                                               <span>: </span>
+                                               <entity-signature mentity='mparam.mtype' />
+                                       </span>
+                                       <span ng-if='mparam.is_vararg'>...</span>
+                               </span>
+                               <span ng-if='!first && !$last'>, </span>
+                       </span>
+                       <span>)</span>
+               </span>
+               <span ng-if='mentity.is_intro !== false && mentity.msignature.return_mtype'>
+                       <span>: </span>
+                       <entity-signature mentity='mentity.msignature.return_mtype' />
+               </span>
+       </span>
+       <span ng-if='mentity.is_intro !== false && mentity.static_mtype'>
+               <span>: </span>
+               <entity-signature mentity='mentity.static_mtype' />
+       </span>
+       <span ng-if='mentity.bound'>
+               <span>: </span>
+               <entity-signature mentity='mentity.bound' />
+       </span>
+</span>
diff --git a/share/nitweb/directives/entity/tag.html b/share/nitweb/directives/entity/tag.html
new file mode 100644 (file)
index 0000000..7c69f1e
--- /dev/null
@@ -0,0 +1,5 @@
+<span class="glyphicon glyphicon-tag" ng-class='{
+               "text-success": mentity.visibility == "public",
+               "text-warning": mentity.visibility == "protected",
+               "text-danger": mentity.visibility == "private",
+}' />
diff --git a/share/nitweb/directives/group-block.html b/share/nitweb/directives/group-block.html
new file mode 100644 (file)
index 0000000..ad7f00e
--- /dev/null
@@ -0,0 +1,21 @@
+<div class='media'>
+       <div class='media-left text-center' ng-if='mentity.visibility' ng-class='{
+                       "text-success": mentity.visibility == "public",
+                       "text-warning": mentity.visibility == "protected",
+                       "text-danger": mentity.visibility == "private",
+       }'>
+               <span class="glyphicon glyphicon-tag"></span>
+       </div>
+       <div class='media-body'>
+               <h5 class='media-heading'>
+                       <entity-signature mentity='mentity'/>
+               </h5>
+               <span class='synopsis'>{{mentity.mdoc.synopsis}}</span>
+               <div ng-if='recursive && mentity.mgroups'>
+                       <group-block mentity-id='mgroup' ng-repeat='mgroup in mentity.mgroups' />
+               </div>
+               <div ng-if='recursive && mentity.mmodules'>
+                       <module-block mentity-id='mmodule' ng-repeat='mmodule in mentity.mmodules' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/ui-filter-button-vis.html b/share/nitweb/directives/ui-filter-button-vis.html
new file mode 100644 (file)
index 0000000..8d4f8d4
--- /dev/null
@@ -0,0 +1,6 @@
+<button
+       class='btn btn-link btn-xs'
+       ng-click='toggle()'>
+       <span ng-if='property' ng-class='classesOn'/>
+       <span ng-if='!property' ng-class='classesOff'/>
+</button>
diff --git a/share/nitweb/directives/ui-filter-field.html b/share/nitweb/directives/ui-filter-field.html
new file mode 100644 (file)
index 0000000..05c51e4
--- /dev/null
@@ -0,0 +1,4 @@
+<div class='form-group has-icon'>
+       <input type='text' class='form-control' ng-model='property' placeholder='Filter...'>
+       <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
+</div>
diff --git a/share/nitweb/directives/ui-filter-form.html b/share/nitweb/directives/ui-filter-form.html
new file mode 100644 (file)
index 0000000..9782af8
--- /dev/null
@@ -0,0 +1,6 @@
+<form class='form-inline'>
+       <ui-filter-field property='searchFilter.$' />
+       <div class='pull-right'>
+               <ui-filter-group-vis property='visibilityFilter' />
+       </div>
+</form>
diff --git a/share/nitweb/directives/ui-filter-group-vis.html b/share/nitweb/directives/ui-filter-group-vis.html
new file mode 100644 (file)
index 0000000..7fcd475
--- /dev/null
@@ -0,0 +1,14 @@
+<div class='form-group'>
+       <ui-filter-button-vis property='property.public'
+                       classes-on='"glyphicon glyphicon-eye-open text-success"'
+                       classes-off='"glyphicon glyphicon-eye-close text-success"'
+                       title='Toggle public' />
+       <ui-filter-button-vis property='property.protected'
+                       classes-on='"glyphicon glyphicon-eye-open text-warning"'
+                       classes-off='"glyphicon glyphicon-eye-close text-warning"'
+                       title='Toggle protected' />
+       <ui-filter-button-vis property='property.private'
+                       classes-on='"glyphicon glyphicon-eye-open text-danger"'
+                       classes-off='"glyphicon glyphicon-eye-close text-danger"'
+                       title='Toggle private' />
+</div>
diff --git a/share/nitweb/index.html b/share/nitweb/index.html
new file mode 100644 (file)
index 0000000..c615d41
--- /dev/null
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html lang='en' ng-app='nitweb'>
+       <head>
+               <base href='/'>
+               <meta charset='utf-8'>
+               <meta http-equiv='X-UA-Compatible' content='IE=edge'>
+               <meta name='viewport' content='width=device-width, initial-scale=1'>
+               <title>ng-doc</title>
+
+               <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'
+                       integrity='sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7'
+                       crossorigin='anonymous' rel='stylesheet'>
+
+               <link href='//cdnjs.cloudflare.com/ajax/libs/angular-loading-bar/0.9.0/loading-bar.min.css'
+                       type='text/css' rel='stylesheet' media='all'>
+
+
+               <link href='/stylesheets/nitweb_bootstrap.css' rel='stylesheet'>
+               <link href='/stylesheets/nitweb.css' rel='stylesheet'>
+       </head>
+       <body>
+               <nav class='navbar navbar-default navbar-fixed-top'>
+                       <div class='container-fluid'>
+                               <div class='col-xs-3'>
+                                       <div class='navbar-header'>
+                                               <a class='navbar-brand' ng-href='/'>Nitdoc</a>
+                                       </div>
+                                       <ul class="nav navbar-nav">
+                                               <li><a href="/docdown?edit=true">DocDown</a></li>
+                                       </ul>
+                               </div>
+                               <div class='col-xs-7'>
+                                       <form ng-controller='SearchCtrl as searchCtrl' >
+                                               <div class='form-group has-icon'>
+                                                       <input placeholder='Search...' type='text' class='form-control search-input'
+                                                               ng-model-options='{ debounce: 150 }' ng-model='query'
+                                                               ng-keydown='update($event)' ng-change='search()'>
+                                                       <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
+                                               </div>
+                                               <div ng-if='results.length > 0' class='search-results'>
+                                                       <div class='card-list'>
+                                                               <entity-card ng-click='reset()' ng-class='{active: activeItem == $index}' mentity='mentity' ng-repeat='mentity in results' />
+                                                       </div>
+                                               </div>
+                                       </form>
+                               </div>
+                       </div>
+               </nav>
+               <div ng-view></div>
+
+               <script src='https://code.jquery.com/jquery-1.12.4.min.js'
+                       integrity='sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ='
+                       crossorigin='anonymous''></script>
+               <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'
+                       integrity='sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS'
+                       crossorigin='anonymous'></script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'>
+               </script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'>
+               </script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-sanitize.js'>
+               </script>
+               <script type='text/javascript'
+                       src='//cdnjs.cloudflare.com/ajax/libs/angular-loading-bar/0.9.0/loading-bar.min.js'>
+               </script>
+
+               <script src='/javascripts/nitweb.js'></script>
+               <script src='/javascripts/model.js'></script>
+               <script src='/javascripts/entities.js'></script>
+               <script src='/javascripts/ui.js'></script>
+               <script src='/javascripts/index.js'></script>
+               <script src='/javascripts/docdown.js'></script>
+       </body>
+</html>
diff --git a/share/nitweb/javascripts/docdown.js b/share/nitweb/javascripts/docdown.js
new file mode 100644 (file)
index 0000000..365b8c9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular
+               .module('docdown', ['model', 'ngSanitize'])
+
+               .controller('DocdownCtrl', ['$routeParams', '$sce', '$scope', '$location', 'DocDown', function($routeParams, $sce, $scope, $location, DocDown) {
+
+                       this.updateSnippet = function() {
+                               this.updateLink();
+                               this.updateHtml();
+                       }
+
+                       this.updateLink = function() {
+                               $scope.link = $location.protocol()+ '://' + $location.host() + ':' +
+                                       $location.port() + $location.path() + '?snippet=' +
+                                       encodeURIComponent(btoa($scope.markdown));
+                       }
+
+                       this.updateHtml = function() {
+                               DocDown.postMarkdown($scope.markdown,
+                                       function(data) {
+                                               $scope.html = $sce.trustAsHtml(data);
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.editMode = function(isEdit) {
+                               $scope.edit = isEdit;
+                       }
+
+                       $scope.markdown = 'Type some markdown...';
+                       if($location.search().snippet) {
+                               $scope.markdown = atob($location.search().snippet);
+                       }
+                       $scope.edit = false;
+                       if($location.search().edit) {
+                               $scope.edit = Boolean($location.search().edit);
+                       }
+
+                       this.updateSnippet();
+               }])
+})();
diff --git a/share/nitweb/javascripts/entities.js b/share/nitweb/javascripts/entities.js
new file mode 100644 (file)
index 0000000..9333050
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular
+               .module('entities', ['ngSanitize', 'ui', 'model'])
+
+               .controller('EntityCtrl', ['Model', '$routeParams', '$scope', '$sce', function(Model, $routeParams, $scope, $sce) {
+                       $scope.entityId = $routeParams.id;
+
+                       this.loadEntityLinearization = function() {
+                               Model.loadEntityLinearization($routeParams.id,
+                                       function(data) {
+                                               $scope.linearization = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.loadEntityDefs = function() {
+                               Model.loadEntityDefs($routeParams.id,
+                                       function(data) {
+                                               $scope.defs = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.loadEntityCode = function() {
+                               Model.loadEntityCode($routeParams.id,
+                                       function(data) {
+                                               $scope.code = data;
+                                       }, function(err) {
+                                               $scope.code = err;
+                                       });
+                       };
+
+                       this.loadEntityGraph = function(e) {
+                               Model.loadEntityGraph($routeParams.id,
+                                       function(data) {
+                                               $scope.graph = $sce.trustAsHtml(data);
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       Model.loadEntity($routeParams.id,
+                               function(data) {
+                                       $scope.mentity = data;
+                               }, function(message, status) {
+                                       $scope.error = {message: message, status: status};
+                               });
+               }])
+
+               .directive('entityLink', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/link.html'
+                       };
+               })
+
+               .directive('entityDoc', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/doc.html'
+                       };
+               })
+
+               .directive('entitySignature', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/signature.html'
+                       };
+               })
+
+               .directive('entityNamespace', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/namespace.html'
+                       };
+               })
+
+               .directive('entityTag', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/entity/tag.html'
+                       };
+               })
+
+               .directive('entityLocation', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/location.html'
+                       };
+               })
+
+               .directive('entityCard', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/entity/card.html'
+                       };
+               })
+
+               .directive('entityList', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       listEntities: '=',
+                                       listId: '@',
+                                       listTitle: '@',
+                                       listObjectFilter: '=',
+                               },
+                               templateUrl: '/directives/entity/list.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.showFilters = false;
+                                       if(!$scope.listObjectFilter) {
+                                               $scope.listObjectFilter = {};
+                                       }
+                                       if(!$scope.visibilityFilter) {
+                                               $scope.visibilityFilter = {
+                                                       public: true,
+                                                       protected: true,
+                                                       private: false
+                                               };
+                                       }
+                                       $scope.toggleFilters = function() {
+                                               $scope.showFilters = !$scope.showFilters;
+                                       };
+                               }
+                       };
+               })
+
+               .directive('entityLinearization', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       listEntities: '=',
+                                       listTitle: '@',
+                                       listFocus: '='
+                               },
+                               templateUrl: '/directives/entity/linearization.html'
+                       };
+               })
+
+               .directive('entityDef', ['Model', function(Model, Code) {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       definition: '=',
+                                       focus: '='
+                               },
+                               templateUrl: '/directives/entity/defcard.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.codeId = 'code_' + $scope.definition.full_name.replace(/[^a-zA-Z0-9]/g, '_');
+                                       $scope.loadCardCode = function() {
+                                               if(!$scope.code) {
+                                                       Model.loadEntityCode($scope.definition.full_name,
+                                                               function(data) {
+                                                                       $scope.code = data;
+                                                                       setTimeout(function() { // smooth collapse
+                                                                               $('#' + $scope.codeId).collapse('show')
+                                                                       }, 1);
+                                                               }, function(err) {
+                                                                       $scope.code = err;
+                                                               });
+                                               } else {
+                                                       if($('#' + $scope.codeId).hasClass('in')) {
+                                                               $('#' + $scope.codeId).collapse('hide');
+                                                       } else {
+                                                               $('#' + $scope.codeId).collapse('show');
+                                                       }
+                                               }
+                                       };
+                               }
+                       };
+               }])
+})();
diff --git a/share/nitweb/javascripts/index.js b/share/nitweb/javascripts/index.js
new file mode 100644 (file)
index 0000000..534f462
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular
+               .module('index', ['model', 'ngSanitize'])
+
+               .run(['$anchorScroll', function($anchorScroll) {
+                       $anchorScroll.yOffset = 80;
+               }])
+
+               .controller('IndexCtrl', ['Catalog', '$routeParams', '$sce', '$scope', '$location', '$anchorScroll', function(Catalog, $routeParams, $sce, $scope, $location, $anchorScroll) {
+                       this.loadHighlighted = function() {
+                               Catalog.loadHightlighted(
+                                       function(data) {
+                                               $scope.highlighted = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.loadMostRequired = function() {
+                               Catalog.loadMostRequired(
+                                       function(data) {
+                                               $scope.required = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.loadByTags = function() {
+                               Catalog.loadByTags(
+                                       function(data) {
+                                               $scope.bytags = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.loadStats = function() {
+                               Catalog.loadStats(
+                                       function(data) {
+                                               $scope.stats = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+                       this.loadContributors = function() {
+                               Catalog.loadContributors(
+                                       function(data) {
+                                               $scope.contributors = data;
+                                       }, function(err) {
+                                               $scope.error = err;
+                                       });
+                       };
+
+
+                       this.scrollTo = function(hash) {
+                               $anchorScroll(hash);
+                       }
+
+                       this.loadHighlighted();
+                       this.loadStats();
+                       this.loadContributors();
+               }])
+
+               .directive('contributorList', ['Model', function(Model) {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       listId: '@',
+                                       listTitle: '@',
+                                       listContributors: '='
+                               },
+                               templateUrl: '/directives/contributor-list.html'
+                       };
+               }])
+})();
diff --git a/share/nitweb/javascripts/model.js b/share/nitweb/javascripts/model.js
new file mode 100644 (file)
index 0000000..3accab2
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       var apiUrl = '/api';
+
+       angular
+               .module('model', [])
+
+               .factory('Model', [ '$http', function($http) {
+                       return {
+
+                               loadEntity: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/entity/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadEntityLinearization: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/linearization/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadEntityDefs: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/defs/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadEntityCode: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/code/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadEntityGraph: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/graph/inheritance/' + id + '?cdepth=3')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               search: function(q, n, cb, cbErr) {
+                                       $http.get(apiUrl + '/search?q=' + q + '&n=' + n)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+                       };
+               }])
+
+               .factory('Catalog', [ '$http', function($http) {
+                       return {
+                               loadHightlighted: function(cb, cbErr) {
+                                       $http.get(apiUrl + '/catalog/highlighted')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadMostRequired: function(cb, cbErr) {
+                                       $http.get(apiUrl + '/catalog/required')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadByTags: function(cb, cbErr) {
+                                       $http.get(apiUrl + '/catalog/bytags')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadStats: function(cb, cbErr) {
+                                       $http.get(apiUrl + '/catalog/stats')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+
+                               loadContributors: function(cb, cbErr) {
+                                       $http.get(apiUrl + '/catalog/contributors')
+                                               .success(cb)
+                                               .error(cbErr);
+                               },
+                       }
+               }])
+
+               .factory('DocDown', [ '$http', function($http) {
+                       return {
+                               postMarkdown: function(md, cb, cbErr) {
+                                       $http.post(apiUrl + '/docdown', md)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+                       }
+               }])
+})();
diff --git a/share/nitweb/javascripts/nitweb.js b/share/nitweb/javascripts/nitweb.js
new file mode 100644 (file)
index 0000000..7fa1142
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular.module('nitweb', ['ngRoute', 'ngSanitize', 'angular-loading-bar', 'entities', 'docdown', 'index'])
+       .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
+               cfpLoadingBarProvider.includeSpinner = false;
+       }])
+       .config(function($routeProvider, $locationProvider) {
+               $routeProvider
+                       .when('/', {
+                               templateUrl: 'views/index.html',
+                               controller: 'IndexCtrl',
+                               controllerAs: 'indexCtrl'
+                       })
+                       .when('/docdown', {
+                               templateUrl: 'views/docdown.html',
+                               controller: 'DocdownCtrl',
+                               controllerAs: 'docdownCtrl'
+                       })
+                       .when('/doc/:id', {
+                               templateUrl: 'views/doc.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .otherwise({
+                               redirectTo: '/'
+                       });
+               $locationProvider.html5Mode(true);
+       });
+})();
diff --git a/share/nitweb/javascripts/ui.js b/share/nitweb/javascripts/ui.js
new file mode 100644 (file)
index 0000000..cd30d06
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular
+               .module('ui', [ 'model' ])
+
+               .controller('SearchCtrl', ['Model', '$routeParams', '$scope', '$window', function(Model, $routeParams, $scope, $window) {
+                       $scope.query = '';
+
+                       $scope.reset = function() {
+                               $scope.activeItem = 0;
+                               $scope.results = [];
+                       }
+
+                       $scope.update = function(e) {
+                               if(e.keyCode == 38) {
+                                       $scope.selectUp();
+                               } else if(e.keyCode == 40) {
+                                       $scope.selectDown();
+                               } else if(e.keyCode == 27) {
+                                       $scope.selectEscape();
+                               } else if(e.keyCode == 13) {
+                                       $scope.selectEnter();
+                               }
+                       }
+
+                       $scope.selectUp = function() {
+                               if($scope.activeItem > 0) {
+                                       $scope.activeItem -= 1;
+                               }
+                       }
+
+                       $scope.selectDown = function() {
+                               if($scope.activeItem < $scope.results.length - 1) {
+                                       $scope.activeItem += 1;
+                               }
+                       }
+
+                       $scope.selectEnter = function() {
+                               $window.location.href = $scope.results[$scope.activeItem].web_url;
+                               $scope.reset();
+                       }
+
+                       $scope.selectEscape = function() {
+                               $scope.reset();
+                       }
+
+                       $scope.search = function() {
+                               if(!$scope.query) {
+                                       $scope.reset();
+                                       return;
+                               }
+                               Model.search($scope.query, 10,
+                                       function(data) {
+                                               $scope.reset();
+                                               $scope.results = data;
+                                       }, function(err) {
+                                               $scope.reset();
+                                               $scope.error = err;
+                                       });
+                       }
+
+                       $scope.reset();
+               }])
+
+               .directive('uiFilters', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '=',
+                                       classesOn: '=',
+                                       classesOff: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-button-vis.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.toggle = function() {
+                                               $scope.property = !$scope.property;
+                                       }
+                               }
+                       };
+               })
+
+               .filter('visibility', function() {
+                       return function(input, visibilityFilter) {
+                               var res = [];
+                               input.forEach(function(entry) {
+                                       if(visibilityFilter.public == false && entry.visibility == "public") {
+                                               return;
+                                       }
+                                       if(visibilityFilter.protected == false && entry.visibility == "protected") {
+                                               return;
+                                       }
+                                       if(visibilityFilter.private == false && entry.visibility == "private") {
+                                               return;
+                                       }
+                                       res.push(entry);
+                               });
+                               return res;
+                       };
+               })
+
+               .directive('uiFilterForm', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       searchFilter: '=',
+                                       visibilityFilter: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-form.html'
+                       };
+               })
+
+               .directive('uiFilterField', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-field.html'
+                       };
+               })
+
+               .directive('uiFilterGroupVis', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-group-vis.html'
+                       };
+               })
+
+               .directive('uiFilterButtonVis', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '=',
+                                       classesOn: '=',
+                                       classesOff: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-button-vis.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.toggle = function() {
+                                               $scope.property = !$scope.property;
+                                       }
+                               }
+                       };
+               })
+})();
diff --git a/share/nitweb/stylesheets/nitweb.css b/share/nitweb/stylesheets/nitweb.css
new file mode 100644 (file)
index 0000000..9471a5d
--- /dev/null
@@ -0,0 +1,256 @@
+/* Body */
+
+body {
+       background: #f2f2f2;
+       margin-top: 70px;
+       margin-bottom: 70px;
+}
+
+h1, h2, h3, h4, h5, h6 {
+       color: #666;
+}
+
+a {
+       cursor: pointer;
+}
+
+.nitdoc h1, .nitdoc h2, .nitdoc h3, .nitdoc h4, .nitdoc h5, .nitdoc h6 {
+       color: #333;
+}
+
+.page-header {
+    margin-top: 0;
+    border: none;
+}
+
+/* cards */
+
+.card.active {
+       border: 1px solid #1E9431;
+}
+
+.card, .card-body { overflow: hidden; }
+
+.card-heading {
+    margin-top: 0;
+    margin-bottom: 5px;
+}
+
+.card {
+       background: #fff;
+       border: 1px solid #ccc;
+       margin-top: 10px;
+       box-shadow: 0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
+}
+
+.card-body {
+    padding: 15px;
+    width: 10000px;
+}
+
+.card-body, .card-right, .card-left {
+    display: table-cell;
+    vertical-align: top;
+}
+
+.card-left, .card>.pull-left {
+    padding: 15px;
+       padding-right: 0px;
+}
+.card-right, .card>.pull-right {
+    padding: 15px;
+       padding-left: 0px;
+}
+
+.card-list {
+       margin-top: 10px;
+}
+
+.card-list > .card:first-child {
+       border-top: 1px solid #ccc;
+}
+
+.card-list > .card {
+       margin-top: 0;
+       border-top: none;
+}
+
+/* ui */
+
+.btn-bar { margin-top: -5px; float: right }
+.btn-bar .btn { padding: 5px 10px; }
+
+entity-list .btn-filter {
+       visibility: hidden;
+}
+
+entity-list:hover .btn-filter {
+       visibility: visible;
+}
+
+/* doc */
+
+.nitdoc .synopsys {
+       font-size: 2em;
+}
+
+.signature {
+       color: #666;
+       font-family: monospace;
+}
+
+.signature .name {
+       font-weight: bold;
+}
+
+.page-header .signature .name, .signature .signature .name {
+       font-weight: normal;
+}
+
+.signature .signature a {
+       color: #666;
+       font-family: monospace;
+}
+
+/* tabs */
+
+.nav-tabs li { cursor: pointer; }
+
+.nav>li.warning>a {
+    color: #fff;
+    background-color: #f0ad4e;
+}
+
+.nav>li.warning>a:focus, .nav>li.warning>a:hover {
+    background-color: #ff9c0f;
+}
+
+/* forms */
+
+.has-icon {
+    position: relative;
+}
+
+.has-icon .form-control {
+       padding-left: 35px;
+}
+
+.form-control-icon {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 2;
+    display: block;
+    width: 34px;
+    height: 34px;
+    line-height: 34px;
+    text-align: center;
+    pointer-events: none;
+}
+
+/* search */
+
+.search-input {
+       width: 100%;
+}
+
+.search-results {
+       position: absolute;
+       right: 0;
+}
+
+.search-results .card.active {
+       background: #eee;
+       border-color: #eee;
+}
+
+/* loading bar */
+
+#loading-bar .bar {
+       background: #FF8100;
+}
+
+/* navs */
+
+.nav-tabs li { cursor: pointer; }
+
+.navbar-fixed-top {
+       background-color: #1E9431;
+       box-shadow: 0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28);
+}
+
+.navbar-fixed-top .form-control:hover, .navbar-fixed-top .form-control:focus {
+       background: rgba(255, 255, 255, 0.2);
+}
+
+.navbar-fixed-top .form-control {
+       background: rgba(255, 255, 255, 0.1);
+    border: none;
+    color: #fff;
+    box-shadow: none;
+}
+
+.navbar-fixed-top .form-control-icon {
+       color: #fff;
+}
+
+.navbar-fixed-top *::-webkit-input-placeholder {
+    color: #fff;
+}
+.navbar-fixed-top *:-moz-placeholder {
+    /* FF 4-18 */
+    color: #fff;
+}
+.navbar-fixed-top *::-moz-placeholder {
+    /* FF 19+ */
+    color: #fff;
+}
+.navbar-fixed-top *:-ms-input-placeholder {
+    /* IE 10+ */
+    color: #fff;
+}
+
+.navbar-fixed-top .form-group {
+       margin-top: 8px;
+       margin-bottom: 0px;
+}
+/*
+ * Users
+ */
+
+.avatar {
+       border-radius: 2px;
+}
+
+/*
+ * Code Highlighting
+ */
+
+.nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
+.nitcode a:hover { text-decoration: underline; } /* underline links */
+.nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
+/* lexical raw tokens. independent of usage or semantic: */
+.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
+.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
+.nitcode .nc_k { font-weight: bold; } /* keyword */
+.nitcode .nc_o {} /* operator */
+.nitcode .nc_i {} /* standard identifier */
+.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
+.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
+.nitcode .nc_l { color: #009999; } /* char and number literal */
+.nitcode .nc_s { color: #8F1546; } /* string literal */
+/* syntactic token usage. added because of their position in the AST */
+.nitcode .nc_ast { color: blue; } /* assert label */
+.nitcode .nc_la { color: blue; } /* break/continue label */
+.nitcode .nc_m { color: #445588; } /* module name */
+/* syntactic groups */
+.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
+.nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
+.nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
+.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
+.nitcode .nc_cdef {} /* A whole class definition */
+.nitcode .nc_pdef {} /* A whole property definition */
+/* semantic token usage */
+.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
+.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
+.nitcode .nc_error { border: 1px red solid;} /* not used */
diff --git a/share/nitweb/stylesheets/nitweb_bootstrap.css b/share/nitweb/stylesheets/nitweb_bootstrap.css
new file mode 100644 (file)
index 0000000..e82732b
--- /dev/null
@@ -0,0 +1,5769 @@
+/*! normalize.css v3.0.0 | MIT License | git.io/normalize */\r
+html {\r
+  font-family: sans-serif;\r
+  -ms-text-size-adjust: 100%;\r
+  -webkit-text-size-adjust: 100%;\r
+}\r
+body {\r
+  margin: 0;\r
+}\r
+article,\r
+aside,\r
+details,\r
+figcaption,\r
+figure,\r
+footer,\r
+header,\r
+hgroup,\r
+main,\r
+nav,\r
+section,\r
+summary {\r
+  display: block;\r
+}\r
+audio,\r
+canvas,\r
+progress,\r
+video {\r
+  display: inline-block;\r
+  vertical-align: baseline;\r
+}\r
+audio:not([controls]) {\r
+  display: none;\r
+  height: 0;\r
+}\r
+[hidden],\r
+template {\r
+  display: none;\r
+}\r
+a {\r
+  background: transparent;\r
+}\r
+a:active,\r
+a:hover {\r
+  outline: 0;\r
+}\r
+abbr[title] {\r
+  border-bottom: 1px dotted;\r
+}\r
+b,\r
+strong {\r
+  font-weight: bold;\r
+}\r
+dfn {\r
+  font-style: italic;\r
+}\r
+h1 {\r
+  font-size: 2em;\r
+  margin: 0.67em 0;\r
+}\r
+mark {\r
+  background: #ff0;\r
+  color: #000;\r
+}\r
+small {\r
+  font-size: 80%;\r
+}\r
+sub,\r
+sup {\r
+  font-size: 75%;\r
+  line-height: 0;\r
+  position: relative;\r
+  vertical-align: baseline;\r
+}\r
+sup {\r
+  top: -0.5em;\r
+}\r
+sub {\r
+  bottom: -0.25em;\r
+}\r
+img {\r
+  border: 0;\r
+}\r
+svg:not(:root) {\r
+  overflow: hidden;\r
+}\r
+figure {\r
+  margin: 1em 40px;\r
+}\r
+hr {\r
+  -moz-box-sizing: content-box;\r
+  box-sizing: content-box;\r
+  height: 0;\r
+}\r
+pre {\r
+  overflow: auto;\r
+}\r
+code,\r
+kbd,\r
+pre,\r
+samp {\r
+  font-family: monospace, monospace;\r
+  font-size: 1em;\r
+}\r
+button,\r
+input,\r
+optgroup,\r
+select,\r
+textarea {\r
+  color: inherit;\r
+  font: inherit;\r
+  margin: 0;\r
+}\r
+button {\r
+  overflow: visible;\r
+}\r
+button,\r
+select {\r
+  text-transform: none;\r
+}\r
+button,\r
+html input[type="button"],\r
+input[type="reset"],\r
+input[type="submit"] {\r
+  -webkit-appearance: button;\r
+  cursor: pointer;\r
+}\r
+button[disabled],\r
+html input[disabled] {\r
+  cursor: default;\r
+}\r
+button::-moz-focus-inner,\r
+input::-moz-focus-inner {\r
+  border: 0;\r
+  padding: 0;\r
+}\r
+input {\r
+  line-height: normal;\r
+}\r
+input[type="checkbox"],\r
+input[type="radio"] {\r
+  box-sizing: border-box;\r
+  padding: 0;\r
+}\r
+input[type="number"]::-webkit-inner-spin-button,\r
+input[type="number"]::-webkit-outer-spin-button {\r
+  height: auto;\r
+}\r
+input[type="search"] {\r
+  -webkit-appearance: textfield;\r
+  -moz-box-sizing: content-box;\r
+  -webkit-box-sizing: content-box;\r
+  box-sizing: content-box;\r
+}\r
+input[type="search"]::-webkit-search-cancel-button,\r
+input[type="search"]::-webkit-search-decoration {\r
+  -webkit-appearance: none;\r
+}\r
+fieldset {\r
+  border: 1px solid #c0c0c0;\r
+  margin: 0 2px;\r
+  padding: 0.35em 0.625em 0.75em;\r
+}\r
+legend {\r
+  border: 0;\r
+  padding: 0;\r
+}\r
+textarea {\r
+  overflow: auto;\r
+}\r
+optgroup {\r
+  font-weight: bold;\r
+}\r
+table {\r
+  border-collapse: collapse;\r
+  border-spacing: 0;\r
+}\r
+td,\r
+th {\r
+  padding: 0;\r
+}\r
+@media print {\r
+  * {\r
+    text-shadow: none !important;\r
+    color: #000 !important;\r
+    background: transparent !important;\r
+    box-shadow: none !important;\r
+  }\r
+  a,\r
+  a:visited {\r
+    text-decoration: underline;\r
+  }\r
+  a[href]:after {\r
+    content: " (" attr(href) ")";\r
+  }\r
+  abbr[title]:after {\r
+    content: " (" attr(title) ")";\r
+  }\r
+  a[href^="javascript:"]:after,\r
+  a[href^="#"]:after {\r
+    content: "";\r
+  }\r
+  pre,\r
+  blockquote {\r
+    border: 1px solid #999;\r
+    page-break-inside: avoid;\r
+  }\r
+  thead {\r
+    display: table-header-group;\r
+  }\r
+  tr,\r
+  img {\r
+    page-break-inside: avoid;\r
+  }\r
+  img {\r
+    max-width: 100% !important;\r
+  }\r
+  p,\r
+  h2,\r
+  h3 {\r
+    orphans: 3;\r
+    widows: 3;\r
+  }\r
+  h2,\r
+  h3 {\r
+    page-break-after: avoid;\r
+  }\r
+  select {\r
+    background: #fff !important;\r
+  }\r
+  .navbar {\r
+    display: none;\r
+  }\r
+  .table td,\r
+  .table th {\r
+    background-color: #fff !important;\r
+  }\r
+  .btn > .caret,\r
+  .dropup > .btn > .caret {\r
+    border-top-color: #000 !important;\r
+  }\r
+  .label {\r
+    border: 1px solid #000;\r
+  }\r
+  .table {\r
+    border-collapse: collapse !important;\r
+  }\r
+  .table-bordered th,\r
+  .table-bordered td {\r
+    border: 1px solid #ddd !important;\r
+  }\r
+}\r
+* {\r
+  -webkit-box-sizing: border-box;\r
+  -moz-box-sizing: border-box;\r
+  box-sizing: border-box;\r
+}\r
+*:before,\r
+*:after {\r
+  -webkit-box-sizing: border-box;\r
+  -moz-box-sizing: border-box;\r
+  box-sizing: border-box;\r
+}\r
+html {\r
+  font-size: 62.5%;\r
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\r
+}\r
+body {\r
+  font-family: sans-serif;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  color: #333333;\r
+  background-color: #f2f2f2;\r
+}\r
+input,\r
+button,\r
+select,\r
+textarea {\r
+  font-family: inherit;\r
+  font-size: inherit;\r
+  line-height: inherit;\r
+}\r
+a {\r
+  color: #0d8921;\r
+  text-decoration: none;\r
+}\r
+a:hover,\r
+a:focus {\r
+  color: #064310;\r
+  text-decoration: underline;\r
+}\r
+a:focus {\r
+  outline: thin dotted;\r
+  outline: 5px auto -webkit-focus-ring-color;\r
+  outline-offset: -2px;\r
+}\r
+figure {\r
+  margin: 0;\r
+}\r
+img {\r
+  vertical-align: middle;\r
+}\r
+.img-responsive,\r
+.thumbnail > img,\r
+.thumbnail a > img,\r
+.carousel-inner > .item > img,\r
+.carousel-inner > .item > a > img {\r
+  display: block;\r
+  max-width: 100%;\r
+  height: auto;\r
+}\r
+.img-rounded {\r
+  border-radius: 0px;\r
+}\r
+.img-thumbnail {\r
+  padding: 4px;\r
+  line-height: 1.428571429;\r
+  background-color: #f2f2f2;\r
+  border: 1px solid #dddddd;\r
+  border-radius: 0px;\r
+  -webkit-transition: all 0.2s ease-in-out;\r
+  transition: all 0.2s ease-in-out;\r
+  display: inline-block;\r
+  max-width: 100%;\r
+  height: auto;\r
+}\r
+.img-circle {\r
+  border-radius: 50%;\r
+}\r
+hr {\r
+  margin-top: 20px;\r
+  margin-bottom: 20px;\r
+  border: 0;\r
+  border-top: 1px solid #eeeeee;\r
+}\r
+.sr-only {\r
+  position: absolute;\r
+  width: 1px;\r
+  height: 1px;\r
+  margin: -1px;\r
+  padding: 0;\r
+  overflow: hidden;\r
+  clip: rect(0, 0, 0, 0);\r
+  border: 0;\r
+}\r
+h1,\r
+h2,\r
+h3,\r
+h4,\r
+h5,\r
+h6,\r
+.h1,\r
+.h2,\r
+.h3,\r
+.h4,\r
+.h5,\r
+.h6 {\r
+  font-family: sans-serif;\r
+  font-weight: 500;\r
+  line-height: 1.1;\r
+  color: inherit;\r
+}\r
+h1 small,\r
+h2 small,\r
+h3 small,\r
+h4 small,\r
+h5 small,\r
+h6 small,\r
+.h1 small,\r
+.h2 small,\r
+.h3 small,\r
+.h4 small,\r
+.h5 small,\r
+.h6 small,\r
+h1 .small,\r
+h2 .small,\r
+h3 .small,\r
+h4 .small,\r
+h5 .small,\r
+h6 .small,\r
+.h1 .small,\r
+.h2 .small,\r
+.h3 .small,\r
+.h4 .small,\r
+.h5 .small,\r
+.h6 .small {\r
+  font-weight: normal;\r
+  line-height: 1;\r
+  color: #999999;\r
+}\r
+h1,\r
+.h1,\r
+h2,\r
+.h2,\r
+h3,\r
+.h3 {\r
+  margin-top: 20px;\r
+  margin-bottom: 10px;\r
+}\r
+h1 small,\r
+.h1 small,\r
+h2 small,\r
+.h2 small,\r
+h3 small,\r
+.h3 small,\r
+h1 .small,\r
+.h1 .small,\r
+h2 .small,\r
+.h2 .small,\r
+h3 .small,\r
+.h3 .small {\r
+  font-size: 65%;\r
+}\r
+h4,\r
+.h4,\r
+h5,\r
+.h5,\r
+h6,\r
+.h6 {\r
+  margin-top: 10px;\r
+  margin-bottom: 10px;\r
+}\r
+h4 small,\r
+.h4 small,\r
+h5 small,\r
+.h5 small,\r
+h6 small,\r
+.h6 small,\r
+h4 .small,\r
+.h4 .small,\r
+h5 .small,\r
+.h5 .small,\r
+h6 .small,\r
+.h6 .small {\r
+  font-size: 75%;\r
+}\r
+h1,\r
+.h1 {\r
+  font-size: 36px;\r
+}\r
+h2,\r
+.h2 {\r
+  font-size: 30px;\r
+}\r
+h3,\r
+.h3 {\r
+  font-size: 23px;\r
+}\r
+h4,\r
+.h4 {\r
+  font-size: 17px;\r
+}\r
+h5,\r
+.h5 {\r
+  font-size: 14px;\r
+}\r
+h6,\r
+.h6 {\r
+  font-size: 11px;\r
+}\r
+p {\r
+  margin: 0 0 10px;\r
+}\r
+.lead {\r
+  margin-bottom: 20px;\r
+  font-size: 16px;\r
+  font-weight: 200;\r
+  line-height: 1.4;\r
+}\r
+@media (min-width: 768px) {\r
+  .lead {\r
+    font-size: 21px;\r
+  }\r
+}\r
+small,\r
+.small {\r
+  font-size: 85%;\r
+}\r
+cite {\r
+  font-style: normal;\r
+}\r
+.text-left {\r
+  text-align: left;\r
+}\r
+.text-right {\r
+  text-align: right;\r
+}\r
+.text-center {\r
+  text-align: center;\r
+}\r
+.text-justify {\r
+  text-align: justify;\r
+}\r
+.text-muted {\r
+  color: #999999;\r
+}\r
+.text-primary {\r
+  color: #0d8921;\r
+}\r
+a.text-primary:hover {\r
+  color: #095a16;\r
+}\r
+.text-success {\r
+  color: #5cb85c;\r
+}\r
+a.text-success:hover {\r
+  color: #449d44;\r
+}\r
+.text-info {\r
+  color: #5bc0de;\r
+}\r
+a.text-info:hover {\r
+  color: #31b0d5;\r
+}\r
+.text-warning {\r
+  color: #f0ad4e;\r
+}\r
+a.text-warning:hover {\r
+  color: #ec971f;\r
+}\r
+.text-danger {\r
+  color: #d9534f;\r
+}\r
+a.text-danger:hover {\r
+  color: #c9302c;\r
+}\r
+.bg-primary {\r
+  color: #fff;\r
+  background-color: #0d8921;\r
+}\r
+a.bg-primary:hover {\r
+  background-color: #095a16;\r
+}\r
+.bg-success {\r
+  background-color: #dff0d8;\r
+}\r
+a.bg-success:hover {\r
+  background-color: #c1e2b3;\r
+}\r
+.bg-info {\r
+  background-color: #d9edf7;\r
+}\r
+a.bg-info:hover {\r
+  background-color: #afd9ee;\r
+}\r
+.bg-warning {\r
+  background-color: #fcf8e3;\r
+}\r
+a.bg-warning:hover {\r
+  background-color: #f7ecb5;\r
+}\r
+.bg-danger {\r
+  background-color: #f2dede;\r
+}\r
+a.bg-danger:hover {\r
+  background-color: #e4b9b9;\r
+}\r
+.page-header {\r
+  padding-bottom: 9px;\r
+  margin: 40px 0 20px;\r
+  border-bottom: 1px solid #eeeeee;\r
+}\r
+ul,\r
+ol {\r
+  margin-top: 0;\r
+  margin-bottom: 10px;\r
+}\r
+ul ul,\r
+ol ul,\r
+ul ol,\r
+ol ol {\r
+  margin-bottom: 0;\r
+}\r
+.list-unstyled {\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.list-inline {\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.list-inline > li {\r
+  display: inline-block;\r
+  padding-left: 5px;\r
+  padding-right: 5px;\r
+}\r
+.list-inline > li:first-child {\r
+  padding-left: 0;\r
+}\r
+dl {\r
+  margin-top: 0;\r
+  margin-bottom: 20px;\r
+}\r
+dt,\r
+dd {\r
+  line-height: 1.428571429;\r
+}\r
+dt {\r
+  font-weight: bold;\r
+}\r
+dd {\r
+  margin-left: 0;\r
+}\r
+@media (min-width: 768px) {\r
+  .dl-horizontal dt {\r
+    float: left;\r
+    width: 160px;\r
+    clear: left;\r
+    text-align: right;\r
+    overflow: hidden;\r
+    text-overflow: ellipsis;\r
+    white-space: nowrap;\r
+  }\r
+  .dl-horizontal dd {\r
+    margin-left: 180px;\r
+  }\r
+}\r
+abbr[title],\r
+abbr[data-original-title] {\r
+  cursor: help;\r
+  border-bottom: 1px dotted #999999;\r
+}\r
+.initialism {\r
+  font-size: 90%;\r
+  text-transform: uppercase;\r
+}\r
+blockquote {\r
+  padding: 10px 20px;\r
+  margin: 0 0 20px;\r
+  font-size: 17.5px;\r
+  border-left: 5px solid #eeeeee;\r
+}\r
+blockquote p:last-child,\r
+blockquote ul:last-child,\r
+blockquote ol:last-child {\r
+  margin-bottom: 0;\r
+}\r
+blockquote footer,\r
+blockquote small,\r
+blockquote .small {\r
+  display: block;\r
+  font-size: 80%;\r
+  line-height: 1.428571429;\r
+  color: #999999;\r
+}\r
+blockquote footer:before,\r
+blockquote small:before,\r
+blockquote .small:before {\r
+  content: '\2014 \00A0';\r
+}\r
+.blockquote-reverse,\r
+blockquote.pull-right {\r
+  padding-right: 15px;\r
+  padding-left: 0;\r
+  border-right: 5px solid #eeeeee;\r
+  border-left: 0;\r
+  text-align: right;\r
+}\r
+.blockquote-reverse footer:before,\r
+blockquote.pull-right footer:before,\r
+.blockquote-reverse small:before,\r
+blockquote.pull-right small:before,\r
+.blockquote-reverse .small:before,\r
+blockquote.pull-right .small:before {\r
+  content: '';\r
+}\r
+.blockquote-reverse footer:after,\r
+blockquote.pull-right footer:after,\r
+.blockquote-reverse small:after,\r
+blockquote.pull-right small:after,\r
+.blockquote-reverse .small:after,\r
+blockquote.pull-right .small:after {\r
+  content: '\00A0 \2014';\r
+}\r
+blockquote:before,\r
+blockquote:after {\r
+  content: "";\r
+}\r
+address {\r
+  margin-bottom: 20px;\r
+  font-style: normal;\r
+  line-height: 1.428571429;\r
+}\r
+code,\r
+kbd,\r
+pre,\r
+samp {\r
+  font-family: monospace;\r
+}\r
+code {\r
+  padding: 2px 4px;\r
+  font-size: 90%;\r
+  color: #c7254e;\r
+  background-color: #f9f2f4;\r
+  white-space: nowrap;\r
+  border-radius: 0px;\r
+}\r
+kbd {\r
+  padding: 2px 4px;\r
+  font-size: 90%;\r
+  color: #ffffff;\r
+  background-color: #333333;\r
+  border-radius: 0px;\r
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\r
+}\r
+pre {\r
+  display: block;\r
+  padding: 9.5px;\r
+  margin: 0 0 10px;\r
+  font-size: 13px;\r
+  line-height: 1.428571429;\r
+  word-break: break-all;\r
+  word-wrap: break-word;\r
+  color: #333333;\r
+  background-color: #f5f5f5;\r
+  border: 1px solid #cccccc;\r
+  border-radius: 0px;\r
+}\r
+pre code {\r
+  padding: 0;\r
+  font-size: inherit;\r
+  color: inherit;\r
+  white-space: pre-wrap;\r
+  background-color: transparent;\r
+  border-radius: 0;\r
+}\r
+.pre-scrollable {\r
+  max-height: 340px;\r
+  overflow-y: scroll;\r
+}\r
+.container {\r
+  margin-right: auto;\r
+  margin-left: auto;\r
+  padding-left: 15px;\r
+  padding-right: 15px;\r
+}\r
+@media (min-width: 768px) {\r
+  .container {\r
+    width: 750px;\r
+  }\r
+}\r
+@media (min-width: 992px) {\r
+  .container {\r
+    width: 970px;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .container {\r
+    width: 1170px;\r
+  }\r
+}\r
+.container-fluid {\r
+  margin-right: auto;\r
+  margin-left: auto;\r
+  padding-left: 15px;\r
+  padding-right: 15px;\r
+}\r
+.row {\r
+  margin-left: -15px;\r
+  margin-right: -15px;\r
+}\r
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\r
+  position: relative;\r
+  min-height: 1px;\r
+  padding-left: 15px;\r
+  padding-right: 15px;\r
+}\r
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\r
+  float: left;\r
+}\r
+.col-xs-12 {\r
+  width: 100%;\r
+}\r
+.col-xs-11 {\r
+  width: 91.66666666666666%;\r
+}\r
+.col-xs-10 {\r
+  width: 83.33333333333334%;\r
+}\r
+.col-xs-9 {\r
+  width: 75%;\r
+}\r
+.col-xs-8 {\r
+  width: 66.66666666666666%;\r
+}\r
+.col-xs-7 {\r
+  width: 58.333333333333336%;\r
+}\r
+.col-xs-6 {\r
+  width: 50%;\r
+}\r
+.col-xs-5 {\r
+  width: 41.66666666666667%;\r
+}\r
+.col-xs-4 {\r
+  width: 33.33333333333333%;\r
+}\r
+.col-xs-3 {\r
+  width: 25%;\r
+}\r
+.col-xs-2 {\r
+  width: 16.666666666666664%;\r
+}\r
+.col-xs-1 {\r
+  width: 8.333333333333332%;\r
+}\r
+.col-xs-pull-12 {\r
+  right: 100%;\r
+}\r
+.col-xs-pull-11 {\r
+  right: 91.66666666666666%;\r
+}\r
+.col-xs-pull-10 {\r
+  right: 83.33333333333334%;\r
+}\r
+.col-xs-pull-9 {\r
+  right: 75%;\r
+}\r
+.col-xs-pull-8 {\r
+  right: 66.66666666666666%;\r
+}\r
+.col-xs-pull-7 {\r
+  right: 58.333333333333336%;\r
+}\r
+.col-xs-pull-6 {\r
+  right: 50%;\r
+}\r
+.col-xs-pull-5 {\r
+  right: 41.66666666666667%;\r
+}\r
+.col-xs-pull-4 {\r
+  right: 33.33333333333333%;\r
+}\r
+.col-xs-pull-3 {\r
+  right: 25%;\r
+}\r
+.col-xs-pull-2 {\r
+  right: 16.666666666666664%;\r
+}\r
+.col-xs-pull-1 {\r
+  right: 8.333333333333332%;\r
+}\r
+.col-xs-pull-0 {\r
+  right: 0%;\r
+}\r
+.col-xs-push-12 {\r
+  left: 100%;\r
+}\r
+.col-xs-push-11 {\r
+  left: 91.66666666666666%;\r
+}\r
+.col-xs-push-10 {\r
+  left: 83.33333333333334%;\r
+}\r
+.col-xs-push-9 {\r
+  left: 75%;\r
+}\r
+.col-xs-push-8 {\r
+  left: 66.66666666666666%;\r
+}\r
+.col-xs-push-7 {\r
+  left: 58.333333333333336%;\r
+}\r
+.col-xs-push-6 {\r
+  left: 50%;\r
+}\r
+.col-xs-push-5 {\r
+  left: 41.66666666666667%;\r
+}\r
+.col-xs-push-4 {\r
+  left: 33.33333333333333%;\r
+}\r
+.col-xs-push-3 {\r
+  left: 25%;\r
+}\r
+.col-xs-push-2 {\r
+  left: 16.666666666666664%;\r
+}\r
+.col-xs-push-1 {\r
+  left: 8.333333333333332%;\r
+}\r
+.col-xs-push-0 {\r
+  left: 0%;\r
+}\r
+.col-xs-offset-12 {\r
+  margin-left: 100%;\r
+}\r
+.col-xs-offset-11 {\r
+  margin-left: 91.66666666666666%;\r
+}\r
+.col-xs-offset-10 {\r
+  margin-left: 83.33333333333334%;\r
+}\r
+.col-xs-offset-9 {\r
+  margin-left: 75%;\r
+}\r
+.col-xs-offset-8 {\r
+  margin-left: 66.66666666666666%;\r
+}\r
+.col-xs-offset-7 {\r
+  margin-left: 58.333333333333336%;\r
+}\r
+.col-xs-offset-6 {\r
+  margin-left: 50%;\r
+}\r
+.col-xs-offset-5 {\r
+  margin-left: 41.66666666666667%;\r
+}\r
+.col-xs-offset-4 {\r
+  margin-left: 33.33333333333333%;\r
+}\r
+.col-xs-offset-3 {\r
+  margin-left: 25%;\r
+}\r
+.col-xs-offset-2 {\r
+  margin-left: 16.666666666666664%;\r
+}\r
+.col-xs-offset-1 {\r
+  margin-left: 8.333333333333332%;\r
+}\r
+.col-xs-offset-0 {\r
+  margin-left: 0%;\r
+}\r
+@media (min-width: 768px) {\r
+  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\r
+    float: left;\r
+  }\r
+  .col-sm-12 {\r
+    width: 100%;\r
+  }\r
+  .col-sm-11 {\r
+    width: 91.66666666666666%;\r
+  }\r
+  .col-sm-10 {\r
+    width: 83.33333333333334%;\r
+  }\r
+  .col-sm-9 {\r
+    width: 75%;\r
+  }\r
+  .col-sm-8 {\r
+    width: 66.66666666666666%;\r
+  }\r
+  .col-sm-7 {\r
+    width: 58.333333333333336%;\r
+  }\r
+  .col-sm-6 {\r
+    width: 50%;\r
+  }\r
+  .col-sm-5 {\r
+    width: 41.66666666666667%;\r
+  }\r
+  .col-sm-4 {\r
+    width: 33.33333333333333%;\r
+  }\r
+  .col-sm-3 {\r
+    width: 25%;\r
+  }\r
+  .col-sm-2 {\r
+    width: 16.666666666666664%;\r
+  }\r
+  .col-sm-1 {\r
+    width: 8.333333333333332%;\r
+  }\r
+  .col-sm-pull-12 {\r
+    right: 100%;\r
+  }\r
+  .col-sm-pull-11 {\r
+    right: 91.66666666666666%;\r
+  }\r
+  .col-sm-pull-10 {\r
+    right: 83.33333333333334%;\r
+  }\r
+  .col-sm-pull-9 {\r
+    right: 75%;\r
+  }\r
+  .col-sm-pull-8 {\r
+    right: 66.66666666666666%;\r
+  }\r
+  .col-sm-pull-7 {\r
+    right: 58.333333333333336%;\r
+  }\r
+  .col-sm-pull-6 {\r
+    right: 50%;\r
+  }\r
+  .col-sm-pull-5 {\r
+    right: 41.66666666666667%;\r
+  }\r
+  .col-sm-pull-4 {\r
+    right: 33.33333333333333%;\r
+  }\r
+  .col-sm-pull-3 {\r
+    right: 25%;\r
+  }\r
+  .col-sm-pull-2 {\r
+    right: 16.666666666666664%;\r
+  }\r
+  .col-sm-pull-1 {\r
+    right: 8.333333333333332%;\r
+  }\r
+  .col-sm-pull-0 {\r
+    right: 0%;\r
+  }\r
+  .col-sm-push-12 {\r
+    left: 100%;\r
+  }\r
+  .col-sm-push-11 {\r
+    left: 91.66666666666666%;\r
+  }\r
+  .col-sm-push-10 {\r
+    left: 83.33333333333334%;\r
+  }\r
+  .col-sm-push-9 {\r
+    left: 75%;\r
+  }\r
+  .col-sm-push-8 {\r
+    left: 66.66666666666666%;\r
+  }\r
+  .col-sm-push-7 {\r
+    left: 58.333333333333336%;\r
+  }\r
+  .col-sm-push-6 {\r
+    left: 50%;\r
+  }\r
+  .col-sm-push-5 {\r
+    left: 41.66666666666667%;\r
+  }\r
+  .col-sm-push-4 {\r
+    left: 33.33333333333333%;\r
+  }\r
+  .col-sm-push-3 {\r
+    left: 25%;\r
+  }\r
+  .col-sm-push-2 {\r
+    left: 16.666666666666664%;\r
+  }\r
+  .col-sm-push-1 {\r
+    left: 8.333333333333332%;\r
+  }\r
+  .col-sm-push-0 {\r
+    left: 0%;\r
+  }\r
+  .col-sm-offset-12 {\r
+    margin-left: 100%;\r
+  }\r
+  .col-sm-offset-11 {\r
+    margin-left: 91.66666666666666%;\r
+  }\r
+  .col-sm-offset-10 {\r
+    margin-left: 83.33333333333334%;\r
+  }\r
+  .col-sm-offset-9 {\r
+    margin-left: 75%;\r
+  }\r
+  .col-sm-offset-8 {\r
+    margin-left: 66.66666666666666%;\r
+  }\r
+  .col-sm-offset-7 {\r
+    margin-left: 58.333333333333336%;\r
+  }\r
+  .col-sm-offset-6 {\r
+    margin-left: 50%;\r
+  }\r
+  .col-sm-offset-5 {\r
+    margin-left: 41.66666666666667%;\r
+  }\r
+  .col-sm-offset-4 {\r
+    margin-left: 33.33333333333333%;\r
+  }\r
+  .col-sm-offset-3 {\r
+    margin-left: 25%;\r
+  }\r
+  .col-sm-offset-2 {\r
+    margin-left: 16.666666666666664%;\r
+  }\r
+  .col-sm-offset-1 {\r
+    margin-left: 8.333333333333332%;\r
+  }\r
+  .col-sm-offset-0 {\r
+    margin-left: 0%;\r
+  }\r
+}\r
+@media (min-width: 992px) {\r
+  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\r
+    float: left;\r
+  }\r
+  .col-md-12 {\r
+    width: 100%;\r
+  }\r
+  .col-md-11 {\r
+    width: 91.66666666666666%;\r
+  }\r
+  .col-md-10 {\r
+    width: 83.33333333333334%;\r
+  }\r
+  .col-md-9 {\r
+    width: 75%;\r
+  }\r
+  .col-md-8 {\r
+    width: 66.66666666666666%;\r
+  }\r
+  .col-md-7 {\r
+    width: 58.333333333333336%;\r
+  }\r
+  .col-md-6 {\r
+    width: 50%;\r
+  }\r
+  .col-md-5 {\r
+    width: 41.66666666666667%;\r
+  }\r
+  .col-md-4 {\r
+    width: 33.33333333333333%;\r
+  }\r
+  .col-md-3 {\r
+    width: 25%;\r
+  }\r
+  .col-md-2 {\r
+    width: 16.666666666666664%;\r
+  }\r
+  .col-md-1 {\r
+    width: 8.333333333333332%;\r
+  }\r
+  .col-md-pull-12 {\r
+    right: 100%;\r
+  }\r
+  .col-md-pull-11 {\r
+    right: 91.66666666666666%;\r
+  }\r
+  .col-md-pull-10 {\r
+    right: 83.33333333333334%;\r
+  }\r
+  .col-md-pull-9 {\r
+    right: 75%;\r
+  }\r
+  .col-md-pull-8 {\r
+    right: 66.66666666666666%;\r
+  }\r
+  .col-md-pull-7 {\r
+    right: 58.333333333333336%;\r
+  }\r
+  .col-md-pull-6 {\r
+    right: 50%;\r
+  }\r
+  .col-md-pull-5 {\r
+    right: 41.66666666666667%;\r
+  }\r
+  .col-md-pull-4 {\r
+    right: 33.33333333333333%;\r
+  }\r
+  .col-md-pull-3 {\r
+    right: 25%;\r
+  }\r
+  .col-md-pull-2 {\r
+    right: 16.666666666666664%;\r
+  }\r
+  .col-md-pull-1 {\r
+    right: 8.333333333333332%;\r
+  }\r
+  .col-md-pull-0 {\r
+    right: 0%;\r
+  }\r
+  .col-md-push-12 {\r
+    left: 100%;\r
+  }\r
+  .col-md-push-11 {\r
+    left: 91.66666666666666%;\r
+  }\r
+  .col-md-push-10 {\r
+    left: 83.33333333333334%;\r
+  }\r
+  .col-md-push-9 {\r
+    left: 75%;\r
+  }\r
+  .col-md-push-8 {\r
+    left: 66.66666666666666%;\r
+  }\r
+  .col-md-push-7 {\r
+    left: 58.333333333333336%;\r
+  }\r
+  .col-md-push-6 {\r
+    left: 50%;\r
+  }\r
+  .col-md-push-5 {\r
+    left: 41.66666666666667%;\r
+  }\r
+  .col-md-push-4 {\r
+    left: 33.33333333333333%;\r
+  }\r
+  .col-md-push-3 {\r
+    left: 25%;\r
+  }\r
+  .col-md-push-2 {\r
+    left: 16.666666666666664%;\r
+  }\r
+  .col-md-push-1 {\r
+    left: 8.333333333333332%;\r
+  }\r
+  .col-md-push-0 {\r
+    left: 0%;\r
+  }\r
+  .col-md-offset-12 {\r
+    margin-left: 100%;\r
+  }\r
+  .col-md-offset-11 {\r
+    margin-left: 91.66666666666666%;\r
+  }\r
+  .col-md-offset-10 {\r
+    margin-left: 83.33333333333334%;\r
+  }\r
+  .col-md-offset-9 {\r
+    margin-left: 75%;\r
+  }\r
+  .col-md-offset-8 {\r
+    margin-left: 66.66666666666666%;\r
+  }\r
+  .col-md-offset-7 {\r
+    margin-left: 58.333333333333336%;\r
+  }\r
+  .col-md-offset-6 {\r
+    margin-left: 50%;\r
+  }\r
+  .col-md-offset-5 {\r
+    margin-left: 41.66666666666667%;\r
+  }\r
+  .col-md-offset-4 {\r
+    margin-left: 33.33333333333333%;\r
+  }\r
+  .col-md-offset-3 {\r
+    margin-left: 25%;\r
+  }\r
+  .col-md-offset-2 {\r
+    margin-left: 16.666666666666664%;\r
+  }\r
+  .col-md-offset-1 {\r
+    margin-left: 8.333333333333332%;\r
+  }\r
+  .col-md-offset-0 {\r
+    margin-left: 0%;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\r
+    float: left;\r
+  }\r
+  .col-lg-12 {\r
+    width: 100%;\r
+  }\r
+  .col-lg-11 {\r
+    width: 91.66666666666666%;\r
+  }\r
+  .col-lg-10 {\r
+    width: 83.33333333333334%;\r
+  }\r
+  .col-lg-9 {\r
+    width: 75%;\r
+  }\r
+  .col-lg-8 {\r
+    width: 66.66666666666666%;\r
+  }\r
+  .col-lg-7 {\r
+    width: 58.333333333333336%;\r
+  }\r
+  .col-lg-6 {\r
+    width: 50%;\r
+  }\r
+  .col-lg-5 {\r
+    width: 41.66666666666667%;\r
+  }\r
+  .col-lg-4 {\r
+    width: 33.33333333333333%;\r
+  }\r
+  .col-lg-3 {\r
+    width: 25%;\r
+  }\r
+  .col-lg-2 {\r
+    width: 16.666666666666664%;\r
+  }\r
+  .col-lg-1 {\r
+    width: 8.333333333333332%;\r
+  }\r
+  .col-lg-pull-12 {\r
+    right: 100%;\r
+  }\r
+  .col-lg-pull-11 {\r
+    right: 91.66666666666666%;\r
+  }\r
+  .col-lg-pull-10 {\r
+    right: 83.33333333333334%;\r
+  }\r
+  .col-lg-pull-9 {\r
+    right: 75%;\r
+  }\r
+  .col-lg-pull-8 {\r
+    right: 66.66666666666666%;\r
+  }\r
+  .col-lg-pull-7 {\r
+    right: 58.333333333333336%;\r
+  }\r
+  .col-lg-pull-6 {\r
+    right: 50%;\r
+  }\r
+  .col-lg-pull-5 {\r
+    right: 41.66666666666667%;\r
+  }\r
+  .col-lg-pull-4 {\r
+    right: 33.33333333333333%;\r
+  }\r
+  .col-lg-pull-3 {\r
+    right: 25%;\r
+  }\r
+  .col-lg-pull-2 {\r
+    right: 16.666666666666664%;\r
+  }\r
+  .col-lg-pull-1 {\r
+    right: 8.333333333333332%;\r
+  }\r
+  .col-lg-pull-0 {\r
+    right: 0%;\r
+  }\r
+  .col-lg-push-12 {\r
+    left: 100%;\r
+  }\r
+  .col-lg-push-11 {\r
+    left: 91.66666666666666%;\r
+  }\r
+  .col-lg-push-10 {\r
+    left: 83.33333333333334%;\r
+  }\r
+  .col-lg-push-9 {\r
+    left: 75%;\r
+  }\r
+  .col-lg-push-8 {\r
+    left: 66.66666666666666%;\r
+  }\r
+  .col-lg-push-7 {\r
+    left: 58.333333333333336%;\r
+  }\r
+  .col-lg-push-6 {\r
+    left: 50%;\r
+  }\r
+  .col-lg-push-5 {\r
+    left: 41.66666666666667%;\r
+  }\r
+  .col-lg-push-4 {\r
+    left: 33.33333333333333%;\r
+  }\r
+  .col-lg-push-3 {\r
+    left: 25%;\r
+  }\r
+  .col-lg-push-2 {\r
+    left: 16.666666666666664%;\r
+  }\r
+  .col-lg-push-1 {\r
+    left: 8.333333333333332%;\r
+  }\r
+  .col-lg-push-0 {\r
+    left: 0%;\r
+  }\r
+  .col-lg-offset-12 {\r
+    margin-left: 100%;\r
+  }\r
+  .col-lg-offset-11 {\r
+    margin-left: 91.66666666666666%;\r
+  }\r
+  .col-lg-offset-10 {\r
+    margin-left: 83.33333333333334%;\r
+  }\r
+  .col-lg-offset-9 {\r
+    margin-left: 75%;\r
+  }\r
+  .col-lg-offset-8 {\r
+    margin-left: 66.66666666666666%;\r
+  }\r
+  .col-lg-offset-7 {\r
+    margin-left: 58.333333333333336%;\r
+  }\r
+  .col-lg-offset-6 {\r
+    margin-left: 50%;\r
+  }\r
+  .col-lg-offset-5 {\r
+    margin-left: 41.66666666666667%;\r
+  }\r
+  .col-lg-offset-4 {\r
+    margin-left: 33.33333333333333%;\r
+  }\r
+  .col-lg-offset-3 {\r
+    margin-left: 25%;\r
+  }\r
+  .col-lg-offset-2 {\r
+    margin-left: 16.666666666666664%;\r
+  }\r
+  .col-lg-offset-1 {\r
+    margin-left: 8.333333333333332%;\r
+  }\r
+  .col-lg-offset-0 {\r
+    margin-left: 0%;\r
+  }\r
+}\r
+table {\r
+  max-width: 100%;\r
+  background-color: transparent;\r
+}\r
+th {\r
+  text-align: left;\r
+}\r
+.table {\r
+  width: 100%;\r
+  margin-bottom: 20px;\r
+}\r
+.table > thead > tr > th,\r
+.table > tbody > tr > th,\r
+.table > tfoot > tr > th,\r
+.table > thead > tr > td,\r
+.table > tbody > tr > td,\r
+.table > tfoot > tr > td {\r
+  padding: 8px;\r
+  line-height: 1.428571429;\r
+  vertical-align: top;\r
+  border-top: 1px solid #dddddd;\r
+}\r
+.table > thead > tr > th {\r
+  vertical-align: bottom;\r
+  border-bottom: 2px solid #dddddd;\r
+}\r
+.table > caption + thead > tr:first-child > th,\r
+.table > colgroup + thead > tr:first-child > th,\r
+.table > thead:first-child > tr:first-child > th,\r
+.table > caption + thead > tr:first-child > td,\r
+.table > colgroup + thead > tr:first-child > td,\r
+.table > thead:first-child > tr:first-child > td {\r
+  border-top: 0;\r
+}\r
+.table > tbody + tbody {\r
+  border-top: 2px solid #dddddd;\r
+}\r
+.table .table {\r
+  background-color: #f2f2f2;\r
+}\r
+.table-condensed > thead > tr > th,\r
+.table-condensed > tbody > tr > th,\r
+.table-condensed > tfoot > tr > th,\r
+.table-condensed > thead > tr > td,\r
+.table-condensed > tbody > tr > td,\r
+.table-condensed > tfoot > tr > td {\r
+  padding: 5px;\r
+}\r
+.table-bordered {\r
+  border: 1px solid #dddddd;\r
+}\r
+.table-bordered > thead > tr > th,\r
+.table-bordered > tbody > tr > th,\r
+.table-bordered > tfoot > tr > th,\r
+.table-bordered > thead > tr > td,\r
+.table-bordered > tbody > tr > td,\r
+.table-bordered > tfoot > tr > td {\r
+  border: 1px solid #dddddd;\r
+}\r
+.table-bordered > thead > tr > th,\r
+.table-bordered > thead > tr > td {\r
+  border-bottom-width: 2px;\r
+}\r
+.table-striped > tbody > tr:nth-child(odd) > td,\r
+.table-striped > tbody > tr:nth-child(odd) > th {\r
+  background-color: #f9f9f9;\r
+}\r
+.table-hover > tbody > tr:hover > td,\r
+.table-hover > tbody > tr:hover > th {\r
+  background-color: #dbdbdb;\r
+}\r
+table col[class*="col-"] {\r
+  position: static;\r
+  float: none;\r
+  display: table-column;\r
+}\r
+table td[class*="col-"],\r
+table th[class*="col-"] {\r
+  position: static;\r
+  float: none;\r
+  display: table-cell;\r
+}\r
+.table > thead > tr > td.active,\r
+.table > tbody > tr > td.active,\r
+.table > tfoot > tr > td.active,\r
+.table > thead > tr > th.active,\r
+.table > tbody > tr > th.active,\r
+.table > tfoot > tr > th.active,\r
+.table > thead > tr.active > td,\r
+.table > tbody > tr.active > td,\r
+.table > tfoot > tr.active > td,\r
+.table > thead > tr.active > th,\r
+.table > tbody > tr.active > th,\r
+.table > tfoot > tr.active > th {\r
+  background-color: #dbdbdb;\r
+}\r
+.table-hover > tbody > tr > td.active:hover,\r
+.table-hover > tbody > tr > th.active:hover,\r
+.table-hover > tbody > tr.active:hover > td,\r
+.table-hover > tbody > tr.active:hover > th {\r
+  background-color: #cecece;\r
+}\r
+.table > thead > tr > td.success,\r
+.table > tbody > tr > td.success,\r
+.table > tfoot > tr > td.success,\r
+.table > thead > tr > th.success,\r
+.table > tbody > tr > th.success,\r
+.table > tfoot > tr > th.success,\r
+.table > thead > tr.success > td,\r
+.table > tbody > tr.success > td,\r
+.table > tfoot > tr.success > td,\r
+.table > thead > tr.success > th,\r
+.table > tbody > tr.success > th,\r
+.table > tfoot > tr.success > th {\r
+  background-color: #dff0d8;\r
+}\r
+.table-hover > tbody > tr > td.success:hover,\r
+.table-hover > tbody > tr > th.success:hover,\r
+.table-hover > tbody > tr.success:hover > td,\r
+.table-hover > tbody > tr.success:hover > th {\r
+  background-color: #d0e9c6;\r
+}\r
+.table > thead > tr > td.info,\r
+.table > tbody > tr > td.info,\r
+.table > tfoot > tr > td.info,\r
+.table > thead > tr > th.info,\r
+.table > tbody > tr > th.info,\r
+.table > tfoot > tr > th.info,\r
+.table > thead > tr.info > td,\r
+.table > tbody > tr.info > td,\r
+.table > tfoot > tr.info > td,\r
+.table > thead > tr.info > th,\r
+.table > tbody > tr.info > th,\r
+.table > tfoot > tr.info > th {\r
+  background-color: #d9edf7;\r
+}\r
+.table-hover > tbody > tr > td.info:hover,\r
+.table-hover > tbody > tr > th.info:hover,\r
+.table-hover > tbody > tr.info:hover > td,\r
+.table-hover > tbody > tr.info:hover > th {\r
+  background-color: #c4e3f3;\r
+}\r
+.table > thead > tr > td.warning,\r
+.table > tbody > tr > td.warning,\r
+.table > tfoot > tr > td.warning,\r
+.table > thead > tr > th.warning,\r
+.table > tbody > tr > th.warning,\r
+.table > tfoot > tr > th.warning,\r
+.table > thead > tr.warning > td,\r
+.table > tbody > tr.warning > td,\r
+.table > tfoot > tr.warning > td,\r
+.table > thead > tr.warning > th,\r
+.table > tbody > tr.warning > th,\r
+.table > tfoot > tr.warning > th {\r
+  background-color: #fcf8e3;\r
+}\r
+.table-hover > tbody > tr > td.warning:hover,\r
+.table-hover > tbody > tr > th.warning:hover,\r
+.table-hover > tbody > tr.warning:hover > td,\r
+.table-hover > tbody > tr.warning:hover > th {\r
+  background-color: #faf2cc;\r
+}\r
+.table > thead > tr > td.danger,\r
+.table > tbody > tr > td.danger,\r
+.table > tfoot > tr > td.danger,\r
+.table > thead > tr > th.danger,\r
+.table > tbody > tr > th.danger,\r
+.table > tfoot > tr > th.danger,\r
+.table > thead > tr.danger > td,\r
+.table > tbody > tr.danger > td,\r
+.table > tfoot > tr.danger > td,\r
+.table > thead > tr.danger > th,\r
+.table > tbody > tr.danger > th,\r
+.table > tfoot > tr.danger > th {\r
+  background-color: #f2dede;\r
+}\r
+.table-hover > tbody > tr > td.danger:hover,\r
+.table-hover > tbody > tr > th.danger:hover,\r
+.table-hover > tbody > tr.danger:hover > td,\r
+.table-hover > tbody > tr.danger:hover > th {\r
+  background-color: #ebcccc;\r
+}\r
+@media (max-width: 767px) {\r
+  .table-responsive {\r
+    width: 100%;\r
+    margin-bottom: 15px;\r
+    overflow-y: hidden;\r
+    overflow-x: scroll;\r
+    -ms-overflow-style: -ms-autohiding-scrollbar;\r
+    border: 1px solid #dddddd;\r
+    -webkit-overflow-scrolling: touch;\r
+  }\r
+  .table-responsive > .table {\r
+    margin-bottom: 0;\r
+  }\r
+  .table-responsive > .table > thead > tr > th,\r
+  .table-responsive > .table > tbody > tr > th,\r
+  .table-responsive > .table > tfoot > tr > th,\r
+  .table-responsive > .table > thead > tr > td,\r
+  .table-responsive > .table > tbody > tr > td,\r
+  .table-responsive > .table > tfoot > tr > td {\r
+    white-space: nowrap;\r
+  }\r
+  .table-responsive > .table-bordered {\r
+    border: 0;\r
+  }\r
+  .table-responsive > .table-bordered > thead > tr > th:first-child,\r
+  .table-responsive > .table-bordered > tbody > tr > th:first-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\r
+  .table-responsive > .table-bordered > thead > tr > td:first-child,\r
+  .table-responsive > .table-bordered > tbody > tr > td:first-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\r
+    border-left: 0;\r
+  }\r
+  .table-responsive > .table-bordered > thead > tr > th:last-child,\r
+  .table-responsive > .table-bordered > tbody > tr > th:last-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\r
+  .table-responsive > .table-bordered > thead > tr > td:last-child,\r
+  .table-responsive > .table-bordered > tbody > tr > td:last-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\r
+    border-right: 0;\r
+  }\r
+  .table-responsive > .table-bordered > tbody > tr:last-child > th,\r
+  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\r
+  .table-responsive > .table-bordered > tbody > tr:last-child > td,\r
+  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\r
+    border-bottom: 0;\r
+  }\r
+}\r
+fieldset {\r
+  padding: 0;\r
+  margin: 0;\r
+  border: 0;\r
+  min-width: 0;\r
+}\r
+legend {\r
+  display: block;\r
+  width: 100%;\r
+  padding: 0;\r
+  margin-bottom: 20px;\r
+  font-size: 21px;\r
+  line-height: inherit;\r
+  color: #333333;\r
+  border: 0;\r
+  border-bottom: 1px solid #e5e5e5;\r
+}\r
+label {\r
+  display: inline-block;\r
+  margin-bottom: 5px;\r
+  font-weight: bold;\r
+}\r
+input[type="search"] {\r
+  -webkit-box-sizing: border-box;\r
+  -moz-box-sizing: border-box;\r
+  box-sizing: border-box;\r
+}\r
+input[type="radio"],\r
+input[type="checkbox"] {\r
+  margin: 4px 0 0;\r
+  margin-top: 1px \9;\r
+  /* IE8-9 */\r
+\r
+  line-height: normal;\r
+}\r
+input[type="file"] {\r
+  display: block;\r
+}\r
+input[type="range"] {\r
+  display: block;\r
+  width: 100%;\r
+}\r
+select[multiple],\r
+select[size] {\r
+  height: auto;\r
+}\r
+input[type="file"]:focus,\r
+input[type="radio"]:focus,\r
+input[type="checkbox"]:focus {\r
+  outline: thin dotted;\r
+  outline: 5px auto -webkit-focus-ring-color;\r
+  outline-offset: -2px;\r
+}\r
+output {\r
+  display: block;\r
+  padding-top: 7px;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  color: #555555;\r
+}\r
+.form-control {\r
+  display: block;\r
+  width: 100%;\r
+  height: 34px;\r
+  padding: 6px 12px;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  color: #555555;\r
+  background-color: #ffffff;\r
+  background-image: none;\r
+  border: 1px solid #cccccc;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\r
+  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\r
+}\r
+.form-control:focus {\r
+  border-color: #66afe9;\r
+  outline: 0;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\r
+  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\r
+}\r
+.form-control::-moz-placeholder {\r
+  color: #999999;\r
+  opacity: 1;\r
+}\r
+.form-control:-ms-input-placeholder {\r
+  color: #999999;\r
+}\r
+.form-control::-webkit-input-placeholder {\r
+  color: #999999;\r
+}\r
+.form-control[disabled],\r
+.form-control[readonly],\r
+fieldset[disabled] .form-control {\r
+  cursor: not-allowed;\r
+  background-color: #eeeeee;\r
+  opacity: 1;\r
+}\r
+textarea.form-control {\r
+  height: auto;\r
+}\r
+input[type="search"] {\r
+  -webkit-appearance: none;\r
+}\r
+input[type="date"] {\r
+  line-height: 34px;\r
+}\r
+.form-group {\r
+  margin-bottom: 15px;\r
+}\r
+.radio,\r
+.checkbox {\r
+  display: block;\r
+  min-height: 20px;\r
+  margin-top: 10px;\r
+  margin-bottom: 10px;\r
+  padding-left: 20px;\r
+}\r
+.radio label,\r
+.checkbox label {\r
+  display: inline;\r
+  font-weight: normal;\r
+  cursor: pointer;\r
+}\r
+.radio input[type="radio"],\r
+.radio-inline input[type="radio"],\r
+.checkbox input[type="checkbox"],\r
+.checkbox-inline input[type="checkbox"] {\r
+  float: left;\r
+  margin-left: -20px;\r
+}\r
+.radio + .radio,\r
+.checkbox + .checkbox {\r
+  margin-top: -5px;\r
+}\r
+.radio-inline,\r
+.checkbox-inline {\r
+  display: inline-block;\r
+  padding-left: 20px;\r
+  margin-bottom: 0;\r
+  vertical-align: middle;\r
+  font-weight: normal;\r
+  cursor: pointer;\r
+}\r
+.radio-inline + .radio-inline,\r
+.checkbox-inline + .checkbox-inline {\r
+  margin-top: 0;\r
+  margin-left: 10px;\r
+}\r
+input[type="radio"][disabled],\r
+input[type="checkbox"][disabled],\r
+.radio[disabled],\r
+.radio-inline[disabled],\r
+.checkbox[disabled],\r
+.checkbox-inline[disabled],\r
+fieldset[disabled] input[type="radio"],\r
+fieldset[disabled] input[type="checkbox"],\r
+fieldset[disabled] .radio,\r
+fieldset[disabled] .radio-inline,\r
+fieldset[disabled] .checkbox,\r
+fieldset[disabled] .checkbox-inline {\r
+  cursor: not-allowed;\r
+}\r
+.input-sm {\r
+  height: 30px;\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+select.input-sm {\r
+  height: 30px;\r
+  line-height: 30px;\r
+}\r
+textarea.input-sm,\r
+select[multiple].input-sm {\r
+  height: auto;\r
+}\r
+.input-lg {\r
+  height: 45px;\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  line-height: 1.33;\r
+  border-radius: 0px;\r
+}\r
+select.input-lg {\r
+  height: 45px;\r
+  line-height: 45px;\r
+}\r
+textarea.input-lg,\r
+select[multiple].input-lg {\r
+  height: auto;\r
+}\r
+.has-feedback {\r
+  position: relative;\r
+}\r
+.has-feedback .form-control {\r
+  padding-right: 42.5px;\r
+}\r
+.has-feedback .form-control-feedback {\r
+  position: absolute;\r
+  top: 25px;\r
+  right: 0;\r
+  display: block;\r
+  width: 34px;\r
+  height: 34px;\r
+  line-height: 34px;\r
+  text-align: center;\r
+}\r
+.has-success .help-block,\r
+.has-success .control-label,\r
+.has-success .radio,\r
+.has-success .checkbox,\r
+.has-success .radio-inline,\r
+.has-success .checkbox-inline {\r
+  color: #5cb85c;\r
+}\r
+.has-success .form-control {\r
+  border-color: #5cb85c;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+}\r
+.has-success .form-control:focus {\r
+  border-color: #449d44;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #a3d7a3;\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #a3d7a3;\r
+}\r
+.has-success .input-group-addon {\r
+  color: #5cb85c;\r
+  border-color: #5cb85c;\r
+  background-color: #dff0d8;\r
+}\r
+.has-success .form-control-feedback {\r
+  color: #5cb85c;\r
+}\r
+.has-warning .help-block,\r
+.has-warning .control-label,\r
+.has-warning .radio,\r
+.has-warning .checkbox,\r
+.has-warning .radio-inline,\r
+.has-warning .checkbox-inline {\r
+  color: #f0ad4e;\r
+}\r
+.has-warning .form-control {\r
+  border-color: #f0ad4e;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+}\r
+.has-warning .form-control:focus {\r
+  border-color: #ec971f;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f8d9ac;\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f8d9ac;\r
+}\r
+.has-warning .input-group-addon {\r
+  color: #f0ad4e;\r
+  border-color: #f0ad4e;\r
+  background-color: #fcf8e3;\r
+}\r
+.has-warning .form-control-feedback {\r
+  color: #f0ad4e;\r
+}\r
+.has-error .help-block,\r
+.has-error .control-label,\r
+.has-error .radio,\r
+.has-error .checkbox,\r
+.has-error .radio-inline,\r
+.has-error .checkbox-inline {\r
+  color: #d9534f;\r
+}\r
+.has-error .form-control {\r
+  border-color: #d9534f;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+}\r
+.has-error .form-control:focus {\r
+  border-color: #c9302c;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3;\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3;\r
+}\r
+.has-error .input-group-addon {\r
+  color: #d9534f;\r
+  border-color: #d9534f;\r
+  background-color: #f2dede;\r
+}\r
+.has-error .form-control-feedback {\r
+  color: #d9534f;\r
+}\r
+.form-control-static {\r
+  margin-bottom: 0;\r
+}\r
+.help-block {\r
+  display: block;\r
+  margin-top: 5px;\r
+  margin-bottom: 10px;\r
+  color: #737373;\r
+}\r
+@media (min-width: 768px) {\r
+  .form-inline .form-group {\r
+    display: inline-block;\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .form-control {\r
+    display: inline-block;\r
+    width: auto;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .input-group > .form-control {\r
+    width: 100%;\r
+  }\r
+  .form-inline .control-label {\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .radio,\r
+  .form-inline .checkbox {\r
+    display: inline-block;\r
+    margin-top: 0;\r
+    margin-bottom: 0;\r
+    padding-left: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .radio input[type="radio"],\r
+  .form-inline .checkbox input[type="checkbox"] {\r
+    float: none;\r
+    margin-left: 0;\r
+  }\r
+  .form-inline .has-feedback .form-control-feedback {\r
+    top: 0;\r
+  }\r
+}\r
+.form-horizontal .control-label,\r
+.form-horizontal .radio,\r
+.form-horizontal .checkbox,\r
+.form-horizontal .radio-inline,\r
+.form-horizontal .checkbox-inline {\r
+  margin-top: 0;\r
+  margin-bottom: 0;\r
+  padding-top: 7px;\r
+}\r
+.form-horizontal .radio,\r
+.form-horizontal .checkbox {\r
+  min-height: 27px;\r
+}\r
+.form-horizontal .form-group {\r
+  margin-left: -15px;\r
+  margin-right: -15px;\r
+}\r
+.form-horizontal .form-control-static {\r
+  padding-top: 7px;\r
+}\r
+@media (min-width: 768px) {\r
+  .form-horizontal .control-label {\r
+    text-align: right;\r
+  }\r
+}\r
+.form-horizontal .has-feedback .form-control-feedback {\r
+  top: 0;\r
+  right: 15px;\r
+}\r
+.btn {\r
+  display: inline-block;\r
+  margin-bottom: 0;\r
+  font-weight: normal;\r
+  text-align: center;\r
+  vertical-align: middle;\r
+  cursor: pointer;\r
+  background-image: none;\r
+  border: 1px solid transparent;\r
+  white-space: nowrap;\r
+  padding: 6px 12px;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  border-radius: 0px;\r
+  -webkit-user-select: none;\r
+  -moz-user-select: none;\r
+  -ms-user-select: none;\r
+  -o-user-select: none;\r
+  user-select: none;\r
+}\r
+.btn:focus {\r
+  outline: thin dotted;\r
+  outline: 5px auto -webkit-focus-ring-color;\r
+  outline-offset: -2px;\r
+}\r
+.btn:hover,\r
+.btn:focus {\r
+  color: #333333;\r
+  text-decoration: none;\r
+}\r
+.btn:active,\r
+.btn.active {\r
+  outline: 0;\r
+  background-image: none;\r
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+}\r
+.btn.disabled,\r
+.btn[disabled],\r
+fieldset[disabled] .btn {\r
+  cursor: not-allowed;\r
+  pointer-events: none;\r
+  opacity: 0.65;\r
+  filter: alpha(opacity=65);\r
+  -webkit-box-shadow: none;\r
+  box-shadow: none;\r
+}\r
+.btn-default {\r
+  color: #333333;\r
+  background-color: #ffffff;\r
+  border-color: #cccccc;\r
+}\r
+.btn-default:hover,\r
+.btn-default:focus,\r
+.btn-default:active,\r
+.btn-default.active,\r
+.open .dropdown-toggle.btn-default {\r
+  color: #333333;\r
+  background-color: #ebebeb;\r
+  border-color: #adadad;\r
+}\r
+.btn-default:active,\r
+.btn-default.active,\r
+.open .dropdown-toggle.btn-default {\r
+  background-image: none;\r
+}\r
+.btn-default.disabled,\r
+.btn-default[disabled],\r
+fieldset[disabled] .btn-default,\r
+.btn-default.disabled:hover,\r
+.btn-default[disabled]:hover,\r
+fieldset[disabled] .btn-default:hover,\r
+.btn-default.disabled:focus,\r
+.btn-default[disabled]:focus,\r
+fieldset[disabled] .btn-default:focus,\r
+.btn-default.disabled:active,\r
+.btn-default[disabled]:active,\r
+fieldset[disabled] .btn-default:active,\r
+.btn-default.disabled.active,\r
+.btn-default[disabled].active,\r
+fieldset[disabled] .btn-default.active {\r
+  background-color: #ffffff;\r
+  border-color: #cccccc;\r
+}\r
+.btn-default .badge {\r
+  color: #ffffff;\r
+  background-color: #333333;\r
+}\r
+.btn-primary {\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0b721b;\r
+}\r
+.btn-primary:hover,\r
+.btn-primary:focus,\r
+.btn-primary:active,\r
+.btn-primary.active,\r
+.open .dropdown-toggle.btn-primary {\r
+  color: #ffffff;\r
+  background-color: #096418;\r
+  border-color: #053a0e;\r
+}\r
+.btn-primary:active,\r
+.btn-primary.active,\r
+.open .dropdown-toggle.btn-primary {\r
+  background-image: none;\r
+}\r
+.btn-primary.disabled,\r
+.btn-primary[disabled],\r
+fieldset[disabled] .btn-primary,\r
+.btn-primary.disabled:hover,\r
+.btn-primary[disabled]:hover,\r
+fieldset[disabled] .btn-primary:hover,\r
+.btn-primary.disabled:focus,\r
+.btn-primary[disabled]:focus,\r
+fieldset[disabled] .btn-primary:focus,\r
+.btn-primary.disabled:active,\r
+.btn-primary[disabled]:active,\r
+fieldset[disabled] .btn-primary:active,\r
+.btn-primary.disabled.active,\r
+.btn-primary[disabled].active,\r
+fieldset[disabled] .btn-primary.active {\r
+  background-color: #0d8921;\r
+  border-color: #0b721b;\r
+}\r
+.btn-primary .badge {\r
+  color: #0d8921;\r
+  background-color: #ffffff;\r
+}\r
+.btn-success {\r
+  color: #ffffff;\r
+  background-color: #5cb85c;\r
+  border-color: #4cae4c;\r
+}\r
+.btn-success:hover,\r
+.btn-success:focus,\r
+.btn-success:active,\r
+.btn-success.active,\r
+.open .dropdown-toggle.btn-success {\r
+  color: #ffffff;\r
+  background-color: #47a447;\r
+  border-color: #398439;\r
+}\r
+.btn-success:active,\r
+.btn-success.active,\r
+.open .dropdown-toggle.btn-success {\r
+  background-image: none;\r
+}\r
+.btn-success.disabled,\r
+.btn-success[disabled],\r
+fieldset[disabled] .btn-success,\r
+.btn-success.disabled:hover,\r
+.btn-success[disabled]:hover,\r
+fieldset[disabled] .btn-success:hover,\r
+.btn-success.disabled:focus,\r
+.btn-success[disabled]:focus,\r
+fieldset[disabled] .btn-success:focus,\r
+.btn-success.disabled:active,\r
+.btn-success[disabled]:active,\r
+fieldset[disabled] .btn-success:active,\r
+.btn-success.disabled.active,\r
+.btn-success[disabled].active,\r
+fieldset[disabled] .btn-success.active {\r
+  background-color: #5cb85c;\r
+  border-color: #4cae4c;\r
+}\r
+.btn-success .badge {\r
+  color: #5cb85c;\r
+  background-color: #ffffff;\r
+}\r
+.btn-info {\r
+  color: #ffffff;\r
+  background-color: #5bc0de;\r
+  border-color: #46b8da;\r
+}\r
+.btn-info:hover,\r
+.btn-info:focus,\r
+.btn-info:active,\r
+.btn-info.active,\r
+.open .dropdown-toggle.btn-info {\r
+  color: #ffffff;\r
+  background-color: #39b3d7;\r
+  border-color: #269abc;\r
+}\r
+.btn-info:active,\r
+.btn-info.active,\r
+.open .dropdown-toggle.btn-info {\r
+  background-image: none;\r
+}\r
+.btn-info.disabled,\r
+.btn-info[disabled],\r
+fieldset[disabled] .btn-info,\r
+.btn-info.disabled:hover,\r
+.btn-info[disabled]:hover,\r
+fieldset[disabled] .btn-info:hover,\r
+.btn-info.disabled:focus,\r
+.btn-info[disabled]:focus,\r
+fieldset[disabled] .btn-info:focus,\r
+.btn-info.disabled:active,\r
+.btn-info[disabled]:active,\r
+fieldset[disabled] .btn-info:active,\r
+.btn-info.disabled.active,\r
+.btn-info[disabled].active,\r
+fieldset[disabled] .btn-info.active {\r
+  background-color: #5bc0de;\r
+  border-color: #46b8da;\r
+}\r
+.btn-info .badge {\r
+  color: #5bc0de;\r
+  background-color: #ffffff;\r
+}\r
+.btn-warning {\r
+  color: #ffffff;\r
+  background-color: #f0ad4e;\r
+  border-color: #eea236;\r
+}\r
+.btn-warning:hover,\r
+.btn-warning:focus,\r
+.btn-warning:active,\r
+.btn-warning.active,\r
+.open .dropdown-toggle.btn-warning {\r
+  color: #ffffff;\r
+  background-color: #ed9c28;\r
+  border-color: #d58512;\r
+}\r
+.btn-warning:active,\r
+.btn-warning.active,\r
+.open .dropdown-toggle.btn-warning {\r
+  background-image: none;\r
+}\r
+.btn-warning.disabled,\r
+.btn-warning[disabled],\r
+fieldset[disabled] .btn-warning,\r
+.btn-warning.disabled:hover,\r
+.btn-warning[disabled]:hover,\r
+fieldset[disabled] .btn-warning:hover,\r
+.btn-warning.disabled:focus,\r
+.btn-warning[disabled]:focus,\r
+fieldset[disabled] .btn-warning:focus,\r
+.btn-warning.disabled:active,\r
+.btn-warning[disabled]:active,\r
+fieldset[disabled] .btn-warning:active,\r
+.btn-warning.disabled.active,\r
+.btn-warning[disabled].active,\r
+fieldset[disabled] .btn-warning.active {\r
+  background-color: #f0ad4e;\r
+  border-color: #eea236;\r
+}\r
+.btn-warning .badge {\r
+  color: #f0ad4e;\r
+  background-color: #ffffff;\r
+}\r
+.btn-danger {\r
+  color: #ffffff;\r
+  background-color: #d9534f;\r
+  border-color: #d43f3a;\r
+}\r
+.btn-danger:hover,\r
+.btn-danger:focus,\r
+.btn-danger:active,\r
+.btn-danger.active,\r
+.open .dropdown-toggle.btn-danger {\r
+  color: #ffffff;\r
+  background-color: #d2322d;\r
+  border-color: #ac2925;\r
+}\r
+.btn-danger:active,\r
+.btn-danger.active,\r
+.open .dropdown-toggle.btn-danger {\r
+  background-image: none;\r
+}\r
+.btn-danger.disabled,\r
+.btn-danger[disabled],\r
+fieldset[disabled] .btn-danger,\r
+.btn-danger.disabled:hover,\r
+.btn-danger[disabled]:hover,\r
+fieldset[disabled] .btn-danger:hover,\r
+.btn-danger.disabled:focus,\r
+.btn-danger[disabled]:focus,\r
+fieldset[disabled] .btn-danger:focus,\r
+.btn-danger.disabled:active,\r
+.btn-danger[disabled]:active,\r
+fieldset[disabled] .btn-danger:active,\r
+.btn-danger.disabled.active,\r
+.btn-danger[disabled].active,\r
+fieldset[disabled] .btn-danger.active {\r
+  background-color: #d9534f;\r
+  border-color: #d43f3a;\r
+}\r
+.btn-danger .badge {\r
+  color: #d9534f;\r
+  background-color: #ffffff;\r
+}\r
+.btn-link {\r
+  color: #0d8921;\r
+  font-weight: normal;\r
+  cursor: pointer;\r
+  border-radius: 0;\r
+}\r
+.btn-link,\r
+.btn-link:active,\r
+.btn-link[disabled],\r
+fieldset[disabled] .btn-link {\r
+  background-color: transparent;\r
+  -webkit-box-shadow: none;\r
+  box-shadow: none;\r
+}\r
+.btn-link,\r
+.btn-link:hover,\r
+.btn-link:focus,\r
+.btn-link:active {\r
+  border-color: transparent;\r
+}\r
+.btn-link:hover,\r
+.btn-link:focus {\r
+  color: #064310;\r
+  text-decoration: underline;\r
+  background-color: transparent;\r
+}\r
+.btn-link[disabled]:hover,\r
+fieldset[disabled] .btn-link:hover,\r
+.btn-link[disabled]:focus,\r
+fieldset[disabled] .btn-link:focus {\r
+  color: #999999;\r
+  text-decoration: none;\r
+}\r
+.btn-lg,\r
+.btn-group-lg > .btn {\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  line-height: 1.33;\r
+  border-radius: 0px;\r
+}\r
+.btn-sm,\r
+.btn-group-sm > .btn {\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+.btn-xs,\r
+.btn-group-xs > .btn {\r
+  padding: 1px 5px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+.btn-block {\r
+  display: block;\r
+  width: 100%;\r
+  padding-left: 0;\r
+  padding-right: 0;\r
+}\r
+.btn-block + .btn-block {\r
+  margin-top: 5px;\r
+}\r
+input[type="submit"].btn-block,\r
+input[type="reset"].btn-block,\r
+input[type="button"].btn-block {\r
+  width: 100%;\r
+}\r
+.fade {\r
+  opacity: 0;\r
+  -webkit-transition: opacity 0.15s linear;\r
+  transition: opacity 0.15s linear;\r
+}\r
+.fade.in {\r
+  opacity: 1;\r
+}\r
+.collapse {\r
+  display: none;\r
+}\r
+.collapse.in {\r
+  display: block;\r
+}\r
+.collapsing {\r
+  position: relative;\r
+  height: 0;\r
+  overflow: hidden;\r
+  -webkit-transition: height 0.35s ease;\r
+  transition: height 0.35s ease;\r
+}\r
+@font-face {\r
+  font-family: 'Glyphicons Halflings';\r
+  src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.eot');\r
+  src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.woff') format('woff'), url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\r
+}\r
+.glyphicon {\r
+  position: relative;\r
+  top: 1px;\r
+  display: inline-block;\r
+  font-family: 'Glyphicons Halflings';\r
+  font-style: normal;\r
+  font-weight: normal;\r
+  line-height: 1;\r
+  -webkit-font-smoothing: antialiased;\r
+  -moz-osx-font-smoothing: grayscale;\r
+}\r
+.glyphicon-asterisk:before {\r
+  content: "\2a";\r
+}\r
+.glyphicon-plus:before {\r
+  content: "\2b";\r
+}\r
+.glyphicon-euro:before {\r
+  content: "\20ac";\r
+}\r
+.glyphicon-minus:before {\r
+  content: "\2212";\r
+}\r
+.glyphicon-cloud:before {\r
+  content: "\2601";\r
+}\r
+.glyphicon-envelope:before {\r
+  content: "\2709";\r
+}\r
+.glyphicon-pencil:before {\r
+  content: "\270f";\r
+}\r
+.glyphicon-glass:before {\r
+  content: "\e001";\r
+}\r
+.glyphicon-music:before {\r
+  content: "\e002";\r
+}\r
+.glyphicon-search:before {\r
+  content: "\e003";\r
+}\r
+.glyphicon-heart:before {\r
+  content: "\e005";\r
+}\r
+.glyphicon-star:before {\r
+  content: "\e006";\r
+}\r
+.glyphicon-star-empty:before {\r
+  content: "\e007";\r
+}\r
+.glyphicon-user:before {\r
+  content: "\e008";\r
+}\r
+.glyphicon-film:before {\r
+  content: "\e009";\r
+}\r
+.glyphicon-th-large:before {\r
+  content: "\e010";\r
+}\r
+.glyphicon-th:before {\r
+  content: "\e011";\r
+}\r
+.glyphicon-th-list:before {\r
+  content: "\e012";\r
+}\r
+.glyphicon-ok:before {\r
+  content: "\e013";\r
+}\r
+.glyphicon-remove:before {\r
+  content: "\e014";\r
+}\r
+.glyphicon-zoom-in:before {\r
+  content: "\e015";\r
+}\r
+.glyphicon-zoom-out:before {\r
+  content: "\e016";\r
+}\r
+.glyphicon-off:before {\r
+  content: "\e017";\r
+}\r
+.glyphicon-signal:before {\r
+  content: "\e018";\r
+}\r
+.glyphicon-cog:before {\r
+  content: "\e019";\r
+}\r
+.glyphicon-trash:before {\r
+  content: "\e020";\r
+}\r
+.glyphicon-home:before {\r
+  content: "\e021";\r
+}\r
+.glyphicon-file:before {\r
+  content: "\e022";\r
+}\r
+.glyphicon-time:before {\r
+  content: "\e023";\r
+}\r
+.glyphicon-road:before {\r
+  content: "\e024";\r
+}\r
+.glyphicon-download-alt:before {\r
+  content: "\e025";\r
+}\r
+.glyphicon-download:before {\r
+  content: "\e026";\r
+}\r
+.glyphicon-upload:before {\r
+  content: "\e027";\r
+}\r
+.glyphicon-inbox:before {\r
+  content: "\e028";\r
+}\r
+.glyphicon-play-circle:before {\r
+  content: "\e029";\r
+}\r
+.glyphicon-repeat:before {\r
+  content: "\e030";\r
+}\r
+.glyphicon-refresh:before {\r
+  content: "\e031";\r
+}\r
+.glyphicon-list-alt:before {\r
+  content: "\e032";\r
+}\r
+.glyphicon-lock:before {\r
+  content: "\e033";\r
+}\r
+.glyphicon-flag:before {\r
+  content: "\e034";\r
+}\r
+.glyphicon-headphones:before {\r
+  content: "\e035";\r
+}\r
+.glyphicon-volume-off:before {\r
+  content: "\e036";\r
+}\r
+.glyphicon-volume-down:before {\r
+  content: "\e037";\r
+}\r
+.glyphicon-volume-up:before {\r
+  content: "\e038";\r
+}\r
+.glyphicon-qrcode:before {\r
+  content: "\e039";\r
+}\r
+.glyphicon-barcode:before {\r
+  content: "\e040";\r
+}\r
+.glyphicon-tag:before {\r
+  content: "\e041";\r
+}\r
+.glyphicon-tags:before {\r
+  content: "\e042";\r
+}\r
+.glyphicon-book:before {\r
+  content: "\e043";\r
+}\r
+.glyphicon-bookmark:before {\r
+  content: "\e044";\r
+}\r
+.glyphicon-print:before {\r
+  content: "\e045";\r
+}\r
+.glyphicon-camera:before {\r
+  content: "\e046";\r
+}\r
+.glyphicon-font:before {\r
+  content: "\e047";\r
+}\r
+.glyphicon-bold:before {\r
+  content: "\e048";\r
+}\r
+.glyphicon-italic:before {\r
+  content: "\e049";\r
+}\r
+.glyphicon-text-height:before {\r
+  content: "\e050";\r
+}\r
+.glyphicon-text-width:before {\r
+  content: "\e051";\r
+}\r
+.glyphicon-align-left:before {\r
+  content: "\e052";\r
+}\r
+.glyphicon-align-center:before {\r
+  content: "\e053";\r
+}\r
+.glyphicon-align-right:before {\r
+  content: "\e054";\r
+}\r
+.glyphicon-align-justify:before {\r
+  content: "\e055";\r
+}\r
+.glyphicon-list:before {\r
+  content: "\e056";\r
+}\r
+.glyphicon-indent-left:before {\r
+  content: "\e057";\r
+}\r
+.glyphicon-indent-right:before {\r
+  content: "\e058";\r
+}\r
+.glyphicon-facetime-video:before {\r
+  content: "\e059";\r
+}\r
+.glyphicon-picture:before {\r
+  content: "\e060";\r
+}\r
+.glyphicon-map-marker:before {\r
+  content: "\e062";\r
+}\r
+.glyphicon-adjust:before {\r
+  content: "\e063";\r
+}\r
+.glyphicon-tint:before {\r
+  content: "\e064";\r
+}\r
+.glyphicon-edit:before {\r
+  content: "\e065";\r
+}\r
+.glyphicon-share:before {\r
+  content: "\e066";\r
+}\r
+.glyphicon-check:before {\r
+  content: "\e067";\r
+}\r
+.glyphicon-move:before {\r
+  content: "\e068";\r
+}\r
+.glyphicon-step-backward:before {\r
+  content: "\e069";\r
+}\r
+.glyphicon-fast-backward:before {\r
+  content: "\e070";\r
+}\r
+.glyphicon-backward:before {\r
+  content: "\e071";\r
+}\r
+.glyphicon-play:before {\r
+  content: "\e072";\r
+}\r
+.glyphicon-pause:before {\r
+  content: "\e073";\r
+}\r
+.glyphicon-stop:before {\r
+  content: "\e074";\r
+}\r
+.glyphicon-forward:before {\r
+  content: "\e075";\r
+}\r
+.glyphicon-fast-forward:before {\r
+  content: "\e076";\r
+}\r
+.glyphicon-step-forward:before {\r
+  content: "\e077";\r
+}\r
+.glyphicon-eject:before {\r
+  content: "\e078";\r
+}\r
+.glyphicon-chevron-left:before {\r
+  content: "\e079";\r
+}\r
+.glyphicon-chevron-right:before {\r
+  content: "\e080";\r
+}\r
+.glyphicon-plus-sign:before {\r
+  content: "\e081";\r
+}\r
+.glyphicon-minus-sign:before {\r
+  content: "\e082";\r
+}\r
+.glyphicon-remove-sign:before {\r
+  content: "\e083";\r
+}\r
+.glyphicon-ok-sign:before {\r
+  content: "\e084";\r
+}\r
+.glyphicon-question-sign:before {\r
+  content: "\e085";\r
+}\r
+.glyphicon-info-sign:before {\r
+  content: "\e086";\r
+}\r
+.glyphicon-screenshot:before {\r
+  content: "\e087";\r
+}\r
+.glyphicon-remove-circle:before {\r
+  content: "\e088";\r
+}\r
+.glyphicon-ok-circle:before {\r
+  content: "\e089";\r
+}\r
+.glyphicon-ban-circle:before {\r
+  content: "\e090";\r
+}\r
+.glyphicon-arrow-left:before {\r
+  content: "\e091";\r
+}\r
+.glyphicon-arrow-right:before {\r
+  content: "\e092";\r
+}\r
+.glyphicon-arrow-up:before {\r
+  content: "\e093";\r
+}\r
+.glyphicon-arrow-down:before {\r
+  content: "\e094";\r
+}\r
+.glyphicon-share-alt:before {\r
+  content: "\e095";\r
+}\r
+.glyphicon-resize-full:before {\r
+  content: "\e096";\r
+}\r
+.glyphicon-resize-small:before {\r
+  content: "\e097";\r
+}\r
+.glyphicon-exclamation-sign:before {\r
+  content: "\e101";\r
+}\r
+.glyphicon-gift:before {\r
+  content: "\e102";\r
+}\r
+.glyphicon-leaf:before {\r
+  content: "\e103";\r
+}\r
+.glyphicon-fire:before {\r
+  content: "\e104";\r
+}\r
+.glyphicon-eye-open:before {\r
+  content: "\e105";\r
+}\r
+.glyphicon-eye-close:before {\r
+  content: "\e106";\r
+}\r
+.glyphicon-warning-sign:before {\r
+  content: "\e107";\r
+}\r
+.glyphicon-plane:before {\r
+  content: "\e108";\r
+}\r
+.glyphicon-calendar:before {\r
+  content: "\e109";\r
+}\r
+.glyphicon-random:before {\r
+  content: "\e110";\r
+}\r
+.glyphicon-comment:before {\r
+  content: "\e111";\r
+}\r
+.glyphicon-magnet:before {\r
+  content: "\e112";\r
+}\r
+.glyphicon-chevron-up:before {\r
+  content: "\e113";\r
+}\r
+.glyphicon-chevron-down:before {\r
+  content: "\e114";\r
+}\r
+.glyphicon-retweet:before {\r
+  content: "\e115";\r
+}\r
+.glyphicon-shopping-cart:before {\r
+  content: "\e116";\r
+}\r
+.glyphicon-folder-close:before {\r
+  content: "\e117";\r
+}\r
+.glyphicon-folder-open:before {\r
+  content: "\e118";\r
+}\r
+.glyphicon-resize-vertical:before {\r
+  content: "\e119";\r
+}\r
+.glyphicon-resize-horizontal:before {\r
+  content: "\e120";\r
+}\r
+.glyphicon-hdd:before {\r
+  content: "\e121";\r
+}\r
+.glyphicon-bullhorn:before {\r
+  content: "\e122";\r
+}\r
+.glyphicon-bell:before {\r
+  content: "\e123";\r
+}\r
+.glyphicon-certificate:before {\r
+  content: "\e124";\r
+}\r
+.glyphicon-thumbs-up:before {\r
+  content: "\e125";\r
+}\r
+.glyphicon-thumbs-down:before {\r
+  content: "\e126";\r
+}\r
+.glyphicon-hand-right:before {\r
+  content: "\e127";\r
+}\r
+.glyphicon-hand-left:before {\r
+  content: "\e128";\r
+}\r
+.glyphicon-hand-up:before {\r
+  content: "\e129";\r
+}\r
+.glyphicon-hand-down:before {\r
+  content: "\e130";\r
+}\r
+.glyphicon-circle-arrow-right:before {\r
+  content: "\e131";\r
+}\r
+.glyphicon-circle-arrow-left:before {\r
+  content: "\e132";\r
+}\r
+.glyphicon-circle-arrow-up:before {\r
+  content: "\e133";\r
+}\r
+.glyphicon-circle-arrow-down:before {\r
+  content: "\e134";\r
+}\r
+.glyphicon-globe:before {\r
+  content: "\e135";\r
+}\r
+.glyphicon-wrench:before {\r
+  content: "\e136";\r
+}\r
+.glyphicon-tasks:before {\r
+  content: "\e137";\r
+}\r
+.glyphicon-filter:before {\r
+  content: "\e138";\r
+}\r
+.glyphicon-briefcase:before {\r
+  content: "\e139";\r
+}\r
+.glyphicon-fullscreen:before {\r
+  content: "\e140";\r
+}\r
+.glyphicon-dashboard:before {\r
+  content: "\e141";\r
+}\r
+.glyphicon-paperclip:before {\r
+  content: "\e142";\r
+}\r
+.glyphicon-heart-empty:before {\r
+  content: "\e143";\r
+}\r
+.glyphicon-link:before {\r
+  content: "\e144";\r
+}\r
+.glyphicon-phone:before {\r
+  content: "\e145";\r
+}\r
+.glyphicon-pushpin:before {\r
+  content: "\e146";\r
+}\r
+.glyphicon-usd:before {\r
+  content: "\e148";\r
+}\r
+.glyphicon-gbp:before {\r
+  content: "\e149";\r
+}\r
+.glyphicon-sort:before {\r
+  content: "\e150";\r
+}\r
+.glyphicon-sort-by-alphabet:before {\r
+  content: "\e151";\r
+}\r
+.glyphicon-sort-by-alphabet-alt:before {\r
+  content: "\e152";\r
+}\r
+.glyphicon-sort-by-order:before {\r
+  content: "\e153";\r
+}\r
+.glyphicon-sort-by-order-alt:before {\r
+  content: "\e154";\r
+}\r
+.glyphicon-sort-by-attributes:before {\r
+  content: "\e155";\r
+}\r
+.glyphicon-sort-by-attributes-alt:before {\r
+  content: "\e156";\r
+}\r
+.glyphicon-unchecked:before {\r
+  content: "\e157";\r
+}\r
+.glyphicon-expand:before {\r
+  content: "\e158";\r
+}\r
+.glyphicon-collapse-down:before {\r
+  content: "\e159";\r
+}\r
+.glyphicon-collapse-up:before {\r
+  content: "\e160";\r
+}\r
+.glyphicon-log-in:before {\r
+  content: "\e161";\r
+}\r
+.glyphicon-flash:before {\r
+  content: "\e162";\r
+}\r
+.glyphicon-log-out:before {\r
+  content: "\e163";\r
+}\r
+.glyphicon-new-window:before {\r
+  content: "\e164";\r
+}\r
+.glyphicon-record:before {\r
+  content: "\e165";\r
+}\r
+.glyphicon-save:before {\r
+  content: "\e166";\r
+}\r
+.glyphicon-open:before {\r
+  content: "\e167";\r
+}\r
+.glyphicon-saved:before {\r
+  content: "\e168";\r
+}\r
+.glyphicon-import:before {\r
+  content: "\e169";\r
+}\r
+.glyphicon-export:before {\r
+  content: "\e170";\r
+}\r
+.glyphicon-send:before {\r
+  content: "\e171";\r
+}\r
+.glyphicon-floppy-disk:before {\r
+  content: "\e172";\r
+}\r
+.glyphicon-floppy-saved:before {\r
+  content: "\e173";\r
+}\r
+.glyphicon-floppy-remove:before {\r
+  content: "\e174";\r
+}\r
+.glyphicon-floppy-save:before {\r
+  content: "\e175";\r
+}\r
+.glyphicon-floppy-open:before {\r
+  content: "\e176";\r
+}\r
+.glyphicon-credit-card:before {\r
+  content: "\e177";\r
+}\r
+.glyphicon-transfer:before {\r
+  content: "\e178";\r
+}\r
+.glyphicon-cutlery:before {\r
+  content: "\e179";\r
+}\r
+.glyphicon-header:before {\r
+  content: "\e180";\r
+}\r
+.glyphicon-compressed:before {\r
+  content: "\e181";\r
+}\r
+.glyphicon-earphone:before {\r
+  content: "\e182";\r
+}\r
+.glyphicon-phone-alt:before {\r
+  content: "\e183";\r
+}\r
+.glyphicon-tower:before {\r
+  content: "\e184";\r
+}\r
+.glyphicon-stats:before {\r
+  content: "\e185";\r
+}\r
+.glyphicon-sd-video:before {\r
+  content: "\e186";\r
+}\r
+.glyphicon-hd-video:before {\r
+  content: "\e187";\r
+}\r
+.glyphicon-subtitles:before {\r
+  content: "\e188";\r
+}\r
+.glyphicon-sound-stereo:before {\r
+  content: "\e189";\r
+}\r
+.glyphicon-sound-dolby:before {\r
+  content: "\e190";\r
+}\r
+.glyphicon-sound-5-1:before {\r
+  content: "\e191";\r
+}\r
+.glyphicon-sound-6-1:before {\r
+  content: "\e192";\r
+}\r
+.glyphicon-sound-7-1:before {\r
+  content: "\e193";\r
+}\r
+.glyphicon-copyright-mark:before {\r
+  content: "\e194";\r
+}\r
+.glyphicon-registration-mark:before {\r
+  content: "\e195";\r
+}\r
+.glyphicon-cloud-download:before {\r
+  content: "\e197";\r
+}\r
+.glyphicon-cloud-upload:before {\r
+  content: "\e198";\r
+}\r
+.glyphicon-tree-conifer:before {\r
+  content: "\e199";\r
+}\r
+.glyphicon-tree-deciduous:before {\r
+  content: "\e200";\r
+}\r
+.caret {\r
+  display: inline-block;\r
+  width: 0;\r
+  height: 0;\r
+  margin-left: 2px;\r
+  vertical-align: middle;\r
+  border-top: 4px solid;\r
+  border-right: 4px solid transparent;\r
+  border-left: 4px solid transparent;\r
+}\r
+.dropdown {\r
+  position: relative;\r
+}\r
+.dropdown-toggle:focus {\r
+  outline: 0;\r
+}\r
+.dropdown-menu {\r
+  position: absolute;\r
+  top: 100%;\r
+  left: 0;\r
+  z-index: 1000;\r
+  display: none;\r
+  float: left;\r
+  min-width: 160px;\r
+  padding: 5px 0;\r
+  margin: 2px 0 0;\r
+  list-style: none;\r
+  font-size: 14px;\r
+  background-color: #ffffff;\r
+  border: 1px solid #cccccc;\r
+  border: 1px solid rgba(0, 0, 0, 0.15);\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\r
+  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\r
+  background-clip: padding-box;\r
+}\r
+.dropdown-menu.pull-right {\r
+  right: 0;\r
+  left: auto;\r
+}\r
+.dropdown-menu .divider {\r
+  height: 1px;\r
+  margin: 9px 0;\r
+  overflow: hidden;\r
+  background-color: #e5e5e5;\r
+}\r
+.dropdown-menu > li > a {\r
+  display: block;\r
+  padding: 3px 20px;\r
+  clear: both;\r
+  font-weight: normal;\r
+  line-height: 1.428571429;\r
+  color: #333333;\r
+  white-space: nowrap;\r
+}\r
+.dropdown-menu > li > a:hover,\r
+.dropdown-menu > li > a:focus {\r
+  text-decoration: none;\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+}\r
+.dropdown-menu > .active > a,\r
+.dropdown-menu > .active > a:hover,\r
+.dropdown-menu > .active > a:focus {\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  outline: 0;\r
+  background-color: #0d8921;\r
+}\r
+.dropdown-menu > .disabled > a,\r
+.dropdown-menu > .disabled > a:hover,\r
+.dropdown-menu > .disabled > a:focus {\r
+  color: #999999;\r
+}\r
+.dropdown-menu > .disabled > a:hover,\r
+.dropdown-menu > .disabled > a:focus {\r
+  text-decoration: none;\r
+  background-color: transparent;\r
+  background-image: none;\r
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\r
+  cursor: not-allowed;\r
+}\r
+.open > .dropdown-menu {\r
+  display: block;\r
+}\r
+.open > a {\r
+  outline: 0;\r
+}\r
+.dropdown-menu-right {\r
+  left: auto;\r
+  right: 0;\r
+}\r
+.dropdown-menu-left {\r
+  left: 0;\r
+  right: auto;\r
+}\r
+.dropdown-header {\r
+  display: block;\r
+  padding: 3px 20px;\r
+  font-size: 12px;\r
+  line-height: 1.428571429;\r
+  color: #999999;\r
+}\r
+.dropdown-backdrop {\r
+  position: fixed;\r
+  left: 0;\r
+  right: 0;\r
+  bottom: 0;\r
+  top: 0;\r
+  z-index: 990;\r
+}\r
+.pull-right > .dropdown-menu {\r
+  right: 0;\r
+  left: auto;\r
+}\r
+.dropup .caret,\r
+.navbar-fixed-bottom .dropdown .caret {\r
+  border-top: 0;\r
+  border-bottom: 4px solid;\r
+  content: "";\r
+}\r
+.dropup .dropdown-menu,\r
+.navbar-fixed-bottom .dropdown .dropdown-menu {\r
+  top: auto;\r
+  bottom: 100%;\r
+  margin-bottom: 1px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-right .dropdown-menu {\r
+    left: auto;\r
+    right: 0;\r
+  }\r
+  .navbar-right .dropdown-menu-left {\r
+    left: 0;\r
+    right: auto;\r
+  }\r
+}\r
+.btn-group,\r
+.btn-group-vertical {\r
+  position: relative;\r
+  display: inline-block;\r
+  vertical-align: middle;\r
+}\r
+.btn-group > .btn,\r
+.btn-group-vertical > .btn {\r
+  position: relative;\r
+  float: left;\r
+}\r
+.btn-group > .btn:hover,\r
+.btn-group-vertical > .btn:hover,\r
+.btn-group > .btn:focus,\r
+.btn-group-vertical > .btn:focus,\r
+.btn-group > .btn:active,\r
+.btn-group-vertical > .btn:active,\r
+.btn-group > .btn.active,\r
+.btn-group-vertical > .btn.active {\r
+  z-index: 2;\r
+}\r
+.btn-group > .btn:focus,\r
+.btn-group-vertical > .btn:focus {\r
+  outline: none;\r
+}\r
+.btn-group .btn + .btn,\r
+.btn-group .btn + .btn-group,\r
+.btn-group .btn-group + .btn,\r
+.btn-group .btn-group + .btn-group {\r
+  margin-left: -1px;\r
+}\r
+.btn-toolbar {\r
+  margin-left: -5px;\r
+}\r
+.btn-toolbar .btn-group,\r
+.btn-toolbar .input-group {\r
+  float: left;\r
+}\r
+.btn-toolbar > .btn,\r
+.btn-toolbar > .btn-group,\r
+.btn-toolbar > .input-group {\r
+  margin-left: 5px;\r
+}\r
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\r
+  border-radius: 0;\r
+}\r
+.btn-group > .btn:first-child {\r
+  margin-left: 0;\r
+}\r
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\r
+  border-bottom-right-radius: 0;\r
+  border-top-right-radius: 0;\r
+}\r
+.btn-group > .btn:last-child:not(:first-child),\r
+.btn-group > .dropdown-toggle:not(:first-child) {\r
+  border-bottom-left-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group > .btn-group {\r
+  float: left;\r
+}\r
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\r
+  border-radius: 0;\r
+}\r
+.btn-group > .btn-group:first-child > .btn:last-child,\r
+.btn-group > .btn-group:first-child > .dropdown-toggle {\r
+  border-bottom-right-radius: 0;\r
+  border-top-right-radius: 0;\r
+}\r
+.btn-group > .btn-group:last-child > .btn:first-child {\r
+  border-bottom-left-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group .dropdown-toggle:active,\r
+.btn-group.open .dropdown-toggle {\r
+  outline: 0;\r
+}\r
+.btn-group > .btn + .dropdown-toggle {\r
+  padding-left: 8px;\r
+  padding-right: 8px;\r
+}\r
+.btn-group > .btn-lg + .dropdown-toggle {\r
+  padding-left: 12px;\r
+  padding-right: 12px;\r
+}\r
+.btn-group.open .dropdown-toggle {\r
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+}\r
+.btn-group.open .dropdown-toggle.btn-link {\r
+  -webkit-box-shadow: none;\r
+  box-shadow: none;\r
+}\r
+.btn .caret {\r
+  margin-left: 0;\r
+}\r
+.btn-lg .caret {\r
+  border-width: 5px 5px 0;\r
+  border-bottom-width: 0;\r
+}\r
+.dropup .btn-lg .caret {\r
+  border-width: 0 5px 5px;\r
+}\r
+.btn-group-vertical > .btn,\r
+.btn-group-vertical > .btn-group,\r
+.btn-group-vertical > .btn-group > .btn {\r
+  display: block;\r
+  float: none;\r
+  width: 100%;\r
+  max-width: 100%;\r
+}\r
+.btn-group-vertical > .btn-group > .btn {\r
+  float: none;\r
+}\r
+.btn-group-vertical > .btn + .btn,\r
+.btn-group-vertical > .btn + .btn-group,\r
+.btn-group-vertical > .btn-group + .btn,\r
+.btn-group-vertical > .btn-group + .btn-group {\r
+  margin-top: -1px;\r
+  margin-left: 0;\r
+}\r
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\r
+  border-radius: 0;\r
+}\r
+.btn-group-vertical > .btn:first-child:not(:last-child) {\r
+  border-top-right-radius: 0px;\r
+  border-bottom-right-radius: 0;\r
+  border-bottom-left-radius: 0;\r
+}\r
+.btn-group-vertical > .btn:last-child:not(:first-child) {\r
+  border-bottom-left-radius: 0px;\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\r
+  border-radius: 0;\r
+}\r
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\r
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\r
+  border-bottom-right-radius: 0;\r
+  border-bottom-left-radius: 0;\r
+}\r
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group-justified {\r
+  display: table;\r
+  width: 100%;\r
+  table-layout: fixed;\r
+  border-collapse: separate;\r
+}\r
+.btn-group-justified > .btn,\r
+.btn-group-justified > .btn-group {\r
+  float: none;\r
+  display: table-cell;\r
+  width: 1%;\r
+}\r
+.btn-group-justified > .btn-group .btn {\r
+  width: 100%;\r
+}\r
+[data-toggle="buttons"] > .btn > input[type="radio"],\r
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {\r
+  display: none;\r
+}\r
+.input-group {\r
+  position: relative;\r
+  display: table;\r
+  border-collapse: separate;\r
+}\r
+.input-group[class*="col-"] {\r
+  float: none;\r
+  padding-left: 0;\r
+  padding-right: 0;\r
+}\r
+.input-group .form-control {\r
+  float: left;\r
+  width: 100%;\r
+  margin-bottom: 0;\r
+}\r
+.input-group-lg > .form-control,\r
+.input-group-lg > .input-group-addon,\r
+.input-group-lg > .input-group-btn > .btn {\r
+  height: 45px;\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  line-height: 1.33;\r
+  border-radius: 0px;\r
+}\r
+select.input-group-lg > .form-control,\r
+select.input-group-lg > .input-group-addon,\r
+select.input-group-lg > .input-group-btn > .btn {\r
+  height: 45px;\r
+  line-height: 45px;\r
+}\r
+textarea.input-group-lg > .form-control,\r
+textarea.input-group-lg > .input-group-addon,\r
+textarea.input-group-lg > .input-group-btn > .btn,\r
+select[multiple].input-group-lg > .form-control,\r
+select[multiple].input-group-lg > .input-group-addon,\r
+select[multiple].input-group-lg > .input-group-btn > .btn {\r
+  height: auto;\r
+}\r
+.input-group-sm > .form-control,\r
+.input-group-sm > .input-group-addon,\r
+.input-group-sm > .input-group-btn > .btn {\r
+  height: 30px;\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+select.input-group-sm > .form-control,\r
+select.input-group-sm > .input-group-addon,\r
+select.input-group-sm > .input-group-btn > .btn {\r
+  height: 30px;\r
+  line-height: 30px;\r
+}\r
+textarea.input-group-sm > .form-control,\r
+textarea.input-group-sm > .input-group-addon,\r
+textarea.input-group-sm > .input-group-btn > .btn,\r
+select[multiple].input-group-sm > .form-control,\r
+select[multiple].input-group-sm > .input-group-addon,\r
+select[multiple].input-group-sm > .input-group-btn > .btn {\r
+  height: auto;\r
+}\r
+.input-group-addon,\r
+.input-group-btn,\r
+.input-group .form-control {\r
+  display: table-cell;\r
+}\r
+.input-group-addon:not(:first-child):not(:last-child),\r
+.input-group-btn:not(:first-child):not(:last-child),\r
+.input-group .form-control:not(:first-child):not(:last-child) {\r
+  border-radius: 0;\r
+}\r
+.input-group-addon,\r
+.input-group-btn {\r
+  width: 1%;\r
+  white-space: nowrap;\r
+  vertical-align: middle;\r
+}\r
+.input-group-addon {\r
+  padding: 6px 12px;\r
+  font-size: 14px;\r
+  font-weight: normal;\r
+  line-height: 1;\r
+  color: #555555;\r
+  text-align: center;\r
+  background-color: #eeeeee;\r
+  border: 1px solid #cccccc;\r
+  border-radius: 0px;\r
+}\r
+.input-group-addon.input-sm {\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  border-radius: 0px;\r
+}\r
+.input-group-addon.input-lg {\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  border-radius: 0px;\r
+}\r
+.input-group-addon input[type="radio"],\r
+.input-group-addon input[type="checkbox"] {\r
+  margin-top: 0;\r
+}\r
+.input-group .form-control:first-child,\r
+.input-group-addon:first-child,\r
+.input-group-btn:first-child > .btn,\r
+.input-group-btn:first-child > .btn-group > .btn,\r
+.input-group-btn:first-child > .dropdown-toggle,\r
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\r
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\r
+  border-bottom-right-radius: 0;\r
+  border-top-right-radius: 0;\r
+}\r
+.input-group-addon:first-child {\r
+  border-right: 0;\r
+}\r
+.input-group .form-control:last-child,\r
+.input-group-addon:last-child,\r
+.input-group-btn:last-child > .btn,\r
+.input-group-btn:last-child > .btn-group > .btn,\r
+.input-group-btn:last-child > .dropdown-toggle,\r
+.input-group-btn:first-child > .btn:not(:first-child),\r
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\r
+  border-bottom-left-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.input-group-addon:last-child {\r
+  border-left: 0;\r
+}\r
+.input-group-btn {\r
+  position: relative;\r
+  font-size: 0;\r
+  white-space: nowrap;\r
+}\r
+.input-group-btn > .btn {\r
+  position: relative;\r
+}\r
+.input-group-btn > .btn + .btn {\r
+  margin-left: -1px;\r
+}\r
+.input-group-btn > .btn:hover,\r
+.input-group-btn > .btn:focus,\r
+.input-group-btn > .btn:active {\r
+  z-index: 2;\r
+}\r
+.input-group-btn:first-child > .btn,\r
+.input-group-btn:first-child > .btn-group {\r
+  margin-right: -1px;\r
+}\r
+.input-group-btn:last-child > .btn,\r
+.input-group-btn:last-child > .btn-group {\r
+  margin-left: -1px;\r
+}\r
+.nav {\r
+  margin-bottom: 0;\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.nav > li {\r
+  position: relative;\r
+  display: block;\r
+}\r
+.nav > li > a {\r
+  position: relative;\r
+  display: block;\r
+  padding: 10px 15px;\r
+}\r
+.nav > li > a:hover,\r
+.nav > li > a:focus {\r
+  text-decoration: none;\r
+  background-color: #dddddd;\r
+}\r
+.nav > li.disabled > a {\r
+  color: #999999;\r
+}\r
+.nav > li.disabled > a:hover,\r
+.nav > li.disabled > a:focus {\r
+  color: #999999;\r
+  text-decoration: none;\r
+  background-color: transparent;\r
+  cursor: not-allowed;\r
+}\r
+.nav .open > a,\r
+.nav .open > a:hover,\r
+.nav .open > a:focus {\r
+  background-color: #dddddd;\r
+  border-color: #0d8921;\r
+}\r
+.nav .nav-divider {\r
+  height: 1px;\r
+  margin: 9px 0;\r
+  overflow: hidden;\r
+  background-color: #e5e5e5;\r
+}\r
+.nav > li > a > img {\r
+  max-width: none;\r
+}\r
+.nav-tabs {\r
+  border-bottom: 1px solid #dddddd;\r
+}\r
+.nav-tabs > li {\r
+  float: left;\r
+  margin-bottom: -1px;\r
+}\r
+.nav-tabs > li > a {\r
+  margin-right: 2px;\r
+  line-height: 1.428571429;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px 0px 0 0;\r
+}\r
+.nav-tabs > li > a:hover {\r
+  border-color: #eeeeee #eeeeee #dddddd;\r
+}\r
+.nav-tabs > li.active > a,\r
+.nav-tabs > li.active > a:hover,\r
+.nav-tabs > li.active > a:focus {\r
+  color: #555555;\r
+  background-color: #f2f2f2;\r
+  border: 1px solid #dddddd;\r
+  border-bottom-color: transparent;\r
+  cursor: default;\r
+}\r
+.nav-tabs.nav-justified {\r
+  width: 100%;\r
+  border-bottom: 0;\r
+}\r
+.nav-tabs.nav-justified > li {\r
+  float: none;\r
+}\r
+.nav-tabs.nav-justified > li > a {\r
+  text-align: center;\r
+  margin-bottom: 5px;\r
+}\r
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {\r
+  top: auto;\r
+  left: auto;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-tabs.nav-justified > li {\r
+    display: table-cell;\r
+    width: 1%;\r
+  }\r
+  .nav-tabs.nav-justified > li > a {\r
+    margin-bottom: 0;\r
+  }\r
+}\r
+.nav-tabs.nav-justified > li > a {\r
+  margin-right: 0;\r
+  border-radius: 0px;\r
+}\r
+.nav-tabs.nav-justified > .active > a,\r
+.nav-tabs.nav-justified > .active > a:hover,\r
+.nav-tabs.nav-justified > .active > a:focus {\r
+  border: 1px solid #dddddd;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-tabs.nav-justified > li > a {\r
+    border-bottom: 1px solid #dddddd;\r
+    border-radius: 0px 0px 0 0;\r
+  }\r
+  .nav-tabs.nav-justified > .active > a,\r
+  .nav-tabs.nav-justified > .active > a:hover,\r
+  .nav-tabs.nav-justified > .active > a:focus {\r
+    border-bottom-color: #f2f2f2;\r
+  }\r
+}\r
+.nav-pills > li {\r
+  float: left;\r
+}\r
+.nav-pills > li > a {\r
+  border-radius: 0px;\r
+}\r
+.nav-pills > li + li {\r
+  margin-left: 2px;\r
+}\r
+.nav-pills > li.active > a,\r
+.nav-pills > li.active > a:hover,\r
+.nav-pills > li.active > a:focus {\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+}\r
+.nav-stacked > li {\r
+  float: none;\r
+}\r
+.nav-stacked > li + li {\r
+  margin-top: 2px;\r
+  margin-left: 0;\r
+}\r
+.nav-justified {\r
+  width: 100%;\r
+}\r
+.nav-justified > li {\r
+  float: none;\r
+}\r
+.nav-justified > li > a {\r
+  text-align: center;\r
+  margin-bottom: 5px;\r
+}\r
+.nav-justified > .dropdown .dropdown-menu {\r
+  top: auto;\r
+  left: auto;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-justified > li {\r
+    display: table-cell;\r
+    width: 1%;\r
+  }\r
+  .nav-justified > li > a {\r
+    margin-bottom: 0;\r
+  }\r
+}\r
+.nav-tabs-justified {\r
+  border-bottom: 0;\r
+}\r
+.nav-tabs-justified > li > a {\r
+  margin-right: 0;\r
+  border-radius: 0px;\r
+}\r
+.nav-tabs-justified > .active > a,\r
+.nav-tabs-justified > .active > a:hover,\r
+.nav-tabs-justified > .active > a:focus {\r
+  border: 1px solid #dddddd;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-tabs-justified > li > a {\r
+    border-bottom: 1px solid #dddddd;\r
+    border-radius: 0px 0px 0 0;\r
+  }\r
+  .nav-tabs-justified > .active > a,\r
+  .nav-tabs-justified > .active > a:hover,\r
+  .nav-tabs-justified > .active > a:focus {\r
+    border-bottom-color: #f2f2f2;\r
+  }\r
+}\r
+.tab-content > .tab-pane {\r
+  display: none;\r
+}\r
+.tab-content > .active {\r
+  display: block;\r
+}\r
+.nav-tabs .dropdown-menu {\r
+  margin-top: -1px;\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.navbar {\r
+  position: relative;\r
+  min-height: 50px;\r
+  margin-bottom: 20px;\r
+  border: 1px solid transparent;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar {\r
+    border-radius: 0px;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-header {\r
+    float: left;\r
+  }\r
+}\r
+.navbar-collapse {\r
+  max-height: 340px;\r
+  overflow-x: visible;\r
+  padding-right: 15px;\r
+  padding-left: 15px;\r
+  border-top: 1px solid transparent;\r
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\r
+  -webkit-overflow-scrolling: touch;\r
+}\r
+.navbar-collapse.in {\r
+  overflow-y: auto;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-collapse {\r
+    width: auto;\r
+    border-top: 0;\r
+    box-shadow: none;\r
+  }\r
+  .navbar-collapse.collapse {\r
+    display: block !important;\r
+    height: auto !important;\r
+    padding-bottom: 0;\r
+    overflow: visible !important;\r
+  }\r
+  .navbar-collapse.in {\r
+    overflow-y: visible;\r
+  }\r
+  .navbar-fixed-top .navbar-collapse,\r
+  .navbar-static-top .navbar-collapse,\r
+  .navbar-fixed-bottom .navbar-collapse {\r
+    padding-left: 0;\r
+    padding-right: 0;\r
+  }\r
+}\r
+.container > .navbar-header,\r
+.container-fluid > .navbar-header,\r
+.container > .navbar-collapse,\r
+.container-fluid > .navbar-collapse {\r
+  margin-right: -15px;\r
+  margin-left: -15px;\r
+}\r
+@media (min-width: 768px) {\r
+  .container > .navbar-header,\r
+  .container-fluid > .navbar-header,\r
+  .container > .navbar-collapse,\r
+  .container-fluid > .navbar-collapse {\r
+    margin-right: 0;\r
+    margin-left: 0;\r
+  }\r
+}\r
+.navbar-static-top {\r
+  z-index: 1000;\r
+  border-width: 0 0 1px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-static-top {\r
+    border-radius: 0;\r
+  }\r
+}\r
+.navbar-fixed-top,\r
+.navbar-fixed-bottom {\r
+  position: fixed;\r
+  right: 0;\r
+  left: 0;\r
+  z-index: 1030;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-fixed-top,\r
+  .navbar-fixed-bottom {\r
+    border-radius: 0;\r
+  }\r
+}\r
+.navbar-fixed-top {\r
+  top: 0;\r
+  border-width: 0 0 1px;\r
+}\r
+.navbar-fixed-bottom {\r
+  bottom: 0;\r
+  margin-bottom: 0;\r
+  border-width: 1px 0 0;\r
+}\r
+.navbar-brand {\r
+  float: left;\r
+  padding: 15px 15px;\r
+  font-size: 18px;\r
+  line-height: 20px;\r
+  height: 50px;\r
+}\r
+.navbar-brand:hover,\r
+.navbar-brand:focus {\r
+  text-decoration: none;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar > .container .navbar-brand,\r
+  .navbar > .container-fluid .navbar-brand {\r
+    margin-left: -15px;\r
+  }\r
+}\r
+.navbar-toggle {\r
+  position: relative;\r
+  float: right;\r
+  margin-right: 15px;\r
+  padding: 9px 10px;\r
+  margin-top: 8px;\r
+  margin-bottom: 8px;\r
+  background-color: transparent;\r
+  background-image: none;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px;\r
+}\r
+.navbar-toggle:focus {\r
+  outline: none;\r
+}\r
+.navbar-toggle .icon-bar {\r
+  display: block;\r
+  width: 22px;\r
+  height: 2px;\r
+  border-radius: 1px;\r
+}\r
+.navbar-toggle .icon-bar + .icon-bar {\r
+  margin-top: 4px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-toggle {\r
+    display: none;\r
+  }\r
+}\r
+.navbar-nav {\r
+  margin: 7.5px -15px;\r
+}\r
+.navbar-nav > li > a {\r
+  padding-top: 10px;\r
+  padding-bottom: 10px;\r
+  line-height: 20px;\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-nav .open .dropdown-menu {\r
+    position: static;\r
+    float: none;\r
+    width: auto;\r
+    margin-top: 0;\r
+    background-color: transparent;\r
+    border: 0;\r
+    box-shadow: none;\r
+  }\r
+  .navbar-nav .open .dropdown-menu > li > a,\r
+  .navbar-nav .open .dropdown-menu .dropdown-header {\r
+    padding: 5px 15px 5px 25px;\r
+  }\r
+  .navbar-nav .open .dropdown-menu > li > a {\r
+    line-height: 20px;\r
+  }\r
+  .navbar-nav .open .dropdown-menu > li > a:hover,\r
+  .navbar-nav .open .dropdown-menu > li > a:focus {\r
+    background-image: none;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-nav {\r
+    float: left;\r
+    margin: 0;\r
+  }\r
+  .navbar-nav > li {\r
+    float: left;\r
+  }\r
+  .navbar-nav > li > a {\r
+    padding-top: 15px;\r
+    padding-bottom: 15px;\r
+  }\r
+  .navbar-nav.navbar-right:last-child {\r
+    margin-right: -15px;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-left {\r
+    float: left !important;\r
+  }\r
+  .navbar-right {\r
+    float: right !important;\r
+  }\r
+}\r
+.navbar-form {\r
+  margin-left: -15px;\r
+  margin-right: -15px;\r
+  padding: 10px 15px;\r
+  border-top: 1px solid transparent;\r
+  border-bottom: 1px solid transparent;\r
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\r
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\r
+  margin-top: 8px;\r
+  margin-bottom: 8px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-form .form-group {\r
+    display: inline-block;\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .form-control {\r
+    display: inline-block;\r
+    width: auto;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .input-group > .form-control {\r
+    width: 100%;\r
+  }\r
+  .navbar-form .control-label {\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .radio,\r
+  .navbar-form .checkbox {\r
+    display: inline-block;\r
+    margin-top: 0;\r
+    margin-bottom: 0;\r
+    padding-left: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .radio input[type="radio"],\r
+  .navbar-form .checkbox input[type="checkbox"] {\r
+    float: none;\r
+    margin-left: 0;\r
+  }\r
+  .navbar-form .has-feedback .form-control-feedback {\r
+    top: 0;\r
+  }\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-form .form-group {\r
+    margin-bottom: 5px;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-form {\r
+    width: auto;\r
+    border: 0;\r
+    margin-left: 0;\r
+    margin-right: 0;\r
+    padding-top: 0;\r
+    padding-bottom: 0;\r
+    -webkit-box-shadow: none;\r
+    box-shadow: none;\r
+  }\r
+  .navbar-form.navbar-right:last-child {\r
+    margin-right: -15px;\r
+  }\r
+}\r
+.navbar-nav > li > .dropdown-menu {\r
+  margin-top: 0;\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\r
+  border-bottom-right-radius: 0;\r
+  border-bottom-left-radius: 0;\r
+}\r
+.navbar-btn {\r
+  margin-top: 8px;\r
+  margin-bottom: 8px;\r
+}\r
+.navbar-btn.btn-sm {\r
+  margin-top: 10px;\r
+  margin-bottom: 10px;\r
+}\r
+.navbar-btn.btn-xs {\r
+  margin-top: 14px;\r
+  margin-bottom: 14px;\r
+}\r
+.navbar-text {\r
+  margin-top: 15px;\r
+  margin-bottom: 15px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-text {\r
+    float: left;\r
+    margin-left: 15px;\r
+    margin-right: 15px;\r
+  }\r
+  .navbar-text.navbar-right:last-child {\r
+    margin-right: 0;\r
+  }\r
+}\r
+.navbar-default {\r
+  background-color: #0d8921;\r
+  border-color: none;\r
+}\r
+.navbar-default .navbar-brand {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-brand:hover,\r
+.navbar-default .navbar-brand:focus {\r
+  color: #e6e6e6;\r
+  background-color: transparent;\r
+}\r
+.navbar-default .navbar-text {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-nav > li > a {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-nav > li > a:hover,\r
+.navbar-default .navbar-nav > li > a:focus {\r
+  color: #dddddd;\r
+  background-color: transparent;\r
+}\r
+.navbar-default .navbar-nav > .active > a,\r
+.navbar-default .navbar-nav > .active > a:hover,\r
+.navbar-default .navbar-nav > .active > a:focus {\r
+  color: #ffffff;\r
+  background-color: #0a6b1a;\r
+}\r
+.navbar-default .navbar-nav > .disabled > a,\r
+.navbar-default .navbar-nav > .disabled > a:hover,\r
+.navbar-default .navbar-nav > .disabled > a:focus {\r
+  color: #cccccc;\r
+  background-color: transparent;\r
+}\r
+.navbar-default .navbar-toggle {\r
+  border-color: #dddddd;\r
+}\r
+.navbar-default .navbar-toggle:hover,\r
+.navbar-default .navbar-toggle:focus {\r
+  background-color: #dddddd;\r
+}\r
+.navbar-default .navbar-toggle .icon-bar {\r
+  background-color: #ffffff;\r
+}\r
+.navbar-default .navbar-collapse,\r
+.navbar-default .navbar-form {\r
+  border-color: none;\r
+}\r
+.navbar-default .navbar-nav > .open > a,\r
+.navbar-default .navbar-nav > .open > a:hover,\r
+.navbar-default .navbar-nav > .open > a:focus {\r
+  background-color: #0a6b1a;\r
+  color: #ffffff;\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\r
+    color: #ffffff;\r
+  }\r
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\r
+    color: #dddddd;\r
+    background-color: transparent;\r
+  }\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\r
+    color: #ffffff;\r
+    background-color: #0a6b1a;\r
+  }\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\r
+    color: #cccccc;\r
+    background-color: transparent;\r
+  }\r
+}\r
+.navbar-default .navbar-link {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-link:hover {\r
+  color: #dddddd;\r
+}\r
+.navbar-inverse {\r
+  background-color: #222222;\r
+  border-color: #080808;\r
+}\r
+.navbar-inverse .navbar-brand {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-brand:hover,\r
+.navbar-inverse .navbar-brand:focus {\r
+  color: #ffffff;\r
+  background-color: transparent;\r
+}\r
+.navbar-inverse .navbar-text {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-nav > li > a {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-nav > li > a:hover,\r
+.navbar-inverse .navbar-nav > li > a:focus {\r
+  color: #ffffff;\r
+  background-color: transparent;\r
+}\r
+.navbar-inverse .navbar-nav > .active > a,\r
+.navbar-inverse .navbar-nav > .active > a:hover,\r
+.navbar-inverse .navbar-nav > .active > a:focus {\r
+  color: #ffffff;\r
+  background-color: #080808;\r
+}\r
+.navbar-inverse .navbar-nav > .disabled > a,\r
+.navbar-inverse .navbar-nav > .disabled > a:hover,\r
+.navbar-inverse .navbar-nav > .disabled > a:focus {\r
+  color: #444444;\r
+  background-color: transparent;\r
+}\r
+.navbar-inverse .navbar-toggle {\r
+  border-color: #333333;\r
+}\r
+.navbar-inverse .navbar-toggle:hover,\r
+.navbar-inverse .navbar-toggle:focus {\r
+  background-color: #333333;\r
+}\r
+.navbar-inverse .navbar-toggle .icon-bar {\r
+  background-color: #ffffff;\r
+}\r
+.navbar-inverse .navbar-collapse,\r
+.navbar-inverse .navbar-form {\r
+  border-color: #101010;\r
+}\r
+.navbar-inverse .navbar-nav > .open > a,\r
+.navbar-inverse .navbar-nav > .open > a:hover,\r
+.navbar-inverse .navbar-nav > .open > a:focus {\r
+  background-color: #080808;\r
+  color: #ffffff;\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\r
+    border-color: #080808;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\r
+    background-color: #080808;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\r
+    color: #999999;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\r
+    color: #ffffff;\r
+    background-color: transparent;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\r
+    color: #ffffff;\r
+    background-color: #080808;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\r
+    color: #444444;\r
+    background-color: transparent;\r
+  }\r
+}\r
+.navbar-inverse .navbar-link {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-link:hover {\r
+  color: #ffffff;\r
+}\r
+.breadcrumb {\r
+  padding: 8px 15px;\r
+  margin-bottom: 20px;\r
+  list-style: none;\r
+  background-color: #f5f5f5;\r
+  border-radius: 0px;\r
+}\r
+.breadcrumb > li {\r
+  display: inline-block;\r
+}\r
+.breadcrumb > li + li:before {\r
+  content: "\00a0";\r
+  padding: 0 5px;\r
+  color: #cccccc;\r
+}\r
+.breadcrumb > .active {\r
+  color: #999999;\r
+}\r
+.pagination {\r
+  display: inline-block;\r
+  padding-left: 0;\r
+  margin: 20px 0;\r
+  border-radius: 0px;\r
+}\r
+.pagination > li {\r
+  display: inline;\r
+}\r
+.pagination > li > a,\r
+.pagination > li > span {\r
+  position: relative;\r
+  float: left;\r
+  padding: 6px 12px;\r
+  line-height: 1.428571429;\r
+  text-decoration: none;\r
+  color: #0d8921;\r
+  background-color: #ffffff;\r
+  border: 1px solid #dddddd;\r
+  margin-left: -1px;\r
+}\r
+.pagination > li:first-child > a,\r
+.pagination > li:first-child > span {\r
+  margin-left: 0;\r
+  border-bottom-left-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.pagination > li:last-child > a,\r
+.pagination > li:last-child > span {\r
+  border-bottom-right-radius: 0px;\r
+  border-top-right-radius: 0px;\r
+}\r
+.pagination > li > a:hover,\r
+.pagination > li > span:hover,\r
+.pagination > li > a:focus,\r
+.pagination > li > span:focus {\r
+  color: #064310;\r
+  background-color: #eeeeee;\r
+  border-color: #dddddd;\r
+}\r
+.pagination > .active > a,\r
+.pagination > .active > span,\r
+.pagination > .active > a:hover,\r
+.pagination > .active > span:hover,\r
+.pagination > .active > a:focus,\r
+.pagination > .active > span:focus {\r
+  z-index: 2;\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0d8921;\r
+  cursor: default;\r
+}\r
+.pagination > .disabled > span,\r
+.pagination > .disabled > span:hover,\r
+.pagination > .disabled > span:focus,\r
+.pagination > .disabled > a,\r
+.pagination > .disabled > a:hover,\r
+.pagination > .disabled > a:focus {\r
+  color: #999999;\r
+  background-color: #ffffff;\r
+  border-color: #dddddd;\r
+  cursor: not-allowed;\r
+}\r
+.pagination-lg > li > a,\r
+.pagination-lg > li > span {\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+}\r
+.pagination-lg > li:first-child > a,\r
+.pagination-lg > li:first-child > span {\r
+  border-bottom-left-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.pagination-lg > li:last-child > a,\r
+.pagination-lg > li:last-child > span {\r
+  border-bottom-right-radius: 0px;\r
+  border-top-right-radius: 0px;\r
+}\r
+.pagination-sm > li > a,\r
+.pagination-sm > li > span {\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+}\r
+.pagination-sm > li:first-child > a,\r
+.pagination-sm > li:first-child > span {\r
+  border-bottom-left-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.pagination-sm > li:last-child > a,\r
+.pagination-sm > li:last-child > span {\r
+  border-bottom-right-radius: 0px;\r
+  border-top-right-radius: 0px;\r
+}\r
+.pager {\r
+  padding-left: 0;\r
+  margin: 20px 0;\r
+  list-style: none;\r
+  text-align: center;\r
+}\r
+.pager li {\r
+  display: inline;\r
+}\r
+.pager li > a,\r
+.pager li > span {\r
+  display: inline-block;\r
+  padding: 5px 14px;\r
+  background-color: #ffffff;\r
+  border: 1px solid #dddddd;\r
+  border-radius: 15px;\r
+}\r
+.pager li > a:hover,\r
+.pager li > a:focus {\r
+  text-decoration: none;\r
+  background-color: #eeeeee;\r
+}\r
+.pager .next > a,\r
+.pager .next > span {\r
+  float: right;\r
+}\r
+.pager .previous > a,\r
+.pager .previous > span {\r
+  float: left;\r
+}\r
+.pager .disabled > a,\r
+.pager .disabled > a:hover,\r
+.pager .disabled > a:focus,\r
+.pager .disabled > span {\r
+  color: #999999;\r
+  background-color: #ffffff;\r
+  cursor: not-allowed;\r
+}\r
+.label {\r
+  display: inline;\r
+  padding: .2em .6em .3em;\r
+  font-size: 75%;\r
+  font-weight: bold;\r
+  line-height: 1;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  white-space: nowrap;\r
+  vertical-align: baseline;\r
+  border-radius: .25em;\r
+}\r
+.label[href]:hover,\r
+.label[href]:focus {\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  cursor: pointer;\r
+}\r
+.label:empty {\r
+  display: none;\r
+}\r
+.btn .label {\r
+  position: relative;\r
+  top: -1px;\r
+}\r
+.label-default {\r
+  background-color: #999999;\r
+}\r
+.label-default[href]:hover,\r
+.label-default[href]:focus {\r
+  background-color: #808080;\r
+}\r
+.label-primary {\r
+  background-color: #0d8921;\r
+}\r
+.label-primary[href]:hover,\r
+.label-primary[href]:focus {\r
+  background-color: #095a16;\r
+}\r
+.label-success {\r
+  background-color: #5cb85c;\r
+}\r
+.label-success[href]:hover,\r
+.label-success[href]:focus {\r
+  background-color: #449d44;\r
+}\r
+.label-info {\r
+  background-color: #5bc0de;\r
+}\r
+.label-info[href]:hover,\r
+.label-info[href]:focus {\r
+  background-color: #31b0d5;\r
+}\r
+.label-warning {\r
+  background-color: #f0ad4e;\r
+}\r
+.label-warning[href]:hover,\r
+.label-warning[href]:focus {\r
+  background-color: #ec971f;\r
+}\r
+.label-danger {\r
+  background-color: #d9534f;\r
+}\r
+.label-danger[href]:hover,\r
+.label-danger[href]:focus {\r
+  background-color: #c9302c;\r
+}\r
+.badge {\r
+  display: inline-block;\r
+  min-width: 10px;\r
+  padding: 3px 7px;\r
+  font-size: 12px;\r
+  font-weight: bold;\r
+  color: : #fff;\r
+  line-height: 1;\r
+  vertical-align: baseline;\r
+  white-space: nowrap;\r
+  text-align: center;\r
+  background-color: #999999;\r
+  border-radius: 10px;\r
+}\r
+.badge:empty {\r
+  display: none;\r
+}\r
+.btn .badge {\r
+  position: relative;\r
+  top: -1px;\r
+}\r
+.btn-xs .badge {\r
+  top: 0;\r
+  padding: 1px 5px;\r
+}\r
+a.badge:hover,\r
+a.badge:focus {\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  cursor: pointer;\r
+}\r
+a.list-group-item.active > .badge,\r
+.nav-pills > .active > a > .badge {\r
+  color: #0d8921;\r
+  background-color: #ffffff;\r
+}\r
+.nav-pills > li > a > .badge {\r
+  margin-left: 3px;\r
+}\r
+.jumbotron {\r
+  padding: 30px;\r
+  margin-bottom: 30px;\r
+  color: inherit;\r
+  background-color: #ffffff;\r
+}\r
+.jumbotron h1,\r
+.jumbotron .h1 {\r
+  color: inherit;\r
+}\r
+.jumbotron p {\r
+  margin-bottom: 15px;\r
+  font-size: 21px;\r
+  font-weight: 200;\r
+}\r
+.container .jumbotron {\r
+  border-radius: 0px;\r
+}\r
+.jumbotron .container {\r
+  max-width: 100%;\r
+}\r
+@media screen and (min-width: 768px) {\r
+  .jumbotron {\r
+    padding-top: 48px;\r
+    padding-bottom: 48px;\r
+  }\r
+  .container .jumbotron {\r
+    padding-left: 60px;\r
+    padding-right: 60px;\r
+  }\r
+  .jumbotron h1,\r
+  .jumbotron .h1 {\r
+    font-size: 63px;\r
+  }\r
+}\r
+.thumbnail {\r
+  display: block;\r
+  padding: 4px;\r
+  margin-bottom: 20px;\r
+  line-height: 1.428571429;\r
+  background-color: #f2f2f2;\r
+  border: 1px solid #dddddd;\r
+  border-radius: 0px;\r
+  -webkit-transition: all 0.2s ease-in-out;\r
+  transition: all 0.2s ease-in-out;\r
+}\r
+.thumbnail > img,\r
+.thumbnail a > img {\r
+  margin-left: auto;\r
+  margin-right: auto;\r
+}\r
+a.thumbnail:hover,\r
+a.thumbnail:focus,\r
+a.thumbnail.active {\r
+  border-color: #0d8921;\r
+}\r
+.thumbnail .caption {\r
+  padding: 9px;\r
+  color: #333333;\r
+}\r
+.alert {\r
+  padding: 15px;\r
+  margin-bottom: 20px;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px;\r
+}\r
+.alert h4 {\r
+  margin-top: 0;\r
+  color: inherit;\r
+}\r
+.alert .alert-link {\r
+  font-weight: bold;\r
+}\r
+.alert > p,\r
+.alert > ul {\r
+  margin-bottom: 0;\r
+}\r
+.alert > p + p {\r
+  margin-top: 5px;\r
+}\r
+.alert-dismissable {\r
+  padding-right: 35px;\r
+}\r
+.alert-dismissable .close {\r
+  position: relative;\r
+  top: -2px;\r
+  right: -21px;\r
+  color: inherit;\r
+}\r
+.alert-success {\r
+  background-color: #dff0d8;\r
+  border-color: #d6e9c6;\r
+  color: #5cb85c;\r
+}\r
+.alert-success hr {\r
+  border-top-color: #c9e2b3;\r
+}\r
+.alert-success .alert-link {\r
+  color: #449d44;\r
+}\r
+.alert-info {\r
+  background-color: #d9edf7;\r
+  border-color: #bce8f1;\r
+  color: #5bc0de;\r
+}\r
+.alert-info hr {\r
+  border-top-color: #a6e1ec;\r
+}\r
+.alert-info .alert-link {\r
+  color: #31b0d5;\r
+}\r
+.alert-warning {\r
+  background-color: #fcf8e3;\r
+  border-color: #fbeed5;\r
+  color: #f0ad4e;\r
+}\r
+.alert-warning hr {\r
+  border-top-color: #f8e5be;\r
+}\r
+.alert-warning .alert-link {\r
+  color: #ec971f;\r
+}\r
+.alert-danger {\r
+  background-color: #f2dede;\r
+  border-color: #eed3d7;\r
+  color: #d9534f;\r
+}\r
+.alert-danger hr {\r
+  border-top-color: #e6c1c7;\r
+}\r
+.alert-danger .alert-link {\r
+  color: #c9302c;\r
+}\r
+@-webkit-keyframes progress-bar-stripes {\r
+  from {\r
+    background-position: 40px 0;\r
+  }\r
+  to {\r
+    background-position: 0 0;\r
+  }\r
+}\r
+@keyframes progress-bar-stripes {\r
+  from {\r
+    background-position: 40px 0;\r
+  }\r
+  to {\r
+    background-position: 0 0;\r
+  }\r
+}\r
+.progress {\r
+  overflow: hidden;\r
+  height: 20px;\r
+  margin-bottom: 20px;\r
+  background-color: #f5f5f5;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\r
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\r
+}\r
+.progress-bar {\r
+  float: left;\r
+  width: 0%;\r
+  height: 100%;\r
+  font-size: 12px;\r
+  line-height: 20px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  background-color: #0d8921;\r
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\r
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\r
+  -webkit-transition: width 0.6s ease;\r
+  transition: width 0.6s ease;\r
+}\r
+.progress-striped .progress-bar {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-size: 40px 40px;\r
+}\r
+.progress.active .progress-bar {\r
+  -webkit-animation: progress-bar-stripes 2s linear infinite;\r
+  animation: progress-bar-stripes 2s linear infinite;\r
+}\r
+.progress-bar-success {\r
+  background-color: #5cb85c;\r
+}\r
+.progress-striped .progress-bar-success {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.progress-bar-info {\r
+  background-color: #5bc0de;\r
+}\r
+.progress-striped .progress-bar-info {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.progress-bar-warning {\r
+  background-color: #f0ad4e;\r
+}\r
+.progress-striped .progress-bar-warning {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.progress-bar-danger {\r
+  background-color: #d9534f;\r
+}\r
+.progress-striped .progress-bar-danger {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.media,\r
+.media-body {\r
+  overflow: hidden;\r
+  zoom: 1;\r
+}\r
+.media,\r
+.media .media {\r
+  margin-top: 15px;\r
+}\r
+.media:first-child {\r
+  margin-top: 0;\r
+}\r
+.media-object {\r
+  display: block;\r
+}\r
+.media-heading {\r
+  margin: 0 0 5px;\r
+}\r
+.media > .pull-left {\r
+  margin-right: 10px;\r
+}\r
+.media > .pull-right {\r
+  margin-left: 10px;\r
+}\r
+.media-list {\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.list-group {\r
+  margin-bottom: 20px;\r
+  padding-left: 0;\r
+}\r
+.list-group-item {\r
+  position: relative;\r
+  display: block;\r
+  padding: 10px 15px;\r
+  margin-bottom: -1px;\r
+  background-color: #ffffff;\r
+  border: 1px solid #dddddd;\r
+}\r
+.list-group-item:first-child {\r
+  border-top-right-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.list-group-item:last-child {\r
+  margin-bottom: 0;\r
+  border-bottom-right-radius: 0px;\r
+  border-bottom-left-radius: 0px;\r
+}\r
+.list-group-item > .badge {\r
+  float: right;\r
+}\r
+.list-group-item > .badge + .badge {\r
+  margin-right: 5px;\r
+}\r
+a.list-group-item {\r
+  color: #555555;\r
+}\r
+a.list-group-item .list-group-item-heading {\r
+  color: #333333;\r
+}\r
+a.list-group-item:hover,\r
+a.list-group-item:focus {\r
+  text-decoration: none;\r
+  background-color: #f5f5f5;\r
+}\r
+a.list-group-item.active,\r
+a.list-group-item.active:hover,\r
+a.list-group-item.active:focus {\r
+  z-index: 2;\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0d8921;\r
+}\r
+a.list-group-item.active .list-group-item-heading,\r
+a.list-group-item.active:hover .list-group-item-heading,\r
+a.list-group-item.active:focus .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item.active .list-group-item-text,\r
+a.list-group-item.active:hover .list-group-item-text,\r
+a.list-group-item.active:focus .list-group-item-text {\r
+  color: #cccccc;\r
+}\r
+.list-group-item-success {\r
+  color: #5cb85c;\r
+  background-color: #dff0d8;\r
+}\r
+a.list-group-item-success {\r
+  color: #5cb85c;\r
+}\r
+a.list-group-item-success .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-success:hover,\r
+a.list-group-item-success:focus {\r
+  color: #5cb85c;\r
+  background-color: #d0e9c6;\r
+}\r
+a.list-group-item-success.active,\r
+a.list-group-item-success.active:hover,\r
+a.list-group-item-success.active:focus {\r
+  color: #fff;\r
+  background-color: #5cb85c;\r
+  border-color: #5cb85c;\r
+}\r
+.list-group-item-info {\r
+  color: #5bc0de;\r
+  background-color: #d9edf7;\r
+}\r
+a.list-group-item-info {\r
+  color: #5bc0de;\r
+}\r
+a.list-group-item-info .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-info:hover,\r
+a.list-group-item-info:focus {\r
+  color: #5bc0de;\r
+  background-color: #c4e3f3;\r
+}\r
+a.list-group-item-info.active,\r
+a.list-group-item-info.active:hover,\r
+a.list-group-item-info.active:focus {\r
+  color: #fff;\r
+  background-color: #5bc0de;\r
+  border-color: #5bc0de;\r
+}\r
+.list-group-item-warning {\r
+  color: #f0ad4e;\r
+  background-color: #fcf8e3;\r
+}\r
+a.list-group-item-warning {\r
+  color: #f0ad4e;\r
+}\r
+a.list-group-item-warning .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-warning:hover,\r
+a.list-group-item-warning:focus {\r
+  color: #f0ad4e;\r
+  background-color: #faf2cc;\r
+}\r
+a.list-group-item-warning.active,\r
+a.list-group-item-warning.active:hover,\r
+a.list-group-item-warning.active:focus {\r
+  color: #fff;\r
+  background-color: #f0ad4e;\r
+  border-color: #f0ad4e;\r
+}\r
+.list-group-item-danger {\r
+  color: #d9534f;\r
+  background-color: #f2dede;\r
+}\r
+a.list-group-item-danger {\r
+  color: #d9534f;\r
+}\r
+a.list-group-item-danger .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-danger:hover,\r
+a.list-group-item-danger:focus {\r
+  color: #d9534f;\r
+  background-color: #ebcccc;\r
+}\r
+a.list-group-item-danger.active,\r
+a.list-group-item-danger.active:hover,\r
+a.list-group-item-danger.active:focus {\r
+  color: #fff;\r
+  background-color: #d9534f;\r
+  border-color: #d9534f;\r
+}\r
+.list-group-item-heading {\r
+  margin-top: 0;\r
+  margin-bottom: 5px;\r
+}\r
+.list-group-item-text {\r
+  margin-bottom: 0;\r
+  line-height: 1.3;\r
+}\r
+.panel {\r
+  margin-bottom: 20px;\r
+  background-color: #ffffff;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\r
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\r
+}\r
+.panel-body {\r
+  padding: 15px;\r
+}\r
+.panel-heading {\r
+  padding: 10px 15px;\r
+  border-bottom: 1px solid transparent;\r
+  border-top-right-radius: -1px;\r
+  border-top-left-radius: -1px;\r
+}\r
+.panel-heading > .dropdown .dropdown-toggle {\r
+  color: inherit;\r
+}\r
+.panel-title {\r
+  margin-top: 0;\r
+  margin-bottom: 0;\r
+  font-size: 16px;\r
+  color: inherit;\r
+}\r
+.panel-title > a {\r
+  color: inherit;\r
+}\r
+.panel-footer {\r
+  padding: 10px 15px;\r
+  background-color: #f5f5f5;\r
+  border-top: 1px solid #dddddd;\r
+  border-bottom-right-radius: -1px;\r
+  border-bottom-left-radius: -1px;\r
+}\r
+.panel > .list-group {\r
+  margin-bottom: 0;\r
+}\r
+.panel > .list-group .list-group-item {\r
+  border-width: 1px 0;\r
+  border-radius: 0;\r
+}\r
+.panel > .list-group .list-group-item:first-child {\r
+  border-top: 0;\r
+}\r
+.panel > .list-group .list-group-item:last-child {\r
+  border-bottom: 0;\r
+}\r
+.panel > .list-group:first-child .list-group-item:first-child {\r
+  border-top-right-radius: -1px;\r
+  border-top-left-radius: -1px;\r
+}\r
+.panel > .list-group:last-child .list-group-item:last-child {\r
+  border-bottom-right-radius: -1px;\r
+  border-bottom-left-radius: -1px;\r
+}\r
+.panel-heading + .list-group .list-group-item:first-child {\r
+  border-top-width: 0;\r
+}\r
+.panel > .table,\r
+.panel > .table-responsive > .table {\r
+  margin-bottom: 0;\r
+}\r
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\r
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\r
+  border-top-left-radius: -1px;\r
+}\r
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\r
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\r
+  border-top-right-radius: -1px;\r
+}\r
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\r
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\r
+  border-bottom-left-radius: -1px;\r
+}\r
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\r
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\r
+  border-bottom-right-radius: -1px;\r
+}\r
+.panel > .panel-body + .table,\r
+.panel > .panel-body + .table-responsive {\r
+  border-top: 1px solid #dddddd;\r
+}\r
+.panel > .table > tbody:first-child > tr:first-child th,\r
+.panel > .table > tbody:first-child > tr:first-child td {\r
+  border-top: 0;\r
+}\r
+.panel > .table-bordered,\r
+.panel > .table-responsive > .table-bordered {\r
+  border: 0;\r
+}\r
+.panel > .table-bordered > thead > tr > th:first-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\r
+.panel > .table-bordered > tbody > tr > th:first-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\r
+.panel > .table-bordered > tfoot > tr > th:first-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\r
+.panel > .table-bordered > thead > tr > td:first-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\r
+.panel > .table-bordered > tbody > tr > td:first-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\r
+.panel > .table-bordered > tfoot > tr > td:first-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\r
+  border-left: 0;\r
+}\r
+.panel > .table-bordered > thead > tr > th:last-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\r
+.panel > .table-bordered > tbody > tr > th:last-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\r
+.panel > .table-bordered > tfoot > tr > th:last-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\r
+.panel > .table-bordered > thead > tr > td:last-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\r
+.panel > .table-bordered > tbody > tr > td:last-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\r
+.panel > .table-bordered > tfoot > tr > td:last-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\r
+  border-right: 0;\r
+}\r
+.panel > .table-bordered > thead > tr:first-child > td,\r
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\r
+.panel > .table-bordered > tbody > tr:first-child > td,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\r
+.panel > .table-bordered > thead > tr:first-child > th,\r
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\r
+.panel > .table-bordered > tbody > tr:first-child > th,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\r
+  border-bottom: 0;\r
+}\r
+.panel > .table-bordered > tbody > tr:last-child > td,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\r
+.panel > .table-bordered > tfoot > tr:last-child > td,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\r
+.panel > .table-bordered > tbody > tr:last-child > th,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\r
+.panel > .table-bordered > tfoot > tr:last-child > th,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\r
+  border-bottom: 0;\r
+}\r
+.panel > .table-responsive {\r
+  border: 0;\r
+  margin-bottom: 0;\r
+}\r
+.panel-group {\r
+  margin-bottom: 20px;\r
+}\r
+.panel-group .panel {\r
+  margin-bottom: 0;\r
+  border-radius: 0px;\r
+  overflow: hidden;\r
+}\r
+.panel-group .panel + .panel {\r
+  margin-top: 5px;\r
+}\r
+.panel-group .panel-heading {\r
+  border-bottom: 0;\r
+}\r
+.panel-group .panel-heading + .panel-collapse .panel-body {\r
+  border-top: 1px solid #dddddd;\r
+}\r
+.panel-group .panel-footer {\r
+  border-top: 0;\r
+}\r
+.panel-group .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom: 1px solid #dddddd;\r
+}\r
+.panel-default {\r
+  border-color: #dddddd;\r
+}\r
+.panel-default > .panel-heading {\r
+  color: #333333;\r
+  background-color: #f5f5f5;\r
+  border-color: #dddddd;\r
+}\r
+.panel-default > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #dddddd;\r
+}\r
+.panel-default > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #dddddd;\r
+}\r
+.panel-primary {\r
+  border-color: #0d8921;\r
+}\r
+.panel-primary > .panel-heading {\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0d8921;\r
+}\r
+.panel-primary > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #0d8921;\r
+}\r
+.panel-primary > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #0d8921;\r
+}\r
+.panel-success {\r
+  border-color: #d6e9c6;\r
+}\r
+.panel-success > .panel-heading {\r
+  color: #5cb85c;\r
+  background-color: #dff0d8;\r
+  border-color: #d6e9c6;\r
+}\r
+.panel-success > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #d6e9c6;\r
+}\r
+.panel-success > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #d6e9c6;\r
+}\r
+.panel-info {\r
+  border-color: #bce8f1;\r
+}\r
+.panel-info > .panel-heading {\r
+  color: #5bc0de;\r
+  background-color: #d9edf7;\r
+  border-color: #bce8f1;\r
+}\r
+.panel-info > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #bce8f1;\r
+}\r
+.panel-info > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #bce8f1;\r
+}\r
+.panel-warning {\r
+  border-color: #fbeed5;\r
+}\r
+.panel-warning > .panel-heading {\r
+  color: #f0ad4e;\r
+  background-color: #fcf8e3;\r
+  border-color: #fbeed5;\r
+}\r
+.panel-warning > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #fbeed5;\r
+}\r
+.panel-warning > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #fbeed5;\r
+}\r
+.panel-danger {\r
+  border-color: #eed3d7;\r
+}\r
+.panel-danger > .panel-heading {\r
+  color: #d9534f;\r
+  background-color: #f2dede;\r
+  border-color: #eed3d7;\r
+}\r
+.panel-danger > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #eed3d7;\r
+}\r
+.panel-danger > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #eed3d7;\r
+}\r
+.well {\r
+  min-height: 20px;\r
+  padding: 19px;\r
+  margin-bottom: 20px;\r
+  background-color: #f5f5f5;\r
+  border: 1px solid #e3e3e3;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\r
+}\r
+.well blockquote {\r
+  border-color: #ddd;\r
+  border-color: rgba(0, 0, 0, 0.15);\r
+}\r
+.well-lg {\r
+  padding: 24px;\r
+  border-radius: 0px;\r
+}\r
+.well-sm {\r
+  padding: 9px;\r
+  border-radius: 0px;\r
+}\r
+.close {\r
+  float: right;\r
+  font-size: 21px;\r
+  font-weight: bold;\r
+  line-height: 1;\r
+  color: #000000;\r
+  text-shadow: 0 1px 0 #ffffff;\r
+  opacity: 0.2;\r
+  filter: alpha(opacity=20);\r
+}\r
+.close:hover,\r
+.close:focus {\r
+  color: #000000;\r
+  text-decoration: none;\r
+  cursor: pointer;\r
+  opacity: 0.5;\r
+  filter: alpha(opacity=50);\r
+}\r
+button.close {\r
+  padding: 0;\r
+  cursor: pointer;\r
+  background: transparent;\r
+  border: 0;\r
+  -webkit-appearance: none;\r
+}\r
+.modal-open {\r
+  overflow: hidden;\r
+}\r
+.modal {\r
+  display: none;\r
+  overflow: auto;\r
+  overflow-y: scroll;\r
+  position: fixed;\r
+  top: 0;\r
+  right: 0;\r
+  bottom: 0;\r
+  left: 0;\r
+  z-index: 1050;\r
+  -webkit-overflow-scrolling: touch;\r
+  outline: 0;\r
+}\r
+.modal.fade .modal-dialog {\r
+  -webkit-transform: translate(0, -25%);\r
+  -ms-transform: translate(0, -25%);\r
+  transform: translate(0, -25%);\r
+  -webkit-transition: -webkit-transform 0.3s ease-out;\r
+  -moz-transition: -moz-transform 0.3s ease-out;\r
+  -o-transition: -o-transform 0.3s ease-out;\r
+  transition: transform 0.3s ease-out;\r
+}\r
+.modal.in .modal-dialog {\r
+  -webkit-transform: translate(0, 0);\r
+  -ms-transform: translate(0, 0);\r
+  transform: translate(0, 0);\r
+}\r
+.modal-dialog {\r
+  position: relative;\r
+  width: auto;\r
+  margin: 10px;\r
+}\r
+.modal-content {\r
+  position: relative;\r
+  background-color: #ffffff;\r
+  border: 1px solid #999999;\r
+  border: 1px solid rgba(0, 0, 0, 0.2);\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\r
+  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\r
+  background-clip: padding-box;\r
+  outline: none;\r
+}\r
+.modal-backdrop {\r
+  position: fixed;\r
+  top: 0;\r
+  right: 0;\r
+  bottom: 0;\r
+  left: 0;\r
+  z-index: 1040;\r
+  background-color: #000000;\r
+}\r
+.modal-backdrop.fade {\r
+  opacity: 0;\r
+  filter: alpha(opacity=0);\r
+}\r
+.modal-backdrop.in {\r
+  opacity: 0.5;\r
+  filter: alpha(opacity=50);\r
+}\r
+.modal-header {\r
+  padding: 15px;\r
+  border-bottom: 1px solid #e5e5e5;\r
+  min-height: 16.428571429px;\r
+}\r
+.modal-header .close {\r
+  margin-top: -2px;\r
+}\r
+.modal-title {\r
+  margin: 0;\r
+  line-height: 1.428571429;\r
+}\r
+.modal-body {\r
+  position: relative;\r
+  padding: 20px;\r
+}\r
+.modal-footer {\r
+  margin-top: 15px;\r
+  padding: 19px 20px 20px;\r
+  text-align: right;\r
+  border-top: 1px solid #e5e5e5;\r
+}\r
+.modal-footer .btn + .btn {\r
+  margin-left: 5px;\r
+  margin-bottom: 0;\r
+}\r
+.modal-footer .btn-group .btn + .btn {\r
+  margin-left: -1px;\r
+}\r
+.modal-footer .btn-block + .btn-block {\r
+  margin-left: 0;\r
+}\r
+@media (min-width: 768px) {\r
+  .modal-dialog {\r
+    width: 600px;\r
+    margin: 30px auto;\r
+  }\r
+  .modal-content {\r
+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\r
+    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\r
+  }\r
+  .modal-sm {\r
+    width: 300px;\r
+  }\r
+}\r
+@media (min-width: 992px) {\r
+  .modal-lg {\r
+    width: 900px;\r
+  }\r
+}\r
+.tooltip {\r
+  position: absolute;\r
+  z-index: 1030;\r
+  display: block;\r
+  visibility: visible;\r
+  font-size: 12px;\r
+  line-height: 1.4;\r
+  opacity: 0;\r
+  filter: alpha(opacity=0);\r
+}\r
+.tooltip.in {\r
+  opacity: 0.9;\r
+  filter: alpha(opacity=90);\r
+}\r
+.tooltip.top {\r
+  margin-top: -3px;\r
+  padding: 5px 0;\r
+}\r
+.tooltip.right {\r
+  margin-left: 3px;\r
+  padding: 0 5px;\r
+}\r
+.tooltip.bottom {\r
+  margin-top: 3px;\r
+  padding: 5px 0;\r
+}\r
+.tooltip.left {\r
+  margin-left: -3px;\r
+  padding: 0 5px;\r
+}\r
+.tooltip-inner {\r
+  max-width: 200px;\r
+  padding: 3px 8px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  text-decoration: none;\r
+  background-color: #000000;\r
+  border-radius: 0px;\r
+}\r
+.tooltip-arrow {\r
+  position: absolute;\r
+  width: 0;\r
+  height: 0;\r
+  border-color: transparent;\r
+  border-style: solid;\r
+}\r
+.tooltip.top .tooltip-arrow {\r
+  bottom: 0;\r
+  left: 50%;\r
+  margin-left: -5px;\r
+  border-width: 5px 5px 0;\r
+  border-top-color: #000000;\r
+}\r
+.tooltip.top-left .tooltip-arrow {\r
+  bottom: 0;\r
+  left: 5px;\r
+  border-width: 5px 5px 0;\r
+  border-top-color: #000000;\r
+}\r
+.tooltip.top-right .tooltip-arrow {\r
+  bottom: 0;\r
+  right: 5px;\r
+  border-width: 5px 5px 0;\r
+  border-top-color: #000000;\r
+}\r
+.tooltip.right .tooltip-arrow {\r
+  top: 50%;\r
+  left: 0;\r
+  margin-top: -5px;\r
+  border-width: 5px 5px 5px 0;\r
+  border-right-color: #000000;\r
+}\r
+.tooltip.left .tooltip-arrow {\r
+  top: 50%;\r
+  right: 0;\r
+  margin-top: -5px;\r
+  border-width: 5px 0 5px 5px;\r
+  border-left-color: #000000;\r
+}\r
+.tooltip.bottom .tooltip-arrow {\r
+  top: 0;\r
+  left: 50%;\r
+  margin-left: -5px;\r
+  border-width: 0 5px 5px;\r
+  border-bottom-color: #000000;\r
+}\r
+.tooltip.bottom-left .tooltip-arrow {\r
+  top: 0;\r
+  left: 5px;\r
+  border-width: 0 5px 5px;\r
+  border-bottom-color: #000000;\r
+}\r
+.tooltip.bottom-right .tooltip-arrow {\r
+  top: 0;\r
+  right: 5px;\r
+  border-width: 0 5px 5px;\r
+  border-bottom-color: #000000;\r
+}\r
+.popover {\r
+  position: absolute;\r
+  top: 0;\r
+  left: 0;\r
+  z-index: 1010;\r
+  display: none;\r
+  max-width: 276px;\r
+  padding: 1px;\r
+  text-align: left;\r
+  background-color: #ffffff;\r
+  background-clip: padding-box;\r
+  border: 1px solid #cccccc;\r
+  border: 1px solid rgba(0, 0, 0, 0.2);\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\r
+  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\r
+  white-space: normal;\r
+}\r
+.popover.top {\r
+  margin-top: -10px;\r
+}\r
+.popover.right {\r
+  margin-left: 10px;\r
+}\r
+.popover.bottom {\r
+  margin-top: 10px;\r
+}\r
+.popover.left {\r
+  margin-left: -10px;\r
+}\r
+.popover-title {\r
+  margin: 0;\r
+  padding: 8px 14px;\r
+  font-size: 14px;\r
+  font-weight: normal;\r
+  line-height: 18px;\r
+  background-color: #f7f7f7;\r
+  border-bottom: 1px solid #ebebeb;\r
+  border-radius: 5px 5px 0 0;\r
+}\r
+.popover-content {\r
+  padding: 9px 14px;\r
+}\r
+.popover > .arrow,\r
+.popover > .arrow:after {\r
+  position: absolute;\r
+  display: block;\r
+  width: 0;\r
+  height: 0;\r
+  border-color: transparent;\r
+  border-style: solid;\r
+}\r
+.popover > .arrow {\r
+  border-width: 11px;\r
+}\r
+.popover > .arrow:after {\r
+  border-width: 10px;\r
+  content: "";\r
+}\r
+.popover.top > .arrow {\r
+  left: 50%;\r
+  margin-left: -11px;\r
+  border-bottom-width: 0;\r
+  border-top-color: #999999;\r
+  border-top-color: rgba(0, 0, 0, 0.25);\r
+  bottom: -11px;\r
+}\r
+.popover.top > .arrow:after {\r
+  content: " ";\r
+  bottom: 1px;\r
+  margin-left: -10px;\r
+  border-bottom-width: 0;\r
+  border-top-color: #ffffff;\r
+}\r
+.popover.right > .arrow {\r
+  top: 50%;\r
+  left: -11px;\r
+  margin-top: -11px;\r
+  border-left-width: 0;\r
+  border-right-color: #999999;\r
+  border-right-color: rgba(0, 0, 0, 0.25);\r
+}\r
+.popover.right > .arrow:after {\r
+  content: " ";\r
+  left: 1px;\r
+  bottom: -10px;\r
+  border-left-width: 0;\r
+  border-right-color: #ffffff;\r
+}\r
+.popover.bottom > .arrow {\r
+  left: 50%;\r
+  margin-left: -11px;\r
+  border-top-width: 0;\r
+  border-bottom-color: #999999;\r
+  border-bottom-color: rgba(0, 0, 0, 0.25);\r
+  top: -11px;\r
+}\r
+.popover.bottom > .arrow:after {\r
+  content: " ";\r
+  top: 1px;\r
+  margin-left: -10px;\r
+  border-top-width: 0;\r
+  border-bottom-color: #ffffff;\r
+}\r
+.popover.left > .arrow {\r
+  top: 50%;\r
+  right: -11px;\r
+  margin-top: -11px;\r
+  border-right-width: 0;\r
+  border-left-color: #999999;\r
+  border-left-color: rgba(0, 0, 0, 0.25);\r
+}\r
+.popover.left > .arrow:after {\r
+  content: " ";\r
+  right: 1px;\r
+  border-right-width: 0;\r
+  border-left-color: #ffffff;\r
+  bottom: -10px;\r
+}\r
+.carousel {\r
+  position: relative;\r
+}\r
+.carousel-inner {\r
+  position: relative;\r
+  overflow: hidden;\r
+  width: 100%;\r
+}\r
+.carousel-inner > .item {\r
+  display: none;\r
+  position: relative;\r
+  -webkit-transition: 0.6s ease-in-out left;\r
+  transition: 0.6s ease-in-out left;\r
+}\r
+.carousel-inner > .item > img,\r
+.carousel-inner > .item > a > img {\r
+  line-height: 1;\r
+}\r
+.carousel-inner > .active,\r
+.carousel-inner > .next,\r
+.carousel-inner > .prev {\r
+  display: block;\r
+}\r
+.carousel-inner > .active {\r
+  left: 0;\r
+}\r
+.carousel-inner > .next,\r
+.carousel-inner > .prev {\r
+  position: absolute;\r
+  top: 0;\r
+  width: 100%;\r
+}\r
+.carousel-inner > .next {\r
+  left: 100%;\r
+}\r
+.carousel-inner > .prev {\r
+  left: -100%;\r
+}\r
+.carousel-inner > .next.left,\r
+.carousel-inner > .prev.right {\r
+  left: 0;\r
+}\r
+.carousel-inner > .active.left {\r
+  left: -100%;\r
+}\r
+.carousel-inner > .active.right {\r
+  left: 100%;\r
+}\r
+.carousel-control {\r
+  position: absolute;\r
+  top: 0;\r
+  left: 0;\r
+  bottom: 0;\r
+  width: 15%;\r
+  opacity: 0.5;\r
+  filter: alpha(opacity=50);\r
+  font-size: 20px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\r
+}\r
+.carousel-control.left {\r
+  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%));\r
+  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\r
+  background-repeat: repeat-x;\r
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\r
+}\r
+.carousel-control.right {\r
+  left: auto;\r
+  right: 0;\r
+  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%));\r
+  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\r
+  background-repeat: repeat-x;\r
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\r
+}\r
+.carousel-control:hover,\r
+.carousel-control:focus {\r
+  outline: none;\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  opacity: 0.9;\r
+  filter: alpha(opacity=90);\r
+}\r
+.carousel-control .icon-prev,\r
+.carousel-control .icon-next,\r
+.carousel-control .glyphicon-chevron-left,\r
+.carousel-control .glyphicon-chevron-right {\r
+  position: absolute;\r
+  top: 50%;\r
+  z-index: 5;\r
+  display: inline-block;\r
+}\r
+.carousel-control .icon-prev,\r
+.carousel-control .glyphicon-chevron-left {\r
+  left: 50%;\r
+}\r
+.carousel-control .icon-next,\r
+.carousel-control .glyphicon-chevron-right {\r
+  right: 50%;\r
+}\r
+.carousel-control .icon-prev,\r
+.carousel-control .icon-next {\r
+  width: 20px;\r
+  height: 20px;\r
+  margin-top: -10px;\r
+  margin-left: -10px;\r
+  font-family: serif;\r
+}\r
+.carousel-control .icon-prev:before {\r
+  content: '\2039';\r
+}\r
+.carousel-control .icon-next:before {\r
+  content: '\203a';\r
+}\r
+.carousel-indicators {\r
+  position: absolute;\r
+  bottom: 10px;\r
+  left: 50%;\r
+  z-index: 15;\r
+  width: 60%;\r
+  margin-left: -30%;\r
+  padding-left: 0;\r
+  list-style: none;\r
+  text-align: center;\r
+}\r
+.carousel-indicators li {\r
+  display: inline-block;\r
+  width: 10px;\r
+  height: 10px;\r
+  margin: 1px;\r
+  text-indent: -999px;\r
+  border: 1px solid #ffffff;\r
+  border-radius: 10px;\r
+  cursor: pointer;\r
+  background-color: #000 \9;\r
+  background-color: rgba(0, 0, 0, 0);\r
+}\r
+.carousel-indicators .active {\r
+  margin: 0;\r
+  width: 12px;\r
+  height: 12px;\r
+  background-color: #ffffff;\r
+}\r
+.carousel-caption {\r
+  position: absolute;\r
+  left: 15%;\r
+  right: 15%;\r
+  bottom: 20px;\r
+  z-index: 10;\r
+  padding-top: 20px;\r
+  padding-bottom: 20px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\r
+}\r
+.carousel-caption .btn {\r
+  text-shadow: none;\r
+}\r
+@media screen and (min-width: 768px) {\r
+  .carousel-control .glyphicons-chevron-left,\r
+  .carousel-control .glyphicons-chevron-right,\r
+  .carousel-control .icon-prev,\r
+  .carousel-control .icon-next {\r
+    width: 30px;\r
+    height: 30px;\r
+    margin-top: -15px;\r
+    margin-left: -15px;\r
+    font-size: 30px;\r
+  }\r
+  .carousel-caption {\r
+    left: 20%;\r
+    right: 20%;\r
+    padding-bottom: 30px;\r
+  }\r
+  .carousel-indicators {\r
+    bottom: 20px;\r
+  }\r
+}\r
+.clearfix:before,\r
+.clearfix:after,\r
+.container:before,\r
+.container:after,\r
+.container-fluid:before,\r
+.container-fluid:after,\r
+.row:before,\r
+.row:after,\r
+.form-horizontal .form-group:before,\r
+.form-horizontal .form-group:after,\r
+.btn-toolbar:before,\r
+.btn-toolbar:after,\r
+.btn-group-vertical > .btn-group:before,\r
+.btn-group-vertical > .btn-group:after,\r
+.nav:before,\r
+.nav:after,\r
+.navbar:before,\r
+.navbar:after,\r
+.navbar-header:before,\r
+.navbar-header:after,\r
+.navbar-collapse:before,\r
+.navbar-collapse:after,\r
+.pager:before,\r
+.pager:after,\r
+.panel-body:before,\r
+.panel-body:after,\r
+.modal-footer:before,\r
+.modal-footer:after {\r
+  content: " ";\r
+  display: table;\r
+}\r
+.clearfix:after,\r
+.container:after,\r
+.container-fluid:after,\r
+.row:after,\r
+.form-horizontal .form-group:after,\r
+.btn-toolbar:after,\r
+.btn-group-vertical > .btn-group:after,\r
+.nav:after,\r
+.navbar:after,\r
+.navbar-header:after,\r
+.navbar-collapse:after,\r
+.pager:after,\r
+.panel-body:after,\r
+.modal-footer:after {\r
+  clear: both;\r
+}\r
+.center-block {\r
+  display: block;\r
+  margin-left: auto;\r
+  margin-right: auto;\r
+}\r
+.pull-right {\r
+  float: right !important;\r
+}\r
+.pull-left {\r
+  float: left !important;\r
+}\r
+.hide {\r
+  display: none !important;\r
+}\r
+.show {\r
+  display: block !important;\r
+}\r
+.invisible {\r
+  visibility: hidden;\r
+}\r
+.text-hide {\r
+  font: 0/0 a;\r
+  color: transparent;\r
+  text-shadow: none;\r
+  background-color: transparent;\r
+  border: 0;\r
+}\r
+.hidden {\r
+  display: none !important;\r
+  visibility: hidden !important;\r
+}\r
+.affix {\r
+  position: fixed;\r
+}\r
+@-ms-viewport {\r
+  width: device-width;\r
+}\r
+.visible-xs,\r
+.visible-sm,\r
+.visible-md,\r
+.visible-lg {\r
+  display: none !important;\r
+}\r
+@media (max-width: 767px) {\r
+  .visible-xs {\r
+    display: block !important;\r
+  }\r
+  table.visible-xs {\r
+    display: table;\r
+  }\r
+  tr.visible-xs {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-xs,\r
+  td.visible-xs {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (min-width: 768px) and (max-width: 991px) {\r
+  .visible-sm {\r
+    display: block !important;\r
+  }\r
+  table.visible-sm {\r
+    display: table;\r
+  }\r
+  tr.visible-sm {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-sm,\r
+  td.visible-sm {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (min-width: 992px) and (max-width: 1199px) {\r
+  .visible-md {\r
+    display: block !important;\r
+  }\r
+  table.visible-md {\r
+    display: table;\r
+  }\r
+  tr.visible-md {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-md,\r
+  td.visible-md {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .visible-lg {\r
+    display: block !important;\r
+  }\r
+  table.visible-lg {\r
+    display: table;\r
+  }\r
+  tr.visible-lg {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-lg,\r
+  td.visible-lg {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (max-width: 767px) {\r
+  .hidden-xs {\r
+    display: none !important;\r
+  }\r
+}\r
+@media (min-width: 768px) and (max-width: 991px) {\r
+  .hidden-sm {\r
+    display: none !important;\r
+  }\r
+}\r
+@media (min-width: 992px) and (max-width: 1199px) {\r
+  .hidden-md {\r
+    display: none !important;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .hidden-lg {\r
+    display: none !important;\r
+  }\r
+}\r
+.visible-print {\r
+  display: none !important;\r
+}\r
+@media print {\r
+  .visible-print {\r
+    display: block !important;\r
+  }\r
+  table.visible-print {\r
+    display: table;\r
+  }\r
+  tr.visible-print {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-print,\r
+  td.visible-print {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media print {\r
+  .hidden-print {\r
+    display: none !important;\r
+  }\r
+}\r
+   \r
diff --git a/share/nitweb/views/class.html b/share/nitweb/views/class.html
new file mode 100644 (file)
index 0000000..54da8af
--- /dev/null
@@ -0,0 +1,61 @@
+<ul class='nav nav-tabs'>
+       <li role='presentation' class='active'>
+               <a data-toggle='tab' data-target='#doc'>
+                       <span class='glyphicon glyphicon-book'/> Doc
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
+                       <span class='glyphicon glyphicon-object-align-vertical'/> Inheritance
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#all_props'>
+                       <span class='glyphicon glyphicon-tags'/> All properties
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization' ng-click='entityCtrl.loadEntityLinearization()'>
+                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+               <entity-doc mentity='mentity.intro'/>
+
+               <entity-list list-title='Parents'
+                       list-entities='mentity.parents'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Constructors'
+                       list-entities='mentity.all_mproperties'
+                       list-object-filter='{is_init: true}' />
+
+               <entity-list list-title='Introduced properties'
+                       list-entities='mentity.intro_mproperties'
+                       list-object-filter='{is_init: "!true"}' />
+
+               <entity-list list-title='Redefined properties'
+                       list-entities='mentity.redef_mproperties'
+                       list-object-filter='{is_init: "!true"}' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='all_props'>
+               <entity-list list-title='All properties' list-entities='mentity.all_mproperties'
+                       list-object-filter='{}' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='linearization'>
+               <entity-linearization
+                       list-title='Class definitions'
+                       list-entities='linearization'
+                       list-focus='mentity.intro' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='graph'>
+               <div class='card'>
+                       <div class='card-body text-center'>
+                               <div class='graph' ng-bind-html='graph'></div>
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/classdef.html b/share/nitweb/views/classdef.html
new file mode 100644 (file)
index 0000000..f44478f
--- /dev/null
@@ -0,0 +1,34 @@
+<ul class='nav nav-tabs' role='tablist'>
+       <li role='presentation' class='warning'>
+               <a ng-href='{{mentity.mclass.web_url}}'>
+                       <span class='glyphicon glyphicon-chevron-left'/> Go to class
+               </a>
+       </li>
+       <li role='presentation' class='active'>
+               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization'>
+                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#code' ng-click="entityCtrl.loadEntityCode()">
+                       <span class='glyphicon glyphicon-console'/> Code
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='linearization'>
+               <entity-linearization
+                       list-title='Class definitions'
+                       list-entities='linearization'
+                       list-focus='mentity' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='code'>
+               <div class='card'>
+                       <div class='card-body'>
+                               <pre ng-bind-html='code' />
+                               <entity-location mentity='mentity' />
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/doc.html b/share/nitweb/views/doc.html
new file mode 100644 (file)
index 0000000..53f8a2a
--- /dev/null
@@ -0,0 +1,39 @@
+<div class='container-fluid'>
+
+       <div ng-if='error' class='alert alert-danger' role='alert'>
+               <span class='glyphicon glyphicon-exclamation-sign' aria-hidden='true'></span>
+               <span class='sr-only'>Error:</span>
+               <span ng-switch='error.status'>
+                       <span ng-switch-when='404'>Entity <code>{{entityId}}</code> not found!</span>
+                       <span ng-switch-default>An error occured<br/>{{error.status}}: {{error.message}}</span>
+               </span>
+       </div>
+
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity' /></h2>
+               <entity-namespace mentity='mentity' />
+       </div>
+       <div ng-switch='mentity.class_name'>
+               <div ng-switch-when='MPackage'>
+                       <div ng-include src='"/views/package.html"' />
+               </div>
+               <div ng-switch-when='MGroup'>
+                       <div ng-include src='"/views/group.html"' />
+               </div>
+               <div ng-switch-when='MModule'>
+                       <div ng-include src='"/views/module.html"' />
+               </div>
+               <div ng-switch-when='MClass'>
+                       <div ng-include src='"/views/class.html"' />
+               </div>
+               <div ng-switch-when='MClassDef'>
+                       <div ng-include src='"/views/classdef.html"' />
+               </div>
+               <div ng-switch-when='MMethod' ng-switch-when='MAttribute' ng-switch-when='MVirtualTypeProp'>
+                       <div ng-include src='"/views/property.html"' />
+               </div>
+               <div ng-switch-when='MMethodDef' ng-switch-when='MAttributeDef' ng-switch-when='MVirtualTypeDef'>
+                       <div ng-include src='"/views/propdef.html"' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/docdown.html b/share/nitweb/views/docdown.html
new file mode 100644 (file)
index 0000000..75fe6df
--- /dev/null
@@ -0,0 +1,33 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2>Docdown snippets</h2>
+               <p class='text-muted'>Sharable documentation snippets.</p>
+               <div class="input-group">
+                       <span ng-if='edit' class="input-group-btn">
+                               <button class='btn btn-success' ng-click='docdownCtrl.editMode(false)'>
+                                       <span class='glyphicon glyphicon-link' /> View
+                               </button>
+                       </span>
+                       <span ng-if='!edit' class="input-group-btn">
+                               <button class='btn btn-success' ng-click='docdownCtrl.editMode(true)'>
+                                       <span class='glyphicon glyphicon-edit' /> Edit
+                               </button>
+                       </span>
+                       <input class='form-control' type='text' ng-model='link' />
+               </div>
+       </div>
+       <div class='row'>
+               <div ng-show='edit' class='col-xs-6'>
+                       <div class='card'>
+                               <textarea ng-model='markdown' ng-model-options='{ debounce: 100 }' ng-change='docdownCtrl.updateSnippet()' class='form-control' rows='20'></textarea>
+                       </div>
+               </div>
+               <div ng-class='edit ? "col-xs-6" : "col-xs-12"'>
+                       <div class='card'>
+                               <div class='card-body nitdoc'>
+                                       <div ng-bind-html='html' />
+                               </div>
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/group.html b/share/nitweb/views/group.html
new file mode 100644 (file)
index 0000000..0b58963
--- /dev/null
@@ -0,0 +1,34 @@
+<ul class='nav nav-tabs'>
+       <li class='active'>
+               <a data-toggle='tab' data-target='#doc'>
+                       <span class='glyphicon glyphicon-book'/> Doc
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
+                       <span class='glyphicon glyphicon-object-align-vertical'/> Imports
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+               <entity-doc mentity='mentity'/>
+
+               <entity-list list-title='Parent group' list-entities='[mentity.parent]'
+                       list-object-filter='{}' ng-if='mentity.parent' />
+
+               <entity-list list-title='Subgroups' list-entities='mentity.mgroups'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Modules' list-entities='mentity.mmodules'
+                       list-object-filter='{}' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='graph'>
+               <div class='card'>
+                       <div class='card-body text-center'>
+                               <div class='graph' ng-bind-html='graph'></div>
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/index.html b/share/nitweb/views/index.html
new file mode 100644 (file)
index 0000000..b7bee8e
--- /dev/null
@@ -0,0 +1,70 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2>Welcome to NitWeb!</h2>
+               <p class='text-muted'>The Nit knowledge base.</p>
+       </div>
+
+       <ul class='nav nav-tabs' role='tablist'>
+               <li role='presentation' class='active'>
+                       <a data-toggle='tab' role='tab' data-target='#highlighted' aria-controls='highlighted'>
+                               <span class='glyphicon glyphicon-book'/> Highlighed
+                       </a>
+               </li>
+               <li role='presentation'>
+                       <a data-toggle='tab' role='tab' data-target='#required' aria-controls='required'
+                               ng-click='indexCtrl.loadMostRequired()'>
+                               <span class='glyphicon glyphicon-book'/> Most required
+                       </a>
+               </li>
+               <li role='presentation'>
+                       <a data-toggle='tab' role='tab' data-target='#bytags' aria-controls='bytags'
+                               ng-click='indexCtrl.loadByTags()'>
+                               <span class='glyphicon glyphicon-book'/> By tags
+                       </a>
+               </li>
+       </ul>
+       <table class='table'>
+               <tr>
+                       <td ng-repeat='(key, value) in stats'>
+                               <h5><strong>{{value}}</strong>&nbsp;<span>{{key}}</span></h5>
+                       </td>
+               </tr>
+       </table>
+
+       <div class='container-fluid'>
+               <div class='col-xs-9'>
+                       <div class='tab-content'>
+                               <div role='tabpanel' class='tab-pane fade in active' id='highlighted'>
+                                       <entity-list list-title='Highlighted packages'
+                                               list-entities='highlighted'
+                                               list-object-filter='{}' />
+                               </div>
+                               <div role='tabpanel' class='tab-pane fade' id='required'>
+                                       <entity-list list-title='Most required'
+                                               list-entities='required'
+                                               list-object-filter='{}' />
+                               </div>
+                               <div role='tabpanel' class='tab-pane fade' id='bytags'>
+                                       <h3>Tags</h3>
+                                       <div class='container-fluid'>
+                                               <div class='col-xs-3' ng-repeat='(tag, packages) in bytags'>
+                                                       <span class='badge'>{{packages.length}}</span>
+                                                       <a ng-click='indexCtrl.scrollTo(tag)'>{{tag}}</a>
+                                               </div>
+                                       </div>
+                                       <div ng-repeat='(tag, packages) in bytags'>
+                                               <entity-list list-id='{{tag}}' list-title='{{tag}}'
+                                                       list-entities='packages'
+                                                       list-object-filter='{}' />
+                                       </div>
+                               </div>
+                       </div>
+               </div>
+               <div class='col-xs-3'>
+                       <contributor-list list-title='Maintainers'
+                                       list-contributors='contributors.maintainers' />
+                       <contributor-list list-title='Contributors'
+                                       list-contributors='contributors.contributors' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/module.html b/share/nitweb/views/module.html
new file mode 100644 (file)
index 0000000..e80f9cf
--- /dev/null
@@ -0,0 +1,57 @@
+<ul class='nav nav-tabs'>
+       <li role='presentation' class='active'>
+               <a data-toggle='tab' data-target='#doc'>
+                       <span class='glyphicon glyphicon-book'/> Doc
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
+                       <span class='glyphicon glyphicon-object-align-vertical'/> Imports
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#code' ng-click="entityCtrl.loadEntityCode()">
+                       <span class='glyphicon glyphicon-console'/> Code
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#defs' ng-click="entityCtrl.loadEntityDefs()">
+                       <span class='glyphicon glyphicon-asterisk'/> Class definitions
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+               <entity-doc mentity='mentity'/>
+
+               <entity-list list-title='Imported modules' list-entities='mentity.imports'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Introduced classes' list-entities='mentity.intro_mclasses'
+                       list-object-filter='{}' />
+
+               <entity-list list-title='Class redefinitions' list-entities='mentity.redef_mclassdefs'
+                       list-object-filter='{}' />
+
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='code'>
+               <div class='card'>
+                       <div class='card-body'>
+                               <pre ng-bind-html='code' />
+                               <entity-location mentity='mentity' />
+                       </div>
+               </div>
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='defs'>
+               <entity-list list-title='Class definitions' list-entities='defs'
+                       list-object-filter='{}' />
+       </div>
+       <div class='tab-pane fade' id='graph'>
+               <div class='card'>
+                       <div class='card-body text-center'>
+                               <div class='graph' ng-bind-html='graph'></div>
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/package.html b/share/nitweb/views/package.html
new file mode 100644 (file)
index 0000000..32d9a2f
--- /dev/null
@@ -0,0 +1,28 @@
+<ul class='nav nav-tabs' role='tablist'>
+       <li role='presentation' class='active'>
+               <a data-toggle='tab' role='tab' data-target='#doc' aria-controls="doc">
+                       <span class='glyphicon glyphicon-book'/> Doc
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#graph' ng-click="entityCtrl.loadEntityGraph()">
+                       <span class='glyphicon glyphicon-object-align-vertical'/> Dependencies
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+               <entity-doc mentity='mentity'/>
+
+               <entity-list list-title='Groups' list-entities='mentity.mgroups'
+                       list-object-filter='{}' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='graph'>
+               <div class='card'>
+                       <div class='card-body text-center'>
+                               <div class='graph' ng-bind-html='graph'></div>
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/propdef.html b/share/nitweb/views/propdef.html
new file mode 100644 (file)
index 0000000..014e820
--- /dev/null
@@ -0,0 +1,34 @@
+<ul class='nav nav-tabs'>
+       <li role='presentation' class='warning'>
+               <a href='{{mentity.mproperty.web_url}}'>
+                       <span class='glyphicon glyphicon-chevron-left'/> Go to property
+               </a>
+       </li>
+       <li role='presentation' class='active'>
+               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization'>
+                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' data-target='#code' ng-click="entityCtrl.loadEntityCode()">
+                       <span class='glyphicon glyphicon-console'/> Code
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='linearization'>
+               <entity-linearization
+                       list-title='Class definitions'
+                       list-entities='linearization'
+                       list-focus='mentity' />
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='code'>
+               <div class='card'>
+                       <div class='card-body'>
+                               <pre ng-bind-html='code' />
+                               <entity-location mentity='mentity' />
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/property.html b/share/nitweb/views/property.html
new file mode 100644 (file)
index 0000000..44e439e
--- /dev/null
@@ -0,0 +1,24 @@
+<ul class='nav nav-tabs'>
+       <li role='presentation' class='active'>
+               <a data-toggle='tab' data-target='#doc'>
+                       <span class='glyphicon glyphicon-book'/> Doc
+               </a>
+       </li>
+       <li role='presentation'>
+               <a data-toggle='tab' role='tab' data-target='#linearization' aria-controls='linearization' ng-click='entityCtrl.loadEntityLinearization()'>
+                       <span class='glyphicon glyphicon-arrow-down'/> Linearization
+               </a>
+       </li>
+</ul>
+
+<div class='tab-content'>
+       <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+               <entity-doc mentity='mentity.intro'/>
+       </div>
+       <div role='tabpanel' class='tab-pane fade' id='linearization'>
+               <entity-linearization
+                       list-title='Class definitions'
+                       list-entities='linearization'
+                       list-focus='mentity.intro' />
+       </div>
+</div>
index 3ebc01e..92510bc 100644 (file)
@@ -229,6 +229,18 @@ class Catalog
        # Number of line of code by package
        var loc = new Counter[MPackage]
 
+       # Number of errors
+       var errors = new Counter[MPackage]
+
+       # Number of warnings and advices
+       var warnings = new Counter[MPackage]
+
+       # Number of warnings per 1000 lines of code (w/kloc)
+       var warnings_per_kloc = new Counter[MPackage]
+
+       # Documentation score (between 0 and 100)
+       var documentation_score = new Counter[MPackage]
+
        # Number of commits by package
        var commits = new Counter[MPackage]
 
@@ -245,8 +257,13 @@ class Catalog
        do
                var p = persons.get_or_null(person)
                if p == null then
-                       p = new Person.parse(person)
-                       persons[person] = p
+                       var new_p = new Person.parse(person)
+                       # Maybe, we already have this person in fact?
+                       p = persons.get_or_null(new_p.to_s)
+                       if p == null then
+                               p = new_p
+                               persons[p.to_s] = p
+                       end
                end
                var projs = contrib2proj[p]
                if not projs.has(mpackage) then
@@ -338,9 +355,27 @@ class Catalog
                var mclasses = 0
                var mmethods = 0
                var loc = 0
+               var errors = 0
+               var warnings = 0
+               # The documentation value of each entity is ad hoc.
+               var entity_score = 0.0
+               var doc_score = 0.0
                for g in mpackage.mgroups do
                        mmodules += g.mmodules.length
+                       var gs = 1.0
+                       entity_score += gs
+                       if g.mdoc != null then doc_score += gs
                        for m in g.mmodules do
+                               var source = m.location.file
+                               if source != null then
+                                       for msg in source.messages do
+                                               if msg.level == 2 then
+                                                       errors += 1
+                                               else
+                                                       warnings += 1
+                                               end
+                                       end
+                               end
                                var am = modelbuilder.mmodule2node(m)
                                if am != null then
                                        var file = am.location.file
@@ -348,9 +383,23 @@ class Catalog
                                                loc += file.line_starts.length - 1
                                        end
                                end
+                               var ms = gs
+                               if m.is_test_suite then ms /= 100.0
+                               entity_score += ms
+                               if m.mdoc != null then doc_score += ms else ms /= 10.0
                                for cd in m.mclassdefs do
+                                       var cs = ms * 0.2
+                                       if not cd.is_intro then cs /= 100.0
+                                       if not cd.mclass.visibility <= private_visibility then cs /= 100.0
+                                       entity_score += cs
+                                       if cd.mdoc != null then doc_score += cs
                                        mclasses += 1
                                        for pd in cd.mpropdefs do
+                                               var ps = ms * 0.1
+                                               if not pd.is_intro then ps /= 100.0
+                                               if not pd.mproperty.visibility <= private_visibility then ps /= 100.0
+                                               entity_score += ps
+                                               if pd.mdoc != null then doc_score += ps
                                                if not pd isa MMethodDef then continue
                                                mmethods += 1
                                        end
@@ -361,11 +410,19 @@ class Catalog
                self.mclasses[mpackage] = mclasses
                self.mmethods[mpackage] = mmethods
                self.loc[mpackage] = loc
+               self.errors[mpackage] = errors
+               self.warnings[mpackage] = warnings
+               if loc > 0 then
+                       self.warnings_per_kloc[mpackage] = warnings * 1000 / loc
+               end
+               var documentation_score =  (100.0 * doc_score / entity_score).to_i
+               self.documentation_score[mpackage] = documentation_score
 
                #score += mmodules.score
                score += mclasses.score
                score += mmethods.score
                score += loc.score
+               score += documentation_score.score
 
                self.score[mpackage] = score.to_i
        end
index 7d2f983..9526e0e 100644 (file)
@@ -577,7 +577,7 @@ abstract class AbstractCompiler
 
        # The list of all associated files
        # Used to generate .c files
-       var files = new List[CodeFile]
+       var files = new Array[CodeFile]
 
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
@@ -779,6 +779,20 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add "\}"
        end
 
+       # Hook to add specif piece of code before the the main C function.
+       #
+       # Is called by `compile_main_function`
+       fun compile_before_main(v: VISITOR)
+       do
+       end
+
+       # Hook to add specif piece of code at the begin on the main C function.
+       #
+       # Is called by `compile_main_function`
+       fun compile_begin_main(v: VISITOR)
+       do
+       end
+
        # Generate the main C function.
        #
        # This function:
@@ -876,12 +890,16 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add_decl("exit(status);")
                v.add_decl("\}")
 
+               compile_before_main(v)
+
                if no_main then
                        v.add_decl("int nit_main(int argc, char** argv) \{")
                else
                        v.add_decl("int main(int argc, char** argv) \{")
                end
 
+               compile_begin_main(v)
+
                v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
                v.add("signal(SIGABRT, sig_handler);")
                v.add("signal(SIGFPE, sig_handler);")
@@ -1127,8 +1145,8 @@ end
 # Where to store generated lines
 class CodeWriter
        var file: CodeFile
-       var lines: List[String] = new List[String]
-       var decl_lines: List[String] = new List[String]
+       var lines = new Array[String]
+       var decl_lines = new Array[String]
 
        # Add a line in the main part of the generated C
        fun add(s: String) do self.lines.add(s)
@@ -1185,8 +1203,6 @@ abstract class AbstractCompilerVisitor
 
        fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
 
-       fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
-
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
 
        # Return an element of a native array.
@@ -1197,6 +1213,16 @@ abstract class AbstractCompilerVisitor
        # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
        fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
 
+       # Allocate `size` bytes with the low_level `nit_alloc` C function
+       #
+       # This method can be redefined to inject statistic or tracing code.
+       #
+       # `tag` if any, is used to mark the class of the allocated object.
+       fun nit_alloc(size: String, tag: nullable String): String
+       do
+               return "nit_alloc({size})"
+       end
+
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of runtime variables to use in the call.
@@ -1629,9 +1655,9 @@ abstract class AbstractCompilerVisitor
                var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
-               var bytelen = self.int_instance(string.bytelen)
+               var byte_length = self.int_instance(string.byte_length)
                var unilen = self.int_instance(string.length)
-               self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
+               self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, byte_length, unilen]).as(not null)};")
                self.add("{name} = {res};")
                self.add("\}")
                return res
@@ -2528,7 +2554,8 @@ redef class AMethPropdef
                                v.ret(v.new_expr("!{res}", ret.as(not null)))
                                return true
                        else if pname == "new" then
-                               v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+                               var alloc = v.nit_alloc(arguments[1].to_s, "NativeString")
+                               v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
                                return true
                        else if pname == "fetch_4_chars" then
                                v.ret(v.new_expr("(long)*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
@@ -2981,12 +3008,6 @@ redef class AMethPropdef
                else if pname == "sys" then
                        v.ret(v.new_expr("glob_sys", ret.as(not null)))
                        return true
-               else if pname == "calloc_string" then
-                       v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
-                       return true
-               else if pname == "calloc_array" then
-                       v.calloc_array(ret.as(not null), arguments)
-                       return true
                else if pname == "object_id" then
                        v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                        return true
@@ -3139,7 +3160,7 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
-               if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
+               if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
        end
 
        # Evaluate, store and return the default value of the attribute
@@ -3948,12 +3969,36 @@ redef class ANewExpr
                        return v.native_array_instance(elttype, l)
                end
 
-               var recv = v.init_instance_or_extern(mtype)
-
                var callsite = self.callsite
-               if callsite == null then return recv
+               if callsite == null then return v.init_instance_or_extern(mtype)
                if callsite.is_broken then return null
 
+               var recv
+               # new factories are badly implemented.
+               # They assume a stub temporary receiver exists.
+               # This temporary receiver is required because it
+               # currently holds the method and the formal types.
+               #
+               # However, this object could be reused if the formal types are the same.
+               # Therefore, the following code will `once` it in these case
+               if callsite.mproperty.is_new and not mtype.need_anchor then
+                       var name = v.get_name("varoncenew")
+                       var guard = v.get_name(name + "_guard")
+                       v.add_decl("static {mtype.ctype} {name};")
+                       v.add_decl("static int {guard};")
+                       recv = v.new_var(mtype)
+                       v.add("if (likely({guard})) \{")
+                       v.add("{recv} = {name};")
+                       v.add("\} else \{")
+                       var i = v.init_instance_or_extern(mtype)
+                       v.add("{recv} = {i};")
+                       v.add("{name} = {recv};")
+                       v.add("{guard} = 1;")
+                       v.add("\}")
+               else
+                       recv = v.init_instance_or_extern(mtype)
+               end
+
                var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
                var res2 = v.compile_callsite(callsite, args)
                if res2 != null then
index c013295..3c0ab59 100644 (file)
@@ -18,6 +18,7 @@ module compiler
 import separate_erasure_compiler
 import global_compiler
 import compiler_ffi
+import memory_logger
 
 import platform::android
 import platform::pnacl
index 0ac6e66..628e78a 100644 (file)
@@ -426,11 +426,6 @@ class GlobalCompilerVisitor
                self.add("{recv}[{i}]={val};")
        end
 
-       redef fun calloc_array(ret_type, arguments)
-       do
-               self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
-       end
-
        redef fun send(m, args)
        do
                var types = self.collect_types(args.first)
diff --git a/src/compiler/memory_logger.nit b/src/compiler/memory_logger.nit
new file mode 100644 (file)
index 0000000..1f96451
--- /dev/null
@@ -0,0 +1,76 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Extension to inject memory-tracing instrumentation in code generated by `nitc`.
+module memory_logger
+
+import abstract_compiler
+
+redef class ToolContext
+       # --trace-memory
+       var opt_trace_memory = new OptionBool("Enable dynamic measure of memory usage", "--trace-memory")
+
+       init do
+               self.option_context.add_option opt_trace_memory
+       end
+end
+
+redef class AbstractCompiler
+       redef fun compile_before_main(v)
+       do
+               super
+
+               if not modelbuilder.toolcontext.opt_trace_memory.value then return
+
+               header.add_decl("#include <time.h>")
+               header.add_decl("extern FILE *mlog;")
+               header.add_decl("extern struct timespec mlog_last;")
+               header.add_decl("extern struct timespec mlog_time0;")
+               v.add_decl("FILE *mlog;")
+               v.add_decl("struct timespec mlog_last;")
+               v.add_decl("struct timespec mlog_time0;")
+       end
+
+       redef fun compile_begin_main(v)
+       do
+               super
+
+               if not modelbuilder.toolcontext.opt_trace_memory.value then return
+
+               v.add("mlog = fopen(\"memory.log\", \"w\");")
+               v.add("clock_gettime(CLOCK_MONOTONIC, &mlog_time0);")
+       end
+end
+
+redef class AbstractCompilerVisitor
+       redef fun nit_alloc(size, tag)
+       do
+               if not compiler.modelbuilder.toolcontext.opt_trace_memory.value then return super
+
+               # Log time each 10ms (ie 1e7ns)
+               var tw = get_name("mtw")
+               add_decl("struct timespec {tw};")
+               add("clock_gettime(CLOCK_MONOTONIC, &{tw});")
+               add("if(mlog_last.tv_sec < {tw}.tv_sec || mlog_last.tv_nsec + 1e7 < {tw}.tv_nsec) \{")
+               add("mlog_last = {tw};")
+               add("fprintf(mlog, \"# %f\\n\", 1000.0*{tw}.tv_sec + 1e-6*{tw}.tv_nsec - (1000.0*mlog_time0.tv_sec + 1e-6*mlog_time0.tv_nsec));")
+               add("\}")
+
+               # Print size and tag the mlog
+               var str = "\"+\\t%d\\t%s\\n\", {size}, \"{tag or else "?"}\""
+               add("fprintf(mlog, {str});")
+
+               return super
+       end
+end
index 362bb52..63f5517 100644 (file)
@@ -877,7 +877,8 @@ class SeparateCompiler
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
-                       v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
+                       v.add("struct instance_{c_name}*res = {alloc};")
                        v.compiler.undead_types.add(mtype)
                        v.require_declaration("type_{c_name}")
                        v.add("res->type = &type_{c_name};")
@@ -899,7 +900,8 @@ class SeparateCompiler
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
-                               v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
+                               alloc = v.nit_alloc("sizeof(struct instance_{mtype.c_name})", mclass.full_name)
+                               v.add("{res} = {alloc};")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
@@ -926,7 +928,8 @@ class SeparateCompiler
                        var res = v.get_name("self")
                        v.add_decl("struct instance_{c_name} *{res};")
                        var mtype_elt = mtype.arguments.first
-                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype})", mclass.full_name)
+                       v.add("{res} = {alloc};")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
@@ -949,7 +952,8 @@ class SeparateCompiler
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
-                               v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
+                               var alloc = v.nit_alloc("sizeof(struct instance_{pointer_type.c_name})", mclass.full_name)
+                               v.add("{res} = {alloc};")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
@@ -972,9 +976,11 @@ class SeparateCompiler
                        res.is_exact = true
                        var attrs = self.attr_tables.get_or_null(mclass)
                        if attrs == null then
-                               v.add("{res} = nit_alloc(sizeof(struct instance));")
+                               var alloc = v.nit_alloc("sizeof(struct instance)", mclass.full_name)
+                               v.add("{res} = {alloc};")
                        else
-                               v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
+                               var alloc = v.nit_alloc("sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t)", mclass.full_name)
+                               v.add("{res} = {alloc};")
                        end
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
index 6f7d1e8..c370461 100644 (file)
@@ -657,11 +657,4 @@ class SeparateErasureCompilerVisitor
                self.add("{res} = NEW_{nclass.c_name}({length});")
                return res
        end
-
-       redef fun calloc_array(ret_type, arguments)
-       do
-               var ret = ret_type.as(MClassType)
-               self.require_declaration("NEW_{ret.mclass.c_name}")
-               self.ret(self.new_expr("NEW_{ret.mclass.c_name}({arguments[1]})", ret_type))
-       end
 end
index e69bb38..1537704 100644 (file)
@@ -19,8 +19,6 @@
 # * `nitdoc` wikilinks like `[[doc: MEntity::name]]`
 module doc_commands
 
-import doc_base
-
 # A command aimed at a documentation tool like `nitdoc` or `nitx`.
 #
 # `DocCommand` are generally of the form `command: args`.
@@ -57,6 +55,8 @@ interface DocCommand
                        return new CallCommand(command_string)
                else if command_string.has_prefix("code:") then
                        return new CodeCommand(command_string)
+               else if command_string.has_prefix("graph:") then
+                       return new GraphCommand(command_string)
                end
                return new UnknownCommand(command_string)
        end
@@ -153,3 +153,11 @@ end
 class CodeCommand
        super AbstractDocCommand
 end
+
+# A `DocCommand` that display an graph for a `MEntity`.
+#
+# Syntax:
+# * `graph: MEntity::name`
+class GraphCommand
+       super AbstractDocCommand
+end
index bc5a762..9bb75af 100644 (file)
@@ -64,6 +64,8 @@ redef class MDoc
        # Renders markdown line as a HTML comment block.
        private fun lines_to_html(lines: Array[String]): Writable do
                var res = new Template
+               var decorator = markdown_proc.emitter.decorator.as(NitdocDecorator)
+               decorator.current_mdoc = self
                res.add "<div class=\"nitdoc\">"
                # do not use DocUnit as synopsys
                if not lines.is_empty then
@@ -88,15 +90,26 @@ redef class MDoc
                # add other lines
                res.add markdown_proc.process(lines.join("\n"))
                res.add "</div>"
+               decorator.current_mdoc = null
                return res
 
        end
 end
 
-private class NitdocDecorator
+# The specific markdown decorator used internally to process MDoc object.
+#
+# You should use the various methods of `MDoc` like `MDoc::html_documentation`
+#
+# The class is public so specific behavior can be plugged on it.
+class NitdocDecorator
        super HTMLDecorator
 
-       var toolcontext = new ToolContext
+       private var toolcontext = new ToolContext
+
+       # The currently processed mdoc.
+       #
+       # Unfortunately, this seems to be the simpler way to get the currently processed `MDoc` object.
+       var current_mdoc: nullable MDoc = null
 
        redef fun add_code(v, block) do
                var meta = block.meta or else "nit"
@@ -143,7 +156,7 @@ private class NitdocDecorator
                v.add "</code>"
        end
 
-       fun code_from_text(buffer: Text, from, to: Int): String do
+       private fun code_from_text(buffer: Text, from, to: Int): String do
                var out = new FlatBuffer
                for i in [from..to[ do out.add buffer[i]
                return out.write_to_string
index c2ed988..a8d86b6 100644 (file)
@@ -31,9 +31,6 @@ redef class ToolContext
        var opt_source = new OptionString("Format to link source code (%f for filename, " +
                "%l for first line, %L for last line)", "--source")
 
-       # Directory where the CSS and JS is stored.
-       var opt_sharedir = new OptionString("Directory containing nitdoc assets", "--sharedir")
-
        # Use a shareurl instead of copy shared files.
        #
        # This is usefull if you don't want to store the Nitdoc templates with your
@@ -77,7 +74,7 @@ redef class ToolContext
                super
 
                option_context.add_option(
-                       opt_source, opt_sharedir, opt_shareurl, opt_custom_title,
+                       opt_source, opt_share_dir, opt_shareurl, opt_custom_title,
                        opt_custom_footer, opt_custom_intro, opt_custom_brand,
                        opt_github_upstream, opt_github_base_sha1, opt_github_gitdir,
                        opt_piwik_tracker, opt_piwik_site_id,
@@ -120,15 +117,7 @@ class RenderHTMLPhase
                var output_dir = ctx.output_dir
                if not output_dir.file_exists then output_dir.mkdir
                # locate share dir
-               var sharedir = ctx.opt_sharedir.value
-               if sharedir == null then
-                       var dir = ctx.nit_dir
-                       sharedir = dir/"share/nitdoc"
-                       if not sharedir.file_exists then
-                               print "Error: cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
-                               abort
-                       end
-               end
+               var sharedir = ctx.share_dir / "nitdoc"
                # copy shared files
                if ctx.opt_shareurl.value == null then
                        sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/")
index 7623c40..b8107ee 100644 (file)
@@ -18,7 +18,7 @@ module html_model
 import doc_base
 import html_components
 import ordered_tree
-import web::model_html
+import model_html
 
 redef class MEntity
        # URL of this entity’s Nitdoc page.
similarity index 93%
rename from src/web/model_html.nit
rename to src/doc/html_templates/model_html.nit
index 2d47728..e0dfb95 100644 (file)
@@ -32,12 +32,12 @@ redef class MEntity
        # * MPropdef: `foo(e)`
        var html_name: String is lazy do return name.html_escape
 
-       # MEntity namespace escaped for html.
-       fun html_raw_namespace: String is abstract
+       # Returns the MEntity full_name escaped for html.
+       var html_full_name: String is lazy do return full_name.html_escape
 
        # Link to MEntity in the web server.
        # TODO this should be parameterizable... but how?
-       fun html_link: Link do return new Link("/doc/{html_raw_namespace}", html_name)
+       fun html_link: Link do return new Link("/doc/{full_name}", html_name)
 
        # Returns the list of keyword used in `self` declaration.
        fun html_modifiers: Array[String] is abstract
@@ -107,22 +107,12 @@ redef class MEntity
 end
 
 redef class MPackage
-       redef fun html_raw_namespace do return html_name
-
        redef var html_modifiers = ["package"]
        redef fun html_namespace do return html_link
        redef var css_classes = ["public"]
 end
 
 redef class MGroup
-       redef fun html_raw_namespace do
-               var parent = self.parent
-               if parent != null then
-                       return "{parent.html_raw_namespace}::{html_name}"
-               end
-               return "{mpackage.html_raw_namespace}::{html_name}"
-       end
-
        redef var html_modifiers = ["group"]
 
        # Depends if `self` is root or not.
@@ -161,17 +151,6 @@ redef class MModule
                return tpl
        end
 
-       redef fun html_raw_namespace do
-               var mpackage = self.mpackage
-               var mgroup = self.mgroup
-               if mgroup != null then
-                       return "{mgroup.html_raw_namespace}::{html_name}"
-               else if mpackage != null then
-                       return "{mpackage.html_raw_namespace}::{html_name}"
-               end
-               return html_name
-       end
-
        redef var css_classes = ["public"]
 end
 
@@ -211,8 +190,6 @@ redef class MClass
                return tpl
        end
 
-       redef fun html_raw_namespace do return intro.html_raw_namespace
-
        # Returns `intro.html_short_signature`.
        fun html_short_signature: Template do return intro.html_short_signature
 
@@ -224,8 +201,6 @@ redef class MClass
 end
 
 redef class MClassDef
-       redef fun html_raw_namespace do return "{mmodule.html_raw_namespace}::{html_name}"
-
        redef fun mdoc_or_fallback do return mdoc or else mclass.mdoc_or_fallback
 
        # Depends if `self` is an intro or not.
@@ -342,8 +317,6 @@ redef class MProperty
                return tpl
        end
 
-       redef fun html_raw_namespace do return intro.html_raw_namespace
-
        # Returns `intro.html_short_signature`.
        fun html_short_signature: Template do return intro.html_short_signature
 
@@ -354,7 +327,6 @@ redef class MProperty
 end
 
 redef class MPropDef
-       redef fun html_raw_namespace do return "{mclassdef.html_raw_namespace}::{html_name}"
        redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback
 
        # Depends if `self` is an intro or not.
@@ -593,12 +565,10 @@ end
 redef class MParameterType
        redef fun html_short_signature do return html_link
        redef fun html_signature do return html_link
-       redef fun html_raw_namespace do return html_name
 end
 
 redef class MVirtualType
        redef fun html_signature do return html_link
-       redef fun html_raw_namespace do return html_name
 end
 
 redef class MSignature
diff --git a/src/examples/README.md b/src/examples/README.md
new file mode 100644 (file)
index 0000000..17a6638
--- /dev/null
@@ -0,0 +1,6 @@
+# Example programs on the Nit model and compiler
+
+This directory contains small sample programs that use the various services of `nitc`.
+
+Most of these programs should be short, simple and focuses on a few concerns.
+Successful ones might move to the parent directory and become a full stand-alone tool (with a manpage!)
diff --git a/src/examples/get_mclasses.nit b/src/examples/get_mclasses.nit
new file mode 100644 (file)
index 0000000..79e22ba
--- /dev/null
@@ -0,0 +1,86 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Sample program that search classes in a model
+#
+# eg.
+#
+# ~~~raw
+# get_mclasses ../../lib core::Array Array care::Arrow Fail
+# ~~~
+module get_mclasses
+
+import parser_util
+import modelbuilder
+import modelize
+import more_collections
+
+# Manage the logistic of the tool
+var tc = new ToolContext
+tc.tooldescription = "Usage: get_mclasses file qualified_class_name..."
+tc.process_options(args)
+
+# Parse the first argument to fill a model
+var model = new Model
+var mb = new ModelBuilder(model, tc)
+mb.parse_full([args.shift])
+mb.run_phases
+
+# Query remaining arguments
+for arg in args do
+       # Ask the parser to parse the argument. It's its job!
+       var n = tc.parse_something(arg)
+       if n isa AType then
+               # Only the class qualified identifier is useful
+               # We just ignore `nullable` or generic arguments.
+               var qid = n.n_qid
+               var full_name = qid.full_name
+               print "search: {full_name}"
+
+               # Iterate on all classes of the model to find one that matches the qualified name.
+               # Because we are efficient, we iterate only on the classes with the same short name.
+               var short_name = qid.n_id.text
+               var classes = model.get_mclasses_by_name(short_name)
+               var found = false
+               if classes != null then for c in classes do
+                       if qid.accept(c) then
+                               print " * {c.full_name}"
+                               found = true
+                       end
+               end
+
+               if not found then
+                       # We are unlucky, maybe a misspell?
+                       # Look for all classes with a similar name
+                       var bests = new BestDistance[MClass](full_name.length)
+                       for c in model.mclasses do
+                               # Check with both the full_name and the short_name
+                               var lev = full_name.levenshtein_distance(c.full_name)
+                               bests.update(lev, c)
+                               lev = full_name.levenshtein_distance(c.name)
+                               bests.update(lev, c)
+                       end
+                       if bests.best_items.is_empty then
+                               print " Found nothing :("
+                       else
+                               print " Did you mean?"
+                               for c in bests.best_items do
+                                       print " * {c.full_name}"
+                               end
+                       end
+               end
+       else
+               print "`{arg}` is {n}, wanted a class name :("
+       end
+end
diff --git a/src/examples/nitlight_as_a_service.nit b/src/examples/nitlight_as_a_service.nit
new file mode 100644 (file)
index 0000000..e117268
--- /dev/null
@@ -0,0 +1,220 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is an example of a client of the frontend without command-line processing.
+#
+# It offers a simple nitcorn web server that offers a textarea and nitpick and nitlignt it.
+module nitlight_as_a_service
+
+import frontend
+import highlight
+import nitcorn
+import nitcorn::log
+import template
+
+# Fully process a content as a nit source file.
+fun hightlightcode(hl: HighlightVisitor, content: String): HLCode
+do
+       # Prepare a stand-alone tool context
+       var tc = new ToolContext
+       tc.nit_dir = tc.locate_nit_dir # still use the common lib to have core
+       tc.keep_going = true # no exit, obviously
+       tc.opt_warn.value = -1 # no output, obviously
+
+       # Prepare an stand-alone model and model builder.
+       # Unfortunately, models are enclosing and append-only.
+       # There is no way (yet?) to have a shared module `core` with
+       # isolated and throwable user modules.
+       var model = new Model
+       var mb = new ModelBuilder(model, tc)
+
+       # Parse the code
+       var source = new SourceFile.from_string("", content + "\n")
+       var lexer = new Lexer(source)
+       var parser = new Parser(lexer)
+       var tree = parser.parse
+
+       var hlcode = new HLCode(hl, content, source)
+
+       # Check syntax error
+       var eof = tree.n_eof
+       if eof isa AError then
+               mb.error(eof, eof.message)
+               hl.hightlight_source(source)
+               return hlcode
+       end
+       var amodule = tree.n_base.as(not null)
+
+       # Load the AST as a module in the model
+       # Then process it
+       mb.load_rt_module(null, amodule, "")
+       mb.run_phases
+
+       # Highlight the processed module
+       hl.enter_visit(amodule)
+       return hlcode
+end
+
+# A standalone highlighted piece of code
+class HLCode
+       # The highlighter used
+       var hl: HighlightVisitor
+
+       # The raw code source
+       var content: String
+
+       # The pseudo source-file
+       var source: SourceFile
+
+       # JavaScript code to update an existing codemirror editor.
+       fun code_mirror_update: Template
+       do
+
+               var res = new Template
+               res.add """
+       function nitmessage() {
+               editor.operation(function(){
+                       for (var i = 0; i < widgets.length; ++i)
+                             editor.removeLineWidget(widgets[i]);
+                       widgets.length = 0;
+"""
+
+               for m in source.messages do
+                       res.add """
+                       var l = document.createElement("div");
+                       l.className = "lint-error"
+                       l.innerHTML = "<span class='glyphicon glyphicon-warning-sign lint-error-icon'></span> {{{m.text.html_escape}}}";
+                       var w = editor.addLineWidget({{{m.location.line_start-1}}}, l);
+                       widgets.push(w);
+"""
+               end
+               res.add """});}"""
+               return res
+       end
+end
+
+# Nitcorn service to hightlight code
+#
+# It's a single stand-alone page that has to form to itself.
+class HighlightAction
+       super Action
+
+       redef fun answer(http_request, turi)
+       do
+               var hl = new HighlightVisitor
+               var page = new Template
+
+               # There is code? Process it
+               var code = http_request.post_args.get_or_null("code")
+               var hlcode = null
+               if code != null then hlcode = hightlightcode(hl, code)
+
+               if http_request.post_args.get_or_null("ajax") == "true" and hlcode != null then
+                       page.add hlcode.code_mirror_update
+                       page.add """
+                       document.getElementById("lightcode").innerHTML = "{{{hl.html.write_to_string.escape_to_c}}}";
+                       nitmessage();
+                       """
+
+                       var response = new HttpResponse(200)
+                       response.header["Content-Type"] = "application/javascript"
+                       response.body = page.write_to_string
+                       return response
+               end
+
+               page.add """
+               <!doctype html><html><head>{{{hl.head_content}}}
+               <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.16.0/codemirror.css">
+               <style>
+                       {{{hl.css_content}}}
+                       textarea {width:100%;}
+                       .lint-error {font-family: arial; font-size: 70%; background: #ffa; color: #a00; padding: 2px 5px 3px; }
+                       .lint-error-icon {color: red; padding: 0 3px; margin-right: 7px;}
+               </style></head><body>
+               """
+               # Add the form+textarea
+               page.add """
+               <form action="#light" method=post><textarea id=code name=code rows=10>{{{code or else ""}}}</textarea><br><input type=submit></form>
+               """
+
+               if hlcode != null then
+                       # Inject highlight
+                       page.add "<pre id=light><code id=lightcode>"
+                       page.add hl.html.write_to_string
+                       page.add "</code></pre><hr>"
+                       page.add "<ul>"
+
+                       # List messages
+                       for m in hlcode.source.messages do
+                               page.add "<li>{m.location.as(not null)}: {m.text}</li>"
+                       end
+                       page.add "</ul>"
+               else
+                       page.add "<pre id=light><code id=lightcode></code></pre>"
+               end
+
+               page.add hl.foot_content
+
+               # Call codemirror
+               page.add """
+               <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.16.0/codemirror.min.js"></script>
+               <script>
+               var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+                       lineNumbers: true
+               });
+               """
+
+               # Callback to update codemirror messages
+               if hlcode != null then
+                       page.add hlcode.code_mirror_update
+               else
+                       page.add "function nitmessage()\{\}"
+               end
+               page.add """
+               var widgets = [];
+               nitmessage();
+
+               function updatePage() {
+               $.post("", { ajax: true, code: editor.getValue()}, function(data) {
+                       eval(data);
+                       $(".popupable").popover({html:true, placement:'top'});
+               });
+               }
+
+               var waiting;
+               editor.on("change", function() {
+                       clearTimeout(waiting);
+                       waiting = setTimeout(updatePage, 500);
+               });
+               waiting = setTimeout(updatePage, 500);
+
+               </script>
+               </body></html>
+               """
+
+               var response = new HttpResponse(200)
+               response.header["Content-Type"] = "text/html"
+               response.body = page.write_to_string
+               return response
+       end
+end
+
+var host = "localhost:8080"
+if args.length > 0 then host = args.first
+
+var vh = new VirtualHost(host)
+vh.routes.add new Route("/", new HighlightAction)
+var factory = new HttpFactory.and_libevent
+factory.config.virtual_hosts.add vh
+factory.run
diff --git a/src/examples/test_loader.nit b/src/examples/test_loader.nit
new file mode 100644 (file)
index 0000000..e81a0af
--- /dev/null
@@ -0,0 +1,90 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Sample program that scan and load things.
+#
+# It shows the difference betwenn the various services of the `loader`.
+module test_loader
+
+import loader
+
+# Display current loaded/known things.
+fun stats(mb: ModelBuilder)
+do
+       var m = mb.model
+       print "  model: mpackages={m.mpackages.length} mmodules={m.mmodules.length}"
+       print "  mb: identified modules={mb.identified_modules.length}; parsed modules={mb.parsed_modules.length}"
+end
+
+
+# Create a tool context to handle options and paths
+var toolcontext = new ToolContext
+toolcontext.keep_going = true
+
+# We do not add other options, so process them now!
+toolcontext.process_options(args)
+var arguments = toolcontext.option_context.rest
+
+# We need a model to collect stuff
+var model = new Model
+# And a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext)
+
+# Identify each argument independently
+for a in arguments do
+       var x
+
+       print "{a}: module?"
+       x = modelbuilder.identify_module(a)
+       if x != null then
+               print "\tmodule {x.full_name} at {x.filepath or else "?"}"
+       else
+               var le = modelbuilder.last_loader_error
+               if le != null then
+                       print "\t{le}"
+               else
+                       print "\tnothing"
+               end
+       end
+
+       print "{a}: group?"
+       x = modelbuilder.identify_group(a)
+       if x != null then
+               print "\tgroup {x.full_name} at {x.filepath or else "?"}"
+       else
+               var le = modelbuilder.last_loader_error
+               if le != null then
+                       print "\t{le}"
+               else
+                       print "\tnothing"
+               end
+       end
+       toolcontext.check_errors
+       stats(modelbuilder)
+end
+
+# Scan everything (including subdirectories)
+var mm = modelbuilder.scan_full(arguments)
+print "scan_full found {mm.length} modules"
+stats(modelbuilder)
+
+# Parse specific modules only
+mm = modelbuilder.parse(arguments)
+print "parse found {mm.length} modules"
+stats(modelbuilder)
+
+# Parse everything (including modules in subdirectories)
+mm = modelbuilder.parse_full(arguments)
+print "parse_full found {mm.length} modules"
+stats(modelbuilder)
index 59e5370..8dd1637 100644 (file)
@@ -43,6 +43,27 @@ class HighlightVisitor
        # The last line to generate, null if finish at the last line
        var last_line: nullable Int = null is writable
 
+       # When highlighting a node, show its messages (errors, warnings), if any.
+       #
+       # default: true
+       var show_messages = true is writable
+
+       # When highlighting a node, attach a full popupable infobox, if any.
+       #
+       # If `false`, only a simple `title` tooltip is used.
+       #
+       # default: true
+       var show_infobox = true is writable
+
+       # A reference to an entity used in generated `<a>` elements.
+       #
+       # It is used to refer to some specific entities when generating links.
+       # If `null` is returned, then no link are generated and `<a>` elements become `<span>`.
+       #
+       # By default, `null` is returned.
+       # Clients are therefore encouraged to redefine the method in a subclass to control where entities should link to.
+       fun hrefto(entitiy: MEntity): nullable String do return null
+
        init
        do
                html.add_class("nitcode")
@@ -108,14 +129,16 @@ class HighlightVisitor
                if infobox == null and anode isa Token then
                        var pa = anode.parent
                        if pa != null then
-                               var c = anode
-                               if c isa TId or c isa TClassid or c isa TAttrid or c isa TokenLiteral or c isa TokenOperator or c isa TComment and pa isa ADoc then
-                                       infobox = pa.decorate_tag(hv, tag, anode)
-                               end
+                               infobox = pa.decorate_tag(hv, tag, anode)
                        end
                end
+               if infobox != null and not show_infobox then
+                       tag.attr("title", infobox.title)
+                       tag.classes.add "titled"
+                       infobox = null
+               end
                var messages = anode.location.messages
-               if messages != null then
+               if messages != null and show_messages then
                        tag.css("border-bottom", "solid 2px red")
                        if infobox == null then
                                infobox = new HInfoBox(hv, "Messages")
@@ -131,14 +154,22 @@ class HighlightVisitor
                return tag
        end
 
+       # Highlight a full lexed source file.
+       #
+       # REQUIRE `source.first_token != null`
+       fun hightlight_source(source: SourceFile)
+       do
+               htmlize(source.first_token.as(not null), null)
+       end
+
        # Produce HTML between two tokens
-       protected fun htmlize(first_token, last_token: Token)
+       protected fun htmlize(first_token: Token, last_token: nullable Token)
        do
                var stack2 = new Array[HTMLTag]
                var stack = new Array[Prod]
                var line = 0
                var c: nullable Token = first_token
-               var hv = new HighlightVisitor
+               var hv = self
                while c != null do
                        var starting
 
@@ -236,6 +267,7 @@ class HighlightVisitor
        do
                return """
 .nitcode a { color: inherit; cursor:pointer; }
+.nitcode .titled:hover { text-decoration: underline; } /* underline titles */
 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
 .nitcode .foldable { display: block } /* for block productions*/
 .nitcode .line{ display: block } /* for lines */
@@ -349,9 +381,6 @@ end
 interface HInfoBoxable
        # An new infobox documenting the entity
        fun infobox(v: HighlightVisitor): HInfoBox is abstract
-
-       # A human-readable hyper-text for the entity
-       fun linkto: HTMLTag is abstract
 end
 
 redef class MDoc
@@ -372,56 +401,69 @@ end
 
 redef class MEntity
        super HInfoBoxable
+
+       # A HTML version of `to_s` with hyper-links.
+       #
+       # By default, `linkto_text(v, to_s)` is used, c.f. see `linkto_text`.
+       #
+       # For some complex entities, like generic types, multiple `<a>` and `<span>` elements can be generated.
+       # E.g. `Array[Int]` might become `<a>Array</a>[<a>Int</a>]` with the correct `href` attributes
+       # provided  by `v.hrefto`.
+       fun linkto(v: HighlightVisitor): HTMLTag do return linkto_text(v, to_s)
+
+       # Link to the `self` with a specific text.
+       #
+       # The whole text is linked with a single `<a>` element.
+       #
+       # The `href` used is provided by `v.hrefto`.
+       # If `href` is null then a `<span>` element is used instead of `<a>`.
+       fun linkto_text(v: HighlightVisitor, text: String): HTMLTag
+       do
+               var href = v.hrefto(self)
+               if href == null then
+                       return (new HTMLTag("span")).text(text)
+               end
+               return (new HTMLTag("a")).attr("href", href).text(text)
+       end
 end
 
 redef class MModule
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, "module {name}")
-               res.href = href
-               res.new_field("module").add(linkto)
+               res.href = v.hrefto(self)
+               res.new_field("module").add(linkto(v))
                var mdoc = self.mdoc
                if mdoc != null then mdoc.fill_infobox(res)
                if in_importation.greaters.length > 1 then
                        var c = res.new_dropdown("imports", "{in_importation.greaters.length-1} modules")
                        for x in in_importation.greaters do
                                if x == self then continue
-                               c.open("li").add x.linkto
+                               c.open("li").add x.linkto(v)
                        end
                end
                return res
        end
 
-       # The module HTML page
-       fun href: String
-       do
-               return c_name + ".html"
-       end
-
-       redef fun linkto do return linkto_text(name)
-
-       # Link to the entitiy with a specific text
-       fun linkto_text(text: String): HTMLTag
-       do
-               return (new HTMLTag("a")).attr("href", href).text(text)
-       end
+       redef fun linkto(v) do return linkto_text(v, name)
 end
 
 redef class MClassDef
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, "class {mclass.name}")
-               res.href = href
+               res.href = v.hrefto(self)
                if is_intro then
                        res.new_field("class").text(mclass.name)
                else
                        res.new_field("redef class").text(mclass.name)
-                       res.new_field("intro").add mclass.intro.linkto_text("in {mclass.intro_mmodule.to_s}")
+                       res.new_field("intro").add mclass.intro.linkto_text(v, "in {mclass.intro_mmodule.to_s}")
                end
                var mdoc = self.mdoc
                if mdoc == null then mdoc = mclass.intro.mdoc
                if mdoc != null then mdoc.fill_infobox(res)
 
+               var in_hierarchy = self.in_hierarchy
                if in_hierarchy == null then return res
 
                if in_hierarchy.greaters.length > 1 then
@@ -429,7 +471,7 @@ redef class MClassDef
                        for x in in_hierarchy.greaters do
                                if x == self then continue
                                if not x.is_intro then continue
-                               c.open("li").add x.linkto
+                               c.open("li").add x.linkto(v)
                        end
                end
                if in_hierarchy.smallers.length > 1 then
@@ -437,52 +479,41 @@ redef class MClassDef
                        for x in in_hierarchy.smallers do
                                if x == self then continue
                                if not x.is_intro then continue
-                               c.open("li").add x.linkto
+                               c.open("li").add x.linkto(v)
                        end
                end
                if mclass.mclassdefs.length > 1 then
                        var c = res.new_dropdown("redefs", "refinements")
                        for x in mclass.mclassdefs do
                                if x == self then continue
-                               c.open("li").add x.linkto_text("in {x.mmodule}")
+                               c.open("li").add x.linkto_text(v, "in {x.mmodule}")
                        end
                end
                return res
        end
-
-       # The class HTML page (an anchor in the module page)
-       fun href: String
-       do
-               return mmodule.href + "#" + to_s
-       end
-
-       redef fun linkto do return linkto_text(mclass.name)
-
-       # Link to the entitiy with a specific text
-       fun linkto_text(text: String): HTMLTag
-       do
-               return (new HTMLTag("a")).attr("href", href).text(text)
-       end
 end
 
 redef class MPropDef
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, to_s)
-               res.href = href
+               res.href = v.hrefto(self)
                if self isa MMethodDef then
-                       if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto
+                       var msignature = self.msignature
+                       if msignature != null then res.new_field("fun").append(mproperty.name).add msignature.linkto(v)
                else if self isa MAttributeDef then
-                       if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto
+                       var static_mtype = self.static_mtype
+                       if static_mtype != null then res.new_field("fun").append(mproperty.name).add static_mtype.linkto(v)
                else if self isa MVirtualTypeDef then
-                       if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto
+                       var bound = self.bound
+                       if bound != null then res.new_field("add").append(mproperty.name).add bound.linkto(v)
                else
                        res.new_field("wat?").append(mproperty.name)
                end
 
                if is_intro then
                else
-                       res.new_field("intro").add mproperty.intro.linkto_text("in {mproperty.intro.mclassdef}")
+                       res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
                end
                var mdoc = self.mdoc
                if mdoc == null then mdoc = mproperty.intro.mdoc
@@ -490,72 +521,54 @@ redef class MPropDef
                if mproperty.mpropdefs.length > 1 then
                        var c = res.new_dropdown("redef", "redefinitions")
                        for x in mproperty.mpropdefs do
-                               c.open("li").add x.linkto_text("in {x.mclassdef}")
+                               c.open("li").add x.linkto_text(v, "in {x.mclassdef}")
                        end
                end
 
                return res
        end
-
-       # The property HTML page (an anchor in the module page)
-       fun href: String
-       do
-               return self.mclassdef.mmodule.href + "#" + self.to_s
-       end
-
-       redef fun linkto do return linkto_text(mproperty.name)
-
-       # Link to the entitiy with a specific text
-       fun linkto_text(text: String): HTMLTag
-       do
-               return (new HTMLTag("a")).attr("href", href).text(text)
-       end
 end
 
 redef class MClassType
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, to_s)
-               res.href = mclass.intro.href
-               res.new_field("class").add mclass.intro.linkto
+               res.href = v.hrefto(self)
+               res.new_field("class").add mclass.intro.linkto(v)
                var mdoc = mclass.mdoc
                if mdoc == null then mdoc = mclass.intro.mdoc
                if mdoc != null then mdoc.fill_infobox(res)
                return res
        end
-       redef fun linkto
+       redef fun linkto(v)
        do
-               return mclass.intro.linkto
+               return mclass.intro.linkto(v)
        end
 end
 redef class MVirtualType
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, to_s)
-               res.href = mproperty.intro.href
+               res.href = v.hrefto(mproperty)
                var p = mproperty
                var pd = p.intro
-               res.new_field("virtual type").add pd.linkto
+               res.new_field("virtual type").add pd.linkto(v)
                var mdoc = pd.mdoc
                if mdoc != null then mdoc.fill_infobox(res)
                return res
        end
-       redef fun linkto
+       redef fun linkto(v)
        do
-               return mproperty.intro.linkto
+               return mproperty.intro.linkto(v)
        end
 end
 redef class MParameterType
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, to_s)
-               res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto
+               res.new_field("parameter type").append("{name} from class ").add mclass.intro.linkto(v)
                return res
        end
-       redef fun linkto
-       do
-               return (new HTMLTag("span")).text(name)
-       end
 end
 
 redef class MNullableType
@@ -563,10 +576,10 @@ redef class MNullableType
        do
                return mtype.infobox(v)
        end
-       redef fun linkto
+       redef fun linkto(v)
        do
                var res = new HTMLTag("span")
-               res.append("nullable ").add(mtype.linkto)
+               res.append("nullable ").add(mtype.linkto(v))
                return res
        end
 end
@@ -576,10 +589,10 @@ redef class MNotNullType
        do
                return mtype.infobox(v)
        end
-       redef fun linkto
+       redef fun linkto(v)
        do
                var res = new HTMLTag("span")
-               res.append("not null ").add(mtype.linkto)
+               res.append("not null ").add(mtype.linkto(v))
                return res
        end
 end
@@ -590,7 +603,7 @@ redef class MNullType
                var res = new HInfoBox(v, to_s)
                return res
        end
-       redef fun linkto
+       redef fun linkto(v)
        do
                var res = new HTMLTag("span")
                res.append("null")
@@ -599,7 +612,7 @@ redef class MNullType
 end
 
 redef class MSignature
-       redef fun linkto
+       redef fun linkto(v)
        do
                var res = new HTMLTag("span")
                var first = true
@@ -613,14 +626,14 @@ redef class MSignature
                                end
                                res.append p.name
                                res.append ": "
-                               res.add p.mtype.linkto
+                               res.add p.mtype.linkto(v)
                        end
                        res.append ")"
                end
                var ret = return_mtype
                if ret != null then
                        res.append ": "
-                       res.add ret.linkto
+                       res.add ret.linkto(v)
                end
                return res
        end
@@ -630,11 +643,11 @@ redef class CallSite
        redef fun infobox(v)
        do
                var res = new HInfoBox(v, "call {mpropdef}")
-               res.href = mpropdef.href
-               res.new_field("call").add(mpropdef.linkto).add(msignature.linkto)
+               res.href = v.hrefto(mpropdef)
+               res.new_field("call").add(mpropdef.linkto(v)).add(msignature.linkto(v))
                if mpropdef.is_intro then
                else
-                       res.new_field("intro").add mproperty.intro.linkto_text("in {mproperty.intro.mclassdef}")
+                       res.new_field("intro").add mproperty.intro.linkto_text(v, "in {mproperty.intro.mclassdef}")
                end
                var mdoc = mpropdef.mdoc
                if mdoc == null then mdoc = mproperty.intro.mdoc
@@ -642,9 +655,9 @@ redef class CallSite
 
                return res
        end
-       redef fun linkto
+       redef fun linkto(v)
        do
-               return mpropdef.linkto
+               return mpropdef.linkto(v)
        end
 end
 
@@ -659,13 +672,9 @@ redef class Variable
                        return res
                end
                var res = new HInfoBox(v, "{name}: {declared_type}")
-               res.new_field("local var").append("{name}:").add(declared_type.linkto)
+               res.new_field("local var").append("{name}:").add(declared_type.linkto(v))
                return res
        end
-       redef fun linkto
-       do
-               return (new HTMLTag("span")).text(name)
-       end
 end
 
 
@@ -687,6 +696,26 @@ redef class ANode
        fun infobox(v: HighlightVisitor): nullable HInfoBox do return null
 end
 
+redef class AQclassid
+       redef fun decorate_tag(v, res, token)
+       do
+               if token != n_id then return null
+               var parent = self.parent
+               if parent == null then return null
+               return parent.decorate_tag(v, res, token)
+       end
+end
+
+redef class AQid
+       redef fun decorate_tag(v, res, token)
+       do
+               if token != n_id then return null
+               var parent = self.parent
+               if parent == null then return null
+               return parent.decorate_tag(v, res, token)
+       end
+end
+
 redef class AStdClassdef
        redef fun make_tag(v)
        do
@@ -737,7 +766,7 @@ end
 redef class Token
        # Produce an HTMLTag with the correct contents and CSS classes
        # Subclasses can redefine it to decorate the tag
-       redef fun make_tag(v: HighlightVisitor): HTMLTag
+       redef fun make_tag(v): HTMLTag
        do
                var res = new HTMLTag("span")
                res.text(text)
@@ -757,8 +786,6 @@ redef class TokenOperator
        redef fun make_tag(v)
        do
                var res = super
-               var p = parent
-               if p != null then p.decorate_tag(v, res, self)
                res.add_class("nc_o")
                return res
        end
@@ -767,6 +794,7 @@ end
 redef class AVarFormExpr
        redef fun decorate_tag(v, res, token)
        do
+               if token != n_id then return null
                var variable = self.variable
                if variable == null then return null
                res.add_class("nc_v")
@@ -777,6 +805,7 @@ end
 redef class AVardeclExpr
        redef fun decorate_tag(v, res, token)
        do
+               if token != n_id then return null
                var variable = self.variable
                if variable == null then return null
                res.add_class("nc_v")
@@ -800,6 +829,7 @@ end
 redef class AParam
        redef fun decorate_tag(v, res, token)
        do
+               if token != n_id then return null
                var mp = mparameter
                if mp == null then return null
                var variable = self.variable
@@ -812,6 +842,7 @@ end
 redef class AAssertExpr
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TId then return null
                res.add_class("nc_ast")
                return null
        end
@@ -820,6 +851,7 @@ end
 redef class ALabel
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TId then return null
                res.add_class("nc_la")
                return null
        end
@@ -828,6 +860,7 @@ end
 redef class ASendExpr
        redef fun decorate_tag(v, res, token)
        do
+               var callsite = self.callsite
                if callsite == null then return null
                return callsite.infobox(v)
        end
@@ -836,6 +869,7 @@ end
 redef class ANewExpr
        redef fun decorate_tag(v, res, token)
        do
+               var callsite = self.callsite
                if callsite == null then return null
                return callsite.infobox(v)
        end
@@ -856,13 +890,16 @@ end
 redef class AModuleName
        redef fun decorate_tag(v, res, token)
        do
-               return parent.decorate_tag(v, res, token)
+               var p = parent
+               if p == null then return null
+               return p.decorate_tag(v, res, token)
        end
 end
 
 redef class AModuledecl
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TId then return null
                res.add_class("nc_def")
                res.add_class("nc_m")
                var p = parent
@@ -876,6 +913,7 @@ end
 redef class AStdImport
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TId then return null
                res.add_class("nc_m")
                var mm = mmodule
                if mm == null then return null
@@ -885,6 +923,7 @@ end
 redef class AAttrPropdef
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TId then return null
                res.add_class("nc_def")
                var mpd: nullable MPropDef
                mpd = mreadpropdef
@@ -898,8 +937,6 @@ redef class TId
        redef fun make_tag(v)
        do
                var res = super
-               var p = parent
-               if p != null then p.decorate_tag(v, res, self)
                res.add_class("nc_i")
                return res
        end
@@ -929,8 +966,6 @@ redef class TAttrid
        redef fun make_tag(v)
        do
                var res = super
-               var p = parent
-               if p != null then p.decorate_tag(v, res, self)
                res.add_class("nc_a")
                return res
        end
@@ -938,6 +973,7 @@ end
 redef class AAttrFormExpr
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TAttrid then return null
                var p = mproperty
                if p == null then return null
                return p.intro.infobox(v)
@@ -947,8 +983,6 @@ redef class TClassid
        redef fun make_tag(v)
        do
                var res = super
-               var p = parent
-               if p != null then p.decorate_tag(v, res, self)
                res.add_class("nc_t")
                return res
        end
@@ -956,6 +990,7 @@ end
 redef class AType
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TClassid then return null
                var mt = mtype
                if mt == null then return null
                mt = mt.undecorate
@@ -968,7 +1003,9 @@ end
 redef class AFormaldef
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TClassid then return null
                res.add_class("nc_vt")
+               var mtype = self.mtype
                if mtype == null then return null
                return mtype.infobox(v)
        end
@@ -976,6 +1013,7 @@ end
 redef class ATypePropdef
        redef fun decorate_tag(v, res, token)
        do
+               if not token isa TClassid then return null
                res.add_class("nc_def")
                var md = mpropdef
                if md == null then return null
@@ -1005,8 +1043,6 @@ redef class TokenLiteral
        do
                var res = super
                res.add_class("nc_l")
-               var p = parent
-               if p != null then p.decorate_tag(v, res, self)
                return res
        end
 end
@@ -1024,7 +1060,7 @@ redef class AStringFormExpr
                # Workaround to tag strings
                res.classes.remove("nc_l")
                res.add_class("nc_s")
-               return null
+               return super
        end
 end
 redef class AExpr
index 5593f87..4e4a5b3 100644 (file)
@@ -93,7 +93,7 @@ redef class NaiveInterpreter
        end
 
        # External compiler used to generate the foreign code library
-       private var c_compiler = "gcc"
+       private var c_compiler = "cc"
 end
 
 redef class AModule
@@ -128,11 +128,16 @@ redef class AModule
                srcs.add_all mmodule.ffi_files
 
                # Compiler options specific to this module
-               var ldflags = mmodule.ldflags[""].join(" ")
+               var ldflags_array = mmodule.ldflags[""]
+               if ldflags_array.has("-lrt") and system("sh -c 'uname -s 2>/dev/null || echo not' | grep Darwin >/dev/null") == 0 then
+                       # Remove -lrt on OS X
+                       ldflags_array.remove "-lrt"
+               end
+               var ldflags = ldflags_array.join(" ")
 
                # Protect pkg-config
                var pkgconfigs = mmodule.pkgconfigs
-               var pkg_command = ""
+               var pkg_cflags = ""
                if not pkgconfigs.is_empty then
                        var cmd = "which pkg-config >/dev/null"
                        if system(cmd) != 0 then
@@ -148,17 +153,18 @@ redef class AModule
                                end
                        end
 
-                       pkg_command = "`pkg-config --cflags --libs {pkgconfigs.join(" ")}`"
+                       pkg_cflags = "`pkg-config --cflags {pkgconfigs.join(" ")}`"
+                       ldflags += " `pkg-config --libs {pkgconfigs.join(" ")}`"
                end
 
                # Compile each source file to an object file (or equivalent)
                var object_files = new Array[String]
                for f in srcs do
-                       f.compile(v, mmodule, object_files)
+                       f.compile(v, mmodule, object_files, pkg_cflags)
                end
 
                # Link everything in a shared library
-               var cmd = "{v.c_compiler} -Wall -shared -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags} {pkg_command}"
+               var cmd = "{v.c_compiler} -Wall -shared -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags}"
                if system(cmd) != 0 then
                        v.fatal "FFI Error: Failed to link native code using `{cmd}`"
                        return false
@@ -398,14 +404,14 @@ end
 redef class ExternFile
        # Compile this source file
        private fun compile(v: NaiveInterpreter, mmodule: MModule,
-               object_files: Array[String]): Bool is abstract
+               object_files: Array[String], pkg_cflags: String): Bool is abstract
 end
 
 redef class ExternCFile
-       redef fun compile(v, mmodule, object_files)
+       redef fun compile(v, mmodule, object_files, pkg_cflags)
        do
                var compile_dir = v.compile_dir
-               var cflags = mmodule.cflags[""].join(" ")
+               var cflags = mmodule.cflags[""].join(" ") + " " + pkg_cflags
                var obj = compile_dir / filename.basename(".c") + ".o"
 
                var cmd = "{v.c_compiler} -Wall -c -fPIC -I {compile_dir} -g -o {obj} {filename} {cflags}"
index 7389e2e..a744fa2 100644 (file)
@@ -117,10 +117,6 @@ class NaiveInterpreter
        # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
        var escapemark: nullable EscapeMark = null
 
-       # Is an abort being executed ?
-       # Set this mark to return to the last `catch` bloc or effectively aborting if there isn't any
-       var catch_mark = new EscapeMark
-
        # The count of `catch` blocs that have been encountered and can catch an abort
        var catch_count = 0
 
@@ -323,10 +319,10 @@ class NaiveInterpreter
        # Return a new native string initialized with `txt`
        fun native_string_instance(txt: String): Instance
        do
-               var instance = native_string_instance_len(txt.bytelen+1)
+               var instance = native_string_instance_len(txt.byte_length+1)
                var val = instance.val
-               val[txt.bytelen] = 0u8
-               txt.to_cstring.copy_to(val, txt.bytelen, 0, 0)
+               val[txt.byte_length] = 0u8
+               txt.to_cstring.copy_to(val, txt.byte_length, 0, 0)
 
                return instance
        end
@@ -356,7 +352,7 @@ class NaiveInterpreter
        fun string_instance(txt: String): Instance
        do
                var nat = native_string_instance(txt)
-               var res = self.send(self.force_get_primitive_method("to_s_full", nat.mtype), [nat, self.int_instance(txt.bytelen), self.int_instance(txt.length)])
+               var res = self.send(self.force_get_primitive_method("to_s_full", nat.mtype), [nat, self.int_instance(txt.byte_length), self.int_instance(txt.length)])
                assert res != null
                return res
        end
@@ -1159,8 +1155,6 @@ redef class AMethPropdef
                        else if pname == "utf8_length" then
                                return v.int_instance(args[0].val.as(NativeString).utf8_length(args[1].to_i, args[2].to_i))
                        end
-               else if pname == "calloc_string" then
-                       return v.native_string_instance_len(args[1].to_i)
                else if cname == "NativeArray" then
                        if pname == "new" then
                                var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
@@ -1496,7 +1490,7 @@ redef class AAttrPropdef
        # Evaluate and set the default value of the attribute in `recv`
        private fun init_expr(v: NaiveInterpreter, recv: Instance)
        do
-               if is_lazy then return
+               if is_lazy or is_optional then return
                if has_value then
                        var f = v.new_frame(self, mreadpropdef.as(not null), [recv])
                        evaluate_expr(v, recv, f)
@@ -1704,8 +1698,7 @@ redef class AAbortExpr
                        fatal(v, "Aborted")
                        exit(1)
                else
-                       # Abort mode, skipping everything until a `catch` bloc is reached
-                       v.escapemark = v.catch_mark
+                       abort
                end
        end
 end
@@ -1750,14 +1743,23 @@ end
 redef class ADoExpr
        redef fun stmt(v)
        do
-               # If this bloc has a catch, register it in the counter
-               if self.n_catch != null then v.catch_count += 1
-               v.stmt(self.n_block)
-               v.is_escape(self.break_mark) # Clear the break (if any)
+               # If this bloc has a catch, handle it with a do ... catch ... end
                if self.n_catch != null then
-                       v.catch_count -= 1
-                       # Are we in abort mode? then this catch is executing
-                       if v.is_escape(v.catch_mark) then v.stmt(self.n_catch)
+                       var frame = v.frame
+                       v.catch_count += 1
+                       do
+                               v.stmt(self.n_block)
+                               v.is_escape(self.break_mark) # Clear the break (if any)
+                               v.catch_count -= 1
+                       catch
+                               # Restore the current frame if needed
+                               while v.frame != frame do v.frames.shift
+                               v.catch_count -= 1
+                               v.stmt(self.n_catch)
+                       end
+               else
+                       v.stmt(self.n_block)
+                       v.is_escape(self.break_mark)
                end
        end
 end
index 6df4d61..ac07e71 100644 (file)
@@ -71,10 +71,12 @@ redef class ModelBuilder
                end
 
                var nit_dir = toolcontext.nit_dir
-               var libname = nit_dir/"lib"
-               if libname.file_exists then paths.add(libname)
-               libname = nit_dir/"contrib"
-               if libname.file_exists then paths.add(libname)
+               if nit_dir != null then
+                       var libname = nit_dir/"lib"
+                       if libname.file_exists then paths.add(libname)
+                       libname = nit_dir/"contrib"
+                       if libname.file_exists then paths.add(libname)
+               end
        end
 
        # Load a bunch of modules.
@@ -152,7 +154,10 @@ redef class ModelBuilder
 
                        var mmodule = identify_module(a)
                        if mmodule == null then
-                               if a.file_exists then
+                               var le = last_loader_error
+                               if le != null then
+                                       toolcontext.error(null, le)
+                               else if a.file_exists then
                                        toolcontext.error(null, "Error: `{a}` is not a Nit source file.")
                                else
                                        toolcontext.error(null, "Error: cannot find module `{a}`.")
@@ -322,6 +327,14 @@ redef class ModelBuilder
        # A parsed module exists in the model but might be not yet analysed (no importation).
        var parsed_modules = new Array[MModule]
 
+       # Some `loader` services are silent and return `null` on error.
+       #
+       # Those services can set `last_loader_error` to precise an specific error message.
+       # if `last_loader_error == null` then a generic error message can be used.
+       #
+       # See `identified_modules` and `identify_group` for details.
+       var last_loader_error: nullable String = null
+
        # Identify a source file and load the associated package and groups if required.
        #
        # This method does what the user expects when giving an argument to a Nit tool.
@@ -334,13 +347,16 @@ redef class ModelBuilder
        #   then the main module of the package `digraph` is searched in `paths` and returned.
        #
        # Silently return `null` if `path` does not exists or cannot be identified.
+       # If `null` is returned, `last_loader_error` can be set to a specific error message.
        #
        # On success, it returns a module that is possibly not yet parsed (no AST), or not yet analysed (no importation).
        # If the module was already identified, or loaded, it is returned.
        fun identify_module(path: String): nullable MModule
        do
+               last_loader_error = null
+
                # special case for not a nit file
-               if not path.has_suffix(".nit") then
+               if not path.has_suffix(".nit") then do
                        # search dirless files in known -I paths
                        if not path.chars.has('/') then
                                var res = search_module_in_paths(null, path, self.paths)
@@ -348,19 +364,66 @@ redef class ModelBuilder
                        end
 
                        # Found nothing? maybe it is a group...
-                       var candidate = null
                        if path.file_exists then
                                var mgroup = identify_group(path)
                                if mgroup != null then
                                        var owner_path = mgroup.filepath.join_path(mgroup.name + ".nit")
-                                       if owner_path.file_exists then candidate = owner_path
+                                       if owner_path.file_exists then
+                                               path = owner_path
+                                               break
+                                       end
                                end
                        end
 
-                       if candidate == null then
-                               return null
+                       # Found nothing? maybe it is a qualified name
+                       if path.chars.has(':') then
+                               var ids = path.split("::")
+                               var g = identify_group(ids.first)
+                               if g != null then
+                                       scan_group(g)
+                                       var ms = g.mmodules_by_name(ids.last)
+
+                                       # Return exact match
+                                       for m in ms do
+                                               if m.full_name == path then
+                                                       return m
+                                               end
+                                       end
+
+                                       # Where there is only one or two names `foo::bar`
+                                       # then accept module that matches `foo::*::bar`
+                                       if ids.length <= 2 then
+                                               if ms.length == 1 then return ms.first
+                                               if ms.length > 1 then
+                                                       var l = new Array[String]
+                                                       for m in ms do
+                                                               var fp = m.filepath
+                                                               if fp != null then fp = " ({fp})" else fp = ""
+                                                               l.add "`{m.full_name}`{fp}"
+                                                       end
+                                                       last_loader_error = "Error: conflicting module for `{path}`: {l.join(", ")} "
+                                                       return null
+                                               end
+                                       end
+
+                                       var bests = new BestDistance[String](path.length / 2)
+                                       # We found nothing. But propose something in the package?
+                                       for sg in g.mpackage.mgroups do
+                                               for m in sg.mmodules do
+                                                       var d = path.levenshtein_distance(m.full_name)
+                                                       bests.update(d, m.full_name)
+                                               end
+                                       end
+                                       var last_loader_error = "Error: cannot find module `{path}`."
+                                       if bests.best_items.not_empty then
+                                               last_loader_error += " Did you mean " + bests.best_items.join(", ", " or ") + "?"
+                                       end
+                                       self.last_loader_error = last_loader_error
+                                       return null
+                               end
                        end
-                       path = candidate
+
+                       return null
                end
 
                # Does the file exists?
@@ -379,11 +442,18 @@ redef class ModelBuilder
                var mgrouppath = path.join_path("..").simplify_path
                var mgroup = identify_group(mgrouppath)
 
+               if mgroup != null then
+                       var mpackage = mgroup.mpackage
+                       if not mpackage.accept(path) then
+                               mgroup = null
+                               toolcontext.info("module `{path}` excluded from package `{mpackage}`", 2)
+                       end
+               end
                if mgroup == null then
                        # singleton package
-                       var mpackage = new MPackage(pn, model)
-                       mgroup = new MGroup(pn, mpackage, null) # same name for the root group
-                       mgroup.filepath = path
+                       var loc = new Location.opaque_file(path)
+                       var mpackage = new MPackage(pn, model, loc)
+                       mgroup = new MGroup(pn, loc, mpackage, null) # same name for the root group
                        mpackage.root = mgroup
                        toolcontext.info("found singleton package `{pn}` at {path}", 2)
 
@@ -395,10 +465,8 @@ redef class ModelBuilder
                        end
                end
 
-               var src = new SourceFile.from_string(path, "")
-               var loc = new Location(src, 0, 0, 0, 0)
+               var loc = new Location.opaque_file(path)
                var res = new MModule(model, mgroup, pn, loc)
-               res.filepath = path
 
                identified_modules_by_path[rp] = res
                identified_modules_by_path[path] = res
@@ -412,12 +480,19 @@ redef class ModelBuilder
        # Return the mgroup associated to a directory path.
        # If the directory is not a group null is returned.
        #
+       # Silently return `null` if `dirpath` does not exists, is not a directory,
+       # cannot be identified or cannot be attached to a mpackage.
+       # If `null` is returned, `last_loader_error` can be set to a specific error message.
+       #
        # Note: `paths` is also used to look for mgroups
        fun identify_group(dirpath: String): nullable MGroup
        do
+               # Reset error
+               last_loader_error = null
+
                var stat = dirpath.file_stat
 
-               if stat == null then do
+               if stat == null or not stat.is_dir then do
                        # search dirless directories in known -I paths
                        if dirpath.chars.has('/') then return null
                        for p in paths do
@@ -433,6 +508,7 @@ redef class ModelBuilder
 
                # Filter out non-directories
                if not stat.is_dir then
+                       last_loader_error = "Error: `{dirpath}` is not a directory."
                        return null
                end
 
@@ -459,6 +535,7 @@ redef class ModelBuilder
                        # The root of the directory hierarchy in the file system.
                        if rdp == "/" then
                                mgroups[rdp] = null
+                               last_loader_error = "Error: `{dirpath}` is not a Nit package."
                                return null
                        end
 
@@ -466,6 +543,7 @@ redef class ModelBuilder
                        if (dirpath/"packages.ini").file_exists then
                                # dirpath cannot be a package since it is a package directory
                                mgroups[rdp] = null
+                               last_loader_error = "Error: `{dirpath}` is not a Nit package."
                                return null
                        end
 
@@ -475,25 +553,34 @@ redef class ModelBuilder
                        if not stopper.file_exists then
                                # Recursively get the parent group
                                parent = identify_group(parentpath)
+                               if parent != null then do
+                                       var mpackage = parent.mpackage
+                                       if not mpackage.accept(dirpath) then
+                                               toolcontext.info("directory `{dirpath}` excluded from package `{mpackage}`", 2)
+                                               parent = null
+                                       end
+                               end
                                if parent == null then
                                        # Parent is not a group, thus we are not a group either
                                        mgroups[rdp] = null
+                                       last_loader_error = "Error: `{dirpath}` is not a Nit package."
                                        return null
                                end
                        end
                end
 
+               var loc = new Location.opaque_file(dirpath)
                var mgroup
                if parent == null then
                        # no parent, thus new package
                        if ini != null then pn = ini["package.name"] or else pn
-                       var mpackage = new MPackage(pn, model)
-                       mgroup = new MGroup(pn, mpackage, null) # same name for the root group
+                       var mpackage = new MPackage(pn, model, loc)
+                       mgroup = new MGroup(pn, loc, mpackage, null) # same name for the root group
                        mpackage.root = mgroup
                        toolcontext.info("found package `{mpackage}` at {dirpath}", 2)
                        mpackage.ini = ini
                else
-                       mgroup = new MGroup(pn, parent.mpackage, parent)
+                       mgroup = new MGroup(pn, loc, parent.mpackage, parent)
                        toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
                end
 
@@ -507,7 +594,6 @@ redef class ModelBuilder
                        mdoc.original_mentity = mgroup
                end
 
-               mgroup.filepath = dirpath
                mgroups[rdp] = mgroup
                return mgroup
        end
@@ -612,6 +698,11 @@ redef class ModelBuilder
                var keep = new Array[String]
                var res = new Array[String]
                for a in args do
+                       var stat = a.to_path.stat
+                       if stat != null and stat.is_dir then
+                               res.add a
+                               continue
+                       end
                        var l = identify_module(a)
                        if l == null then
                                keep.add a
@@ -634,7 +725,10 @@ redef class ModelBuilder
                # Look for the module
                var mmodule = identify_module(filename)
                if mmodule == null then
-                       if filename.file_exists then
+                       var le = last_loader_error
+                       if le != null then
+                               toolcontext.error(null, le)
+                       else if filename.file_exists then
                                toolcontext.error(null, "Error: `{filename}` is not a Nit source file.")
                        else
                                toolcontext.error(null, "Error: cannot find module `{filename}`.")
@@ -683,7 +777,7 @@ redef class ModelBuilder
                if decl != null then
                        var decl_name = decl.n_name.n_id.text
                        if decl_name != mmodule.name then
-                               error(decl.n_name, "Error: module name mismatch; declared {decl_name} file named {mmodule.name}.")
+                               warning(decl.n_name, "module-name-mismatch", "Error: module name mismatch; declared {decl_name} file named {mmodule.name}.")
                        end
                end
 
@@ -1061,6 +1155,27 @@ redef class MPackage
        #
        # Some packages, like stand-alone packages or virtual packages have no `ini` file associated.
        var ini: nullable ConfigTree = null
+
+       # Array of relative source paths excluded according to the `source.exclude` key of the `ini`
+       var excludes: nullable Array[String] is lazy do
+               var ini = self.ini
+               if ini == null then return null
+               var exclude = ini["source.exclude"]
+               if exclude == null then return null
+               var excludes = exclude.split(":")
+               return excludes
+       end
+
+       # Does the source inclusion/inclusion rules of the package `ini` accept such path?
+       fun accept(filepath: String): Bool
+       do
+               var excludes = self.excludes
+               if excludes != null then
+                       var relpath = root.filepath.relpath(filepath)
+                       if excludes.has(relpath) then return false
+               end
+               return true
+       end
 end
 
 redef class MGroup
index 0c5b55d..99083ad 100644 (file)
@@ -140,6 +140,15 @@ class Location
                end
        end
 
+       # Initialize a location corresponding to an opaque file.
+       #
+       # The path is used as is and is not open nor read.
+       init opaque_file(path: String)
+       do
+               var source = new SourceFile.from_string(path, "")
+               init(source, 0, 0, 0, 0)
+       end
+
        # The index in the start character in the source
        fun pstart: Int do return file.line_starts[line_start-1] + column_start-1
 
index 1287c0f..7638758 100644 (file)
@@ -77,11 +77,11 @@ private class InheritanceMetricsPhase
                                cmetrics.clear
                                cmetrics.collect(new HashSet[MClass].from(mod_mclasses))
                                cmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-                               if csv then cmetrics.to_csv.save("{out}/{mgroup}_classes.csv")
+                               if csv then cmetrics.to_csv.write_to_file("{out}/{mgroup}_classes.csv")
                                hmetrics.clear
                                hmetrics.collect(new HashSet[MModule].from(mgroup.mmodules))
                                hmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-                               if csv then hmetrics.to_csv.save("{out}/{mgroup}_inheritance.csv")
+                               if csv then hmetrics.to_csv.write_to_file("{out}/{mgroup}_inheritance.csv")
                        end
                end
                if not mclasses.is_empty then
@@ -90,11 +90,11 @@ private class InheritanceMetricsPhase
                        cmetrics.clear
                        cmetrics.collect(mclasses)
                        cmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-                       if csv then cmetrics.to_csv.save("{out}/summary_classes.csv")
+                       if csv then cmetrics.to_csv.write_to_file("{out}/summary_classes.csv")
                        hmetrics.clear
                        hmetrics.collect(mmodules)
                        hmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-                       if csv then hmetrics.to_csv.save("{out}/summary_inheritance.csv")
+                       if csv then hmetrics.to_csv.write_to_file("{out}/summary_inheritance.csv")
                end
        end
 end
index 89467b2..0951d10 100644 (file)
@@ -71,7 +71,7 @@ private class MClassesMetricsPhase
                                mclasses.add_all(mod_mclasses)
                                metrics.collect(new HashSet[MClass].from(mod_mclasses))
                                metrics.to_console(1, not toolcontext.opt_nocolors.value)
-                               if csv then metrics.to_csv.save("{out}/{mgroup}.csv")
+                               if csv then metrics.to_csv.write_to_file("{out}/{mgroup}.csv")
                        end
                end
                if not mclasses.is_empty then
@@ -80,7 +80,7 @@ private class MClassesMetricsPhase
                        print toolcontext.format_h2("\n ## global metrics")
                        metrics.collect(mclasses)
                        metrics.to_console(1, not toolcontext.opt_nocolors.value)
-                       if csv then metrics.to_csv.save("{out}/summary.csv")
+                       if csv then metrics.to_csv.write_to_file("{out}/summary.csv")
                end
        end
 end
index 2709ef9..1c4b6f8 100644 (file)
@@ -82,7 +82,7 @@ private class MendelMetricsPhase
                var metrics = new MetricSet
                metrics.register(cnblp, cnvi, cnvs)
                metrics.collect(mclasses)
-               if csv then metrics.to_csv.save("{out}/mendel.csv")
+               if csv then metrics.to_csv.write_to_file("{out}/mendel.csv")
 
                var threshold = cnblp.threshold
                print toolcontext.format_h4("\tlarge mclasses (threshold: {threshold})")
@@ -110,7 +110,7 @@ private class MendelMetricsPhase
 
                if csv then
                        var csvh = new CsvDocument
-                       csvh.format = new CsvFormat('"', ';', "\n")
+                       csvh.separator = ';'
                        csvh.header = ["povr", "ovr", "pext", "ext", "pspe", "spe", "prep", "rep", "eq"]
                        for mclass in mclasses do
                                var povr = mclass.is_pure_overrider(model_view).object_id
@@ -124,7 +124,7 @@ private class MendelMetricsPhase
                                var eq = mclass.is_equal(model_view).object_id
                                csvh.add_record(povr, ovr, pext, ext, pspe, spe, prep, rep, eq)
                        end
-                       csvh.save("{out}/inheritance_behaviour.csv")
+                       csvh.write_to_file("{out}/inheritance_behaviour.csv")
                end
        end
 end
index 5d83da2..a2fb201 100644 (file)
@@ -395,8 +395,7 @@ class MetricSet
        # Export the metric set in CSV format
        fun to_csv: CsvDocument do
                var csv = new CsvDocument
-
-               csv.format = new CsvFormat('"', ';', "\n")
+               csv.separator = ';'
 
                # set csv headers
                csv.header.add("entry")
index 335831c..dc944d8 100644 (file)
@@ -53,7 +53,7 @@ private class MModulesMetricsPhase
                                metrics.clear
                                metrics.collect(new HashSet[MModule].from(mgroup.mmodules))
                                metrics.to_console(1, not toolcontext.opt_nocolors.value)
-                               if csv then metrics.to_csv.save("{out}/{mgroup}.csv")
+                               if csv then metrics.to_csv.write_to_file("{out}/{mgroup}.csv")
                        end
                end
                if not mmodules.is_empty then
@@ -62,7 +62,7 @@ private class MModulesMetricsPhase
                        metrics.clear
                        metrics.collect(mmodules)
                        metrics.to_console(1, not toolcontext.opt_nocolors.value)
-                       if csv then metrics.to_csv.save("{out}/summary.csv")
+                       if csv then metrics.to_csv.write_to_file("{out}/summary.csv")
                end
        end
 end
index 6992ff8..abd8180 100644 (file)
@@ -61,7 +61,7 @@ private class NullablesMetricsPhase
                                mclasses.add_all(mod_mclasses)
                                metrics.collect(new HashSet[MClass].from(mod_mclasses))
                                metrics.to_console(1, not toolcontext.opt_nocolors.value)
-                               if csv then metrics.to_csv.save("{out}/{mgroup}.csv")
+                               if csv then metrics.to_csv.write_to_file("{out}/{mgroup}.csv")
                        end
                end
                if not mclasses.is_empty then
@@ -70,7 +70,7 @@ private class NullablesMetricsPhase
                        print toolcontext.format_h2("\n ## global metrics")
                        metrics.collect(mclasses)
                        metrics.to_console(1, not toolcontext.opt_nocolors.value)
-                       if csv then metrics.to_csv.save("{out}/summary.csv")
+                       if csv then metrics.to_csv.write_to_file("{out}/summary.csv")
                end
 
                compute_nullables_metrics(toolcontext.modelbuilder)
index 00f9d02..46d319a 100644 (file)
@@ -48,7 +48,7 @@ private class RTAMetricsPhase
                mmetrics.register(new MNLDD(toolcontext.modelbuilder))
                mmetrics.collect(new HashSet[MModule].from([mainmodule]))
                mmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-               if csv then mmetrics.to_csv.save("{out}/{mainmodule}.csv")
+               if csv then mmetrics.to_csv.write_to_file("{out}/{mainmodule}.csv")
 
                var mtypes = new HashSet[MType]
                var analysis = new RapidTypeAnalysis(toolcontext.modelbuilder, mainmodule)
@@ -61,14 +61,14 @@ private class RTAMetricsPhase
                cmetrics.register(analysis.cnli)
                cmetrics.register(analysis.cnlc)
                cmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-               if csv then cmetrics.to_csv.save("{out}/mclasses.csv")
+               if csv then cmetrics.to_csv.write_to_file("{out}/mclasses.csv")
 
                print toolcontext.format_h2("\n ## Total live instances by mtypes")
                var tmetrics = new MetricSet
                tmetrics.register(analysis.tnli)
                tmetrics.register(analysis.tnlc)
                tmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-               if csv then tmetrics.to_csv.save("{out}/mtypes.csv")
+               if csv then tmetrics.to_csv.write_to_file("{out}/mtypes.csv")
 
                print toolcontext.format_h2("\n ## MType complexity")
                var gmetrics = new MetricSet
@@ -76,13 +76,13 @@ private class RTAMetricsPhase
                gmetrics.register(new TDGS)
                gmetrics.collect(mtypes)
                gmetrics.to_console(1, not toolcontext.opt_nocolors.value)
-               if csv then gmetrics.to_csv.save("{out}/complexity.csv")
+               if csv then gmetrics.to_csv.write_to_file("{out}/complexity.csv")
 
                callsite_info(analysis)
 
                # dump type and method infos
                if csv then
-                       analysis.live_types_to_csv.save("{out}/rta_types.csv")
+                       analysis.live_types_to_csv.write_to_file("{out}/rta_types.csv")
                        analysis.live_methods_to_tree.write_to_file("{out}/rta_methods.dat")
                end
        end
index 7710bb2..1232352 100644 (file)
@@ -17,7 +17,6 @@
 # modules and module hierarchies in the metamodel
 module mmodule
 
-import location
 import mpackage
 private import more_collections
 
@@ -82,7 +81,13 @@ class MModule
        var mgroup: nullable MGroup
 
        # The path of the module source, if any
-       var filepath: nullable String = null is writable
+       #
+       # safe alias to `location.file.filepath`
+       fun filepath: nullable String do
+               var res = self.location.file
+               if res == null then return null
+               return res.filename
+       end
 
        # The package of the module if any
        # Safe alias for `mgroup.mpackage`
@@ -95,8 +100,7 @@ class MModule
        # The short name of the module
        redef var name: String
 
-       # The origin of the definition
-       var location: Location is writable
+       redef var location: Location is writable
 
        # Alias for `name`
        redef fun to_s do return self.name
@@ -109,11 +113,13 @@ class MModule
        # It is usually the `name` prefixed by the package's name.
        # Example: `"package::name"`
        #
-       # If both names are the same (of if the module is package-less), then
-       # the short-name is used alone.
+       # Default modules use a doubled name to distinguish them from the package name.
+       # E.g.: `"core::core"`
+       #
+       # If the module is package-less, then the short-name is used alone.
        redef var full_name is lazy do
                var mgroup = self.mgroup
-               if mgroup == null or mgroup.mpackage.name == self.name then
+               if mgroup == null then
                        return self.name
                else
                        return "{mgroup.mpackage.name}::{self.name}"
index 8207bf0..730dbd5 100644 (file)
@@ -30,6 +30,15 @@ import mdoc
 import ordered_tree
 private import more_collections
 
+redef class MEntity
+       # The visibility of the MEntity.
+       #
+       # MPackages, MGroups and MModules are always public.
+       # The visibility of `MClass` and `MProperty` is defined by the keyword used.
+       # `MClassDef` and `MPropDef` return the visibility of `MClass` and `MProperty`.
+       fun visibility: MVisibility do return public_visibility
+end
+
 redef class Model
        # All known classes
        var mclasses = new Array[MClass]
@@ -285,8 +294,9 @@ redef class MModule
                        if name == "Bool" and self.model.get_mclasses_by_name("Object") != null then
                                # Bool is injected because it is needed by engine to code the result
                                # of the implicit casts.
-                               var c = new MClass(self, name, null, enum_kind, public_visibility)
-                               var cladef = new MClassDef(self, c.mclass_type, new Location(null, 0,0,0,0))
+                               var loc = model.no_location
+                               var c = new MClass(self, name, loc, null, enum_kind, public_visibility)
+                               var cladef = new MClassDef(self, c.mclass_type, loc)
                                cladef.set_supertypes([object_type])
                                cladef.add_in_hierarchy
                                return c
@@ -378,14 +388,17 @@ class MClass
        super MEntity
 
        # The module that introduce the class
+       #
        # While classes are not bound to a specific module,
-       # the introducing module is used for naming an visibility
+       # the introducing module is used for naming and visibility.
        var intro_mmodule: MModule
 
        # The short name of the class
        # In Nit, the name of a class cannot evolve in refinements
        redef var name
 
+       redef var location
+
        # The canonical name of the class
        #
        # It is the name of the class prefixed by the full_name of the `intro_mmodule`
@@ -462,7 +475,7 @@ class MClass
 
        # The visibility of the class
        # In Nit, the visibility of a class cannot evolve in refinements
-       var visibility: MVisibility
+       redef var visibility
 
        init
        do
@@ -506,12 +519,12 @@ class MClass
 
        # The principal static type of the class.
        #
-       # For non-generic class, mclass_type is the only `MClassType` based
+       # For non-generic class, `mclass_type` is the only `MClassType` based
        # on self.
        #
        # For a generic class, the arguments are the formal parameters.
-       # i.e.: for the class Array[E:Object], the `mclass_type` is Array[E].
-       # If you want Array[Object] the see `MClassDef::bound_mtype`
+       # i.e.: for the class `Array[E:Object]`, the `mclass_type` is `Array[E]`.
+       # If you want `Array[Object]`, see `MClassDef::bound_mtype`.
        #
        # For generic classes, the mclass_type is also the way to get a formal
        # generic parameter type.
@@ -552,6 +565,8 @@ class MClass
 
        # Is `self` and abstract class?
        var is_abstract: Bool is lazy do return kind == abstract_kind
+
+       redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
 end
 
 
@@ -588,11 +603,12 @@ class MClassDef
        # ENSURE: `bound_mtype.mclass == self.mclass`
        var bound_mtype: MClassType
 
-       # The origin of the definition
-       var location: Location
+       redef var location: Location
+
+       redef fun visibility do return mclass.visibility
 
        # Internal name combining the module and the class
-       # Example: "mymodule#MyClass"
+       # Example: "mymodule$MyClass"
        redef var to_s is noinit
 
        init
@@ -604,34 +620,34 @@ class MClassDef
                        assert not isset mclass._intro
                        mclass.intro = self
                end
-               self.to_s = "{mmodule}#{mclass}"
+               self.to_s = "{mmodule}${mclass}"
        end
 
        # Actually the name of the `mclass`
        redef fun name do return mclass.name
 
-       # The module and class name separated by a '#'.
+       # The module and class name separated by a '$'.
        #
        # The short-name of the class is used for introduction.
-       # Example: "my_module#MyClass"
+       # Example: "my_module$MyClass"
        #
        # The full-name of the class is used for refinement.
-       # Example: "my_module#intro_module::MyClass"
+       # Example: "my_module$intro_module::MyClass"
        redef var full_name is lazy do
                if is_intro then
-                       # public gives 'p#A'
-                       # private gives 'p::m#A'
-                       return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}"
+                       # public gives 'p$A'
+                       # private gives 'p::m$A'
+                       return "{mmodule.namespace_for(mclass.visibility)}${mclass.name}"
                else if mclass.intro_mmodule.mpackage != mmodule.mpackage then
-                       # public gives 'q::n#p::A'
-                       # private gives 'q::n#p::m::A'
-                       return "{mmodule.full_name}#{mclass.full_name}"
+                       # public gives 'q::n$p::A'
+                       # private gives 'q::n$p::m::A'
+                       return "{mmodule.full_name}${mclass.full_name}"
                else if mclass.visibility > private_visibility then
-                       # public gives 'p::n#A'
-                       return "{mmodule.full_name}#{mclass.name}"
+                       # public gives 'p::n$A'
+                       return "{mmodule.full_name}${mclass.name}"
                else
-                       # private gives 'p::n#::m::A' (redundant p is omitted)
-                       return "{mmodule.full_name}#::{mclass.intro_mmodule.name}::{mclass.name}"
+                       # private gives 'p::n$::m::A' (redundant p is omitted)
+                       return "{mmodule.full_name}$::{mclass.intro_mmodule.name}::{mclass.name}"
                end
        end
 
@@ -1168,6 +1184,8 @@ class MClassType
 
        redef fun model do return self.mclass.intro_mmodule.model
 
+       redef fun location do return mclass.location
+
        # TODO: private init because strongly bounded to its mclass. see `mclass.mclass_type`
 
        # The formal arguments of the type
@@ -1373,6 +1391,8 @@ class MVirtualType
        # Its the definitions of this property that determine the bound or the virtual type.
        var mproperty: MVirtualTypeProp
 
+       redef fun location do return mproperty.location
+
        redef fun model do return self.mproperty.intro_mclassdef.mmodule.model
 
        redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
@@ -1503,6 +1523,8 @@ class MParameterType
 
        redef fun model do return self.mclass.intro_mmodule.model
 
+       redef fun location do return mclass.location
+
        # The position of the parameter (0 for the first parameter)
        # FIXME: is `position` a better name?
        var rank: Int
@@ -1628,6 +1650,8 @@ abstract class MProxyType
        # The base type
        var mtype: MType
 
+       redef fun location do return mtype.location
+
        redef fun model do return self.mtype.model
        redef fun need_anchor do return mtype.need_anchor
        redef fun as_nullable do return mtype.as_nullable
@@ -1954,12 +1978,27 @@ abstract class MProperty
        # The (short) name of the property
        redef var name
 
+       redef var location
+
+       redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
+
        # The canonical name of the property.
        #
-       # It is the short-`name` prefixed by the short-name of the class and the full-name of the module.
+       # It is currently the short-`name` prefixed by the short-name of the class and the full-name of the module.
        # Example: "my_package::my_module::MyClass::my_method"
+       #
+       # The full-name of the module is needed because two distinct modules of the same package can
+       # still refine the same class and introduce homonym properties.
+       #
+       # For public properties not introduced by refinement, the module name is not used.
+       #
+       # Example: `my_package::MyClass::My_method`
        redef var full_name is lazy do
-               return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+               if intro_mclassdef.is_intro then
+                       return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+               else
+                       return "{intro_mclassdef.mmodule.full_name}::{intro_mclassdef.mclass.name}::{name}"
+               end
        end
 
        redef var c_name is lazy do
@@ -1968,7 +2007,7 @@ abstract class MProperty
        end
 
        # The visibility of the property
-       var visibility: MVisibility
+       redef var visibility
 
        # Is the property usable as an initializer?
        var is_autoinit = false is writable
@@ -2233,8 +2272,9 @@ abstract class MPropDef
        # The associated global property
        var mproperty: MPROPERTY
 
-       # The origin of the definition
-       var location: Location
+       redef var location: Location
+
+       redef fun visibility do return mproperty.visibility
 
        init
        do
@@ -2244,7 +2284,7 @@ abstract class MPropDef
                        assert not isset mproperty._intro
                        mproperty.intro = self
                end
-               self.to_s = "{mclassdef}#{mproperty}"
+               self.to_s = "{mclassdef}${mproperty}"
        end
 
        # Actually the name of the `mproperty`
@@ -2258,17 +2298,17 @@ abstract class MPropDef
        #  * a property "p::m::A::x"
        #  * redefined in a refinement of a class "q::n::B"
        #  * in a module "r::o"
-       #  * so "r::o#q::n::B#p::m::A::x"
+       #  * so "r::o$q::n::B$p::m::A::x"
        #
        # Fortunately, the full-name is simplified when entities are repeated.
-       # For the previous case, the simplest form is "p#A#x".
+       # For the previous case, the simplest form is "p$A$x".
        redef var full_name is lazy do
                var res = new FlatBuffer
 
-               # The first part is the mclassdef. Worst case is "r::o#q::n::B"
+               # The first part is the mclassdef. Worst case is "r::o$q::n::B"
                res.append mclassdef.full_name
 
-               res.append "#"
+               res.append "$"
 
                if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
                        # intro are unambiguous in a class
@@ -2277,7 +2317,7 @@ abstract class MPropDef
                        # Just try to simplify each part
                        if mclassdef.mmodule.mpackage != mproperty.intro_mclassdef.mmodule.mpackage then
                                # precise "p::m" only if "p" != "r"
-                               res.append mproperty.intro_mclassdef.mmodule.full_name
+                               res.append mproperty.intro_mclassdef.mmodule.namespace_for(mproperty.visibility)
                                res.append "::"
                        else if mproperty.visibility <= private_visibility then
                                # Same package ("p"=="q"), but private visibility,
@@ -2322,7 +2362,7 @@ abstract class MPropDef
        redef fun model do return mclassdef.model
 
        # Internal name combining the module, the class and the property
-       # Example: "mymodule#MyClass#mymethod"
+       # Example: "mymodule$MyClass$mymethod"
        redef var to_s is noinit
 
        # Is self the definition that introduce the property?
index 5cfe9b2..0c136b8 100644 (file)
@@ -16,6 +16,7 @@
 
 # The abstract concept of model and related common things
 module model_base
+import location
 
 # The container class of a Nit object-oriented model.
 # A model knows modules, classes and properties and can retrieve them.
@@ -23,6 +24,11 @@ class Model
        super MEntity
 
        redef fun model do return self
+
+       # Place-holder object that means no-location
+       #
+       # See `MEntity::location`
+       var no_location = new Location(null, 0, 0, 0, 0)
 end
 
 # A named and possibly documented entity in the model.
@@ -65,6 +71,16 @@ abstract class MEntity
        # indirect use should be restricted (e.g. to name a web-page)
        fun c_name: String is abstract
 
+       # The origin of the definition.
+       #
+       # Most model entities are defined in a specific place in the source base.
+       #
+       # Because most model entities have one,
+       # it is simpler for the client to have a non-nullable return value.
+       # For entities that lack a location, mock-up special locations are used instead.
+       # By default it is `model.no_location`.
+       fun location: Location do return model.no_location
+
        # A Model Entity has a direct link to its model
        fun model: Model is abstract
 
index 65c1c62..a4f530c 100644 (file)
@@ -31,11 +31,174 @@ module model_collect
 
 import model_views
 
+redef class MEntity
+
+       # FIXME used to bypass RTA limitation on type resolution.
+       type MENTITY: SELF
+
+       # Collect modifier keywords like `redef`, `private` etc.
+       fun collect_modifiers: Array[String] do
+               return new Array[String]
+       end
+
+       # Collect `self` linearization anchored on `mainmodule`.
+       fun collect_linearization(mainmodule: MModule): nullable Array[MEntity] do
+               return null
+       end
+
+       # Collect `self` ancestors (direct and indirect).
+       #
+       # The concept of ancestor is abstract at this stage.
+       fun collect_ancestors(view: ModelView): Set[MENTITY] do
+               var done = new HashSet[MENTITY]
+               var todo = new Array[MENTITY]
+
+               todo.add_all collect_parents(view)
+               while todo.not_empty do
+                       var mentity = todo.pop
+                       if mentity == self or done.has(mentity) then continue
+                       print "{mentity} == {self}"
+                       done.add mentity
+                       todo.add_all mentity.collect_parents(view)
+               end
+               return done
+       end
+
+       # Collect `self` parents (direct ancestors).
+       #
+       # The concept of parent is abstract at this stage.
+       fun collect_parents(view: ModelView): Set[MENTITY] is abstract
+
+       # Collect `self` children (direct descendants).
+       #
+       # The concept of child is abstract at this stage.
+       fun collect_children(view: ModelView): Set[MENTITY] is abstract
+
+       # Collect `self` descendants (direct and direct).
+       #
+       # The concept of descendant is abstract at this stage.
+       fun collect_descendants(view: ModelView): Set[MENTITY] do
+               var done = new HashSet[MENTITY]
+               var todo = new Array[MENTITY]
+
+               todo.add_all collect_children(view)
+               while todo.not_empty do
+                       var mentity = todo.pop
+                       if mentity == self or done.has(mentity) then continue
+                       done.add mentity
+                       todo.add_all mentity.collect_children(view)
+               end
+               return done
+       end
+
+       # Build a poset representing `self` in it's own hierarchy.
+       #
+       # The notion of hierarchy depends on the type of MEntity.
+       #
+       # Here a recap:
+       # * MPackage: package dependencies
+       # * MGroup: group dependencies
+       # * MModule: modules imports
+       # * MClass: class inheritance (all classdefs flattened)
+       # * MClassDef: classdef inheritance
+       # * MProperty: property definitions graph (all propdefs flattened)
+       # * MPropDef: property definitions graph
+       fun hierarchy_poset(view: ModelView): POSet[MENTITY] do
+               var done = new HashSet[MENTITY]
+               var mentities = new Array[MENTITY]
+               mentities.add self
+               var poset = new POSet[MENTITY]
+               while mentities.not_empty do
+                       var mentity = mentities.pop
+                       if done.has(mentity) then continue
+                       done.add mentity
+                       poset.add_node mentity
+                       for parent in mentity.collect_parents(view) do
+                               poset.add_edge(mentity, parent)
+                               mentities.add parent
+                       end
+                       for child in mentity.collect_children(view) do
+                               poset.add_edge(child, mentity)
+                               mentities.add child
+                       end
+               end
+               return poset
+       end
+end
+
+redef class MPackage
+       redef fun collect_modifiers do
+               var res = super
+               res.add "package"
+               return res
+       end
+
+       # `MPackage` parents are its direct dependencies.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
+               for mgroup in mgroups do
+                       for parent in mgroup.collect_parents(view) do
+                               var mpackage = parent.mpackage
+                               if mpackage == self or not view.accept_mentity(mpackage) then continue
+                               res.add(mpackage)
+                       end
+               end
+               return res
+       end
+
+       # `MPackage` children are packages that directly depends on `self`.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
+               for mpackage in view.mpackages do
+                       if mpackage.collect_parents(view).has(self) then res.add mpackage
+               end
+               return res
+       end
+end
+
+redef class MGroup
+       redef fun collect_modifiers do
+               var res = super
+               res.add "group"
+               return res
+       end
+
+       # `MGroup` parents are its direct dependencies.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
+               for mmodule in mmodules do
+                       for parent in mmodule.collect_parents(view) do
+                               var mgroup = parent.mgroup
+                               if mgroup == null or mgroup == self then continue
+                               if not view.accept_mentity(mgroup) then continue
+                               res.add(mgroup)
+                       end
+               end
+               return res
+       end
+
+       # `MGroup` children are mgroups that directly depends on `self`.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
+               for mgroup in view.mgroups do
+                       if mgroup == self or not view.accept_mentity(mgroup) then continue
+                       if mgroup.collect_parents(view).has(self) then res.add mgroup
+               end
+               return res
+       end
+end
+
 redef class MModule
 
-       # Collect all transitive imports.
-       fun collect_ancestors(view: ModelView): Set[MModule] do
-               var res = new HashSet[MModule]
+       redef fun collect_modifiers do
+               var res = super
+               res.add "module"
+               return res
+       end
+
+       # `MModule` ancestors are all its transitive imports.
+       redef fun collect_ancestors(view) do
+               var res = new HashSet[MENTITY]
                for mentity in in_importation.greaters do
                        if mentity == self then continue
                        if not view.accept_mentity(mentity) then continue
@@ -44,9 +207,9 @@ redef class MModule
                return res
        end
 
-       # Collect direct imports.
-       fun collect_parents(view: ModelView): Set[MModule] do
-               var res = new HashSet[MModule]
+       # `MModule` parents are all its direct imports.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
                for mentity in in_importation.direct_greaters do
                        if mentity == self then continue
                        if not view.accept_mentity(mentity) then continue
@@ -55,9 +218,9 @@ redef class MModule
                return res
        end
 
-       # Collect direct children (modules that directly import `self`).
-       fun collect_children(view: ModelView): Set[MModule] do
-               var res = new HashSet[MModule]
+       # `MModule` children are modules that directly import `self`.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
                for mentity in in_importation.direct_smallers do
                        if mentity == self then continue
                        if not view.accept_mentity(mentity) then continue
@@ -66,9 +229,9 @@ redef class MModule
                return res
        end
 
-       # Collect all transitive children.
-       fun collect_descendants(view: ModelView): Set[MModule] do
-               var res = new HashSet[MModule]
+       # `MModule` children are modules that transitively import `self`.
+       redef fun collect_descendants(view) do
+               var res = new HashSet[MENTITY]
                for mentity in in_importation.smallers do
                        if mentity == self then continue
                        if not view.accept_mentity(mentity) then continue
@@ -77,17 +240,6 @@ redef class MModule
                return res
        end
 
-       # Build the importation poset for `self`
-       fun importation_poset(view: ModelView): POSet[MModule] do
-               var mmodules = new HashSet[MModule]
-               mmodules.add self
-               mmodules.add_all collect_ancestors(view)
-               mmodules.add_all collect_parents(view)
-               mmodules.add_all collect_children(view)
-               mmodules.add_all collect_descendants(view)
-               return view.mmodules_poset(mmodules)
-       end
-
        # Collect mclassdefs introduced in `self` with `visibility >= to min_visibility`.
        fun collect_intro_mclassdefs(view: ModelView): Set[MClassDef] do
                var res = new HashSet[MClassDef]
@@ -133,56 +285,51 @@ end
 
 redef class MClass
 
-       # Collect direct parents of `self` with `visibility >= to min_visibility`.
-       fun collect_parents(view: ModelView): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mclassdef in mclassdefs do
-                       for mclasstype in mclassdef.supertypes do
-                               var mclass = mclasstype.mclass
-                               if not view.accept_mentity(mclass) then continue
-                               res.add(mclass)
-                       end
-               end
-               return res
+       redef fun collect_modifiers do return intro.collect_modifiers
+
+       redef fun collect_linearization(mainmodule) do
+               var mclassdefs = self.mclassdefs.to_a
+               mainmodule.linearize_mclassdefs(mclassdefs)
+               return mclassdefs
        end
 
-       # Collect all ancestors of `self` with `visibility >= to min_visibility`.
-       fun collect_ancestors(view: ModelView): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mclassdef in self.mclassdefs do
-                       for super_mclassdef in mclassdef.in_hierarchy.greaters do
-                               if super_mclassdef == mclassdef then continue  # skip self
-                               var mclass = super_mclassdef.mclass
-                               if not view.accept_mentity(mclass) then continue
-                               res.add(mclass)
+       # `MClass` parents are the direct parents of `self`.
+       #
+       # This method uses a flattened hierarchy containing all the mclassdefs.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
+               for mclassdef in mclassdefs do
+                       for parent in mclassdef.collect_parents(view) do
+                               var mclass = parent.mclass
+                               if mclass == self or not view.accept_mentity(parent) then continue
+                               res.add mclass
                        end
                end
                return res
        end
 
-       # Collect direct children of `self` with `visibility >= to min_visibility`.
-       fun collect_children(view: ModelView): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mclassdef in self.mclassdefs do
-                       for sub_mclassdef in mclassdef.in_hierarchy.direct_smallers do
-                               if sub_mclassdef == mclassdef then continue  # skip self
-                               var mclass = sub_mclassdef.mclass
-                               if not view.accept_mentity(mclass) then continue
-                               res.add(mclass)
+       # Collect all ancestors of `self` with `visibility >= to min_visibility`.
+       redef fun collect_ancestors(view) do
+               var res = new HashSet[MENTITY]
+               for mclassdef in mclassdefs do
+                       for parent in mclassdef.collect_parents(view) do
+                               if not view.accept_mentity(parent) then continue
+                               res.add parent.mclass
                        end
                end
                return res
        end
 
-       # Collect all descendants of `self` with `visibility >= to min_visibility`.
-       fun collect_descendants(view: ModelView): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mclassdef in self.mclassdefs do
-                       for sub_mclassdef in mclassdef.in_hierarchy.smallers do
-                               if sub_mclassdef == mclassdef then continue  # skip self
-                               var mclass = sub_mclassdef.mclass
-                               if not view.accept_mentity(mclass) then continue
-                               res.add(mclass)
+       # `MClass` parents are the direct parents of `self`.
+       #
+       # This method uses a flattened hierarchy containing all the mclassdefs.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
+               for mclassdef in mclassdefs do
+                       for child in mclassdef.collect_children(view) do
+                               var mclass = child.mclass
+                               if mclass == self or not view.accept_mentity(child) then continue
+                               res.add mclass
                        end
                end
                return res
@@ -372,6 +519,51 @@ end
 
 redef class MClassDef
 
+       redef fun collect_linearization(mainmodule) do
+               var mclassdefs = new Array[MClassDef]
+               for mclassdef in in_hierarchy.as(not null).greaters do
+                       if mclassdef.mclass == self.mclass then mclassdefs.add mclassdef
+               end
+               mainmodule.linearize_mclassdefs(mclassdefs)
+               return mclassdefs
+       end
+
+       # `MClassDef` ancestors are its direct and transitive super classes.
+       redef fun collect_ancestors(view) do
+               var res = new HashSet[MENTITY]
+               var hierarchy = self.in_hierarchy
+               if hierarchy == null then return res
+               for parent in hierarchy.greaters do
+                       if parent == self or not view.accept_mentity(parent) then continue
+                       res.add parent
+               end
+               return res
+       end
+
+       # `MClassDef` parents are its direct super classes.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
+               var hierarchy = self.in_hierarchy
+               if hierarchy == null then return res
+               for parent in hierarchy.direct_greaters do
+                       if parent == self or not view.accept_mentity(parent) then continue
+                       res.add parent
+               end
+               return res
+       end
+
+       # `MClassDef` children are its direct subclasses.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
+               var hierarchy = self.in_hierarchy
+               if hierarchy == null then return res
+               for child in hierarchy.direct_smallers do
+                       if child == self or not view.accept_mentity(child) then continue
+                       res.add child
+               end
+               return res
+       end
+
        # Collect mpropdefs in 'self' with `visibility >= min_visibility`.
        fun collect_mpropdefs(view: ModelView): Set[MPropDef] do
                var res = new HashSet[MPropDef]
@@ -404,9 +596,8 @@ redef class MClassDef
                return res
        end
 
-       # Collect modifiers like redef, private etc.
-       fun collect_modifiers: Array[String] do
-               var res = new Array[String]
+       redef fun collect_modifiers do
+               var res = super
                if not is_intro then
                        res.add "redef"
                else
@@ -417,10 +608,57 @@ redef class MClassDef
        end
 end
 
+redef class MProperty
+       redef fun collect_modifiers do return intro.collect_modifiers
+
+       redef fun collect_linearization(mainmodule) do
+               var mpropdefs = self.mpropdefs.to_a
+               mainmodule.linearize_mpropdefs(mpropdefs)
+               return mpropdefs
+       end
+
+       # Collect mpropdefs in 'self' with `visibility >= min_visibility`.
+       fun collect_mpropdefs(view: ModelView): Set[MPropDef] do
+               var res = new HashSet[MPropDef]
+               for mpropdef in mpropdefs do
+                       if not view.accept_mentity(mpropdef) then continue
+                       res.add mpropdef
+               end
+               return res
+       end
+
+       # `MProperty` parents are all direct super definition of `self`.
+       #
+       # This method uses a flattened hierarchy containing all the mpropdefs.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
+               for mpropdef in mpropdefs do
+                       for parent in mpropdef.collect_parents(view) do
+                               if not view.accept_mentity(parent) then continue
+                               res.add parent.mproperty
+                       end
+               end
+               return res
+       end
+
+       # `MProperty` parents are all direct sub definition of `self`.
+       #
+       # This method uses a flattened hierarchy containing all the mpropdefs.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
+               for mpropdef in mpropdefs do
+                       for child in mpropdef.collect_parents(view) do
+                               if not view.accept_mentity(child) then continue
+                               res.add child.mproperty
+                       end
+               end
+               return res
+       end
+end
+
 redef class MPropDef
-       # Collect modifiers like redef, private, abstract, intern, fun etc.
-       fun collect_modifiers: Array[String] do
-               var res = new Array[String]
+       redef fun collect_modifiers do
+               var res = super
                if not is_intro then
                        res.add "redef"
                else
@@ -440,6 +678,40 @@ redef class MPropDef
                        else
                                res.add "fun"
                        end
+               else if mprop isa MAttributeDef then
+                       res.add "var"
+               end
+               return res
+       end
+
+       redef fun collect_linearization(mainmodule) do
+               var mpropdefs = new Array[MPropDef]
+               var mentity = self
+               while not mentity.is_intro do
+                       mpropdefs.add mentity
+                       mentity = mentity.lookup_next_definition(mainmodule, mentity.mclassdef.bound_mtype)
+               end
+               mpropdefs.add mentity
+               mainmodule.linearize_mpropdefs(mpropdefs)
+               return mpropdefs
+       end
+
+       # `MPropDef` parents include only the next definition of `self`.
+       redef fun collect_parents(view) do
+               var res = new HashSet[MENTITY]
+               var mpropdef = self
+               while not mpropdef.is_intro do
+                       mpropdef = mpropdef.lookup_next_definition(mclassdef.mmodule, mclassdef.bound_mtype)
+                       res.add mpropdef
+               end
+               return res
+       end
+
+       # `MPropdef` children are definitions that directly depends on `self`.
+       redef fun collect_children(view) do
+               var res = new HashSet[MENTITY]
+               for mpropdef in mproperty.collect_mpropdefs(view) do
+                       if mpropdef.collect_parents(view).has(self) then res.add mpropdef
                end
                return res
        end
diff --git a/src/model/model_json.nit b/src/model/model_json.nit
new file mode 100644 (file)
index 0000000..658e73d
--- /dev/null
@@ -0,0 +1,296 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make model entities Jsonable.
+#
+# To avoid cycles, every reference from a MEntity to another is replaced by a
+# MEntityRef.
+#
+# How subobjects are retrieved using the MEntityRef is the responsability of the
+# client. Json objects can be returned as this or inflated with concrete objet
+# rather than the refs.
+#
+# TODO consider serialization module?
+module model_json
+
+import model::model_collect
+import json
+import loader
+
+# A reference to another mentity.
+class MEntityRef
+       super MEntity
+
+       # MEntity to link to.
+       var mentity: MEntity
+
+       # Return `self` as a Json Object.
+       #
+       # By default, MEntity references contain only the `full_name` of the Mentity.
+       # You should redefine this method in your client to implement a different behavior.
+       redef fun json do
+               var obj = new JsonObject
+               obj["full_name"] = mentity.full_name
+               return obj
+       end
+end
+
+redef class MEntity
+       super Jsonable
+
+       # Return `self` as a JsonObject.
+       #
+       # By default, every reference to another MEntity is replaced by a pointer
+       # to the MEntity::json_id.
+       fun json: JsonObject do
+               var obj = new JsonObject
+               obj["name"] = name
+               obj["class_name"] = class_name
+               obj["full_name"] = full_name
+               obj["mdoc"] = mdoc_or_fallback
+               obj["visibility"] = visibility
+               obj["location"] = location
+               var modifiers = new JsonArray
+               for modifier in collect_modifiers do
+                       modifiers.add modifier
+               end
+               obj["modifiers"] = modifiers
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
+end
+
+redef class MDoc
+       super Jsonable
+
+       # Return `self` as a JsonObject.
+       fun json: JsonObject do
+               var obj = new JsonObject
+               obj["content"] = content.join("\n")
+               obj["location"] = location
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
+end
+
+redef class Location
+       super Jsonable
+
+       # Return `self` as a JsonObject.
+       fun json: JsonObject do
+               var obj = new JsonObject
+               obj["column_end"] = column_end
+               obj["column_start"] = column_start
+               obj["line_end"] = line_end
+               obj["line_start"] = line_start
+               var file = self.file
+               if file != null then
+                       obj["file"] = file.filename
+               end
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
+end
+
+redef class MVisibility
+       super Jsonable
+
+       redef fun to_json do return to_s.to_json
+end
+
+redef class MPackage
+
+       redef fun json do
+               var obj = super
+               if ini != null then
+                       obj["ini"] = new JsonObject.from(ini.as(not null).to_map)
+               end
+               obj["root"] = to_mentity_ref(root)
+               obj["mgroups"] = to_mentity_refs(mgroups)
+               return obj
+       end
+end
+
+redef class MGroup
+       redef fun json do
+               var obj = super
+               obj["is_root"] = is_root
+               obj["mpackage"] = to_mentity_ref(mpackage)
+               obj["default_mmodule"] = to_mentity_ref(default_mmodule)
+               obj["parent"] = to_mentity_ref(parent)
+               obj["mmodules"] = to_mentity_refs(mmodules)
+               obj["mgroups"] = to_mentity_refs(in_nesting.direct_smallers)
+               return obj
+       end
+end
+
+redef class MModule
+       redef fun json do
+               var obj = super
+               obj["mpackage"] = to_mentity_ref(mpackage)
+               obj["mgroup"] = to_mentity_ref(mgroup)
+               obj["intro_mclasses"] = to_mentity_refs(intro_mclasses)
+               obj["mclassdefs"] = to_mentity_refs(mclassdefs)
+               return obj
+       end
+end
+
+redef class MClass
+       redef fun json do
+               var obj = super
+               var arr = new JsonArray
+               for mparameter in mparameters do arr.add mparameter
+               obj["mparameters"] = arr
+               obj["intro"] = to_mentity_ref(intro)
+               obj["intro_mmodule"] = to_mentity_ref(intro_mmodule)
+               obj["mpackage"] = to_mentity_ref(intro_mmodule.mpackage)
+               obj["mclassdefs"] = to_mentity_refs(mclassdefs)
+               return obj
+       end
+end
+
+redef class MClassDef
+       redef fun json do
+               var obj = super
+               obj["is_intro"] = is_intro
+               var arr = new JsonArray
+               for mparameter in mclass.mparameters do arr.add mparameter
+               obj["mparameters"] = arr
+               obj["mmodule"] = to_mentity_ref(mmodule)
+               obj["mclass"] = to_mentity_ref(mclass)
+               obj["mpropdefs"] = to_mentity_refs(mpropdefs)
+               obj["intro_mproperties"] = to_mentity_refs(intro_mproperties)
+               return obj
+       end
+end
+
+redef class MProperty
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(intro)
+               obj["intro_mclassdef"] = to_mentity_ref(intro_mclassdef)
+               obj["mpropdefs"] = to_mentity_refs(mpropdefs)
+               return obj
+       end
+end
+
+redef class MMethod
+       redef fun json do
+               var obj = super
+               obj["is_init"] = is_init
+               obj["msignature"] = intro.msignature
+               return obj
+       end
+end
+
+redef class MAttribute
+       redef fun json do
+               var obj = super
+               obj["static_mtype"] = to_mentity_ref(intro.static_mtype)
+               return obj
+       end
+end
+
+redef class MVirtualTypeProp
+       redef fun json do
+               var obj = super
+               obj["mvirtualtype"] = to_mentity_ref(mvirtualtype)
+               obj["bound"] = to_mentity_ref(intro.bound)
+               return obj
+       end
+end
+
+redef class MPropDef
+       redef fun json do
+               var obj = super
+               obj["is_intro"] = is_intro
+               obj["mclassdef"] = to_mentity_ref(mclassdef)
+               obj["mproperty"] = to_mentity_ref(mproperty)
+               return obj
+       end
+end
+
+redef class MMethodDef
+       redef fun json do
+               var obj = super
+               obj["msignature"] = msignature
+               return obj
+       end
+end
+
+redef class MAttributeDef
+       redef fun json do
+               var obj = super
+               obj["static_mtype"] = to_mentity_ref(static_mtype)
+               return obj
+       end
+end
+
+redef class MVirtualTypeDef
+       redef fun json do
+               var obj = super
+               obj["bound"] = to_mentity_ref(bound)
+               obj["is_fixed"] = is_fixed
+               return obj
+       end
+end
+
+redef class MSignature
+       redef fun json do
+               var obj = new JsonObject
+               obj["arity"] = arity
+               var arr = new JsonArray
+               for mparam in mparameters do arr.add mparam
+               obj["mparams"] = arr
+               obj["return_mtype"] = to_mentity_ref(return_mtype)
+               obj["vararg_rank"] = vararg_rank
+               return obj
+       end
+end
+
+redef class MParameterType
+       redef fun json do
+               var obj = new JsonObject
+               obj["name"] = name
+               obj["rank"] = rank
+               obj["mtype"] = to_mentity_ref(mclass.intro.bound_mtype.arguments[rank])
+               return obj
+       end
+end
+
+redef class MParameter
+       redef fun json do
+               var obj = new JsonObject
+               obj["is_vararg"] = is_vararg
+               obj["name"] = name
+               obj["mtype"] = to_mentity_ref(mtype)
+               return obj
+       end
+end
+
+# Create a ref to a `mentity`.
+fun to_mentity_ref(mentity: nullable MEntity): nullable MEntityRef do
+       if mentity == null then return null
+       return new MEntityRef(mentity)
+end
+
+# Return a collection of `mentities` as a JsonArray of MEntityRefs.
+fun to_mentity_refs(mentities: Collection[MEntity]): JsonArray do
+       var array = new JsonArray
+       for mentity in mentities do array.add to_mentity_ref(mentity)
+       return array
+end
index f130f09..566a5bd 100644 (file)
@@ -132,6 +132,14 @@ class ModelView
                return res
        end
 
+       # Searches the MEntity that matches `full_name`.
+       fun mentity_by_full_name(full_name: String): nullable MEntity do
+               for mentity in mentities do
+                       if mentity.full_name == full_name then return mentity
+               end
+               return null
+       end
+
        # Looks up a MEntity by its full `namespace`.
        #
        # Usefull when `mentities_by_name` returns conflicts.
index da881a0..59d3e61 100644 (file)
@@ -144,7 +144,10 @@ redef class MEntity
        # See the specific implementation in the subclasses.
        fun visit_all(v: ModelVisitor) do end
 
-       private fun accept_visibility(min_visibility: nullable MVisibility): Bool do return true
+       private fun accept_visibility(min_visibility: nullable MVisibility): Bool do
+               if min_visibility == null then return true
+               return visibility >= min_visibility
+       end
 end
 
 redef class Model
@@ -183,13 +186,6 @@ redef class MModule
        end
 end
 
-redef class MClass
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return visibility >= min_visibility
-       end
-end
-
 redef class MClassDef
        # Visit all the classes and class definitions of the module.
        #
@@ -202,23 +198,4 @@ redef class MClassDef
                        v.enter_visit(x)
                end
        end
-
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return mclass.visibility >= min_visibility
-       end
-end
-
-redef class MProperty
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return visibility >= min_visibility
-       end
-end
-
-redef class MPropDef
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return mproperty.visibility >= min_visibility
-       end
 end
index c1625ee..8309b5a 100644 (file)
@@ -34,6 +34,8 @@ class MPackage
        # The model of the package
        redef var model: Model
 
+       redef var location
+
        # The root of the group tree
        var root: nullable MGroup = null is writable
 
@@ -66,6 +68,8 @@ class MGroup
        # empty name for a default group in a single-module package
        redef var name: String
 
+       redef var location
+
        # The enclosing package
        var mpackage: MPackage
 
@@ -74,12 +78,15 @@ class MGroup
        var parent: nullable MGroup
 
        # Fully qualified name.
-       # It includes each parent group separated by `/`
+       # It includes each parent group separated by `>`.
+       # The full_name is terminated by `>` to avoid collision with other entities.
+       #
+       # E.g. `core>` and `core>collection>`
        redef fun full_name
        do
                var p = parent
-               if p == null then return name
-               return "{p.full_name}/{name}"
+               if p == null then return "{name}>"
+               return "{p.full_name}{name}>"
        end
 
        # The group is the group tree on the package (`mpackage.mgroups`)
@@ -91,7 +98,14 @@ class MGroup
        fun is_root: Bool do return mpackage.root == self
 
        # The filepath (usually a directory) of the group, if any
-       var filepath: nullable String = null is writable
+       #
+       # safe alias to `location.file.filename`
+       fun filepath: nullable String do
+               var res
+               res = self.location.file
+               if res == null then return null
+               return res.filename
+       end
 
        init
        do
index 06f2396..5cc235f 100644 (file)
@@ -81,6 +81,36 @@ class ModelBuilder
                return res
        end
 
+       # Return a class identified by `qid` visible by the module `mmodule`.
+       # Visibility in modules and qualified names are correctly handled.
+       #
+       # If more than one class exists, then null is silently returned.
+       # It is up to the caller to post-analysis the result and display a correct error message.
+       # The method `class_not_found` can be used to display such a message.
+       fun try_get_mclass_by_qid(qid: AQclassid, mmodule: MModule): nullable MClass
+       do
+               var name = qid.n_id.text
+
+               var classes = model.get_mclasses_by_name(name)
+               if classes == null then
+                       return null
+               end
+
+               var res: nullable MClass = null
+               for mclass in classes do
+                       if not mmodule.in_importation <= mclass.intro_mmodule then continue
+                       if not mmodule.is_visible(mclass.intro_mmodule, mclass.visibility) then continue
+                       if not qid.accept(mclass) then continue
+                       if res == null then
+                               res = mclass
+                       else
+                               return null
+                       end
+               end
+
+               return res
+       end
+
        # Like `try_get_mclass_by_name` but display an error message when the class is not found
        fun get_mclass_by_name(node: ANode, mmodule: MModule, name: String): nullable MClass
        do
@@ -235,7 +265,8 @@ class ModelBuilder
        # FIXME: the name "resolve_mtype" is awful
        fun resolve_mtype_unchecked(mmodule: MModule, mclassdef: nullable MClassDef, ntype: AType, with_virtual: Bool): nullable MType
        do
-               var name = ntype.n_qid.n_id.text
+               var qid = ntype.n_qid
+               var name = qid.n_id.text
                var res: MType
 
                # Check virtual type
@@ -269,7 +300,7 @@ class ModelBuilder
                end
 
                # Check class
-               var mclass = try_get_mclass_by_name(ntype, mmodule, name)
+               var mclass = try_get_mclass_by_qid(qid, mmodule)
                if mclass != null then
                        var arity = ntype.n_types.length
                        if arity != mclass.arity then
@@ -301,11 +332,88 @@ class ModelBuilder
                        end
                end
 
-               # If everything fail, then give up :(
-               error(ntype, "Error: class `{name}` not found in module `{mmodule}`.")
+               # If everything fail, then give up with class by proposing things.
+               #
+               # TODO Give hints on formal types (param and virtual)
+               class_not_found(qid, mmodule)
+               ntype.is_broken = true
                return null
        end
 
+       # Print an error and suggest hints when the class identified by `qid` in `mmodule` is not found.
+       #
+       # This just print error messages.
+       fun class_not_found(qid: AQclassid, mmodule: MModule)
+       do
+               var name = qid.n_id.text
+               var qname = qid.full_name
+
+               if bad_class_names[mmodule].has(qname) then
+                       error(qid, "Error: class `{qname}` not found in module `{mmodule}`.")
+                       return
+               end
+               bad_class_names[mmodule].add(qname)
+
+               var all_classes = model.get_mclasses_by_name(name)
+               var hints = new Array[String]
+
+               # Look for conflicting classes.
+               if all_classes != null then for c in all_classes do
+                       if not mmodule.is_visible(c.intro_mmodule, c.visibility) then continue
+                       if not qid.accept(c) then continue
+                       hints.add "`{c.full_name}`"
+               end
+               if hints.length > 1 then
+                       error(qid, "Error: ambiguous class name `{qname}` in module `{mmodule}`. Conflicts are between {hints.join(",", " and ")}.")
+                       return
+               end
+               hints.clear
+
+               # Look for imported but invisible classes.
+               if all_classes != null then for c in all_classes do
+                       if not mmodule.in_importation <= c.intro_mmodule then continue
+                       if mmodule.is_visible(c.intro_mmodule, c.visibility) then continue
+                       if not qid.accept(c) then continue
+                       error(qid, "Error: class `{c.full_name}` not visible in module `{mmodule}`.")
+                       return
+               end
+
+               # Look for not imported but known classes from importable modules
+               if all_classes != null then for c in all_classes do
+                       if mmodule.in_importation <= c.intro_mmodule then continue
+                       if c.intro_mmodule.in_importation <= mmodule then continue
+                       if c.visibility <= private_visibility then continue
+                       if not qid.accept(c) then continue
+                       hints.add "`{c.intro_mmodule.full_name}`"
+               end
+               if hints.not_empty then
+                       error(qid, "Error: class `{qname}` not found in module `{mmodule}`. Maybe import {hints.join(",", " or ")}?")
+                       return
+               end
+
+               # Look for classes with an approximative name.
+               var bests = new BestDistance[MClass](qname.length - name.length / 2) # limit up to 50% name change
+               for c in model.mclasses do
+                       if not mmodule.in_importation <= c.intro_mmodule then continue
+                       if not mmodule.is_visible(c.intro_mmodule, c.visibility) then continue
+                       var d = qname.levenshtein_distance(c.name)
+                       bests.update(d, c)
+                       d = qname.levenshtein_distance(c.full_name)
+                       bests.update(d, c)
+               end
+               if bests.best_items.not_empty then
+                       for c in bests.best_items do hints.add "`{c.full_name}`"
+                       error(qid, "Error: class `{qname}` not found in module `{mmodule}`. Did you mean {hints.join(",", " or ")}?")
+                       return
+               end
+
+               error(qid, "Error: class `{qname}` not found in module `{mmodule}`.")
+       end
+
+       # List of already reported bad class names.
+       # Used to not perform and repeat hints again and again.
+       private var bad_class_names = new MultiHashMap[MModule, String]
+
        # Return the static type associated to the node `ntype`.
        # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types)
        # In case of problem, an error is displayed on `ntype` and null is returned.
@@ -428,3 +536,53 @@ redef class ADoc
                return res
        end
 end
+
+redef class AQclassid
+       # The name of the package part, if any
+       fun mpackname: nullable String do
+               var nqualified = n_qualified
+               if nqualified == null then return null
+               var nids = nqualified.n_id
+               if nids.length <= 0 then return null
+               return nids[0].text
+       end
+
+       # The name of the module part, if any
+       fun mmodname: nullable String do
+               var nqualified = n_qualified
+               if nqualified == null then return null
+               var nids = nqualified.n_id
+               if nids.length <= 1 then return null
+               return nids[1].text
+       end
+
+       # Does `mclass` match the full qualified name?
+       fun accept(mclass: MClass): Bool
+       do
+               if mclass.name != n_id.text then return false
+               var mpackname = self.mpackname
+               if mpackname != null then
+                       var mpackage = mclass.intro_mmodule.mpackage
+                       if mpackage == null then return false
+                       if mpackage.name != mpackname then return false
+                       var mmodname = self.mmodname
+                       if mmodname != null and mclass.intro_mmodule.name != mmodname then return false
+               end
+               return true
+       end
+
+       # The pretty name represented by self.
+       fun full_name: String
+       do
+               var res = n_id.text
+               var nqualified = n_qualified
+               if nqualified == null then return res
+               var ncid = nqualified.n_classid
+               if ncid != null then res = ncid.text + "::" + res
+               var nids = nqualified.n_id
+               if nids.not_empty then for n in nids.reverse_iterator do
+                       res = n.text + "::" + res
+               end
+               return res
+       end
+end
index 3897abc..045e829 100644 (file)
@@ -46,8 +46,11 @@ redef class ModelBuilder
                var mvisibility: nullable MVisibility
                var arity = 0
                var names = new Array[String]
+               var mclass
                if nclassdef isa AStdClassdef then
-                       name = nclassdef.n_qid.n_id.text
+                       var qid = nclassdef.n_qid
+                       assert qid != null
+                       name = qid.n_id.text
                        nkind = nclassdef.n_classkind
                        mkind = nkind.mkind
                        nvisibility = nclassdef.n_visibility
@@ -74,6 +77,12 @@ redef class ModelBuilder
                                end
                                names.add(ptname)
                        end
+                       mclass = try_get_mclass_by_qid(qid, mmodule)
+                       if mclass == null and (qid.n_qualified != null or nclassdef.n_kwredef != null) then
+                               class_not_found(qid, mmodule)
+                               nclassdef.is_broken = true
+                               return
+                       end
 
                else if nclassdef isa ATopClassdef and nclassdef.n_propdefs.first.as(AMethPropdef).n_methid.collect_text == "sys" then
                        # Special case to keep `sys` in object.
@@ -84,15 +93,16 @@ redef class ModelBuilder
                        mkind = interface_kind
                        nvisibility = null
                        mvisibility = public_visibility
+                       mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
                else
                        name = "Sys"
                        nkind = null
                        mkind = concrete_kind
                        nvisibility = null
                        mvisibility = public_visibility
+                       mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
                end
 
-               var mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
                if mclass == null then
                        if nclassdef isa AStdClassdef and nclassdef.n_kwredef != null then
                                error(nclassdef, "Redef Error: no imported class `{name}` to refine.")
@@ -112,7 +122,7 @@ redef class ModelBuilder
                                end
                        end
 
-                       mclass = new MClass(mmodule, name, names, mkind, mvisibility)
+                       mclass = new MClass(mmodule, name, nclassdef.location, names, mkind, mvisibility)
                        #print "new class {mclass}"
                else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then
                        error(nclassdef, "Error: a class `{name}` is already defined at line {nmodule.mclass2nclassdef[mclass].location.line_start}.")
@@ -223,7 +233,7 @@ redef class ModelBuilder
                if mclassdef.is_intro then
                        self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
                else
-                       self.toolcontext.info("{mclassdef} refine {mclass.kind} {mclass.full_name}", 3)
+                       self.toolcontext.info("{mclassdef} refines {mclass.kind} {mclass.full_name}", 3)
                end
        end
 
index 8c167c6..98f97e2 100644 (file)
@@ -144,7 +144,7 @@ redef class ModelBuilder
                # Look for the init in Object, or create it
                if mclassdef.mclass.name == "Object" and the_root_init_mmethod == null then
                        # Create the implicit root-init method
-                       var mprop = new MMethod(mclassdef, "init", mclassdef.mclass.visibility)
+                       var mprop = new MMethod(mclassdef, "init", nclassdef.location, mclassdef.mclass.visibility)
                        mprop.is_root_init = true
                        var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
                        var mparameters = new Array[MParameter]
@@ -355,7 +355,7 @@ redef class ModelBuilder
 
                # Create a specific new autoinit constructor
                do
-                       var mprop = new MMethod(mclassdef, "autoinit", public_visibility)
+                       var mprop = new MMethod(mclassdef, "autoinit", nclassdef.location, public_visibility)
                        mprop.is_init = true
                        var mpropdef = new MMethodDef(mclassdef, mprop, nclassdef.location)
                        mpropdef.initializers.add_all(initializers)
@@ -836,7 +836,7 @@ redef class AMethPropdef
                end
                if mprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
-                       mprop = new MMethod(mclassdef, name, mvisibility)
+                       mprop = new MMethod(mclassdef, name, self.location, mvisibility)
                        if look_like_a_root_init and modelbuilder.the_root_init_mmethod == null then
                                modelbuilder.the_root_init_mmethod = mprop
                                mprop.is_root_init = true
@@ -850,9 +850,7 @@ redef class AMethPropdef
                                return
                        end
                else
-                       if mprop.is_broken then
-                               return
-                       end
+                       if mprop.is_broken then return
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
                end
@@ -1207,7 +1205,7 @@ redef class AAttrPropdef
                                modelbuilder.error(self, "Error: attempt to define attribute `{name}` in the {mclass.kind} `{mclass}`.")
                        end
 
-                       var mprop = new MAttribute(mclassdef, "_" + name, private_visibility)
+                       var mprop = new MAttribute(mclassdef, "_" + name, self.location, private_visibility)
                        var mpropdef = new MAttributeDef(mclassdef, mprop, self.location)
                        self.mpropdef = mpropdef
                        modelbuilder.mpropdef2npropdef[mpropdef] = self
@@ -1217,9 +1215,13 @@ redef class AAttrPropdef
                var mreadprop = modelbuilder.try_get_mproperty_by_name(nid2, mclassdef, readname).as(nullable MMethod)
                if mreadprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
-                       mreadprop = new MMethod(mclassdef, readname, mvisibility)
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return
+                       mreadprop = new MMethod(mclassdef, readname, self.location, mvisibility)
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then
+                               mreadprop.is_broken = true
+                               return
+                       end
                else
+                       if mreadprop.is_broken then return
                        if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop)
                end
@@ -1269,7 +1271,7 @@ redef class AAttrPropdef
                                return
                        end
                        is_lazy = true
-                       var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
+                       var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, self.location, none_visibility)
                        mlazyprop.is_fictive = true
                        var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
                        mlazypropdef.is_fictive = true
@@ -1316,10 +1318,14 @@ redef class AAttrPropdef
                                # By default, use protected visibility at most
                                if mvisibility > protected_visibility then mvisibility = protected_visibility
                        end
-                       mwriteprop = new MMethod(mclassdef, writename, mvisibility)
-                       if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return
+                       mwriteprop = new MMethod(mclassdef, writename, self.location, mvisibility)
+                       if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then
+                               mwriteprop.is_broken = true
+                               return
+                       end
                        mwriteprop.deprecation = mreadprop.deprecation
                else
+                       if mwriteprop.is_broken then return
                        if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef or else n_kwredef, true, mwriteprop) then return
                        if atwritable != null then
                                check_redef_property_visibility(modelbuilder, atwritable.n_visibility, mwriteprop)
@@ -1484,7 +1490,7 @@ redef class AAttrPropdef
 
                var mlazypropdef = self.mlazypropdef
                if mlazypropdef != null then
-                       mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
+                       mlazypropdef.static_mtype = mmodule.bool_type
                end
                check_repeated_types(modelbuilder)
        end
@@ -1623,12 +1629,13 @@ redef class ATypePropdef
                var mprop = modelbuilder.try_get_mproperty_by_name(self.n_qid, mclassdef, name)
                if mprop == null then
                        var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility)
-                       mprop = new MVirtualTypeProp(mclassdef, name, mvisibility)
+                       mprop = new MVirtualTypeProp(mclassdef, name, self.location, mvisibility)
                        for c in name.chars do if c >= 'a' and c<= 'z' then
                                modelbuilder.warning(n_qid, "bad-type-name", "Warning: lowercase in the virtual type `{name}`.")
                                break
                        end
                else
+                       if mprop.is_broken then return
                        assert mprop isa MVirtualTypeProp
                        check_redef_property_visibility(modelbuilder, self.n_visibility, mprop)
                end
index c110468..1e3fb5d 100644 (file)
@@ -365,9 +365,14 @@ class NeoModel
                node.labels.add "MEntity"
                node.labels.add model_name
                node["name"] = mentity.name
-               if mentity.mdoc != null then
-                       node["mdoc"] = new JsonArray.from(mentity.mdoc.content)
-                       node["mdoc_location"] = mentity.mdoc.location.to_s
+               if not mentity isa MSignature then
+                       #FIXME: MSignature is a MEntity, but has no model :/
+                       node["location"] = mentity.location.to_s
+               end
+               var mdoc = mentity.mdoc
+               if mdoc != null then
+                       node["mdoc"] = new JsonArray.from(mdoc.content)
+                       node["mdoc_location"] = mdoc.location.to_s
                end
                return node
        end
@@ -391,7 +396,8 @@ class NeoModel
                if m isa MPackage then return m
 
                assert node.labels.has("MPackage")
-               var mpackage = new MPackage(node["name"].to_s, model)
+               var location = to_location(node["location"].to_s)
+               var mpackage = new MPackage(node["name"].to_s, model, location)
                mentities[node.id.as(Int)] = mpackage
                set_doc(node, mpackage)
                mpackage.root = to_mgroup(model, node.out_nodes("ROOT").first)
@@ -424,13 +430,14 @@ class NeoModel
                if m isa MGroup then return m
 
                assert node.labels.has("MGroup")
+               var location = to_location(node["location"].to_s)
                var mpackage = to_mpackage(model, node.out_nodes("PROJECT").first)
                var parent: nullable MGroup = null
                var out = node.out_nodes("PARENT")
                if not out.is_empty then
                        parent = to_mgroup(model, out.first)
                end
-               var mgroup = new MGroup(node["name"].to_s, mpackage, parent)
+               var mgroup = new MGroup(node["name"].to_s, location, mpackage, parent)
                mentities[node.id.as(Int)] = mgroup
                set_doc(node, mgroup)
                return mgroup
@@ -440,7 +447,6 @@ class NeoModel
        private fun mmodule_node(mmodule: MModule): NeoNode do
                var node = make_node(mmodule)
                node.labels.add "MModule"
-               node["location"] = mmodule.location.to_s
                for parent in mmodule.in_importation.direct_greaters do
                        node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
                end
@@ -504,6 +510,7 @@ class NeoModel
                assert node.labels.has("MClass")
                var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
                var name = node["name"].to_s
+               var location = to_location(node["location"].to_s)
                var kind = to_kind(node["kind"].to_s)
                var visibility = to_visibility(node["visibility"].to_s)
                var parameter_names = new Array[String]
@@ -512,7 +519,7 @@ class NeoModel
                                parameter_names.add e.to_s
                        end
                end
-               var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
+               var mclass = new MClass(mmodule, name, location, parameter_names, kind, visibility)
                mentities[node.id.as(Int)] = mclass
                set_doc(node, mclass)
                return mclass
@@ -522,7 +529,6 @@ class NeoModel
        private fun mclassdef_node(mclassdef: MClassDef): NeoNode do
                var node = make_node(mclassdef)
                node.labels.add "MClassDef"
-               node["location"] = mclassdef.location.to_s
                node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype)))
                node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass)))
                for mproperty in mclassdef.intro_mproperties do
@@ -590,18 +596,19 @@ class NeoModel
                assert node.labels.has("MProperty")
                var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
                var name = node["name"].to_s
+               var location = to_location(node["location"].to_s)
                var visibility = to_visibility(node["visibility"].to_s)
                var mprop: nullable MProperty = null
                if node.labels.has("MMethod") then
-                       mprop = new MMethod(intro_mclassdef, name, visibility)
+                       mprop = new MMethod(intro_mclassdef, name, location, visibility)
                        mprop.is_init = node["is_init"].as(Bool)
                else if node.labels.has("MAttribute") then
-                       mprop = new MAttribute(intro_mclassdef, name, visibility)
+                       mprop = new MAttribute(intro_mclassdef, name, location, visibility)
                else if node.labels.has("MVirtualTypeProp") then
-                       mprop = new MVirtualTypeProp(intro_mclassdef, name, visibility)
+                       mprop = new MVirtualTypeProp(intro_mclassdef, name, location, visibility)
                else if node.labels.has("MInnerClass") then
                        var inner = to_mclass(model, node.out_nodes("NESTS").first)
-                       mprop = new MInnerClass(intro_mclassdef, name, visibility, inner)
+                       mprop = new MInnerClass(intro_mclassdef, name, location, visibility, inner)
                end
                if mprop == null then
                        print "not yet implemented to_mproperty for {node.labels.join(",")}"
@@ -616,7 +623,6 @@ class NeoModel
        private fun mpropdef_node(mpropdef: MPropDef): NeoNode do
                var node = make_node(mpropdef)
                node.labels.add "MPropDef"
-               node["location"] = mpropdef.location.to_s
                node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mpropdef.mproperty)))
                if mpropdef isa MMethodDef then
                        node.labels.add "MMethodDef"
index 9410b4d..a28bb6f 100644 (file)
@@ -64,6 +64,12 @@ if opt_eval.value then
        modelbuilder.load_rt_module(parent, amodule, "-")
 
        mmodules = [amodule.mmodule.as(not null)]
+else if progname == "-" then
+       var content = stdin.read_all
+       var amodule = toolcontext.parse_module(content)
+       toolcontext.check_errors
+       modelbuilder.load_rt_module(null, amodule, "-")
+       mmodules = [amodule.mmodule.as(not null)]
 else
        mmodules = modelbuilder.parse([progname])
 end
index 29477c1..42dc5dd 100644 (file)
@@ -131,7 +131,68 @@ g.defer=true; g.async=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
        end
 end
 
+redef class NitdocDecorator
+       redef fun add_image(v, link, name, comment)
+       do
+               # Keep absolute links as is
+               if link.has_prefix("http://") or link.has_prefix("https://") then
+                       super
+                       return
+               end
+
+               do
+                       # Get the directory of the doc object to deal with the relative link
+                       var mdoc = current_mdoc
+                       if mdoc == null then break
+                       var source = mdoc.location.file
+                       if source == null then break
+                       var path = source.filename
+                       var stat = path.file_stat
+                       if stat == null then break
+                       if not stat.is_dir then path = path.dirname
+
+                       # Get the full path to the local resource
+                       var fulllink = path / link.to_s
+                       stat = fulllink.file_stat
+                       if stat == null then break
+
+                       # Get a collision-free catalog name for the resource
+                       var hash = fulllink.md5
+                       var ext = fulllink.file_extension
+                       if ext != null then hash = hash + "." + ext
+
+                       # Copy the local resource in the resource directory of the catalog
+                       var res = catalog.outdir / "res" / hash
+                       fulllink.file_copy_to(res)
+
+                       # Hijack the link in the html.
+                       link = ".." / "res" / hash
+                       super(v, link, name, comment)
+                       return
+               end
+
+               # Something went bad
+               catalog.modelbuilder.toolcontext.error(current_mdoc.location, "Error: cannot find local image `{link}`")
+               super
+       end
+
+       # The registered catalog
+       #
+       # It is used to deal with relative links in images.
+       var catalog: Catalog is noautoinit
+end
+
 redef class Catalog
+       redef init
+       do
+               # Register `self` to the global NitdocDecorator
+               # FIXME this is ugly. But no better idea at the moment.
+               modelbuilder.model.nitdoc_md_processor.emitter.decorator.as(NitdocDecorator).catalog = self
+       end
+
+       # The output directory where to generate pages
+       var outdir: String is noautoinit
+
        # Return a empty `CatalogPage`.
        fun new_page(rootpath: String): CatalogPage
        do
@@ -254,6 +315,15 @@ redef class Catalog
                end
                res.add "</ul>\n"
 
+               res.add "<h3>Quality</h3>\n<ul class=\"box\">\n"
+               var errors = errors[mpackage]
+               if errors > 0 then
+                       res.add "<li>{errors} errors</li>\n"
+               end
+               res.add "<li>{warnings[mpackage]} warnings ({warnings_per_kloc[mpackage]}/kloc)</li>\n"
+               res.add "<li>{documentation_score[mpackage]}% documented</li>\n"
+               res.add "</ul>\n"
+
                res.add "<h3>Tags</h3>\n"
                var ts2 = new Array[String]
                for t in mpackage.tags do
@@ -413,6 +483,10 @@ redef class Catalog
                res.add "<th data-field=\"met\" data-sortable=\"true\">methods</th>\n"
                res.add "<th data-field=\"loc\" data-sortable=\"true\">lines</th>\n"
                res.add "<th data-field=\"score\" data-sortable=\"true\">score</th>\n"
+               res.add "<th data-field=\"errors\" data-sortable=\"true\">errors</th>\n"
+               res.add "<th data-field=\"warnings\" data-sortable=\"true\">warnings</th>\n"
+               res.add "<th data-field=\"warnings_per_kloc\" data-sortable=\"true\">w/kloc</th>\n"
+               res.add "<th data-field=\"doc\" data-sortable=\"true\">doc</th>\n"
                res.add "</tr></thead>"
                for p in mpackages do
                        res.add "<tr>"
@@ -432,6 +506,10 @@ redef class Catalog
                        res.add "<td>{mmethods[p]}</td>"
                        res.add "<td>{loc[p]}</td>"
                        res.add "<td>{score[p]}</td>"
+                       res.add "<td>{errors[p]}</td>"
+                       res.add "<td>{warnings[p]}</td>"
+                       res.add "<td>{warnings_per_kloc[p]}</td>"
+                       res.add "<td>{documentation_score[p]}</td>"
                        res.add "</tr>\n"
                end
                res.add "</table>\n"
@@ -483,10 +561,16 @@ end
 
 # Get files or groups
 var args = tc.option_context.rest
+var mmodules
 if opt_no_parse.value then
-       modelbuilder.scan_full(args)
+       mmodules = modelbuilder.scan_full(args)
 else
-       modelbuilder.parse_full(args)
+       mmodules = modelbuilder.parse_full(args)
+end
+var mpackages = new Set[MPackage]
+for m in mmodules do
+       var p = m.mpackage
+       if p != null then mpackages.add p
 end
 
 # Scan packages and compute information
@@ -508,7 +592,7 @@ for p in model.mpackages do
        end
 end
 
-if not opt_no_git.value then for p in model.mpackages do
+if not opt_no_git.value then for p in mpackages do
        catalog.git_info(p)
 end
 
@@ -519,6 +603,9 @@ end
 
 var out = opt_dir.value or else "catalog.out"
 (out/"p").mkdir
+(out/"res").mkdir
+
+catalog.outdir = out
 
 # Generate the css (hard coded)
 var css = """
@@ -622,7 +709,7 @@ css.write_to_file(out/"style.css")
 
 # PAGES
 
-for p in model.mpackages do
+for p in mpackages do
        # print p
        var f = "p/{p.name}.html"
        catalog.package_page(p)
@@ -645,7 +732,7 @@ index.add catalog.list_best(catalog.score)
 if catalog.deps.not_empty then
        index.add "<h2>Most Required</h2>\n"
        var reqs = new Counter[MPackage]
-       for p in model.mpackages do
+       for p in mpackages do
                reqs[p] = catalog.deps[p].smallers.length - 1
        end
        index.add catalog.list_best(reqs)
@@ -662,7 +749,7 @@ index.add """
 <div class="sidebar">
 <h3>Stats</h3>
 <ul class="box">
-<li>{{{model.mpackages.length}}} packages</li>
+<li>{{{mpackages.length}}} packages</li>
 <li>{{{catalog.maint2proj.length}}} maintainers</li>
 <li>{{{catalog.contrib2proj.length}}} contributors</li>
 <li>{{{catalog.tag2proj.length}}} tags</li>
@@ -694,6 +781,6 @@ page = catalog.new_page("")
 page.more_head.add "<title>Projets of Nit</title>"
 page.add """<div class="content">\n<h1>People of Nit</h1>\n"""
 page.add "<h2>Table of Projets</h2>\n"
-page.add catalog.table_packages(model.mpackages)
+page.add catalog.table_packages(mpackages.to_a)
 page.add "</div>\n"
 page.write_to_file(out/"table.html")
index 165ea13..554bc00 100644 (file)
@@ -17,6 +17,71 @@ module nitlight
 
 import highlight
 
+class NitlightVisitor
+       super HighlightVisitor
+
+       # The current highlight module
+       #
+       # It is used to know when to use anchored local links
+       var current_module: MModule
+
+       # List of all highlighted modules
+       #
+       # It is used to have links that only targets highlighted entities
+       #
+       # Entities outside these modules will not be linked.
+       var mmodules: Collection[MModule]
+
+       redef fun hrefto(entitiy) do return entitiy.href(self)
+end
+
+redef class MEntity
+       private fun href(v: NitlightVisitor): nullable String do return null
+end
+
+redef class MModule
+       redef fun href(v)
+       do
+               if self == v.current_module then return ""
+               if not v.mmodules.has(self) then return null
+               return c_name + ".html"
+       end
+end
+
+redef class MClass
+       redef fun href(v)
+       do
+               # Because we only have code, just link to the introduction
+               return intro.href(v)
+       end
+end
+
+redef class MClassDef
+       redef fun href(v)
+       do
+               var m = mmodule.href(v)
+               if m == null then return null
+               return m + "#" + to_s
+       end
+end
+
+redef class MProperty
+       redef fun href(v)
+       do
+               # Because we only have code, just link to the introduction
+               return intro.href(v)
+       end
+end
+
+redef class MPropDef
+       redef fun href(v)
+       do
+               var m = mclassdef.mmodule.href(v)
+               if m == null then return null
+               return m + "#" + to_s
+       end
+end
+
 var toolcontext = new ToolContext
 
 # Try to colorize, even if programs are non valid
@@ -41,7 +106,7 @@ var args = toolcontext.option_context.rest
 var mmodules = modelbuilder.parse_full(args)
 modelbuilder.run_phases
 
-if opt_full.value then mmodules = model.mmodules
+if opt_full.value then mmodules = modelbuilder.parsed_modules
 
 var dir = opt_dir.value
 if dir != null then
@@ -54,7 +119,7 @@ end
 for mm in mmodules do
        if dir != null then toolcontext.info("write {dir}/{mm.c_name}.html", 1)
 
-       var v = new HighlightVisitor
+       var v = new NitlightVisitor(mm, mmodules)
        var prefix = opt_line_id_prefix.value
        if prefix != null then
                v.line_id_prefix = prefix.trim
index f8fa778..4321214 100644 (file)
@@ -178,7 +178,10 @@ if opt_tree.value then
                var pa = mp.mgroup
                while pa != null and not pa.is_interesting do pa = pa.parent
                ot.add(pa, mp)
-               if pa != null then mgroups.add pa
+               while pa != null do
+                       mgroups.add pa
+                       pa = pa.parent
+               end
        end
        for g in mgroups do
                var pa = g.parent
@@ -196,7 +199,7 @@ if opt_source.value then
                if opt_paths.value then
                        print mp.filepath.as(not null)
                else
-                       print "{mp.mgroup.full_name}/{ot.display(mp)}"
+                       print "{mp.mgroup.full_name}{ot.display(mp)}"
                end
        end
 end
index 522591d..f58b916 100644 (file)
@@ -20,7 +20,7 @@ import testing
 
 var toolcontext = new ToolContext
 
-toolcontext.option_context.add_option(toolcontext.opt_full, toolcontext.opt_output, toolcontext.opt_dir, toolcontext.opt_noact, toolcontext.opt_pattern, toolcontext.opt_file, toolcontext.opt_gen_unit, toolcontext.opt_gen_force, toolcontext.opt_gen_private, toolcontext.opt_gen_show)
+toolcontext.option_context.add_option(toolcontext.opt_full, toolcontext.opt_output, toolcontext.opt_dir, toolcontext.opt_noact, toolcontext.opt_pattern, toolcontext.opt_autosav, toolcontext.opt_gen_unit, toolcontext.opt_gen_force, toolcontext.opt_gen_private, toolcontext.opt_gen_show, toolcontext.opt_nitc)
 toolcontext.tooldescription = "Usage: nitunit [OPTION]... <file.nit>...\nExecutes the unit tests from Nit source files."
 
 toolcontext.process_options(args)
@@ -31,10 +31,6 @@ if toolcontext.opt_gen_unit.value then
                print "Option --pattern cannot be used with --gen-suite"
                exit(0)
        end
-       if toolcontext.opt_file.value != null then
-               print "Option --target-file cannot be used with --gen-suite"
-               exit(0)
-       end
 else
        if toolcontext.opt_gen_force.value then
                print "Option --force must be used with --gen-suite"
@@ -63,6 +59,19 @@ if toolcontext.opt_gen_unit.value then
        exit(0)
 end
 
+# When testing `nitunit`, disable time.
+if "NIT_TESTING".environ != "" then
+       toolcontext.opt_no_time.value = true
+end
+
+"NIT_TESTING".setenv("true")
+"NIT_TESTING_ID".setenv(pid.to_s)
+"SRAND".setenv("0")
+
+var test_dir = toolcontext.test_dir
+test_dir.mkdir
+"# This file prevents the Nit modules of the directory to be part of the package".write_to_file(test_dir / "packages.ini")
+
 var page = new HTMLTag("testsuites")
 
 if toolcontext.opt_full.value then mmodules = model.mmodules
@@ -84,25 +93,69 @@ end
 
 for m in mmodules do
        page.add modelbuilder.test_markdown(m)
-       page.add modelbuilder.test_unit(m)
+       var ts = modelbuilder.test_unit(m)
+       if ts != null then page.add ts
 end
 
 var file = toolcontext.opt_output.value
 if file == null then file = "nitunit.xml"
 page.write_to_file(file)
-# print docunits results
-print "DocUnits:"
-if modelbuilder.unit_entities == 0 then
-       print "No doc units found"
-else if modelbuilder.failed_entities == 0 and not toolcontext.opt_noact.value then
-       print "DocUnits Success"
+
+# Print results
+printn "Docunits: Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}"
+if modelbuilder.unit_entities == 0 or toolcontext.opt_noact.value then
+       print ""
+else
+       printn "; Failures: "
+       var cpt = modelbuilder.failed_entities
+       if toolcontext.opt_no_color.value then
+               print cpt
+       else if cpt == 0 then
+               print "0".green.bold
+       else
+               print cpt.to_s.red.bold
+       end
 end
-print "Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}; Failures: {modelbuilder.failed_entities}"
-# print testsuites results
-print "\nTestSuites:"
-if modelbuilder.total_tests == 0 then
-       print "No test cases found"
-else if modelbuilder.failed_tests == 0 and not toolcontext.opt_noact.value then
-       print "TestSuites Success"
+printn "Test suites: Classes: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}"
+if modelbuilder.total_tests == 0 or toolcontext.opt_noact.value then
+       print ""
+else
+       printn "; Failures: "
+       var cpt = modelbuilder.failed_tests
+       if toolcontext.opt_no_color.value then
+               print cpt
+       else if cpt == 0 then
+               print "0".green.bold
+       else
+               print cpt.to_s.red.bold
+       end
 end
-print "Class suites: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}; Failures: {modelbuilder.failed_tests}"
+
+var total = modelbuilder.unit_entities + modelbuilder.total_tests
+var fail = modelbuilder.failed_entities + modelbuilder.failed_tests
+if toolcontext.opt_noact.value then
+       # nothing
+else if total == 0 then
+       var head = "[NOTHING]"
+       if not toolcontext.opt_no_color.value then
+               head = head.yellow
+       end
+       print "{head} No unit tests to execute."
+else if fail == 0 then
+       var head = "[SUCCESS]"
+       if not toolcontext.opt_no_color.value then
+               head = head.green.bold
+       end
+       print "{head} All {total} tests passed."
+else
+       var head = "[FAILURE]"
+       if not toolcontext.opt_no_color.value then
+               head = head.red.bold
+       end
+       print "{head} {fail}/{total} tests failed."
+
+       print "`{test_dir}` is not removed for investigation."
+       exit 1
+end
+
+test_dir.rmdir
index b924ec2..3897c5c 100644 (file)
@@ -43,19 +43,67 @@ private class NitwebPhase
                var model = mainmodule.model
                var modelbuilder = toolcontext.modelbuilder
 
+               # Build catalog
+               var catalog = new Catalog(modelbuilder)
+               for mpackage in model.mpackages do
+                       catalog.deps.add_node(mpackage)
+                       for mgroup in mpackage.mgroups do
+                               for mmodule in mgroup.mmodules do
+                                       for imported in mmodule.in_importation.direct_greaters do
+                                               var ip = imported.mpackage
+                                               if ip == null or ip == mpackage then continue
+                                               catalog.deps.add_edge(mpackage, ip)
+                                       end
+                               end
+                       end
+                       catalog.git_info(mpackage)
+                       catalog.package_page(mpackage)
+               end
+
                # Run the server
                var host = toolcontext.opt_host.value or else "localhost"
                var port = toolcontext.opt_port.value
 
-               var srv = new NitServer(host, port.to_i)
-               srv.routes.add new Route("/random", new RandomAction(srv, model))
-               srv.routes.add new Route("/doc/:namespace", new DocAction(srv, model, modelbuilder))
-               srv.routes.add new Route("/code/:namespace", new CodeAction(srv, model, modelbuilder))
-               srv.routes.add new Route("/search/:namespace", new SearchAction(srv, model))
-               srv.routes.add new Route("/uml/:namespace", new UMLDiagramAction(srv, model, mainmodule))
-               srv.routes.add new Route("/", new TreeAction(srv, model))
+               var app = new App
+
+               app.use_before("/*", new RequestClock)
+               app.use("/api", new APIRouter(model, modelbuilder, mainmodule, catalog))
+               app.use("/*", new StaticHandler(toolcontext.share_dir / "nitweb", "index.html"))
+               app.use_after("/*", new ConsoleLog)
+
+               app.listen(host, port.to_i)
+       end
+end
+
+# Group all api handlers in one router.
+class APIRouter
+       super Router
 
-               srv.listen
+       # Model to pass to handlers.
+       var model: Model
+
+       # ModelBuilder to pass to handlers.
+       var modelbuilder: ModelBuilder
+
+       # Mainmodule to pass to handlers.
+       var mainmodule: MModule
+
+       # Catalog to pass to handlers.
+       var catalog: Catalog
+
+       init do
+               use("/catalog", new APICatalogRouter(model, mainmodule, catalog))
+               use("/list", new APIList(model, mainmodule))
+               use("/search", new APISearch(model, mainmodule))
+               use("/random", new APIRandom(model, mainmodule))
+               use("/entity/:id", new APIEntity(model, mainmodule))
+               use("/code/:id", new APIEntityCode(model, mainmodule, modelbuilder))
+               use("/uml/:id", new APIEntityUML(model, mainmodule))
+               use("/linearization/:id", new APIEntityLinearization(model, mainmodule))
+               use("/defs/:id", new APIEntityDefs(model, mainmodule))
+               use("/inheritance/:id", new APIEntityInheritance(model, mainmodule))
+               use("/graph/", new APIGraphRouter(model, mainmodule))
+               use("/docdown/", new APIDocdown(model, mainmodule, modelbuilder))
        end
 end
 
index 292ff28..c7dae85 100644 (file)
@@ -3,6 +3,8 @@ name=nitc
 tags=devel,cli
 maintainer=Jean Privat <jean@pryen.org>
 license=Apache-2.0
+[source]
+exclude=parser/parser_abs.nit:parser/.parser-nofact.nit
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/src
 git=https://github.com/nitlang/nit.git
index 64ceecf..73ebcd0 100644 (file)
@@ -1,6 +1,6 @@
 Parser and AST for the Nit language
 
-The parser ans the AST are mostly used by all tools.
+The parser and the AST are mostly used by all tools.
 
 The `parser` is the tool that transform source-files into abstract syntax trees (AST) (see `parser_nodes`)
 While the AST is a higher abstraction than blob of text, the AST is still limited and represents only *What the programmer says*.
index 62fbbb0..1d60f15 100644 (file)
@@ -88,8 +88,6 @@ redef class AAnnotation
                        return ""
                else
                        for arg in args do
-                               var format_error = "Syntax Eror: `{name}` expects its arguments to be of type Int or a call to `git_revision`."
-
                                var value
                                value = arg.as_int
                                if value != null then
@@ -107,7 +105,13 @@ redef class AAnnotation
                                        # Get Git short revision
                                        var proc = new ProcessReader("git", "rev-parse", "--short", "HEAD")
                                        proc.wait
-                                       assert proc.status == 0
+                                       if proc.status != 0 then
+                                               # Fallback if this is not a git repository or git bins are missing
+                                               version_fields.add "0"
+                                               modelbuilder.warning(self, "git_revision", "Warning: `git_revision` used outside of a git repository or git binaries not available")
+                                               continue
+                                       end
+
                                        var lines = proc.read_all
                                        var revision = lines.split("\n").first
 
@@ -122,6 +126,7 @@ redef class AAnnotation
                                        continue
                                end
 
+                               var format_error = "Syntax Error: `{name}` expects its arguments to be of type Int or a call to `git_revision`."
                                modelbuilder.error(self, format_error)
                                return ""
                        end
index 5da1494..cea654b 100644 (file)
@@ -132,7 +132,7 @@ class RapidTypeAnalysis
                var types = typeset.to_a
                (new CachedAlphaComparator).sort(types)
                var res = new CsvDocument
-               res.format = new CsvFormat('"', ';', "\n")
+               res.separator = ';'
                res.header = ["Type", "Resolution", "Liveness", "Cast-liveness"]
                for t in types do
                        var reso
@@ -254,6 +254,8 @@ class RapidTypeAnalysis
                                add_cast(paramtype)
                        end
 
+                       if mmethoddef.is_abstract then continue
+
                        var npropdef = modelbuilder.mpropdef2node(mmethoddef)
 
                        if npropdef isa AClassdef then
@@ -319,7 +321,9 @@ class RapidTypeAnalysis
                                if not check_depth(rt) then continue
                                #print "{ot}/{t} -> {rt}"
                                live_types.add(rt)
-                               todo_types.add(rt)
+                               # unshift means a deep-first visit.
+                               # So that the `check_depth` limit is reached sooner.
+                               todo_types.unshift(rt)
                        end
                end
                #print "MType {live_types.length}: {live_types.join(", ")}"
index 5f4c2dc..375f80a 100644 (file)
@@ -641,8 +641,7 @@ end
 class CallSite
        super MEntity
 
-       # The associated location of the callsite
-       var location: Location
+       redef var location: Location
 
        # The static type of the receiver (possibly unresolved)
        var recv: MType
@@ -1671,11 +1670,15 @@ redef class AIsaExpr
 
                var variable = self.n_expr.its_variable
                if variable != null then
-                       #var orig = self.n_expr.mtype
+                       var orig = self.n_expr.mtype
                        #var from = if orig != null then orig.to_s else "invalid"
                        #var to = if mtype != null then mtype.to_s else "invalid"
                        #debug("adapt {variable}: {from} -> {to}")
-                       self.after_flow_context.when_true.set_var(v, variable, mtype)
+
+                       # Do not adapt if there is no information gain (i.e. adapt to a supertype)
+                       if mtype == null or orig == null or not v.is_subtype(orig, mtype) then
+                               self.after_flow_context.when_true.set_var(v, variable, mtype)
+                       end
                end
 
                self.mtype = v.type_bool(self)
index 96d0835..13384f6 100644 (file)
@@ -18,9 +18,16 @@ module test_highlight
 import highlight
 import test_phase
 
+class TestHighlightVisitor
+       super HighlightVisitor
+       redef fun hrefto(e) do
+               return "#" + e.c_name
+       end
+end
+
 redef fun do_work(mainmodule, mmodules, mb)
 do
-       var v = new HighlightVisitor
+       var v = new TestHighlightVisitor
        print """<head>
        <meta charset="utf-8">
        <style type="text/css">
@@ -33,9 +40,9 @@ do
        for mm in mmodules do for cd in mm.mclassdefs do for pd in cd.mpropdefs do
                var n = mb.mpropdef2node(pd)
                if not n isa APropdef then continue
-               var hl = new HighlightVisitor
+               var hl = new TestHighlightVisitor
                hl.enter_visit(n)
-               print "<h1>{pd.full_name}</h1>"
+               print "<h1 id=\"{pd.c_name}\">{pd.full_name}</h1>"
                printn "<pre><code>"
                hl.html.write_to(stdout)
                print "</code></pre>"
@@ -61,7 +68,7 @@ class THLVisitor
                if not seen.has(cn) then
                        seen.add cn
 
-                       var hl = new HighlightVisitor
+                       var hl = new TestHighlightVisitor
                        hl.enter_visit(n)
                        print "<h2>AST node: {cn} at {n.location}</h2>"
                        printn "<pre><code>"
index 3569400..d7cc947 100644 (file)
@@ -26,11 +26,25 @@ class TestModelVisitor
 
        redef fun visit(e) do
                cpt.inc(e.class_name)
+
+               if not e isa Model then
+                       var name = e.full_name
+                       var old = names.get_or_null(name)
+                       if old != null then
+                               names[name + "!CONFLICT!" + old.class_name] = old
+                               name = name + "!CONFLICT!" + e.class_name
+                       end
+                       names[name] = e
+               end
+
                e.visit_all(self)
        end
 
        # Counter of visited entities (by classnames)
        var cpt = new Counter[String]
+
+       # Dictionary of full_names
+       var names = new Map[String, MEntity]
 end
 
 # The body of the specific work.
@@ -46,6 +60,7 @@ do
        v.include_fictive = true
        v.enter_visit(model)
        v.cpt.print_elements(10)
+       var names = v.names
 
        print "All entities:"
        v = new TestModelVisitor
@@ -79,4 +94,29 @@ do
        v.enter_visit(model)
        v.cpt.print_elements(10)
 
+       print "\nNames:"
+       print "\n# Classes of entities"
+       var cpt
+       cpt = new Counter[String]
+       for n, e in names do
+               cpt.inc(e.class_name)
+       end
+       cpt.print_summary
+       cpt.print_elements(10)
+
+       print "\n# Name length of entities"
+       cpt = new Counter[String]
+       for n, e in names do
+               cpt[n] = n.length
+       end
+       cpt.print_summary
+       cpt.print_elements(10)
+
+       print "\n# All entities"
+       for n, e in names do
+               var c = ""
+               var d = e.mdoc_or_fallback
+               if d != null and d.content.not_empty then c = d.content.first
+               print "{n}\t{e.class_name}\t{e.location}\t{c}"
+       end
 end
index 62cfb08..6281977 100644 (file)
@@ -17,6 +17,8 @@ module testing_base
 
 import modelize
 private import parser_util
+import html
+import console
 
 redef class ToolContext
        # opt --full
@@ -27,11 +29,300 @@ redef class ToolContext
        var opt_dir = new OptionString("Working directory (default is '.nitunit')", "--dir")
        # opt --no-act
        var opt_noact = new OptionBool("Does not compile and run tests", "--no-act")
+       # opt --nitc
+       var opt_nitc = new OptionString("nitc compiler to use", "--nitc")
+       # opt --no-time
+       var opt_no_time = new OptionBool("Disable time information in XML", "--no-time")
 
        # Working directory for testing.
        fun test_dir: String do
                var dir = opt_dir.value
-               if dir == null then return ".nitunit"
+               if dir == null then return "nitunit.out"
                return dir
        end
+
+       # Search the `nitc` compiler to use
+       #
+       # If not `nitc` is suitable, then prints an error and quit.
+       fun find_nitc: String
+       do
+               var nitc = opt_nitc.value
+               if nitc != null then
+                       if not nitc.file_exists then
+                               fatal_error(null, "error: cannot find `{nitc}` given by --nitc.")
+                               abort
+                       end
+                       return nitc
+               end
+
+               nitc = "NITC".environ
+               if nitc != "" then
+                       if not nitc.file_exists then
+                               fatal_error(null, "error: cannot find `{nitc}` given by NITC.")
+                               abort
+                       end
+                       return nitc
+               end
+
+               var nit_dir = nit_dir
+               nitc = nit_dir/"bin/nitc"
+               if not nitc.file_exists then
+                       fatal_error(null, "Error: cannot find nitc. Set envvar NIT_DIR or NITC or use the --nitc option.")
+                       abort
+               end
+               return nitc
+       end
+
+       # Execute a system command in a more safe context than `Sys::system`.
+       fun safe_exec(command: String): Int
+       do
+               info(command, 2)
+               var real_command = """
+bash -c "
+ulimit -f {{{ulimit_file}}} 2> /dev/null
+ulimit -t {{{ulimit_usertime}}} 2> /dev/null
+{{{command}}}
+"
+"""
+               return system(real_command)
+       end
+
+       # The maximum size (in KB) of files written by a command executed trough `safe_exec`
+       #
+       # Default: 64MB
+       var ulimit_file = 65536 is writable
+
+       # The maximum amount of cpu time (in seconds) for a command executed trough `safe_exec`
+       #
+       # Default: 10 CPU minute
+       var ulimit_usertime = 600 is writable
+
+       # Show a single-line status to use as a progression.
+       #
+       # If `has_progress_bar` is true, then the output is a progress bar.
+       # The printed the line starts with `'\r'` and is not ended by a `'\n'`.
+       # So it is expected that:
+       # * no other output is printed between two calls
+       # * the last `show_unit_status` is followed by a new-line
+       #
+       # If `has_progress_bar` is false, then only the first and last state is shown
+       fun show_unit_status(name: String, tests: SequenceRead[UnitTest])
+       do
+               var esc = 27.code_point.to_s
+               var line = "\r\x1B[K==== {name} ["
+               var done = tests.length
+               var fails = 0
+               for t in tests do
+                       if not t.is_done then
+                               line += " "
+                               done -= 1
+                       else if t.error == null then
+                               line += ".".green.bold
+                       else
+                               line += "X".red.bold
+                               fails += 1
+                       end
+               end
+
+               if not has_progress_bar then
+                       if done == 0 then
+                               print "==== {name} | tests: {tests.length}"
+                       end
+                       return
+               end
+
+               if done < tests.length then
+                       line += "] {done}/{tests.length}"
+               else
+                       line += "] tests: {tests.length} "
+                       if fails == 0 then
+                               line += "OK".green.bold
+                       else
+                               line += "KO: {fails}".red.bold
+                       end
+               end
+               printn "{line}"
+       end
+
+       # Is a progress bar printed?
+       #
+       # true if color (because and non-verbose mode
+       # (because verbose mode messes up with the progress bar).
+       fun has_progress_bar: Bool
+       do
+               return not opt_no_color.value and opt_verbose.value <= 0
+       end
+
+       # Clear the line if `has_progress_bar` (no-op else)
+       fun clear_progress_bar
+       do
+               if has_progress_bar then printn "\r\x1B[K"
+       end
+
+       # Show the full description of the test-case.
+       #
+       # The output honors `--no-color`.
+       #
+       # `more message`, if any, is added after the error message.
+       fun show_unit(test: UnitTest, more_message: nullable String) do
+               print test.to_screen(more_message, not opt_no_color.value)
+       end
+end
+
+# A unit test is an elementary test discovered, run and reported by nitunit.
+#
+# This class factorizes `DocUnit` and `TestCase`.
+abstract class UnitTest
+       # The name of the unit to show in messages
+       fun full_name: String is abstract
+
+       # The location of the unit test to show in messages.
+       fun location: Location is abstract
+
+       # Flag that indicates if the unit test was compiled/run.
+       var is_done: Bool = false is writable
+
+       # Error message occurred during test-case execution (or compilation).
+       #
+       # e.g.: `Runtime Error`
+       var error: nullable String = null is writable
+
+       # Was the test case executed at least once?
+       #
+       # This will indicate the status of the test (failture or error)
+       var was_exec = false is writable
+
+       # The raw output of the execution (or compilation)
+       #
+       # It merges the standard output and error output
+       var raw_output: nullable String = null is writable
+
+       # The location where the error occurred, if it makes sense.
+       var error_location: nullable Location = null is writable
+
+       # Additional noteworthy information when a test success.
+       var info: nullable String = null
+
+       # Time for the execution, in seconds
+       var real_time: Float = 0.0 is writable
+
+       # A colorful `[OK]` or `[KO]`.
+       fun status_tag(color: nullable Bool): String do
+               color = color or else true
+               if not is_done then
+                       return "[  ]"
+               else if error != null then
+                       var res = "[KO]"
+                       if color then res = res.red.bold
+                       return res
+               else
+                       var res = "[OK]"
+                       if color then res = res.green.bold
+                       return res
+               end
+       end
+
+       # The full (color) description of the test-case.
+       #
+       # `more message`, if any, is added after the error message.
+       fun to_screen(more_message: nullable String, color: nullable Bool): String do
+               color = color or else true
+               var res
+               var error = self.error
+               if error != null then
+                       if more_message != null then error += " " + more_message
+                       var loc = error_location or else location
+                       if color then
+                               res = "{status_tag(color)} {full_name}\n     {loc.to_s.yellow}: {error}\n{loc.colored_line("1;31")}"
+                       else
+                               res = "{status_tag(color)} {full_name}\n     {loc}: {error}"
+                       end
+                       var output = self.raw_output
+                       if output != null then
+                               res += "\n     Output\n\t{output.chomp.replace("\n", "\n\t")}\n"
+                       end
+               else
+                       res = "{status_tag(color)} {full_name}"
+                       if more_message != null then res += more_message
+                       var info = self.info
+                       if info != null then
+                               res += "\n     {info}"
+                       end
+               end
+               return res
+       end
+
+       # Return a `<testcase>` XML node in format compatible with Jenkins unit tests.
+       fun to_xml: HTMLTag do
+               var tc = new HTMLTag("testcase")
+               tc.attr("classname", xml_classname)
+               tc.attr("name", xml_name)
+               tc.attr("time", real_time.to_s)
+
+               var output = self.raw_output
+               if output != null then output = output.trunc(8192).filter_nonprintable
+               var error = self.error
+               if error != null then
+                       var node
+                       if was_exec then
+                               node = tc.open("error").attr("message", error)
+                       else
+                               node = tc.open("failure").attr("message", error)
+                       end
+                       if output != null then
+                               node.append(output)
+                       end
+               else if output != null then
+                       tc.open("system-err").append(output)
+               end
+               return tc
+       end
+
+       # The `classname` attribute of the XML format.
+       #
+       # NOTE: jenkins expects a '.' in the classname attr
+       #
+       # See to_xml
+       fun xml_classname: String is abstract
+
+       # The `name` attribute of the XML format.
+       #
+       # See to_xml
+       fun xml_name: String is abstract
+end
+
+redef class String
+       # If needed, truncate `self` at `max_length` characters and append an informative `message`.
+       #
+       # ~~~
+       # assert "hello".trunc(10) == "hello"
+       # assert "hello".trunc(2) == "he[truncated. Full size is 5]"
+       # assert "hello".trunc(2, "...") == "he..."
+       # ~~~
+       fun trunc(max_length: Int, message: nullable String): String
+       do
+               if length <= max_length then return self
+               if message == null then message = "[truncated. Full size is {length}]"
+               return substring(0, max_length) + message
+       end
+
+       # Use a special notation for whitespace characters that are not `'\n'` (LFD) or `' '` (space).
+       #
+       # ~~~
+       # assert "hello".filter_nonprintable == "hello"
+       # assert "\r\n\t".filter_nonprintable == "^13\n^9"
+       # ~~~
+       fun filter_nonprintable: String
+       do
+               var buf = new Buffer
+               for c in self do
+                       var cp = c.code_point
+                       if cp < 32 and c != '\n' then
+                               buf.append "^{cp}"
+                       else
+                               buf.add c
+                       end
+               end
+               return buf.to_s
+       end
 end
index 2c0e456..b3e9c44 100644 (file)
@@ -19,6 +19,7 @@ private import parser_util
 import testing_base
 import markdown
 import html
+import realtime
 
 # Extractor, Executor and Reporter for the tests in a module
 class NitUnitExecutor
@@ -36,11 +37,8 @@ class NitUnitExecutor
        # The XML node associated to the module
        var testsuite: HTMLTag
 
-       # All blocks of code from a same `ADoc`
-       var blocks = new Array[Buffer]
-
-       # All failures from a same `ADoc`
-       var failures = new Array[String]
+       # The name of the suite
+       var name: String
 
        # Markdown processor used to parse markdown comments and extract code.
        var mdproc = new MarkdownProcessor
@@ -55,56 +53,89 @@ class NitUnitExecutor
        # used to generate distinct names
        var cpt = 0
 
+       # The last docunit extracted from a mdoc.
+       #
+       # Is used because a new code-block might just be added to it.
+       var last_docunit: nullable DocUnit = null
+
+       var xml_classname: String is noautoinit
+
+       var xml_name: String is noautoinit
+
        # The entry point for a new `ndoc` node
        # Fill `docunits` with new discovered unit of tests.
-       #
-       # `tc` (testcase) is the pre-filled XML node
-       fun extract(mdoc: MDoc, tc: HTMLTag)
+       fun extract(mdoc: MDoc, xml_classname, xml_name: String)
        do
-               blocks.clear
-               failures.clear
+               last_docunit = null
+               self.xml_classname = xml_classname
+               self.xml_name = xml_name
 
                self.mdoc = mdoc
 
                # Populate `blocks` from the markdown decorator
                mdproc.process(mdoc.content.join("\n"))
-
-               toolcontext.check_errors
-
-               if not failures.is_empty then
-                       for msg in failures do
-                               var ne = new HTMLTag("failure")
-                               ne.attr("message", msg)
-                               tc.add ne
-                               toolcontext.modelbuilder.unit_entities += 1
-                               toolcontext.modelbuilder.failed_entities += 1
-                       end
-                       if blocks.is_empty then testsuite.add(tc)
-               end
-
-               if blocks.is_empty then return
-               for block in blocks do
-                       docunits.add new DocUnit(mdoc, tc, block.write_to_string)
-               end
        end
 
        # All extracted docunits
        var docunits = new Array[DocUnit]
 
+       fun show_status
+       do
+               toolcontext.show_unit_status(name, docunits)
+       end
+
+       fun mark_done(du: DocUnit)
+       do
+               du.is_done = true
+               toolcontext.clear_progress_bar
+               toolcontext.show_unit(du)
+               show_status
+       end
+
        # Execute all the docunits
        fun run_tests
        do
+               if docunits.is_empty then
+                       return
+               end
+
+               # Try to group each nitunit into a single source file to fasten the compilation
                var simple_du = new Array[DocUnit]
+               show_status
                for du in docunits do
+                       # Skip existing errors
+                       if du.error != null then
+                               continue
+                       end
+
                        var ast = toolcontext.parse_something(du.block)
                        if ast isa AExpr then
                                simple_du.add du
+                       end
+               end
+               test_simple_docunits(simple_du)
+
+               # Now test them in order
+               for du in docunits do
+                       if du.error != null then
+                               # Nothing to execute. Conclude
+                       else if du.test_file != null then
+                               # Already compiled. Execute it.
+                               execute_simple_docunit(du)
                        else
+                               # Need to try to compile it, then execute it
                                test_single_docunit(du)
                        end
+                       mark_done(du)
                end
 
-               test_simple_docunits(simple_du)
+               # Final status
+               show_status
+               print ""
+
+               for du in docunits do
+                       testsuite.add du.to_xml
+               end
        end
 
        # Executes multiples doc-units in a shared program.
@@ -128,7 +159,7 @@ class NitUnitExecutor
 
                        i += 1
                        f.write("fun run_{i} do\n")
-                       f.write("# {du.testcase.attrs["name"]}\n")
+                       f.write("# {du.full_name}\n")
                        f.write(du.block)
                        f.write("end\n")
                end
@@ -144,43 +175,37 @@ class NitUnitExecutor
 
                if res != 0 then
                        # Compilation error.
-                       # Fall-back to individual modes:
-                       for du in dus do
-                               test_single_docunit(du)
-                       end
+                       # They will be executed independently
                        return
                end
 
+               # Compilation was a success.
+               # Store what need to be executed for each one.
                i = 0
                for du in dus do
-                       var tc = du.testcase
-                       toolcontext.modelbuilder.unit_entities += 1
                        i += 1
-                       toolcontext.info("Execute doc-unit {du.testcase.attrs["name"]} in {file} {i}", 1)
-                       var res2 = sys.system("{file.to_program_name}.bin {i} >>'{file}.out1' 2>&1 </dev/null")
-
-                       var msg
-                       f = new FileReader.open("{file}.out1")
-                       var n2
-                       n2 = new HTMLTag("system-err")
-                       tc.add n2
-                       msg = f.read_all
-                       f.close
-
-                       n2 = new HTMLTag("system-out")
-                       tc.add n2
-                       n2.append(du.block)
-
-                       if res2 != 0 then
-                               var ne = new HTMLTag("error")
-                               ne.attr("message", msg)
-                               tc.add ne
-                               toolcontext.warning(du.mdoc.location, "error", "ERROR: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
-                               toolcontext.modelbuilder.failed_entities += 1
-                       end
-                       toolcontext.check_errors
+                       du.test_file = file
+                       du.test_arg = i
+               end
+       end
 
-                       testsuite.add(tc)
+       # Execute a docunit compiled by `test_single_docunit`
+       fun execute_simple_docunit(du: DocUnit)
+       do
+               var file = du.test_file.as(not null)
+               var i = du.test_arg.as(not null)
+               toolcontext.info("Execute doc-unit {du.full_name} in {file} {i}", 1)
+               var clock = new Clock
+               var res2 = toolcontext.safe_exec("{file.to_program_name}.bin {i} >'{file}.out1' 2>&1 </dev/null")
+               if not toolcontext.opt_no_time.value then du.real_time = clock.total
+               du.was_exec = true
+
+               var content = "{file}.out1".to_path.read_all
+               du.raw_output = content
+
+               if res2 != 0 then
+                       du.error = "Runtime error in {file} with argument {i}"
+                       toolcontext.modelbuilder.failed_entities += 1
                end
        end
 
@@ -188,13 +213,10 @@ class NitUnitExecutor
        # Used for docunits larger than a single block of code (with modules, classes, functions etc.)
        fun test_single_docunit(du: DocUnit)
        do
-               var tc = du.testcase
-               toolcontext.modelbuilder.unit_entities += 1
-
                cpt += 1
                var file = "{prefix}-{cpt}.nit"
 
-               toolcontext.info("Execute doc-unit {tc.attrs["name"]} in {file}", 1)
+               toolcontext.info("Execute doc-unit {du.full_name} in {file}", 1)
 
                var f
                f = create_unitfile(file)
@@ -206,38 +228,22 @@ class NitUnitExecutor
                var res = compile_unitfile(file)
                var res2 = 0
                if res == 0 then
-                       res2 = sys.system("{file.to_program_name}.bin >>'{file}.out1' 2>&1 </dev/null")
+                       var clock = new Clock
+                       res2 = toolcontext.safe_exec("{file.to_program_name}.bin >'{file}.out1' 2>&1 </dev/null")
+                       if not toolcontext.opt_no_time.value then du.real_time = clock.total
+                       du.was_exec = true
                end
 
-               var msg
-               f = new FileReader.open("{file}.out1")
-               var n2
-               n2 = new HTMLTag("system-err")
-               tc.add n2
-               msg = f.read_all
-               f.close
-
-               n2 = new HTMLTag("system-out")
-               tc.add n2
-               n2.append(du.block)
-
+               var content = "{file}.out1".to_path.read_all
+               du.raw_output = content
 
                if res != 0 then
-                       var ne = new HTMLTag("failure")
-                       ne.attr("message", msg)
-                       tc.add ne
-                       toolcontext.warning(du.mdoc.location, "failure", "FAILURE: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
+                       du.error = "Compilation error in {file}"
                        toolcontext.modelbuilder.failed_entities += 1
                else if res2 != 0 then
-                       var ne = new HTMLTag("error")
-                       ne.attr("message", msg)
-                       tc.add ne
-                       toolcontext.warning(du.mdoc.location, "error", "ERROR: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): {msg}")
+                       du.error = "Runtime error in {file}"
                        toolcontext.modelbuilder.failed_entities += 1
                end
-               toolcontext.check_errors
-
-               testsuite.add(tc)
        end
 
        # Create and fill the header of a unit file `file`.
@@ -268,18 +274,13 @@ class NitUnitExecutor
        # Can terminate the program if the compiler is not found
        private fun compile_unitfile(file: String): Int
        do
-               var nit_dir = toolcontext.nit_dir
-               var nitc = nit_dir/"bin/nitc"
-               if not nitc.file_exists then
-                       toolcontext.error(null, "Error: cannot find nitc. Set envvar NIT_DIR.")
-                       toolcontext.check_errors
-               end
+               var nitc = toolcontext.find_nitc
                var opts = new Array[String]
                if mmodule != null then
                        opts.add "-I {mmodule.filepath.dirname}"
                end
-               var cmd = "{nitc} --ignore-visibility --no-color '{file}' {opts.join(" ")} >'{file}.out1' 2>&1 </dev/null -o '{file}.bin'"
-               var res = sys.system(cmd)
+               var cmd = "{nitc} --ignore-visibility --no-color -q '{file}' {opts.join(" ")} >'{file}.out1' 2>&1 </dev/null -o '{file}.bin'"
+               var res = toolcontext.safe_exec(cmd)
                return res
        end
 end
@@ -297,39 +298,174 @@ private class NitunitDecorator
                # Try to parse code blocks
                var ast = executor.toolcontext.parse_something(code)
 
+               var mdoc = executor.mdoc
+               assert mdoc != null
+
                # Skip pure comments
                if ast isa TComment then return
 
+               # The location is computed according to the starts of the mdoc and the block
+               # Note, the following assumes that all the comments of the mdoc are correctly aligned.
+               var loc = block.block.location
+               var line_offset = loc.line_start + mdoc.location.line_start - 2
+               var column_offset = loc.column_start + mdoc.location.column_start
+               # Hack to handle precise location in blocks
+               # TODO remove when markdown is more reliable
+               if block isa BlockFence then
+                       # Skip the starting fence
+                       line_offset += 1
+               else
+                       # Account a standard 4 space indentation
+                       column_offset += 4
+               end
+
                # We want executable code
                if not (ast isa AModule or ast isa ABlockExpr or ast isa AExpr) then
-                       var message = ""
-                       if ast isa AError then message = " At {ast.location}: {ast.message}."
-                       executor.toolcontext.warning(executor.mdoc.location, "invalid-block", "Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).{message}")
-                       executor.failures.add("{executor.mdoc.location}: Invalid block of code.{message}")
+                       var message
+                       var l = ast.location
+                       # Get real location of the node (or error)
+                       var location = new Location(mdoc.location.file,
+                               l.line_start + line_offset,
+                               l.line_end + line_offset,
+                               l.column_start + column_offset,
+                               l.column_end + column_offset)
+                       if ast isa AError then
+                               message = ast.message
+                       else
+                               message = "Error: Invalid Nit code."
+                       end
+
+                       var du = new_docunit
+                       du.block += code
+                       du.error_location = location
+                       du.error = message
+                       executor.toolcontext.modelbuilder.failed_entities += 1
                        return
                end
 
                # Create a first block
                # Or create a new block for modules that are more than a main part
-               if executor.blocks.is_empty or ast isa AModule then
-                       executor.blocks.add(new Buffer)
+               var last_docunit = executor.last_docunit
+               if last_docunit == null or ast isa AModule then
+                       last_docunit = new_docunit
+                       executor.last_docunit = last_docunit
                end
 
                # Add it to the file
-               executor.blocks.last.append code
+               last_docunit.block += code
+
+               # In order to retrieve precise positions,
+               # the real position of each line of the raw_content is stored.
+               # See `DocUnit::real_location`
+               line_offset -= loc.line_start - 1
+               for i in [loc.line_start..loc.line_end] do
+                       last_docunit.lines.add i + line_offset
+                       last_docunit.columns.add column_offset
+               end
+       end
+
+       # Return and register a new empty docunit
+       fun new_docunit: DocUnit
+       do
+               var mdoc = executor.mdoc
+               assert mdoc != null
+
+               var next_number = 1
+               var name = executor.xml_name
+               if executor.docunits.not_empty and executor.docunits.last.mdoc == mdoc then
+                       next_number = executor.docunits.last.number + 1
+                       name += "#" + next_number.to_s
+               end
+
+               var res = new DocUnit(mdoc, next_number, "", executor.xml_classname, name)
+               executor.docunits.add res
+               executor.toolcontext.modelbuilder.unit_entities += 1
+               return res
        end
 end
 
-# A unit-test to run
+# A unit-test extracted from some documentation.
+#
+# A docunit is extracted from the code-blocks of mdocs.
+# Each mdoc can contains more than one docunit, and a single docunit can be made of more that a single code-block.
 class DocUnit
+       super UnitTest
+
        # The doc that contains self
        var mdoc: MDoc
 
-       # The XML node that contains the information about the execution
-       var testcase: HTMLTag
+       # The numbering of self in mdoc (starting with 0)
+       var number: Int
 
-       # The text of the code to execute
+       # The generated Nit source file that contains the unit-test
+       #
+       # Note that a same generated file can be used for multiple tests.
+       # See `test_arg` that is used to distinguish them
+       var test_file: nullable String = null
+
+       # The command-line argument to use when executing the test, if any.
+       var test_arg: nullable Int = null
+
+       redef fun full_name do
+               var mentity = mdoc.original_mentity
+               if mentity != null then
+                       var res = mentity.full_name
+                       if number > 1 then
+                               res += "#{number}"
+                       end
+                       return res
+               else
+                       return xml_classname + "." + xml_name
+               end
+       end
+
+       # The text of the code to execute.
+       #
+       # This is the verbatim content on one, or more, code-blocks from `mdoc`
        var block: String
+
+       # For each line in `block`, the associated line in the mdoc
+       #
+       # Is used to give precise locations
+       var lines = new Array[Int]
+
+       # For each line in `block`, the associated column in the mdoc
+       #
+       # Is used to give precise locations
+       var columns = new Array[Int]
+
+       # The location of the whole docunit.
+       #
+       # If `self` is made of multiple code-blocks, then the location
+       # starts at the first code-books and finish at the last one, thus includes anything between.
+       redef var location is lazy do
+               return new Location(mdoc.location.file, lines.first, lines.last+1, columns.first+1, 0)
+       end
+
+       # Compute the real location of a node on the `ast` based on `mdoc.location`
+       #
+       # The result is basically: ast_location + markdown location of the piece + mdoc.location
+       #
+       # The fun is that a single docunit can be made of various pieces of code blocks.
+       fun real_location(ast_location: Location): Location
+       do
+               var mdoc = self.mdoc
+               var res = new Location(mdoc.location.file, lines[ast_location.line_start-1],
+                       lines[ast_location.line_end-1],
+                       columns[ast_location.line_start-1] + ast_location.column_start,
+                       columns[ast_location.line_end-1] + ast_location.column_end)
+               return res
+       end
+
+       redef fun to_xml
+       do
+               var res = super
+               res.open("system-out").append(block)
+               return res
+       end
+
+       redef var xml_classname
+       redef var xml_name
 end
 
 redef class ModelBuilder
@@ -368,9 +504,7 @@ redef class ModelBuilder
 
                var prefix = toolcontext.test_dir
                prefix = prefix.join_path(mmodule.to_s)
-               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts)
-
-               var tc
+               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts, "Docunits of module {mmodule.full_name}")
 
                do
                        total_entities += 1
@@ -379,11 +513,8 @@ redef class ModelBuilder
                        var ndoc = nmoduledecl.n_doc
                        if ndoc == null then break label x
                        doc_entities += 1
-                       tc = new HTMLTag("testcase")
                        # NOTE: jenkins expects a '.' in the classname attr
-                       tc.attr("classname", "nitunit." + mmodule.full_name + ".<module>")
-                       tc.attr("name", "<module>")
-                       d2m.extract(ndoc.to_mdoc, tc)
+                       d2m.extract(ndoc.to_mdoc, "nitunit." + mmodule.full_name + ".<module>", "<module>")
                end label x
                for nclassdef in nmodule.n_classdefs do
                        var mclassdef = nclassdef.mclassdef
@@ -393,10 +524,7 @@ redef class ModelBuilder
                                var ndoc = nclassdef.n_doc
                                if ndoc != null then
                                        doc_entities += 1
-                                       tc = new HTMLTag("testcase")
-                                       tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name)
-                                       tc.attr("name", "<class>")
-                                       d2m.extract(ndoc.to_mdoc, tc)
+                                       d2m.extract(ndoc.to_mdoc, "nitunit." + mclassdef.full_name.replace("$", "."), "<class>")
                                end
                        end
                        for npropdef in nclassdef.n_propdefs do
@@ -406,10 +534,8 @@ redef class ModelBuilder
                                var ndoc = npropdef.n_doc
                                if ndoc != null then
                                        doc_entities += 1
-                                       tc = new HTMLTag("testcase")
-                                       tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name)
-                                       tc.attr("name", mpropdef.mproperty.full_name)
-                                       d2m.extract(ndoc.to_mdoc, tc)
+                                       var a = mpropdef.full_name.split("$")
+                                       d2m.extract(ndoc.to_mdoc, "nitunit." + a[0] + "." + a[1], a[2])
                                end
                        end
                end
@@ -433,20 +559,15 @@ redef class ModelBuilder
 
                var prefix = toolcontext.test_dir
                prefix = prefix.join_path(mgroup.to_s)
-               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts)
-
-               var tc
+               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts, "Docunits of group {mgroup.full_name}")
 
                total_entities += 1
                var mdoc = mgroup.mdoc
                if mdoc == null then return ts
 
                doc_entities += 1
-               tc = new HTMLTag("testcase")
                # NOTE: jenkins expects a '.' in the classname attr
-               tc.attr("classname", "nitunit." + mgroup.full_name)
-               tc.attr("name", "<group>")
-               d2m.extract(mdoc, tc)
+               d2m.extract(mdoc, "nitunit." + mgroup.mpackage.name + "." + mgroup.name + ".<group>", "<group>")
 
                d2m.run_tests
 
@@ -457,26 +578,20 @@ redef class ModelBuilder
        fun test_mdoc(mdoc: MDoc): HTMLTag
        do
                var ts = new HTMLTag("testsuite")
-               var file = mdoc.location.to_s
+               var file = mdoc.location.file.filename
 
                toolcontext.info("nitunit: doc-unit file {file}", 2)
 
                ts.attr("package", file)
 
                var prefix = toolcontext.test_dir / "file"
-               var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts)
-
-               var tc
+               var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts, "Docunits of file {file}")
 
                total_entities += 1
                doc_entities += 1
 
-               tc = new HTMLTag("testcase")
                # NOTE: jenkins expects a '.' in the classname attr
-               tc.attr("classname", "nitunit.<file>")
-               tc.attr("name", file)
-
-               d2m.extract(mdoc, tc)
+               d2m.extract(mdoc, "nitunit.<file>", file)
                d2m.run_tests
 
                return ts
index 0de6e0a..60265ab 100644 (file)
@@ -18,12 +18,13 @@ module testing_suite
 import testing_base
 import html
 private import annotation
+private import realtime
 
 redef class ToolContext
-       # -- target-file
-       var opt_file = new OptionString("Specify test suite location", "-t", "--target-file")
        # --pattern
        var opt_pattern = new OptionString("Only run test case with name that match pattern", "-p", "--pattern")
+       # --autosav
+       var opt_autosav = new OptionBool("Automatically create/update .res files for black box testing", "--autosav")
 end
 
 # Used to test nitunit test files.
@@ -32,20 +33,9 @@ class NitUnitTester
        # `ModelBuilder` used to parse test files.
        var mbuilder: ModelBuilder
 
-       # Parse a file and return the contained `MModule`.
-       private fun parse_module_unit(file: String): nullable MModule do
-               var mmodule = mbuilder.parse([file]).first
-               if mbuilder.get_mmodule_annotation("test_suite", mmodule) == null then return null
-               mbuilder.run_phases
-               return mmodule
-       end
-
-       # Compile and execute the test suite for a NitUnit `file`.
-       fun test_module_unit(file: String): nullable TestSuite do
+       # Compile and execute `mmodule` as a test suite.
+       fun test_module_unit(mmodule: MModule): TestSuite do
                var toolcontext = mbuilder.toolcontext
-               var mmodule = parse_module_unit(file)
-               # is the module a test_suite?
-               if mmodule == null then return null
                var suite = new TestSuite(mmodule, toolcontext)
                # method to execute before all tests in the module
                var before_module = mmodule.before_test
@@ -132,19 +122,47 @@ class TestSuite
        # Test to be executed after the whole test suite.
        var after_module: nullable TestCase = null
 
+       fun show_status
+       do
+               toolcontext.show_unit_status("Test-suite of module " + mmodule.full_name, test_cases)
+       end
+
        # Execute the test suite
        fun run do
+               show_status
                if not toolcontext.test_dir.file_exists then
                        toolcontext.test_dir.mkdir
                end
                write_to_nit
                compile
+               if failure != null then
+                       for case in test_cases do
+                               case.is_done = true
+                               case.error = "Compilation Error"
+                               case.raw_output = failure
+                               toolcontext.modelbuilder.failed_tests += 1
+                               toolcontext.clear_progress_bar
+                               toolcontext.show_unit(case)
+                       end
+                       show_status
+                       print ""
+                       return
+               end
                toolcontext.info("Execute test-suite {mmodule.name}", 1)
                var before_module = self.before_module
                if not before_module == null then before_module.run
-               for case in test_cases do case.run
+               for case in test_cases do
+                       case.run
+                       toolcontext.clear_progress_bar
+                       toolcontext.show_unit(case)
+                       show_status
+               end
+
                var after_module = self.after_module
                if not after_module == null then after_module.run
+
+               show_status
+               print ""
        end
 
        # Write the test unit for `self` in a nit compilable file.
@@ -164,14 +182,7 @@ class TestSuite
        fun to_xml: HTMLTag do
                var n = new HTMLTag("testsuite")
                n.attr("package", mmodule.name)
-               var failure = self.failure
-               if failure != null then
-                       var f = new HTMLTag("failure")
-                       f.attr("message", failure.to_s)
-                       n.add f
-               else
-                       for test in test_cases do n.add test.to_xml
-               end
+               for test in test_cases do n.add test.to_xml
                return n
        end
 
@@ -183,12 +194,7 @@ class TestSuite
        # Compile all `test_cases` cases in one file.
        fun compile do
                # find nitc
-               var nit_dir = toolcontext.nit_dir
-               var nitc = nit_dir/"bin/nitc"
-               if not nitc.file_exists then
-                       toolcontext.error(null, "Error: cannot find nitc. Set envvar NIT_DIR.")
-                       toolcontext.check_errors
-               end
+               var nitc = toolcontext.find_nitc
                # compile test suite
                var file = test_file
                var module_file = mmodule.location.file
@@ -198,19 +204,14 @@ class TestSuite
                        return
                end
                var include_dir = module_file.filename.dirname
-               var cmd = "{nitc} --no-color '{file}.nit' -I {include_dir} -o '{file}.bin' > '{file}.out' 2>&1 </dev/null"
-               var res = sys.system(cmd)
+               var cmd = "{nitc} --no-color -q '{file}.nit' -I {include_dir} -o '{file}.bin' > '{file}.out' 2>&1 </dev/null"
+               var res = toolcontext.safe_exec(cmd)
                var f = new FileReader.open("{file}.out")
                var msg = f.read_all
                f.close
-               # set test case result
-               var loc = mmodule.location
                if res != 0 then
                        failure = msg
-                       toolcontext.warning(loc, "failure", "FAILURE: {mmodule.name} (in file {file}.nit): {msg}")
-                       toolcontext.modelbuilder.failed_tests += 1
                end
-               toolcontext.check_errors
        end
 
        # Error occured during test-suite compilation.
@@ -219,6 +220,7 @@ end
 
 # A test case is a unit test considering only a `MMethodDef`.
 class TestCase
+       super UnitTest
 
        # Test suite wich `self` belongs to.
        var test_suite: TestSuite
@@ -226,6 +228,10 @@ class TestCase
        # Test method to be compiled and tested.
        var test_method: MMethodDef
 
+       redef fun full_name do return test_method.full_name
+
+       redef fun location do return test_method.location
+
        # `ToolContext` to use to display messages and find `nitc` bin.
        var toolcontext: ToolContext
 
@@ -253,47 +259,65 @@ class TestCase
                var method_name = test_method.name
                var test_file = test_suite.test_file
                var res_name = "{test_file}_{method_name.escape_to_c}"
-               var res = sys.system("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
-               var f = new FileReader.open("{res_name}.out1")
-               var msg = f.read_all
-               f.close
+               var clock = new Clock
+               var res = toolcontext.safe_exec("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
+               if not toolcontext.opt_no_time.value then real_time = clock.total
+
+               var raw_output = "{res_name}.out1".to_path.read_all
+               self.raw_output = raw_output
                # set test case result
-               var loc = test_method.location
                if res != 0 then
-                       error = msg
-                       toolcontext.warning(loc, "failure",
-                          "ERROR: {method_name} (in file {test_file}.nit): {msg}")
+                       error = "Runtime Error in file {test_file}.nit"
                        toolcontext.modelbuilder.failed_tests += 1
+               else
+                       # no error, check with res file, if any.
+                       var mmodule = test_method.mclassdef.mmodule
+                       var file = mmodule.filepath
+                       if file != null then
+                               var tries = [ file.dirname / mmodule.name + ".sav" / test_method.name + ".res",
+                                       file.dirname / "sav" / test_method.name + ".res" ,
+                                       file.dirname / test_method.name + ".res" ]
+                               var savs = [ for t in tries do if t.file_exists then t ]
+                               if savs.length == 1 then
+                                       var sav = savs.first
+                                       toolcontext.info("Diff output with {sav}", 1)
+                                       res = toolcontext.safe_exec("diff -u --label 'expected:{sav}' --label 'got:{res_name}.out1' '{sav}' '{res_name}.out1' > '{res_name}.diff' 2>&1 </dev/null")
+                                       if res == 0 then
+                                               # OK
+                                       else if toolcontext.opt_autosav.value then
+                                               raw_output.write_to_file(sav)
+                                               info = "Expected output updated: {sav} (--autoupdate)"
+                                       else
+                                               self.raw_output = "Diff\n" + "{res_name}.diff".to_path.read_all
+                                               error = "Difference with expected output: diff -u {sav} {res_name}.out1"
+                                               toolcontext.modelbuilder.failed_tests += 1
+                                       end
+                               else if savs.length > 1 then
+                                       toolcontext.info("Conflicting diffs: {savs.join(", ")}", 1)
+                                       error = "Conflicting expected output: {savs.join(", ", " and ")} all exist"
+                                       toolcontext.modelbuilder.failed_tests += 1
+                               else if not raw_output.is_empty then
+                                       toolcontext.info("No diff: {tries.join(", ", " or ")} not found", 1)
+                                       if toolcontext.opt_autosav.value then
+                                               var sav = tries.first
+                                               sav.dirname.mkdir
+                                               raw_output.write_to_file(sav)
+                                               info = "Expected output saved: {sav} (--autoupdate)"
+                                       end
+                               end
+                       end
                end
-               toolcontext.check_errors
+               is_done = true
        end
 
-       # Error occured during test-case execution.
-       var error: nullable String = null
-
-       # Was the test case executed at least one?
-       var was_exec = false
+       redef fun xml_classname do
+               var a = test_method.full_name.split("$")
+               return "nitunit.{a[0]}.{a[1]}"
+       end
 
-       # Return the `TestCase` in XML format compatible with Jenkins.
-       fun to_xml: HTMLTag do
-               var mclassdef = test_method.mclassdef
-               var tc = new HTMLTag("testcase")
-               # NOTE: jenkins expects a '.' in the classname attr
-               tc.attr("classname", "nitunit." + mclassdef.mmodule.full_name + "." + mclassdef.mclass.full_name)
-               tc.attr("name", test_method.mproperty.full_name)
-               if was_exec then
-                       tc.add  new HTMLTag("system-err")
-                       var n = new HTMLTag("system-out")
-                       n.append "out"
-                       tc.add n
-                       var error = self.error
-                       if error != null then
-                               n = new HTMLTag("error")
-                               n.attr("message", error.to_s)
-                               tc.add n
-                       end
-               end
-               return tc
+       redef fun xml_name do
+               var a = test_method.full_name.split("$")
+               return a[2]
        end
 end
 
@@ -313,7 +337,7 @@ end
 
 redef class MClassDef
        # Is the class a TestClass?
-       # i.e. begins with "Test"
+       # i.e. is a subclass of `TestSuite`
        private fun is_test: Bool do
                var in_hierarchy = self.in_hierarchy
                if in_hierarchy == null then return false
@@ -358,33 +382,14 @@ redef class ModelBuilder
        # Number of failed tests.
        var failed_tests = 0
 
-       # Run NitUnit test file for mmodule (if exists).
-       fun test_unit(mmodule: MModule): HTMLTag do
-               var ts = new HTMLTag("testsuite")
-               toolcontext.info("nitunit: test-suite test_{mmodule}", 2)
-               var f = toolcontext.opt_file.value
-               var test_file = "test_{mmodule.name}.nit"
-               if f != null then
-                       test_file = f
-               else if not test_file.file_exists then
-                       var module_file = mmodule.location.file
-                       if module_file == null then
-                               toolcontext.info("Skip test for {mmodule}, no file found", 2)
-                               return ts
-                       end
-                       var include_dir = module_file.filename.dirname
-                       test_file = "{include_dir}/{test_file}"
-               end
-               if not test_file.file_exists then
-                       toolcontext.info("Skip test for {mmodule}, no file {test_file} found", 2)
-                       return ts
-               end
+       # Run NitUnit test suite for `mmodule` (if it is one).
+       fun test_unit(mmodule: MModule): nullable HTMLTag do
+               # is the module a test_suite?
+               if get_mmodule_annotation("test_suite", mmodule) == null then return null
+               toolcontext.info("nitunit: test-suite {mmodule}", 2)
+
                var tester = new NitUnitTester(self)
-               var res = tester.test_module_unit(test_file)
-               if res == null then
-                       toolcontext.info("Skip test for {mmodule}, no test suite found", 2)
-                       return ts
-               end
+               var res = tester.test_module_unit(mmodule)
                return res.to_xml
        end
 end
index 850781e..8cea80f 100644 (file)
@@ -58,6 +58,13 @@ class Message
        # * enclose identifiers, keywords and pieces of code with back-quotes.
        var text: String
 
+       # The severity level
+       #
+       # * 0 is advices (see `ToolContext::advice`)
+       # * 1 is warnings (see `ToolContext::warning`)
+       # * 2 is errors (see `ToolContext::error`)
+       var level: Int
+
        # Comparisons are made on message locations.
        redef fun <(other: OTHER): Bool do
                if location == null then return true
@@ -123,9 +130,16 @@ redef class Location
                        messages = ms
                end
                ms.add m
+               var s = file
+               if s != null then s.messages.add m
        end
 end
 
+redef class SourceFile
+       # Errors and warnings associated to the whole source.
+       var messages = new Array[Message]
+end
+
 # Global context for tools
 class ToolContext
        # Number of errors
@@ -225,9 +239,10 @@ class ToolContext
        # Return the message (to add information)
        fun error(l: nullable Location, s: String): Message
        do
-               var m = new Message(l,null,s)
+               var m = new Message(l, null, s, 2)
                if messages.has(m) then return m
                if l != null then l.add_message m
+               if opt_warn.value <= -1 then return m
                messages.add m
                error_count = error_count + 1
                if opt_stop_on_first_error.value then check_errors
@@ -257,12 +272,12 @@ class ToolContext
        # Return the message (to add information) or null if the warning is disabled
        fun warning(l: nullable Location, tag: String, text: String): nullable Message
        do
-               if opt_warning.value.has("no-{tag}") then return null
-               if not opt_warning.value.has(tag) and opt_warn.value == 0 then return null
                if is_warning_blacklisted(l, tag) then return null
-               var m = new Message(l, tag, text)
+               var m = new Message(l, tag, text, 1)
                if messages.has(m) then return null
                if l != null then l.add_message m
+               if opt_warning.value.has("no-{tag}") then return null
+               if not opt_warning.value.has(tag) and opt_warn.value <= 0 then return null
                messages.add m
                warning_count = warning_count + 1
                if opt_stop_on_first_error.value then check_errors
@@ -286,12 +301,12 @@ class ToolContext
        # Return the message (to add information) or null if the warning is disabled
        fun advice(l: nullable Location, tag: String, text: String): nullable Message
        do
-               if opt_warning.value.has("no-{tag}") then return null
-               if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return null
                if is_warning_blacklisted(l, tag) then return null
-               var m = new Message(l, tag, text)
+               var m = new Message(l, tag, text, 0)
                if messages.has(m) then return null
                if l != null then l.add_message m
+               if opt_warning.value.has("no-{tag}") then return null
+               if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return null
                messages.add m
                warning_count = warning_count + 1
                if opt_stop_on_first_error.value then check_errors
@@ -360,6 +375,9 @@ class ToolContext
        # Option --nit-dir
        var opt_nit_dir = new OptionString("Base directory of the Nit installation", "--nit-dir")
 
+       # Option --share-dir
+       var opt_share_dir = new OptionString("Directory containing tools assets", "--share-dir")
+
        # Option --help
        var opt_help = new OptionBool("Show Help (This screen)", "-h", "-?", "--help")
 
@@ -484,7 +502,7 @@ The Nit language documentation and the source code of its tools and libraries ma
                        exit 1
                end
 
-               nit_dir = compute_nit_dir
+               nit_dir = locate_nit_dir
 
                if option_context.rest.is_empty and not accept_no_arguments then
                        print tooldescription
@@ -526,9 +544,40 @@ The Nit language documentation and the source code of its tools and libraries ma
        end
 
        # The identified root directory of the Nit package
-       var nit_dir: String is noinit
+       #
+       # It is assignable but is automatically set by `process_options` with `locate_nit_dir`.
+       var nit_dir: nullable String = null is writable
+
+       # Shared files directory.
+       #
+       # Most often `nit/share/`.
+       var share_dir: String is lazy do
+               var sharedir = opt_share_dir.value
+               if sharedir == null then
+                       sharedir = nit_dir / "share"
+                       if not sharedir.file_exists then
+                               fatal_error(null, "Fatal Error: cannot locate shared files directory in {sharedir}. Uses --share-dir to define it's location.")
+                       end
+               end
+               return sharedir
+       end
 
-       private fun compute_nit_dir: String
+       # Guess a possible nit_dir.
+       #
+       # It uses, in order:
+       #
+       # * the option `opt_no_color`
+       # * the environment variable `NIT_DIR`
+       # * the runpath of the program from argv[0]
+       # * the runpath of the process from /proc
+       # * the search in PATH
+       #
+       # If there is errors (e.g. the indicated path is invalid) or if no
+       # path is found, then an error is displayed and the program exits.
+       #
+       # The result is returned without being assigned to `nit_dir`.
+       # This function is automatically called by `process_options`
+       fun locate_nit_dir: String
        do
                # the option has precedence
                var res = opt_nit_dir.value
diff --git a/src/web/api_catalog.nit b/src/web/api_catalog.nit
new file mode 100644 (file)
index 0000000..72d207f
--- /dev/null
@@ -0,0 +1,138 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module api_catalog
+
+import web_base
+import catalog
+
+# Group all api handlers in one router.
+class APICatalogRouter
+       super Router
+
+       # Model to pass to handlers.
+       var model: Model
+
+       # Mainmodule to pass to handlers.
+       var mainmodule: MModule
+
+       # Catalog to pass to handlers.
+       var catalog: Catalog
+
+       init do
+               use("/highlighted", new APICatalogHighLighted(model, mainmodule, catalog))
+               use("/required", new APICatalogMostRequired(model, mainmodule, catalog))
+               use("/bytags", new APICatalogByTags(model, mainmodule, catalog))
+               use("/contributors", new APICatalogContributors(model, mainmodule, catalog))
+               use("/stats", new APICatalogStats(model, mainmodule, catalog))
+       end
+end
+
+abstract class APICatalogHandler
+       super APIHandler
+
+       var catalog: Catalog
+
+       # List the 10 best packages from `cpt`
+       fun list_best(cpt: Counter[MPackage]): JsonArray do
+               var res = new JsonArray
+               var best = cpt.sort
+               for i in [1..10] do
+                       if i > best.length then break
+                       res.add best[best.length-i]
+               end
+               return res
+       end
+
+       # List packages by group.
+       fun list_by(map: MultiHashMap[Object, MPackage]): JsonObject do
+               var res = new JsonObject
+               var keys = map.keys.to_a
+               alpha_comparator.sort(keys)
+               for k in keys do
+                       var projs = map[k].to_a
+                       alpha_comparator.sort(projs)
+                       res[k.to_s.html_escape] = new JsonArray.from(projs)
+               end
+               return res
+       end
+end
+
+class APICatalogStats
+       super APICatalogHandler
+
+       redef fun get(req, res) do
+               var obj = new JsonObject
+               obj["packages"] = model.mpackages.length
+               obj["maintainers"] = catalog.maint2proj.length
+               obj["contributors"] = catalog.contrib2proj.length
+               obj["modules"] = catalog.mmodules.sum
+               obj["classes"] = catalog.mclasses.sum
+               obj["methods"] = catalog.mmethods.sum
+               obj["loc"] = catalog.loc.sum
+               res.json obj
+       end
+end
+
+class APICatalogHighLighted
+       super APICatalogHandler
+
+       redef fun get(req, res) do res.json list_best(catalog.score)
+end
+
+class APICatalogMostRequired
+       super APICatalogHandler
+
+       redef fun get(req, res) do
+               if catalog.deps.not_empty then
+                       var reqs = new Counter[MPackage]
+                       for p in model.mpackages do
+                               reqs[p] = catalog.deps[p].smallers.length - 1
+                       end
+                       res.json list_best(reqs)
+                       return
+               end
+               res.json new JsonArray
+       end
+end
+
+class APICatalogByTags
+       super APICatalogHandler
+
+       redef fun get(req, res) do res.json list_by(catalog.tag2proj)
+end
+
+class APICatalogContributors
+       super APICatalogHandler
+
+       redef fun get(req, res) do
+               var obj = new JsonObject
+               obj["maintainers"] = new JsonArray.from(catalog.maint2proj.keys)
+               obj["contributors"] = new JsonArray.from(catalog.contrib2proj.keys)
+               res.json obj
+       end
+end
+
+redef class Person
+       super Jsonable
+
+       redef fun to_json do
+               var obj = new JsonObject
+               obj["name"] = name
+               obj["email"] = email
+               obj["page"] = page
+               obj["hash"] = (email or else "").md5.to_lower
+               return obj.to_json
+       end
+end
diff --git a/src/web/api_docdown.nit b/src/web/api_docdown.nit
new file mode 100644 (file)
index 0000000..3510b64
--- /dev/null
@@ -0,0 +1,272 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Nitdoc specific Markdown format handling for Nitweb
+module api_docdown
+
+import api_graph
+intrude import doc_down
+intrude import markdown::wikilinks
+import doc_commands
+
+# Docdown handler accept docdown as POST data and render it as HTML
+class APIDocdown
+       super APIHandler
+
+       # Modelbuilder used by the commands
+       var modelbuilder: ModelBuilder
+
+       # Specific Markdown processor to use within Nitweb
+       var md_processor: MarkdownProcessor is lazy do
+               var proc = new MarkdownProcessor
+               proc.emitter.decorator = new NitwebDecorator(view, modelbuilder)
+               return proc
+       end
+
+       redef fun post(req, res) do
+               res.html md_processor.process(req.body)
+       end
+end
+
+# Specific Markdown decorator for Nitweb
+#
+# We reuse all the implementation of the NitdocDecorator and add the wikilinks handling.
+class NitwebDecorator
+       super NitdocDecorator
+
+       # View used by wikilink commands to find model entities
+       var view: ModelView
+
+       # Modelbuilder used to access code
+       var modelbuilder: ModelBuilder
+
+       redef fun add_wikilink(v, token) do
+               var link = token.link
+               if link == null then return
+               var cmd = new DocCommand(link.write_to_string)
+               cmd.render(v, token, view)
+       end
+end
+
+# Same as `InlineDecorator` but with wikilink commands handling
+class NitwebInlineDecorator
+       super InlineDecorator
+
+       # View used by wikilink commands to find model entities
+       var view: ModelView
+
+       # Modelbuilder used to access code
+       var modelbuilder: ModelBuilder
+
+       redef fun add_wikilink(v, token) do
+               var link = token.link
+               if link == null then return
+               var cmd = new DocCommand(link.write_to_string)
+               cmd.render(v, token, view)
+       end
+end
+
+redef interface DocCommand
+
+       # Emit the HTML related to the execution of this doc command
+       fun render(v: MarkdownEmitter, token: TokenWikiLink, model: ModelView) do
+               write_error(v, "Not yet implemented command `{token.link or else "null"}`")
+       end
+
+       # Find the MEntity ` with `full_name`.
+       fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
+               if full_name == null then return null
+               return model.mentity_by_full_name(full_name.from_percent_encoding)
+       end
+
+       # Write a warning in the output
+       fun write_warning(v: MarkdownEmitter, text: String) do
+               v.emit_text "<p class='text-warning'>Warning: {text}</p>"
+       end
+
+       # Write an error in the output
+       fun write_error(v: MarkdownEmitter, text: String) do
+               v.emit_text "<p class='text-danger'>Error: {text}</p>"
+       end
+
+       # Write a link to a mentity in the output
+       fun write_mentity_link(v: MarkdownEmitter, mentity: MEntity) do
+               var link = mentity.web_url
+               var name = mentity.name
+               var mdoc = mentity.mdoc_or_fallback
+               var comment = null
+               if mdoc != null then comment = mdoc.synopsis
+               v.decorator.add_link(v, link, name, comment)
+       end
+end
+
+redef class UnknownCommand
+       redef fun render(v, token, model) do
+               var link = token.link
+               if link == null then
+                       write_error(v, "Empty command")
+                       return
+               end
+               var full_name = link.write_to_string
+               var mentity = find_mentity(model, full_name)
+               if mentity == null then
+                       write_error(v, "Unknown command `{link}`")
+                       return
+               end
+               write_mentity_link(v, mentity)
+       end
+end
+
+redef class ArticleCommand
+       redef fun render(v, token, model) do
+               if args.is_empty then
+                       write_error(v, "Expected one arg: the MEntity name")
+                       return
+               end
+               var name = args.first
+               var mentity = find_mentity(model, name)
+               if mentity == null then
+                       write_error(v, "No MEntity found for name `{name}`")
+                       return
+               end
+               var mdoc = mentity.mdoc_or_fallback
+               if mdoc == null then
+                       write_warning(v, "No MDoc for mentity `{name}`")
+                       return
+               end
+               v.add "<h3>"
+               write_mentity_link(v, mentity)
+               v.add " - "
+               v.emit_text mdoc.synopsis
+               v.add "</h3>"
+               v.add v.processor.process(mdoc.comment).write_to_string
+       end
+end
+
+redef class CommentCommand
+       redef fun render(v, token, model) do
+               if args.is_empty then
+                       write_error(v, "Expected one arg: the MEntity name")
+                       return
+               end
+               var name = args.first
+               var mentity = find_mentity(model, name)
+               if mentity == null then
+                       write_error(v, "No MEntity found for name `{name}`")
+                       return
+               end
+               var mdoc = mentity.mdoc_or_fallback
+               if mdoc == null then
+                       write_warning(v, "No MDoc for mentity `{name}`")
+                       return
+               end
+               v.add v.processor.process(mdoc.comment).write_to_string
+       end
+end
+
+redef class ListCommand
+       redef fun render(v, token, model) do
+               if args.is_empty then
+                       write_error(v, "Expected one arg: the MEntity name")
+                       return
+               end
+               var name = args.first
+               var mentity = find_mentity(model, name)
+               if mentity isa MPackage then
+                       write_list(v, mentity.mgroups)
+               else if mentity isa MGroup then
+                       var res = new Array[MEntity]
+                       res.add_all mentity.in_nesting.smallers
+                       res.add_all mentity.mmodules
+                       write_list(v, res)
+               else if mentity isa MModule then
+                       write_list(v, mentity.mclassdefs)
+               else if mentity isa MClass then
+                       write_list(v, mentity.collect_intro_mproperties(model))
+               else if mentity isa MClassDef then
+                       write_list(v, mentity.mpropdefs)
+               else if mentity isa MProperty then
+                       write_list(v, mentity.mpropdefs)
+               else
+                       write_error(v, "No list found for name `{name}`")
+               end
+       end
+
+       # Write a mentity list in the output
+       fun write_list(v: MarkdownEmitter, mentities: Collection[MEntity]) do
+               v.add "<ul>"
+               for mentity in mentities do
+                       var mdoc = mentity.mdoc_or_fallback
+                       v.add "<li>"
+                       write_mentity_link(v, mentity)
+                       if mdoc != null then
+                               v.add " - "
+                               v.emit_text mdoc.synopsis
+                       end
+                       v.add "</li>"
+               end
+               v.add "</ul>"
+       end
+end
+
+redef class CodeCommand
+       redef fun render(v, token, model) do
+               if args.is_empty then
+                       write_error(v, "Expected one arg: the MEntity name")
+                       return
+               end
+               var name = args.first
+               var mentity = find_mentity(model, name)
+               if mentity == null then
+                       write_error(v, "No MEntity found for name `{name}`")
+                       return
+               end
+               if mentity isa MClass then mentity = mentity.intro
+               if mentity isa MProperty then mentity = mentity.intro
+               var source = render_source(mentity, v.decorator.as(NitwebDecorator).modelbuilder)
+               if source == null then
+                       write_error(v, "No source for MEntity `{name}`")
+                       return
+               end
+               v.add "<pre>"
+               v.add source
+               v.add "</pre>"
+       end
+
+       # Highlight `mentity` source code.
+       private fun render_source(mentity: MEntity, modelbuilder: ModelBuilder): nullable HTMLTag do
+               var node = modelbuilder.mentity2node(mentity)
+               if node == null then return null
+               var hl = new HighlightVisitor
+               hl.enter_visit node
+               return hl.html
+       end
+end
+
+redef class GraphCommand
+       redef fun render(v, token, model) do
+               if args.is_empty then
+                       write_error(v, "Expected one arg: the MEntity name")
+                       return
+               end
+               var name = args.first
+               var mentity = find_mentity(model, name)
+               if mentity == null then
+                       write_error(v, "No MEntity found for name `{name}`")
+                       return
+               end
+               var g = new InheritanceGraph(mentity, model)
+               v.add g.draw(3, 3).to_svg
+       end
+end
diff --git a/src/web/api_graph.nit b/src/web/api_graph.nit
new file mode 100644 (file)
index 0000000..67a32e2
--- /dev/null
@@ -0,0 +1,273 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Render various graph from a model.
+module api_graph
+
+import web_base
+import dot
+import uml
+
+# Group all api handlers in one router.
+class APIGraphRouter
+       super Router
+
+       # Model to pass to handlers.
+       var model: Model
+
+       # Mainmodule to pass to handlers.
+       var mainmodule: MModule
+
+       init do
+               use("/inheritance/:id", new APIInheritanceGraph(model, mainmodule))
+       end
+end
+
+# Render a hierarchy graph for `mentity` if any.
+class APIInheritanceGraph
+       super APIHandler
+
+       redef fun get(req, res) do
+               var pdepth = req.int_arg("pdepth")
+               var cdepth = req.int_arg("cdepth")
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then
+                       res.error 404
+                       return
+               end
+               var g = new InheritanceGraph(mentity, view)
+               res.send g.draw(pdepth, cdepth).to_svg
+       end
+end
+
+# Graph for mentity hierarchies
+#
+# Recursively build parents and children list from a `center`.
+class InheritanceGraph
+       super ModelVisitor
+
+       # MEntity at the center of this graph
+       var center: MEntity
+
+       # ModelView used to filter graph
+       var view: ModelView
+
+       # Graph generated
+       var graph: DotGraph is lazy do
+               var graph = new DotGraph("package_diagram", "digraph")
+
+               graph["compound"] = "true"
+               graph["rankdir"] = "BT"
+               graph["ranksep"] = 0.3
+               graph["nodesep"] = 0.3
+
+               graph.nodes_attrs["margin"] = 0.1
+               graph.nodes_attrs["width"] = 0
+               graph.nodes_attrs["height"] = 0
+               graph.nodes_attrs["fontsize"] = 10
+               graph.nodes_attrs["fontname"] = "helvetica"
+
+               graph.edges_attrs["dir"] = "none"
+               graph.edges_attrs["color"] = "gray"
+
+               return graph
+       end
+
+       # Build the graph
+       fun draw(parents_depth, children_depth: nullable Int): DotGraph do
+               draw_node center
+               draw_parents(center, parents_depth)
+               draw_children(center, children_depth)
+               return graph
+       end
+
+       private var nodes = new HashMap[MEntity, DotElement]
+       private var done_parents = new HashSet[MEntity]
+       private var done_children = new HashSet[MEntity]
+
+       # Recursively draw parents of mentity
+       fun draw_parents(mentity: MEntity, max_depth: nullable Int, current_depth: nullable Int) do
+               if done_parents.has(mentity) then return
+               done_parents.add mentity
+               current_depth = current_depth or else 0
+               if max_depth != null and current_depth >= max_depth then
+                       from_dotdotdot(mentity)
+                       return
+               end
+               var parents = mentity.collect_parents(view)
+               if parents.length > 10 then
+                       from_dotdotdot(mentity)
+                       return
+               end
+               for parent in parents do
+                       if parent isa MModule then
+                               var mgroup = parent.mgroup
+                               if mgroup != null and mgroup.default_mmodule == parent then parent = mgroup
+                       end
+                       if parent isa MGroup then
+                               if parent.mpackage.mgroups.first == parent then parent = parent.mpackage
+                       end
+                       draw_edge(mentity, parent)
+               end
+               for parent in parents do
+                       if parent isa MModule then
+                               var mgroup = parent.mgroup
+                               if mgroup != null and mgroup.default_mmodule == parent then parent = mgroup
+                       end
+                       if parent isa MGroup then
+                               if parent.mpackage.mgroups.first == parent then parent = parent.mpackage
+                       end
+                       draw_parents(parent, max_depth, current_depth + 1)
+               end
+       end
+
+       # Recursively draw children of mentity
+       fun draw_children(mentity: MEntity, max_depth: nullable Int, current_depth: nullable Int) do
+               if done_children.has(mentity) then return
+               done_children.add mentity
+               current_depth = current_depth or else 0
+               if max_depth != null and current_depth >= max_depth then
+                       to_dotdotdot(mentity)
+                       return
+               end
+               var children = mentity.collect_children(view)
+               if children.length > 10 then
+                       to_dotdotdot(mentity)
+                       return
+               end
+               for child in children do
+                       if child isa MGroup then
+                               if child.mpackage.mgroups.first == child then child = child.mpackage
+                       end
+                       draw_edge(child, mentity)
+               end
+               for child in children do
+                       if child isa MGroup then
+                               if child.mpackage.mgroups.first == child then child = child.mpackage
+                       end
+                       draw_children(child, max_depth, current_depth + 1)
+               end
+       end
+
+       # Draw a node from a `mentity`
+       fun draw_node(mentity: MEntity): DotElement do
+               if nodes.has_key(mentity) then return nodes[mentity]
+               var node: DotElement = mentity.to_dot_node
+               if mentity == center then node = highlight(node)
+               nodes[mentity] = node
+               graph.add node
+               return node
+       end
+
+       private var edges = new HashMap2[MEntity, MEntity, DotEdge]
+
+       # Draw a edges between two mentities
+       fun draw_edge(from, to: MEntity): DotEdge do
+               if edges.has(from, to) then return edges[from, to].as(not null)
+               if edges.has(to, from) then return edges[to, from].as(not null)
+               var nfrom = draw_node(from)
+               var nto = draw_node(to)
+               var edge = new DotEdge(nfrom, nto)
+               edges[from, to] = edge
+               graph.add edge
+               return edge
+       end
+
+       private var to_dots = new HashMap[MEntity, DotElement]
+
+       # Create a link from `mentity` to a `...` node
+       fun to_dotdotdot(mentity: MEntity): DotEdge do
+               var nto = draw_node(mentity)
+               var dots = to_dots.get_or_null(mentity)
+               if dots == null then
+                       dots = dotdotdot("{nto.id}...")
+                       to_dots[mentity] = dots
+               end
+               graph.add dots
+               var edge = new DotEdge(dots, nto)
+               graph.add edge
+               return edge
+       end
+
+       private var from_dots = new HashMap[MEntity, DotElement]
+
+       # Create a link from a `...` node to a `mentity`
+       fun from_dotdotdot(mentity: MEntity): DotEdge do
+               var nfrom = draw_node(mentity)
+               var dots = to_dots.get_or_null(mentity)
+               if dots == null then
+                       dots = dotdotdot("...{nfrom.id}")
+                       from_dots[mentity] = dots
+               end
+               graph.add dots
+               var edge = new DotEdge(dots, nfrom)
+               graph.add edge
+               return edge
+       end
+
+       # Change the border color of the node
+       fun highlight(dot: DotElement): DotElement do
+               dot["color"] = "#1E9431"
+               return dot
+       end
+
+       # Generate a `...` node
+       fun dotdotdot(id: String): DotNode do
+               var node = new DotNode(id)
+               node["label"] = "..."
+               node["shape"] = "none"
+               return node
+       end
+end
+
+redef class MEntity
+       private fun to_dot_node: DotNode do
+               var node = new DotNode(full_name)
+               node["URL"] = web_url
+               node["label"] = name
+               return node
+       end
+end
+
+redef class MPackage
+       redef fun to_dot_node do
+               var node = super
+               node["shape"] = "tab"
+               return node
+       end
+end
+
+redef class MGroup
+       redef fun to_dot_node do
+               var node = super
+               node["shape"] = "folder"
+               return node
+       end
+end
+
+redef class MModule
+       redef fun to_dot_node do
+               var node = super
+               node["shape"] = "note"
+               return node
+       end
+end
+
+redef class MClass
+       redef fun to_dot_node do
+               var node = super
+               node["shape"] = "box"
+               return node
+       end
+end
diff --git a/src/web/model_api.nit b/src/web/model_api.nit
new file mode 100644 (file)
index 0000000..6a7d0a5
--- /dev/null
@@ -0,0 +1,250 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module model_api
+
+import web_base
+import highlight
+import uml
+
+# List all mentities.
+#
+# MEntities can be filtered on their kind using the `k` parameter.
+# Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
+#
+# List size can be limited with the `n` parameter.
+#
+# Example: `GET /list?k=module?n=10`
+class APIList
+       super APIHandler
+
+       # List mentities depending on the `k` kind parameter.
+       fun list_mentities(req: HttpRequest): Array[MEntity] do
+               var k = req.string_arg("k")
+               var mentities = new Array[MEntity]
+               if k == "package" then
+                       for mentity in view.mpackages do mentities.add mentity
+               else if k == "group" then
+                       for mentity in view.mgroups do mentities.add mentity
+               else if k == "module" then
+                       for mentity in view.mmodules do mentities.add mentity
+               else if k == "class" then
+                       for mentity in view.mclasses do mentities.add mentity
+               else if k == "classdef" then
+                       for mentity in view.mclassdefs do mentities.add mentity
+               else if k == "property" then
+                       for mentity in view.mproperties do mentities.add mentity
+               else if k == "propdef" then
+                       for mentity in view.mpropdefs do mentities.add mentity
+               else
+                       for mentity in view.mentities do mentities.add mentity
+               end
+               return mentities
+       end
+
+       # Limit mentities depending on the `n` parameter.
+       fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
+               var n = req.int_arg("n")
+               if n != null then
+                       return mentities.sub(0, n)
+               end
+               return mentities
+       end
+
+       redef fun get(req, res) do
+               var mentities = list_mentities(req)
+               mentities = limit_mentities(req, mentities)
+               res.json new JsonArray.from(mentities)
+       end
+end
+
+# Search mentities from a query string.
+#
+# Example: `GET /search?q=Arr`
+class APISearch
+       super APIList
+
+       redef fun list_mentities(req) do
+               var q = req.string_arg("q")
+               var mentities = new Array[MEntity]
+               if q == null then return mentities
+               for mentity in view.mentities do
+                       if mentity.name.has_prefix(q) then mentities.add mentity
+               end
+               return mentities
+       end
+end
+
+# Return a random list of MEntities.
+#
+# Example: `GET /random?n=10&k=module`
+class APIRandom
+       super APIList
+
+       # Randomize mentities order.
+       fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
+               var res = mentities.to_a
+               res.shuffle
+               return res
+       end
+
+       redef fun get(req, res) do
+               var mentities = list_mentities(req)
+               mentities = limit_mentities(req, mentities)
+               mentities = randomize_mentities(req, mentities)
+               res.json new JsonArray.from(mentities)
+       end
+end
+
+# Return the JSON representation of a MEntity.
+#
+# Example: `GET /entity/core::Array`
+class APIEntity
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+               res.json mentity.api_json(self)
+       end
+end
+
+# List ancestors, parents, child and descendants of MEntity
+#
+# Example: `GET /entity/core::Array/inheritance`
+class APIEntityInheritance
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then
+                       res.error 404
+                       return
+               end
+               res.json mentity.hierarchy_poset(view)[mentity]
+       end
+end
+
+# Linearize super definitions of a MClassDef or a MPropDef if any.
+#
+# Example: `GET /entity/core::Array/linearization`
+class APIEntityLinearization
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then
+                       res.error 404
+                       return
+               end
+               var lin = mentity.collect_linearization(mainmodule)
+               if lin == null then
+                       res.error 404
+                       return
+               end
+               res.json new JsonArray.from(lin)
+       end
+end
+
+# List definitions of a MEntity.
+#
+# Example: `GET /defs/core::Array`
+class APIEntityDefs
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               var arr = new JsonArray
+               if mentity isa MModule then
+                       for mclassdef in mentity.mclassdefs do arr.add mclassdef
+               else if mentity isa MClass then
+                       for mclassdef in mentity.mclassdefs do arr.add mclassdef
+               else if mentity isa MClassDef then
+                       for mpropdef in mentity.mpropdefs do arr.add mpropdef
+               else if mentity isa MProperty then
+                       for mpropdef in mentity.mpropdefs do arr.add mpropdef
+               else
+                       res.error 404
+                       return
+               end
+               res.json arr
+       end
+end
+
+abstract class SVGHandler
+       super APIHandler
+
+       # Render a `dot` string as a svg image.
+       fun render_dot(dot: Text): String do
+               var proc = new ProcessDuplex("dot", "-Tsvg")
+               var svg = proc.write_and_read(dot)
+               proc.close
+               proc.wait
+               return svg
+       end
+end
+
+# Return a UML representation of MEntity.
+#
+# Example: `GET /entity/core::Array/uml`
+class APIEntityUML
+       super SVGHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               var dot
+               if mentity isa MClassDef then mentity = mentity.mclass
+               if mentity isa MClass then
+                       var uml = new UMLModel(view, mainmodule)
+                       dot = uml.generate_class_uml.write_to_string
+               else if mentity isa MModule then
+                       var uml = new UMLModel(view, mentity)
+                       dot = uml.generate_package_uml.write_to_string
+               else
+                       res.error 404
+                       return
+               end
+               res.send render_dot(dot)
+       end
+end
+
+# Return the source code of MEntity.
+#
+# Example: `GET /entity/core::Array/code`
+class APIEntityCode
+       super APIHandler
+
+       # Modelbuilder used to access sources.
+       var modelbuilder: ModelBuilder
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+               var source = render_source(mentity)
+               if source == null then
+                       res.error 404
+                       return
+               end
+               res.send source
+       end
+
+       # Highlight `mentity` source code.
+       private fun render_source(mentity: MEntity): nullable HTMLTag do
+               var node = modelbuilder.mentity2node(mentity)
+               if node == null then return null
+               var hl = new HighlightVisitor
+               hl.enter_visit node
+               return hl.html
+       end
+end
index 6f581f4..e82a0f5 100644 (file)
@@ -15,4 +15,7 @@
 # Components required to build a web server about the nit model.
 module web
 
-import web_actions
+import model_api
+import api_catalog
+import api_graph
+import api_docdown
diff --git a/src/web/web_actions.nit b/src/web/web_actions.nit
deleted file mode 100644 (file)
index f4c4584..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Nitcorn actions used by the nitweb server.
-module web_actions
-
-import web_views
-import uml
-
-# Display the tree of all loaded mentities.
-class TreeAction
-       super ModelAction
-
-       redef fun answer(request, url) do
-               var model = init_model_view(request)
-               var view = new HtmlHomePage(model.to_tree)
-               return render_view(view)
-       end
-end
-
-# Display the list of mentities matching `namespace`.
-class SearchAction
-       super ModelAction
-
-       # TODO handle more than full namespaces.
-       redef fun answer(request, url) do
-               var namespace = request.param("namespace")
-               if namespace == null or namespace.is_empty then
-                       return render_error(400, "Missing :namespace.")
-               end
-               var model = init_model_view(request)
-               var mentities = model.mentities_by_namespace(namespace)
-               if request.is_json_asked then
-                       var json = new JsonArray
-                       for mentity in mentities do
-                               json.add mentity.to_json
-                       end
-                       return render_json(json)
-               end
-               var view = new HtmlResultPage(namespace, mentities)
-               return render_view(view)
-       end
-end
-
-# Display a MEntity source code.
-class CodeAction
-       super ModelAction
-
-       # Modelbuilder used to access sources.
-       var modelbuilder: ModelBuilder
-
-       # TODO handle more than full namespaces.
-       redef fun answer(request, url) do
-               var namespace = request.param("namespace")
-               if namespace == null or namespace.is_empty then
-                       return render_error(400, "Missing :namespace.")
-               end
-               var model = init_model_view(request)
-               var mentities = model.mentities_by_namespace(namespace)
-               if mentities.is_empty then
-                       return render_error(404, "No mentity matching this namespace.")
-               end
-               var view = new HtmlSourcePage(modelbuilder, mentities.first)
-               return render_view(view)
-       end
-end
-
-# Display the doc of a MEntity.
-class DocAction
-       super ModelAction
-
-       # Modelbuilder used to access sources.
-       var modelbuilder: ModelBuilder
-
-       # TODO handle more than full namespaces.
-       redef fun answer(request, url) do
-               var namespace = request.param("namespace")
-               if namespace == null or namespace.is_empty then
-                       return render_error(400, "Missing :namespace.")
-               end
-               var model = init_model_view(request)
-               var mentities = model.mentities_by_namespace(namespace)
-               if mentities.is_empty then
-                       return render_error(404, "No mentity matching this namespace.")
-               end
-               var view = new HtmlDocPage(modelbuilder, mentities.first)
-               return render_view(view)
-       end
-end
-
-# Return an UML diagram for `namespace`.
-class UMLDiagramAction
-       super ModelAction
-
-       # Mainmodule used for hierarchy flattening.
-       var mainmodule: MModule
-
-       redef fun answer(request, url) do
-               var namespace = request.param("namespace")
-               if namespace == null or namespace.is_empty then
-                       return render_error(400, "Missing :namespace.")
-               end
-               var model = init_model_view(request)
-               var mentities = model.mentities_by_namespace(namespace)
-               if mentities.is_empty then
-                       return render_error(404, "No mentity matching this namespace.")
-               end
-               var mentity = mentities.first
-               if mentity isa MClassDef then mentity = mentity.mclass
-
-               var dot
-               if mentity isa MClass then
-                       var uml = new UMLModel(model, mainmodule)
-                       dot = uml.generate_class_uml.write_to_string
-               else if mentity isa MModule then
-                       var uml = new UMLModel(model, mentity)
-                       dot = uml.generate_package_uml.write_to_string
-               else
-                       return render_error(404, "No diagram matching this namespace.")
-               end
-               var view = new HtmlDotPage(dot, mentity.html_name)
-               return render_view(view)
-       end
-end
-
-# Return a random list of MEntities.
-class RandomAction
-       super ModelAction
-
-       # TODO handle more than full namespaces.
-       redef fun answer(request, url) do
-               var n = request.int_arg("n") or else 10
-               var k = request.string_arg("k") or else "modules"
-               var model = init_model_view(request)
-               var mentities: Array[MEntity]
-               if k == "modules" then
-                       mentities = model.mmodules.to_a
-               else if k == "classdefs" then
-                       mentities = model.mclassdefs.to_a
-               else
-                       mentities = model.mpropdefs.to_a
-               end
-               mentities.shuffle
-               mentities = mentities.sub(0, n)
-               if request.is_json_asked then
-                       var json = new JsonArray
-                       for mentity in mentities do
-                               json.add mentity.to_json
-                       end
-                       return render_json(json)
-               end
-               var view = new HtmlResultPage("random", mentities)
-               return render_view(view)
-       end
-end
-
-redef class MEntity
-
-       # Return `self` as a JsonObject.
-       fun to_json: JsonObject do
-               var obj = new JsonObject
-               obj["name"] = html_name
-               obj["namespace"] = html_raw_namespace
-               var mdoc = self.mdoc
-               if mdoc != null then
-                       obj["synopsis"] = mdoc.content.first.html_escape
-                       obj["mdoc"] = mdoc.content.join("\n").html_escape
-               end
-               return obj
-       end
-end
index ecf639e..20d0b05 100644 (file)
 module web_base
 
 import model::model_views
-import nitcorn
-import json
+import model::model_json
+import doc_down
+import popcorn
 
-# Nitcorn server runned by `nitweb`.
-#
-# Usage:
-#
-# ~~~nitish
-# var srv = new NitServer("localhost", 3000)
-# srv.routes.add new Route("/", new MyAction)
-# src.listen
-# ~~~
-class NitServer
+# Specific nitcorn Action that uses a Model
+class ModelHandler
+       super Handler
 
-       # Host to bind.
-       var host: String
+       # Model to use.
+       var model: Model
 
-       # Port to use.
-       var port: Int
+       # MModule used to flatten model.
+       var mainmodule: MModule
 
-       # Routes knwon by the server.
-       var routes = new Array[Route]
+       # Find the MEntity ` with `full_name`.
+       fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
+               if full_name == null then return null
+               return model.mentity_by_full_name(full_name.from_percent_encoding)
+       end
 
-       # Start listen on `host:port`.
-       fun listen do
-               var iface = "{host}:{port}"
-               print "Launching server on http://{iface}/"
+       # Init the model view from the `req` uri parameters.
+       fun init_model_view(req: HttpRequest): ModelView do
+               var view = new ModelView(model)
+               var show_private = req.bool_arg("private") or else false
+               if not show_private then view.min_visibility = protected_visibility
 
-               var vh = new VirtualHost(iface)
-               for route in routes do vh.routes.add route
+               view.include_fictive = req.bool_arg("fictive") or else false
+               view.include_empty_doc = req.bool_arg("empty-doc") or else true
+               view.include_test_suite = req.bool_arg("test-suite") or else false
+               view.include_attribute = req.bool_arg("attributes") or else true
 
-               var fac = new HttpFactory.and_libevent
-               fac.config.virtual_hosts.add vh
-               fac.run
+               return view
        end
 end
 
-# Specific nitcorn Action for nitweb.
-class NitAction
-       super Action
-
-       # Link to the NitServer that runs this action.
-       var srv: NitServer
-
-       # Build a custom http response for errors.
-       fun render_error(code: Int, message: String): HttpResponse do
-               var response = new HttpResponse(code)
-               var tpl = new Template
-               tpl.add "<h1>Error {code}</h1>"
-               tpl.add "<pre><code>{message.html_escape}</code></pre>"
-               response.body = tpl.write_to_string
-               return response
+# Specific handler for nitweb API.
+abstract class APIHandler
+       super ModelHandler
+
+       # The JSON API does not filter anything by default.
+       #
+       # So we can cache the model view.
+       var view: ModelView is lazy do
+               var view = new ModelView(model)
+               view.min_visibility = private_visibility
+               view.include_fictive = true
+               view.include_empty_doc = true
+               view.include_attribute = true
+               view.include_test_suite = true
+               return view
        end
 
-       # Render a view as a HttpResponse 200.
-       fun render_view(view: NitView): HttpResponse do
-               var response = new HttpResponse(200)
-               response.body = view.render(srv).write_to_string
-               return response
+       # Try to load the mentity from uri with `/:id`.
+       #
+       # Send 400 if `:id` is null.
+       # Send 404 if no entity is found.
+       # Return null in both cases.
+       fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
+               var id = req.param("id")
+               if id == null then
+                       res.error 400
+                       return null
+               end
+               var mentity = find_mentity(view, id)
+               if mentity == null then
+                       res.error 404
+               end
+               return mentity
        end
+end
+
+redef class MEntity
+
+       # URL to `self` within the web interface.
+       fun web_url: String do return "/doc/" / full_name
 
-       # Return a HttpResponse containing `json`.
-       fun render_json(json: Jsonable): HttpResponse do
-               var response = new HttpResponse(200)
-               response.body = json.to_json
-               return response
+       # URL to `self` within the JSON api.
+       fun api_url: String do return "/api/entity/" / full_name
+
+       redef fun json do
+               var obj = super
+               obj["web_url"] = web_url
+               obj["api_url"] = api_url
+               return obj
        end
+
+       # Get the full json repesentation of `self` with MEntityRefs resolved.
+       fun api_json(handler: ModelHandler): JsonObject do return json
 end
 
-# Specific nitcorn Action that uses a Model
-class ModelAction
-       super NitAction
+redef class MEntityRef
+       redef fun json do
+               var obj = super
+               obj["web_url"] = mentity.web_url
+               obj["api_url"] = mentity.api_url
+               obj["name"] = mentity.name
+               obj["mdoc"] = mentity.mdoc_or_fallback
+               obj["visibility"] = mentity.visibility
+               obj["location"] = mentity.location
+               var modifiers = new JsonArray
+               for modifier in mentity.collect_modifiers do
+                       modifiers.add modifier
+               end
+               obj["modifiers"] = modifiers
+               var mentity = self.mentity
+               if mentity isa MMethod then
+                       obj["msignature"] = mentity.intro.msignature
+               else if mentity isa MMethodDef then
+                       obj["msignature"] = mentity.msignature
+               else if mentity isa MVirtualTypeProp then
+                       obj["bound"] = to_mentity_ref(mentity.intro.bound)
+               else if mentity isa MVirtualTypeDef then
+                       obj["bound"] = to_mentity_ref(mentity.bound)
+               end
+               return obj
+       end
+end
 
-       # Model to use.
-       var model: Model
+redef class MDoc
+
+       # Add doc down processing
+       redef fun json do
+               var obj = super
+               obj["synopsis"] = synopsis
+               obj["documentation"] = documentation
+               obj["comment"] = comment
+               obj["html_synopsis"] = html_synopsis.write_to_string
+               obj["html_documentation"] = html_documentation.write_to_string
+               obj["html_comment"] = html_comment.write_to_string
+               return obj
+       end
+end
 
-       # Init the model view from the `req` uri parameters.
-       fun init_model_view(req: HttpRequest): ModelView do
-               var view = new ModelView(model)
+redef class MModule
+       redef fun api_json(handler) do
+               var obj = super
+               obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
+               obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
+               obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
+               return obj
+       end
+end
 
-               var show_private = req.bool_arg("private") or else false
-               if not show_private then view.min_visibility = protected_visibility
+redef class MClass
+       redef fun api_json(handler) do
+               var obj = super
+               obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
+               obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
+               obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
+               obj["parents"] = to_mentity_refs(collect_parents(private_view))
+               return obj
+       end
+end
 
-               view.include_fictive = req.bool_arg("fictive") or else false
-               view.include_empty_doc = req.bool_arg("empty-doc") or else true
-               view.include_test_suite = req.bool_arg("test-suite") or else false
-               view.include_attribute = req.bool_arg("attributes") or else true
+redef class MClassDef
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(mclass.intro)
+               obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
+               return obj
+       end
 
-               return view
+       redef fun api_json(handler) do
+               var obj = super
+               obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
+               obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
+               return obj
        end
 end
 
-# A NitView is rendered by an action.
-interface NitView
-       # Renders this view and returns something that can be written to a HTTP response.
-       fun render(srv: NitServer): Writable is abstract
+redef class MProperty
+       redef fun json do
+               var obj = super
+               obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
+               obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
+               return obj
+       end
 end
 
-redef class HttpRequest
-       # Does the client asked for a json formatted response?
-       #
-       # Checks the URL get parameter `?json=true`.
-       fun is_json_asked: Bool do return bool_arg("json") or else false
+redef class MPropDef
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(mproperty.intro)
+               obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
+               obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
+               obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
+               obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
+               return obj
+       end
+end
+
+redef class MClassType
+       redef var web_url = mclass.web_url is lazy
+end
+
+redef class MNullableType
+       redef var web_url = mtype.web_url is lazy
+end
+
+redef class MParameterType
+       redef var web_url = mclass.web_url is lazy
+end
+
+redef class MVirtualType
+       redef var web_url = mproperty.web_url is lazy
+end
+
+redef class POSetElement[E]
+       super Jsonable
+
+       # Return JSON representation of `self`.
+       fun json: JsonObject do
+               assert self isa POSetElement[MEntity]
+               var obj = new JsonObject
+               obj["greaters"] = to_mentity_refs(greaters)
+               obj["direct_greaters"] = to_mentity_refs(direct_greaters)
+               obj["direct_smallers"] = to_mentity_refs(direct_smallers)
+               obj["smallers"] = to_mentity_refs(smallers)
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
 end
diff --git a/src/web/web_views.nit b/src/web/web_views.nit
deleted file mode 100644 (file)
index 950c839..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Nitcorn actions used by the nitweb server.
-module web_views
-
-import web_base
-import model_html
-import highlight
-import markdown
-
-# Html homepage for the `nitweb` server.
-class HtmlHomePage
-       super NitView
-
-       # Loaded model to display.
-       var tree: MEntityTree
-
-       redef fun render(srv) do
-               var tpl = new Template
-               tpl.add new Header(1, "Loaded model")
-               tpl.add tree.html_list
-               return tpl
-       end
-end
-
-# Display a search results list.
-class HtmlResultPage
-       super NitView
-
-       # Initial query.
-       var query: String
-
-       # Result set
-       var results: Array[MEntity]
-
-       redef fun render(srv) do
-               var tpl = new Template
-               tpl.add new Header(1, "Results for {query}")
-               if results.is_empty then
-                       tpl.add "<p>No result for {query}.<p>"
-                       return tpl
-               end
-               var list = new UnorderedList
-               for mentity in results do
-                       var link = mentity.html_link
-                       link.text = mentity.html_raw_namespace
-                       list.add_li new ListItem(link)
-               end
-               tpl.add list
-               return tpl
-       end
-end
-
-# Display the source for each mentities
-class HtmlSourcePage
-       super NitView
-
-       # Modelbuilder used to access sources.
-       var modelbuilder: ModelBuilder
-
-       # MEntity to display
-       var mentity: MEntity
-
-       # HiglightVisitor used to hilight the source code
-       var hl = new HighlightVisitor
-
-       redef fun render(srv) do
-               var tpl = new Template
-               tpl.add new Header(1, "Source Code")
-               tpl.add render_source
-               return tpl
-       end
-
-       private fun render_source: Template do
-               var node = modelbuilder.mentity2node(mentity)
-               var tpl = new Template
-               tpl.add new Header(3, "Source code")
-               if node == null then
-                       tpl.add "<p>Source for {mentity.html_name} not found.<p>"
-               else
-                       hl.enter_visit node
-                       tpl.add "<pre><code>"
-                       tpl.add hl.html
-                       tpl.add "</code></pre>"
-               end
-               return tpl
-       end
-end
-
-# Display the mdoc of the mentities.
-class HtmlDocPage
-       super HtmlSourcePage
-
-       redef fun render(srv) do
-               var tpl = new Template
-               tpl.add new Header(1, mentity.html_name)
-               tpl.add "<p>"
-               tpl.add mentity.html_declaration
-               tpl.add "</p>"
-               tpl.add "<br>"
-               var mdoc = mentity.mdoc
-               if mdoc != null then
-                       tpl.add mdoc.content.join("\n").md_to_html
-               end
-               tpl.add "<br>"
-               tpl.add render_source
-               return tpl
-       end
-end
-
-# Display the source for each mentities
-class HtmlDotPage
-       super NitView
-
-       # Dot to process.
-       var dot: Text
-
-       # Page title.
-       var title: String
-
-       redef fun render(srv) do
-               var tpl = new Template
-               tpl.add new Header(1, title)
-               tpl.add render_dot
-               return tpl
-       end
-
-       private fun render_dot: String do
-               var proc = new ProcessDuplex("dot", "-Tsvg", "-Tcmapx")
-               var svg = proc.write_and_read(dot)
-               proc.close
-               proc.wait
-               return svg
-       end
-end
index b70b71d..6a693ff 100644 (file)
@@ -6,3 +6,4 @@ neo
 mpi
 emscripten
 ui_test
+readline
diff --git a/tests/base_gen_infinite2.nit b/tests/base_gen_infinite2.nit
new file mode 100644 (file)
index 0000000..05c4fb6
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import end
+
+interface Object
+       type SELF: Object
+       fun output_class_name is intern
+end
+
+class A[E]
+       new do return new B[E]
+end
+
+class B[E]
+       super A[E]
+
+       fun foo: A[E] do return new A[SELF]
+end
+
+(new B[Object]).foo.output_class_name
diff --git a/tests/base_is_optional.nit b/tests/base_is_optional.nit
new file mode 100644 (file)
index 0000000..ea13e28
--- /dev/null
@@ -0,0 +1,68 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core::kernel
+
+fun foo(i: Int): Int
+do
+       'f'.output
+       i.output
+       return i
+end
+
+class A
+       # needed on the new
+       var i: Int
+
+       # initialized by the allocation
+       var j: Int = foo(2)
+
+       # optional in the new, default value evaluated if `null` is given
+       var k: Int = foo(3) is optional
+
+       # the `init` will initialize it
+       var l: Int is noautoinit
+       init do l = foo(4)
+
+       # initialized if needed on the first `read`
+       var m: Int = foo(5) is lazy
+
+       fun set
+       do
+               i = 10
+               j = 20
+               k = 30
+               l = 40
+               m = 50
+       end
+
+       fun test
+       do
+               #alt1#set
+               i.output
+               j.output
+               k.output
+               l.output
+               m.output
+               '\n'.output
+       end
+end
+
+var a
+a = new A(foo(100))
+a.test
+a = new A(foo(100), null)
+a.test
+a = new A(foo(100), foo(300))
+a.test
diff --git a/tests/base_name_conflict.nit b/tests/base_name_conflict.nit
new file mode 100644 (file)
index 0000000..245ff36
--- /dev/null
@@ -0,0 +1,40 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import module_1
+import module_1b
+
+redef class module_1::A
+       fun foo do print(100)
+end
+
+redef class module_1b::A
+       fun foob do print(110)
+end
+
+#alt1#redef class A
+#alt1#end
+
+#alt2#redef class fail::A
+#alt2#end
+
+var a = new module_1::A
+a.a1
+a.foo
+#alt3#a.foob
+
+var b = new module_1b::A
+b.a1
+b.foob
+#alt3#b.foo
diff --git a/tests/base_new_factory.nit b/tests/base_new_factory.nit
new file mode 100644 (file)
index 0000000..d8b704c
--- /dev/null
@@ -0,0 +1,71 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core::kernel
+
+interface A
+       new do return new B(5)
+end
+
+class B
+       super A
+       var i: Int
+       redef fun output do
+               'B'.output
+               i.output
+       end
+end
+
+interface G[E: Object]
+       new(a: E) do return new H[E](a)
+       fun dup:G[E] is abstract
+end
+
+class H[F: Object]
+       super G[F]
+       var o: F
+
+       redef fun output do
+               'H'.output
+               o.output
+       end
+
+       redef fun dup do return new G[F](self.o)
+end
+
+var b = new B(1)
+b.output
+var a = new A
+a.output
+
+var ha = new H[A](a)
+ha.output
+var hb = new H[B](b)
+hb.output
+
+var ga = new G[A](a)
+ga.output
+var gb = new G[B](b)
+gb.output
+
+ga.dup.output
+gb.dup.output
+
+var gga = new G[G[A]](ga)
+gga.output
+var ggb = new G[G[B]](gb)
+ggb.output
+
+gga.dup.output
+ggb.dup.output
diff --git a/tests/base_redef.nit b/tests/base_redef.nit
new file mode 100644 (file)
index 0000000..d42bfec
--- /dev/null
@@ -0,0 +1,65 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core::kernel
+
+class A
+       fun f do 1.output
+       fun f2: Int do return 2
+       fun f2=(i:Int) do i.output
+       fun f3=(i:Int) do i.output
+       var v = 4
+       type T: A
+       fun t(t: T): T do return t
+end
+
+class B
+       super A#alt1#
+       redef fun f do 10.output
+       redef var f2 = 20
+       var f3 = 30 is redef writable
+       redef var v = 40
+       redef type T: B
+end
+
+class C
+       super B#alt2#
+       redef fun f do 100.output
+       redef var f2 = 200
+       redef var f3 = 300
+       redef var v = 400
+       redef type T: C
+
+end
+
+var a = new A
+a.f
+a.f2 = -2
+a.f2.output
+a.f3 = -3
+a.t(a).f
+
+a = new B
+a.f
+a.f2 = -2
+a.f2.output
+a.f3 = -3
+a.t(a).f
+
+a = new C
+a.f
+a.f2 = -2
+a.f2.output
+a.f3 = -3
+a.t(a).f
index 88b001a..be157b3 100644 (file)
@@ -23,4 +23,4 @@ while i < n do
        s = "Je dis «{s}» et redis «{s}» et trois fois de plus : «{s}{s}{s}».\n"
        i = i + 1
 end
-print s.bytelen
+print s.byte_length
index f76b13a..0f34f86 100644 (file)
@@ -23,4 +23,4 @@ while i < n do
        s = ["Je dis «", s, "» et redis «", s, "» et trois fois de plus : «", s, s, s, "».\n"].plain_to_s
        i = i + 1
 end
-print(s.bytelen)
+print(s.byte_length)
diff --git a/tests/error_names.nit b/tests/error_names.nit
new file mode 100644 (file)
index 0000000..a2eb360
--- /dev/null
@@ -0,0 +1,37 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+intrude import names::n0
+intrude import names::n1
+intrude import names::n2
+intrude import names::n3
+intrude import names1
+
+var a
+
+a = new A1
+a = new names::A1
+a = new names::n0::A1
+a = new names::n1::A1
+a = new names::n2::A1
+a = new names1::A1
+a = new names1::names1::A1
+
+a = new P1
+a = new names::P1
+a = new names::n0::P1
+a = new names::n1::P1
+a = new names::n2::P1
+a = new names1::P1
+a = new names1::names1::P1
index 4d9517f..76ec29d 100644 (file)
@@ -1,7 +1,5 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-#
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
@@ -14,5 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-var i = new Canard
+var i
+i = new Fail
+i = new Boolean
+i = new ListNode
+i = new POSet
 i.output
diff --git a/tests/error_unk_class2.nit b/tests/error_unk_class2.nit
new file mode 100644 (file)
index 0000000..c452bda
--- /dev/null
@@ -0,0 +1,16 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import poset
+import error_unk_class
diff --git a/tests/get_mclasses.args b/tests/get_mclasses.args
new file mode 100644 (file)
index 0000000..2d339c6
--- /dev/null
@@ -0,0 +1 @@
+names A P P1 names::n0::ZZ
index 661ac30..70e648a 100755 (executable)
@@ -1,18 +1,19 @@
 #!/bin/sh
-printf "%s\n" "$@" \
+ls -1 -- "%s\n" "$@" \
        ../src/nit*.nit \
        ../src/test_*.nit \
+       ../src/examples/*.nit \
        ../examples/*.nit \
        ../examples/*/*.nit \
        ../examples/shoot/src/shoot_logic.nit \
        ../examples/*/src/*_android.nit \
        ../examples/*/src/*_linux.nit \
        ../examples/*/src/*_null.nit \
-       ../examples/nitcorn/src/*.nit \
        ../lib/*/examples/*.nit \
        ../lib/*/examples/*/*.nit \
        ../contrib/friendz/src/solver_cmd.nit \
        ../contrib/neo_doxygen/src/tests/neo_doxygen_*.nit \
        ../contrib/pep8analysis/src/pep8analysis.nit \
        ../contrib/nitiwiki/src/nitiwiki.nit \
-       *.nit
+       *.nit \
+       | grep -v ../lib/popcorn/examples/
index 77f46db..43ff426 100644 (file)
 import module_0
 
 class A # class 1
-   fun a1 
-   do 
-          print(1)
-          print(1)
-   end
-   fun a12 
-   do
-          print(12)
-          print(1)
-   end
-   fun a13
-   do
-          print(13) 
-          print(1)
-   end
-   fun a123 
-   do
-          print(123)
-          print(1)
-   end
+       fun a1
+       do
+               print(1)
+               print(1)
+       end
+       fun a12
+       do
+               print(12)
+               print(1)
+       end
+       fun a13
+       do
+               print(13)
+               print(1)
+       end
+       fun a123
+       do
+               print(123)
+               print(1)
+       end
 end
 
 class B # class 2
        super A
-   redef fun a12 
-   do
-          print(12)
-          print(2)
-   end
-   redef fun a123
-   do
-          print(123)
-          print(2)
-   end
-   fun all2
-   do
-          a1
-          a12
-          a13
-          a123
-   end
-   fun all25
-   do
-          print(250)
-          print(2)
-          a1
-          a12
-          a13
-          a123
-   end
+       redef fun a12
+       do
+               print(12)
+               print(2)
+       end
+       redef fun a123
+       do
+               print(123)
+               print(2)
+       end
+       fun all2
+       do
+               a1
+               a12
+               a13
+               a123
+       end
+       fun all25
+       do
+               print(250)
+               print(2)
+               a1
+               a12
+               a13
+               a123
+       end
 end
 
 var a = new A
diff --git a/tests/module_1b.nit b/tests/module_1b.nit
new file mode 100644 (file)
index 0000000..9bb9788
--- /dev/null
@@ -0,0 +1,39 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import module_0
+
+# Same code than `module_1`. Is used to cause name conflicts
+class A # class 1
+       fun a1
+       do
+               print(1)
+               print(10)
+       end
+       fun a12
+       do
+               print(12)
+               print(10)
+       end
+       fun a13
+       do
+               print(13)
+               print(10)
+       end
+       fun a123
+       do
+               print(123)
+               print(10)
+       end
+end
diff --git a/tests/names/README.md b/tests/names/README.md
new file mode 100644 (file)
index 0000000..9ddf943
--- /dev/null
@@ -0,0 +1 @@
+Group of modules used to test various full_name configurations and conflicts.
diff --git a/tests/names/n0.nit b/tests/names/n0.nit
new file mode 100644 (file)
index 0000000..02008bb
--- /dev/null
@@ -0,0 +1,67 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Root module
+module n0
+
+import end
+
+# Root interface
+interface Object
+end
+
+# A public class
+class A
+       # A public method in a public class
+       fun a do end
+
+       # A private method in a public class
+       private fun z do end
+end
+
+# A public subclass in the same module
+class A0
+       super A
+       super P
+
+       # Redefinition it the same module of a public method
+       redef fun a do end
+
+       # Redefinition it the same module of a private method
+       redef fun z do end
+
+       # Redefinition it the same module of a private method
+       redef fun p do end
+end
+
+# A private class
+private class P
+       # A private method in a private class
+       fun p do end
+end
+
+# A private subclass introduced in the same module
+private class P0
+       super A
+       super P
+
+       # Redefinition it the same module of a public method
+       redef fun a do end
+
+       # Redefinition it the same module of a private method
+       redef fun z do end
+
+       # Redefinition it the same module of a private method
+       redef fun p do end
+end
diff --git a/tests/names/n1.nit b/tests/names/n1.nit
new file mode 100644 (file)
index 0000000..7cfb359
--- /dev/null
@@ -0,0 +1,90 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Second module
+module n1
+
+intrude import n0
+
+# A refinement of a class
+redef class A
+       # A refinement in the same class
+       redef fun a do end
+
+       # A refinement in the same class
+       redef fun z do end
+
+       # A public method introduced in a refinement
+       fun b do end
+end
+
+# A refinement of a subclass
+redef class A0
+       # A refinement+redefinition
+       redef fun a do end
+
+       # A refinement+redefinition
+       redef fun z do end
+
+       # A refinement+redefinition
+       redef fun p do end
+end
+
+# A subclass introduced in a submodule
+class A1
+       super A
+       super P
+
+       # A redefinition in a subclass from a different module
+       redef fun a do end
+
+       # A redefinition in a subclass from a different module
+       redef fun z do end
+
+       # A redefinition in a subclass from a different module
+       redef fun p do end
+end
+
+# A refinement of a class
+redef class P
+       # A refinement in the same class
+       redef fun p do end
+end
+
+# A refinement of a subclass
+redef class P0
+       # A refinement+redefinition
+       redef fun a do end
+
+       # A refinement+redefinition
+       redef fun z do end
+
+       # A refinement+redefinition
+       redef fun p do end
+end
+
+# A private subclass introduced in a different module
+private class P1
+       super A
+       super P
+
+       # A redefinition in a subclass from a different module
+       redef fun a do end
+
+       # A redefinition in a subclass from a different module
+       redef fun z do end
+
+       # A redefinition in a subclass from a different module
+       redef fun p do end
+end
diff --git a/tests/names/n2.nit b/tests/names/n2.nit
new file mode 100644 (file)
index 0000000..96486e2
--- /dev/null
@@ -0,0 +1,33 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A alternative second module, used to make name conflicts
+module n2
+
+import n0
+
+# A refinement of a class
+redef class A
+       # Name conflict? A second public method
+       fun b do end
+
+       # Name conflict? A second private method
+       fun z do end
+end
+
+# Name conflict? A second private class
+private class P
+       # Name conflict? A private method in an homonym class.
+       fun p do end
+end
diff --git a/tests/names/n3.nit b/tests/names/n3.nit
new file mode 100644 (file)
index 0000000..85310c8
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The bottom module
+module n3
+
+intrude import n1
+import n2
+
+# a refinement of a subclass in a submodule
+redef class A1
+       # a refinement (3 distinct modules)
+       redef fun a do end
+       # a refinement (3 distinct modules)
+       redef fun p do end
+end
+
+# a refinement of a subclass in a submodule
+redef class P1
+       # a refinement (3 distinct modules)
+       redef fun a do end
+       # a refinement (3 distinct modules)
+       redef fun p do end
+end
diff --git a/tests/names1.nit b/tests/names1.nit
new file mode 100644 (file)
index 0000000..f141a9b
--- /dev/null
@@ -0,0 +1,90 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# An alternative second module in a distinct package
+module names1
+
+intrude import names::n0
+
+# A refinement of a class
+redef class A
+       # A refinement in the same class
+       redef fun a do end
+
+       # A refinement in the same class
+       redef fun z do end
+
+       # A public method introduced in a refinement
+       fun b do end
+end
+
+# A refinement of a subclass
+redef class A0
+       # A refinement+redefinition
+       redef fun a do end
+
+       # A refinement+redefinition
+       redef fun z do end
+
+       # A refinement+redefinition
+       redef fun p do end
+end
+
+# A subclass introduced in a submodule
+class A1
+       super A
+       super P
+
+       # A redefinition in a subclass from a different module
+       redef fun a do end
+
+       # A redefinition in a subclass from a different module
+       redef fun z do end
+
+       # A redefinition in a subclass from a different module
+       redef fun p do end
+end
+
+# A refinement of a class
+redef class P
+       # A refinement in the same class
+       redef fun p do end
+end
+
+# A refinement of a subclass
+redef class P0
+       # A refinement+redefinition
+       redef fun a do end
+
+       # A refinement+redefinition
+       redef fun z do end
+
+       # A refinement+redefinition
+       redef fun p do end
+end
+
+# A private subclass introduced in a different module
+private class P1
+       super A
+       super P
+
+       # A redefinition in a subclass from a different module
+       redef fun a do end
+
+       # A redefinition in a subclass from a different module
+       redef fun z do end
+
+       # A redefinition in a subclass from a different module
+       redef fun p do end
+end
index cbaafd9..ba1a276 100644 (file)
@@ -5,3 +5,4 @@ base_simple3.nit
 -e 'print "hello world"'
 -n -e 'print line' test_prog/README.md test_prog/test_prog.nit
 test_ffi_c_interpreter.nit
+-
diff --git a/tests/nit_args8.inputs b/tests/nit_args8.inputs
new file mode 100644 (file)
index 0000000..c878eeb
--- /dev/null
@@ -0,0 +1 @@
+print "Hello!"
diff --git a/tests/nitcatalog.args b/tests/nitcatalog.args
new file mode 100644 (file)
index 0000000..9dedd23
--- /dev/null
@@ -0,0 +1 @@
+test_prog --no-git -d "$WRITE" ; cat "$WRITE/p/test_prog.html"
index dae2c37..d93ce27 100644 (file)
@@ -4,6 +4,7 @@ nit_args3
 nit_args4
 nit_args5
 nit_args6
+nit_args8
 nitvm_args1
 nitvm_args3
 nitc_args1
@@ -37,3 +38,7 @@ ui_test
 test_text_stat
 nitsaf_args
 test_ffi_c_lots_of_refs
+test_rubix_cube
+test_rubix_visual
+test_csv
+repeating_key_xor_solve
index 568f76a..5f5a31a 100644 (file)
@@ -3,4 +3,6 @@ base_simple3.nit project1
 -t -r base_simple3.nit project1
 -s base_simple3.nit project1
 -M base_simple3.nit base_simple_import.nit
--td project1/module3.nit
+-td project1/module3.nit project1/subdir/subdir2/subdir3/submodule.nit
+test_prog --no-color
+test_prog/game/excluded.nit test_prog/game/excluded_dir/more.nit -t --no-color
index 396ac26..1ef81d1 100644 (file)
@@ -1,3 +1,4 @@
 --no-color -W test_advice_repeated_types.nit
 --no-color base_simple3.nit; echo $?
 --no-color error_mod_unk.nit; echo $?
+--no-color test_prog
index 390709a..f0bc3ee 100644 (file)
@@ -1,7 +1,7 @@
-test_nitunit.nit --no-color -o $WRITE
+test_nitunit.nit test_test_nitunit.nit --no-color -o $WRITE
 test_nitunit.nit --gen-suite --only-show
 test_nitunit.nit --gen-suite --only-show --private
-test_nitunit2.nit -o $WRITE
+test_nitunit2.nit --no-color -o $WRITE
 test_doc2.nit --no-color -o $WRITE
 test_nitunit3 --no-color -o $WRITE
 test_nitunit_md.md --no-color -o $WRITE
index 6497642..b8ae2ec 100644 (file)
@@ -4,6 +4,7 @@ nit_args3
 nit_args4
 nit_args5
 nit_args6
+nit_args8
 nitvm_args1
 nitvm_args3
 nitc_args1
@@ -37,3 +38,7 @@ ui_test
 test_text_stat
 nitsaf.args
 test_ffi_c_lots_of_refs
+test_rubix_visual
+test_rubix_cube
+test_csv
+repeating_key_xor_solve
diff --git a/tests/project1/subdir/subdir2/subdir3/submodule.nit b/tests/project1/subdir/subdir2/subdir3/submodule.nit
new file mode 100644 (file)
index 0000000..3355283
--- /dev/null
@@ -0,0 +1 @@
+import module0
diff --git a/tests/project1/uselessdir/uselessdir2/uselessfile b/tests/project1/uselessdir/uselessdir2/uselessfile
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/repeating_key_xor_solve.args b/tests/repeating_key_xor_solve.args
new file mode 100644 (file)
index 0000000..2f6c9ff
--- /dev/null
@@ -0,0 +1 @@
+../lib/crapto/examples/repeating_key_xor_cipher.txt
diff --git a/tests/rope_substring_test.nit b/tests/rope_substring_test.nit
new file mode 100644 (file)
index 0000000..2e780b4
--- /dev/null
@@ -0,0 +1,28 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core
+intrude import core::text::ropes
+
+var lft = "Strings are either R"
+var rgt = "opes or FlatStrings!"
+
+var rp = new Concat(lft, rgt)
+
+print rp.length
+print rp.left.length
+print rp.right.length
+
+print rp[8]
+print rp.substring(12, 10)
index 35e4ebe..423bc95 100644 (file)
@@ -1,4 +1,4 @@
-FlatString
+ASCIIFlatString
 Int
 Test
 Test
index 2952126..fbe83ce 100644 (file)
@@ -1,6 +1,7 @@
 base_classid.nit:47,2--8: Warning: expression is already a `A`.
 base_classid.nit:48,2--9: Warning: expression is already a `A`.
 base_classid.nit:49,2--8: Warning: expression is already a `A` since it is a `B`.
+base_classid.nit:52,2--8: Warning: expression is already a `B`.
 true
 true
 true
diff --git a/tests/sav/base_gen_infinite2.res b/tests/sav/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..e38f38e
--- /dev/null
@@ -0,0 +1 @@
+B[B[Object]]
index d856b64..f7e3b6d 100644 (file)
@@ -1,2 +1,2 @@
-alt/base_init_basic_alt3.nit:47,7: Error: cannot generate automatic init for class F. Conflict in the order in inherited initializers base_init_basic_alt3#D#autoinit(c=, b=) and base_init_basic_alt3#E#autoinit(c=, e=). Use `autoinit` to order initializers. eg `autoinit c=, e=, b=`
-alt/base_init_basic_alt3.nit:54,7: Error: cannot generate automatic init for class G. Conflict in the order in inherited initializers base_init_basic_alt3#D#autoinit(c=, b=) and base_init_basic_alt3#E#autoinit(c=, e=). Use `autoinit` to order initializers. eg `autoinit c=, e=, b=, g=`
+alt/base_init_basic_alt3.nit:47,7: Error: cannot generate automatic init for class F. Conflict in the order in inherited initializers base_init_basic_alt3$D$autoinit(c=, b=) and base_init_basic_alt3$E$autoinit(c=, e=). Use `autoinit` to order initializers. eg `autoinit c=, e=, b=`
+alt/base_init_basic_alt3.nit:54,7: Error: cannot generate automatic init for class G. Conflict in the order in inherited initializers base_init_basic_alt3$D$autoinit(c=, b=) and base_init_basic_alt3$E$autoinit(c=, e=). Use `autoinit` to order initializers. eg `autoinit c=, e=, b=, g=`
diff --git a/tests/sav/base_is_optional.res b/tests/sav/base_is_optional.res
new file mode 100644 (file)
index 0000000..3f89579
--- /dev/null
@@ -0,0 +1,33 @@
+f2
+f100
+f3
+f4
+100
+2
+3
+4
+f5
+5
+
+f2
+f100
+f3
+f4
+100
+2
+3
+4
+f5
+5
+
+f2
+f100
+f300
+f4
+100
+2
+300
+4
+f5
+5
+
diff --git a/tests/sav/base_is_optional_alt1.res b/tests/sav/base_is_optional_alt1.res
new file mode 100644 (file)
index 0000000..12d1806
--- /dev/null
@@ -0,0 +1,30 @@
+f2
+f100
+f3
+f4
+10
+20
+30
+40
+50
+
+f2
+f100
+f3
+f4
+10
+20
+30
+40
+50
+
+f2
+f100
+f300
+f4
+10
+20
+30
+40
+50
+
index 0c4b877..0a31c73 100644 (file)
@@ -1,12 +1,20 @@
 base_isa3.nit:59,8--14: Warning: expression is already a `A` since it is a `B`.
+base_isa3.nit:60,8--14: Warning: expression is already a `B`.
 base_isa3.nit:64,8--14: Warning: expression is already a `A` since it is a `C`.
+base_isa3.nit:66,8--14: Warning: expression is already a `C`.
 base_isa3.nit:69,8--14: Warning: expression is already a `A` since it is a `D`.
+base_isa3.nit:71,8--14: Warning: expression is already a `C` since it is a `D`.
+base_isa3.nit:72,8--14: Warning: expression is already a `D`.
 base_isa3.nit:73,8--14: Warning: expression is already a `E` since it is a `D`.
-base_isa3.nit:74,8--14: Warning: expression is already a `E`.
-base_isa3.nit:75,8--14: Warning: expression is already a `E`.
-base_isa3.nit:76,8--14: Warning: expression is already a `E`.
+base_isa3.nit:74,8--14: Warning: expression is already a `E` since it is a `D`.
+base_isa3.nit:75,8--14: Warning: expression is already a `E` since it is a `D`.
+base_isa3.nit:76,8--14: Warning: expression is already a `E` since it is a `D`.
 base_isa3.nit:79,8--14: Warning: expression is already a `A` since it is a `E`.
+base_isa3.nit:81,8--14: Warning: expression is already a `E`.
 base_isa3.nit:84,8--14: Warning: expression is already a `A` since it is a `F`.
+base_isa3.nit:85,8--14: Warning: expression is already a `E` since it is a `F`.
+base_isa3.nit:86,8--14: Warning: expression is already a `F`.
 base_isa3.nit:87,8--14: Warning: expression is already a `G` since it is a `F`.
 base_isa3.nit:91,8--14: Warning: expression is already a `A` since it is a `G`.
+base_isa3.nit:93,8--14: Warning: expression is already a `G`.
 true
index 989bc61..31017d3 100644 (file)
@@ -1,4 +1,7 @@
 base_isa_gen1.nit:62,8--14: Warning: expression is already a `A` since it is a `F`.
+base_isa_gen1.nit:63,8--14: Warning: expression is already a `C` since it is a `F`.
 base_isa_gen1.nit:65,8--30: Warning: expression is already a `D[Object, Object]` since it is a `G[Object]`.
 base_isa_gen1.nit:66,8--30: Warning: expression is already a `D[Object, Object]` since it is a `E[F]`.
+base_isa_gen1.nit:67,8--25: Warning: expression is already a `D[A, Object]` since it is a `E[F]`.
+base_isa_gen1.nit:69,8--25: Warning: expression is already a `D[F, Object]` since it is a `E[F]`.
 true
index 6dde482..1442eb3 100644 (file)
@@ -1,5 +1,10 @@
 base_isa_gen2.nit:39,8--22: Warning: expression is already a `A[Object]`.
 base_isa_gen2.nit:41,8--22: Warning: expression is already a `A[Object]` since it is a `B[Object]`.
+base_isa_gen2.nit:42,8--22: Warning: expression is already a `B[Object]`.
 base_isa_gen2.nit:44,8--22: Warning: expression is already a `A[Object]` since it is a `C[Object]`.
+base_isa_gen2.nit:46,8--22: Warning: expression is already a `C[Object]`.
 base_isa_gen2.nit:48,8--22: Warning: expression is already a `A[Object]` since it is a `D[Object, Object]`.
+base_isa_gen2.nit:49,8--22: Warning: expression is already a `B[Object]` since it is a `D[Object, Object]`.
+base_isa_gen2.nit:50,8--22: Warning: expression is already a `C[Object]` since it is a `D[Object, Object]`.
+base_isa_gen2.nit:51,8--30: Warning: expression is already a `D[Object, Object]`.
 true
index 8725a4a..04c6196 100644 (file)
@@ -1,4 +1,5 @@
 base_isa_gen4.nit:34,8--15: Warning: expression is already a `A` since it is a `B[Canard]`.
+base_isa_gen4.nit:35,8--23: Warning: expression is already a `B[Canard]`.
 base_isa_gen4.nit:36,8--23: Warning: expression is already a `B[Animal]` since it is a `B[Canard]`.
 base_isa_gen4.nit:40,8--26: Warning: expression is already a `B[B[Canard]]`.
 base_isa_gen4.nit:42,8--26: Warning: expression is already a `B[B[Animal]]` since it is a `B[B[Canard]]`.
index c391776..92d4cb2 100644 (file)
@@ -1,4 +1,5 @@
 base_isa_gen5.nit:39,8--15: Warning: expression is already a `A` since it is a `B[Canard]`.
+base_isa_gen5.nit:40,8--23: Warning: expression is already a `B[Canard]`.
 base_isa_gen5.nit:41,8--23: Warning: expression is already a `B[Animal]` since it is a `B[Canard]`.
 base_isa_gen5.nit:46,8--26: Warning: expression is already a `B[B[Canard]]`.
 base_isa_gen5.nit:48,8--26: Warning: expression is already a `B[B[Animal]]` since it is a `B[B[Canard]]`.
index 2b455c4..9553da4 100644 (file)
@@ -1,6 +1,7 @@
 base_isa_nullable1.nit:39,8--15: Warning: expression is already a `A` since it is a `B[Integer]`.
+base_isa_nullable1.nit:40,8--24: Warning: expression is already a `B[Integer]`.
 base_isa_nullable1.nit:41,8--25: Warning: expression is already a `B[Discrete]` since it is a `B[Integer]`.
 base_isa_nullable1.nit:46,8--27: Warning: expression is already a `B[B[Integer]]`.
 base_isa_nullable1.nit:48,8--28: Warning: expression is already a `B[B[Discrete]]` since it is a `B[B[Integer]]`.
-base_isa_nullable1.nit:50,8--34: Warning: expression is already a `B[nullable Discrete]` since it is a `B[Discrete]`.
+base_isa_nullable1.nit:50,8--34: Warning: expression is already a `B[nullable Discrete]` since it is a `B[Integer]`.
 true
index 947e8c1..2440af9 100644 (file)
@@ -1,5 +1,5 @@
 base_isa_nullable2.nit:27,8--23: Warning: expression is already a `nullable A` since it is a `A`.
 base_isa_nullable2.nit:29,8--31: Warning: expression is already a `nullable B[Object]` since it is a `B[Object]`.
-base_isa_nullable2.nit:30,8--40: Warning: expression is already a `nullable B[nullable Object]` since it is a `nullable B[Object]`.
+base_isa_nullable2.nit:30,8--40: Warning: expression is already a `nullable B[nullable Object]` since it is a `B[Object]`.
 base_isa_nullable2.nit:33,8--31: Warning: expression is already a `C[nullable Object]`.
 true
diff --git a/tests/sav/base_name_conflict.res b/tests/sav/base_name_conflict.res
new file mode 100644 (file)
index 0000000..7099acd
--- /dev/null
@@ -0,0 +1,6 @@
+1
+1
+100
+1
+10
+110
diff --git a/tests/sav/base_name_conflict_alt1.res b/tests/sav/base_name_conflict_alt1.res
new file mode 100644 (file)
index 0000000..65c4078
--- /dev/null
@@ -0,0 +1 @@
+alt/base_name_conflict_alt1.nit:26,13: Error: ambiguous class name `A` in module `base_name_conflict_alt1`. Conflicts are between `module_1::A` and `module_1b::A`.
diff --git a/tests/sav/base_name_conflict_alt2.res b/tests/sav/base_name_conflict_alt2.res
new file mode 100644 (file)
index 0000000..a8c9d92
--- /dev/null
@@ -0,0 +1 @@
+alt/base_name_conflict_alt2.nit:29,13--19: Error: class `fail::A` not found in module `base_name_conflict_alt2`. Did you mean `module_1::A` or `module_1b::A`?
diff --git a/tests/sav/base_name_conflict_alt3.res b/tests/sav/base_name_conflict_alt3.res
new file mode 100644 (file)
index 0000000..d091756
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_name_conflict_alt3.nit:35,3--6: Error: method `foob` does not exists in `A`.
+alt/base_name_conflict_alt3.nit:40,3--5: Error: method `foo` does not exists in `A`.
diff --git a/tests/sav/base_new_factory.res b/tests/sav/base_new_factory.res
new file mode 100644 (file)
index 0000000..238446a
--- /dev/null
@@ -0,0 +1,12 @@
+B1
+B5
+HB5
+HB1
+HB5
+HB1
+HB5
+HB1
+HHB5
+HHB1
+HHB5
+HHB1
diff --git a/tests/sav/base_redef.res b/tests/sav/base_redef.res
new file mode 100644 (file)
index 0000000..3b792d6
--- /dev/null
@@ -0,0 +1,11 @@
+1
+-2
+2
+-3
+1
+10
+-2
+10
+100
+-2
+100
diff --git a/tests/sav/base_redef_alt1.res b/tests/sav/base_redef_alt1.res
new file mode 100644 (file)
index 0000000..50e360c
--- /dev/null
@@ -0,0 +1,5 @@
+alt/base_redef_alt1.nit:29,12: Error: no property `B::f` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt1.nit:30,12--13: Error: no property `B::f2` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt1.nit:31,6--7: Error: no property `B::f3=` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt1.nit:32,12: Error: no property `B::v` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt1.nit:33,2--16: Error: no property `B::T` is inherited. Remove the `redef` keyword to define a new property.
diff --git a/tests/sav/base_redef_alt2.res b/tests/sav/base_redef_alt2.res
new file mode 100644 (file)
index 0000000..1c263c4
--- /dev/null
@@ -0,0 +1,5 @@
+alt/base_redef_alt2.nit:38,12: Error: no property `C::f` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt2.nit:39,12--13: Error: no property `C::f2` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt2.nit:40,12--13: Error: no property `C::f3` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt2.nit:41,12: Error: no property `C::v` is inherited. Remove the `redef` keyword to define a new property.
+alt/base_redef_alt2.nit:42,2--16: Error: no property `C::T` is inherited. Remove the `redef` keyword to define a new property.
index a65e559..e60aeeb 100644 (file)
@@ -1 +1 @@
-base_upcast2.nit:26,10: Error: class `T` not found in module `base_upcast2`.
+base_upcast2.nit:26,10: Error: class `T` not found in module `base_upcast2`. Did you mean `base_upcast2::A`,`base_upcast2::B`,`base_upcast2::C` or `base_upcast2::X`?
index 25d108e..5df6c3c 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt1.nit:26,10: Error: class `T` not found in module `base_upcast2_alt1`.
+alt/base_upcast2_alt1.nit:26,10: Error: class `T` not found in module `base_upcast2_alt1`. Did you mean `base_upcast2_alt1::A`,`base_upcast2_alt1::B`,`base_upcast2_alt1::C` or `base_upcast2_alt1::X`?
index 2794534..45e0bf8 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt10.nit:26,10: Error: class `T` not found in module `base_upcast2_alt10`.
+alt/base_upcast2_alt10.nit:26,10: Error: class `T` not found in module `base_upcast2_alt10`. Did you mean `base_upcast2_alt10::A`,`base_upcast2_alt10::B`,`base_upcast2_alt10::C` or `base_upcast2_alt10::X`?
index edeb86c..bc43d41 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt2.nit:26,10: Error: class `T` not found in module `base_upcast2_alt2`.
+alt/base_upcast2_alt2.nit:26,10: Error: class `T` not found in module `base_upcast2_alt2`. Did you mean `base_upcast2_alt2::A`,`base_upcast2_alt2::B`,`base_upcast2_alt2::C` or `base_upcast2_alt2::X`?
index 840d8d6..9742ca4 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt3.nit:26,10: Error: class `T` not found in module `base_upcast2_alt3`.
+alt/base_upcast2_alt3.nit:26,10: Error: class `T` not found in module `base_upcast2_alt3`. Did you mean `base_upcast2_alt3::A`,`base_upcast2_alt3::B`,`base_upcast2_alt3::C` or `base_upcast2_alt3::X`?
index 42ab2e2..c7c4e4f 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt4.nit:26,10: Error: class `T` not found in module `base_upcast2_alt4`.
+alt/base_upcast2_alt4.nit:26,10: Error: class `T` not found in module `base_upcast2_alt4`. Did you mean `base_upcast2_alt4::A`,`base_upcast2_alt4::B`,`base_upcast2_alt4::C` or `base_upcast2_alt4::X`?
index 3ebcdaa..02bf543 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt5.nit:26,10: Error: class `T` not found in module `base_upcast2_alt5`.
+alt/base_upcast2_alt5.nit:26,10: Error: class `T` not found in module `base_upcast2_alt5`. Did you mean `base_upcast2_alt5::A`,`base_upcast2_alt5::B`,`base_upcast2_alt5::C` or `base_upcast2_alt5::X`?
index 60451b6..69f8dd4 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt6.nit:26,10: Error: class `T` not found in module `base_upcast2_alt6`.
+alt/base_upcast2_alt6.nit:26,10: Error: class `T` not found in module `base_upcast2_alt6`. Did you mean `base_upcast2_alt6::A`,`base_upcast2_alt6::B`,`base_upcast2_alt6::C` or `base_upcast2_alt6::X`?
index dab8c00..edad22f 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt7.nit:26,10: Error: class `T` not found in module `base_upcast2_alt7`.
+alt/base_upcast2_alt7.nit:26,10: Error: class `T` not found in module `base_upcast2_alt7`. Did you mean `base_upcast2_alt7::A`,`base_upcast2_alt7::B`,`base_upcast2_alt7::C` or `base_upcast2_alt7::X`?
index 5cfee90..6e4f911 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt8.nit:26,10: Error: class `T` not found in module `base_upcast2_alt8`.
+alt/base_upcast2_alt8.nit:26,10: Error: class `T` not found in module `base_upcast2_alt8`. Did you mean `base_upcast2_alt8::A`,`base_upcast2_alt8::B`,`base_upcast2_alt8::C` or `base_upcast2_alt8::X`?
index 248577e..87d95b0 100644 (file)
@@ -1 +1 @@
-alt/base_upcast2_alt9.nit:26,10: Error: class `T` not found in module `base_upcast2_alt9`.
+alt/base_upcast2_alt9.nit:26,10: Error: class `T` not found in module `base_upcast2_alt9`. Did you mean `base_upcast2_alt9::A`,`base_upcast2_alt9::B`,`base_upcast2_alt9::C` or `base_upcast2_alt9::X`?
index 05e617f..6bc9d3f 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt1.nit:51,1--3: Error: expected at least 1 argument(s) for `foo(a: Char...)`; got 0. See introduction at `base_vararg_alt1::Sys::foo`.
+alt/base_vararg_alt1.nit:51,1--3: Error: expected at least 1 argument(s) for `foo(a: Char...)`; got 0. See introduction at `base_vararg_alt1::base_vararg_alt1::Sys::foo`.
index d953dc6..2550be0 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt2.nit:54,1--3: Error: expected at least 2 argument(s) for `bar(b: Char, a: Char...)`; got 0. See introduction at `base_vararg_alt2::Sys::bar`.
+alt/base_vararg_alt2.nit:54,1--3: Error: expected at least 2 argument(s) for `bar(b: Char, a: Char...)`; got 0. See introduction at `base_vararg_alt2::base_vararg_alt2::Sys::bar`.
index 7f5e21c..e7bc17f 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt3.nit:55,1--3: Error: expected at least 2 argument(s) for `bar(b: Char, a: Char...)`; got 1. See introduction at `base_vararg_alt3::Sys::bar`.
+alt/base_vararg_alt3.nit:55,1--3: Error: expected at least 2 argument(s) for `bar(b: Char, a: Char...)`; got 1. See introduction at `base_vararg_alt3::base_vararg_alt3::Sys::bar`.
index bcd893b..82fd929 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt4.nit:58,1--3: Error: expected at least 2 argument(s) for `baz(a: Char..., b: Char)`; got 0. See introduction at `base_vararg_alt4::Sys::baz`.
+alt/base_vararg_alt4.nit:58,1--3: Error: expected at least 2 argument(s) for `baz(a: Char..., b: Char)`; got 0. See introduction at `base_vararg_alt4::base_vararg_alt4::Sys::baz`.
index 011c15e..dd90331 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt5.nit:59,1--3: Error: expected at least 2 argument(s) for `baz(a: Char..., b: Char)`; got 1. See introduction at `base_vararg_alt5::Sys::baz`.
+alt/base_vararg_alt5.nit:59,1--3: Error: expected at least 2 argument(s) for `baz(a: Char..., b: Char)`; got 1. See introduction at `base_vararg_alt5::base_vararg_alt5::Sys::baz`.
index cbe6263..ece6a22 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt6.nit:62,1--6: Error: expected at least 3 argument(s) for `foobar(b: Char, a: Char..., c: Char)`; got 0. See introduction at `base_vararg_alt6::Sys::foobar`.
+alt/base_vararg_alt6.nit:62,1--6: Error: expected at least 3 argument(s) for `foobar(b: Char, a: Char..., c: Char)`; got 0. See introduction at `base_vararg_alt6::base_vararg_alt6::Sys::foobar`.
index da46a05..f2baff5 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt7.nit:63,1--6: Error: expected at least 3 argument(s) for `foobar(b: Char, a: Char..., c: Char)`; got 1. See introduction at `base_vararg_alt7::Sys::foobar`.
+alt/base_vararg_alt7.nit:63,1--6: Error: expected at least 3 argument(s) for `foobar(b: Char, a: Char..., c: Char)`; got 1. See introduction at `base_vararg_alt7::base_vararg_alt7::Sys::foobar`.
index d6e8a87..d3d6f38 100644 (file)
@@ -1 +1 @@
-alt/base_vararg_alt8.nit:64,1--6: Error: expected at least 3 argument(s) for `foobar(b: Char, a: Char..., c: Char)`; got 2. See introduction at `base_vararg_alt8::Sys::foobar`.
+alt/base_vararg_alt8.nit:64,1--6: Error: expected at least 3 argument(s) for `foobar(b: Char, a: Char..., c: Char)`; got 2. See introduction at `base_vararg_alt8::base_vararg_alt8::Sys::foobar`.
index bf0d1f3..68281d2 100644 (file)
@@ -1 +1 @@
-base_gen_f.nit:19,14: Error: class `E` not found in module `base_gen_f`.
+base_gen_f.nit:19,14: Error: class `E` not found in module `base_gen_f`. Did you mean `base_gen_f::G`?
diff --git a/tests/sav/error_names.res b/tests/sav/error_names.res
new file mode 100644 (file)
index 0000000..da816d4
--- /dev/null
@@ -0,0 +1,6 @@
+error_names.nit:23,9--10: Error: ambiguous class name `A1` in module `error_names`. Conflicts are between `names::A1` and `names1::A1`.
+error_names.nit:25,9--21: Error: class `names::n0::A1` not found in module `error_names`. Did you mean `names::n0::P`,`names::n0::P0` or `names::n1::P1`?
+error_names.nit:27,9--21: Error: class `names::n2::A1` not found in module `error_names`. Did you mean `names::n1::P1` or `names::n2::P`?
+error_names.nit:31,9--10: Error: ambiguous class name `P1` in module `error_names`. Conflicts are between `names::n1::P1` and `names1::names1::P1`.
+error_names.nit:33,9--21: Error: class `names::n0::P1` not found in module `error_names`. Did you mean `names::n0::P`,`names::n0::P0` or `names::n1::P1`?
+error_names.nit:35,9--21: Error: class `names::n2::P1` not found in module `error_names`. Did you mean `names::n1::P1` or `names::n2::P`?
index e07af9a..d45e94d 100644 (file)
@@ -1,4 +1,4 @@
-alt/error_prop_loc_alt1.nit:38,9--12: Warning: conflicting property definitions for property `toto` in `D`: error_prop_loc_alt1#B#toto error_prop_loc_alt1#C#toto
+alt/error_prop_loc_alt1.nit:38,9--12: Warning: conflicting property definitions for property `toto` in `D`: error_prop_loc_alt1$B$toto error_prop_loc_alt1$C$toto
 1
 2
 3
index fdd119e..4ea4483 100644 (file)
@@ -1 +1 @@
-error_redef_class.nit:17,13--16: Redef Error: no imported class `Fail` to refine.
+error_redef_class.nit:17,13--16: Error: class `Fail` not found in module `error_redef_class`.
index 9794efd..82da2a7 100644 (file)
@@ -1,5 +1,5 @@
 error_type_not_ok5.nit:23,8--11: Error: class `Fail` not found in module `error_type_not_ok5`.
-error_type_not_ok5.nit:25,9--21: Error: class `Fail` not found in module `error_type_not_ok5`.
+error_type_not_ok5.nit:25,18--21: Error: class `Fail` not found in module `error_type_not_ok5`.
 error_type_not_ok5.nit:28,14--17: Error: class `Fail` not found in module `error_type_not_ok5`.
 error_type_not_ok5.nit:28,7--17: Type Error: expected `Char`, got `Bool`.
 error_type_not_ok5.nit:29,11--14: Error: class `Fail` not found in module `error_type_not_ok5`.
index ab9e44c..c9d481f 100644 (file)
@@ -1,2 +1,2 @@
 alt/error_type_unk_alt2.nit:9,8--11: Error: class `Fail` not found in module `error_type_unk_alt2`.
-alt/error_type_unk_alt2.nit:11,8--14: Error: class `Fail` not found in module `error_type_unk_alt2`.
+alt/error_type_unk_alt2.nit:11,8--11: Error: class `Fail` not found in module `error_type_unk_alt2`.
index 7ae3800..451d363 100644 (file)
@@ -1 +1,4 @@
-error_unk_class.nit:17,13--18: Error: class `Canard` not found in module `error_unk_class`.
+error_unk_class.nit:16,9--12: Error: class `Fail` not found in module `error_unk_class`.
+error_unk_class.nit:17,9--15: Error: class `Boolean` not found in module `error_unk_class`. Did you mean `core::Bool`?
+error_unk_class.nit:18,9--16: Error: class `core::list::ListNode` not visible in module `error_unk_class`.
+error_unk_class.nit:19,9--13: Error: class `POSet` not found in module `error_unk_class`. Did you mean `core::Set`?
diff --git a/tests/sav/error_unk_class2.res b/tests/sav/error_unk_class2.res
new file mode 100644 (file)
index 0000000..a44ef6e
--- /dev/null
@@ -0,0 +1,4 @@
+error_unk_class.nit:16,9--12: Error: class `Fail` not found in module `error_unk_class`.
+error_unk_class.nit:17,9--15: Error: class `Boolean` not found in module `error_unk_class`. Did you mean `core::Bool`?
+error_unk_class.nit:18,9--16: Error: class `core::list::ListNode` not visible in module `error_unk_class`.
+error_unk_class.nit:19,9--13: Error: class `POSet` not found in module `error_unk_class`. Maybe import `poset::poset`?
index bf0d1f3..68281d2 100644 (file)
@@ -1 +1 @@
-base_gen_f.nit:19,14: Error: class `E` not found in module `base_gen_f`.
+base_gen_f.nit:19,14: Error: class `E` not found in module `base_gen_f`. Did you mean `base_gen_f::G`?
diff --git a/tests/sav/get_mclasses.res b/tests/sav/get_mclasses.res
new file mode 100644 (file)
index 0000000..0dde232
--- /dev/null
@@ -0,0 +1,2 @@
+Usage: get_mclasses file qualified_class_name...
+Use --help for help
diff --git a/tests/sav/get_mclasses_args1.res b/tests/sav/get_mclasses_args1.res
new file mode 100644 (file)
index 0000000..649f723
--- /dev/null
@@ -0,0 +1,11 @@
+search: A
+ * names::A
+search: P
+ * names::n0::P
+ * names::n2::P
+search: P1
+ * names::n1::P1
+search: names::n0::ZZ
+ Did you mean?
+ * names::n0::P
+ * names::n0::P0
diff --git a/tests/sav/module_1b.res b/tests/sav/module_1b.res
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
index bfacdb8..7b38a25 100644 (file)
@@ -6,6 +6,10 @@ This program creates a fake model that can be used to test tools like:
 * `nitmetrics`
 * `nitx`
 * or others `modelbuilder`.
+
+An image:
+
+![Tinks3D](../../contrib/tinks/doc/tinks3d.png)
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tests/sav/nit_args8.res b/tests/sav/nit_args8.res
new file mode 100644 (file)
index 0000000..10ddd6d
--- /dev/null
@@ -0,0 +1 @@
+Hello!
diff --git a/tests/sav/nitcatalog_args1.res b/tests/sav/nitcatalog_args1.res
new file mode 100644 (file)
index 0000000..f97fee4
--- /dev/null
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html>
+<head>
+       <meta charset="utf-8">
+       <link rel="stylesheet" media="all" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
+       <link rel="stylesheet" media="all" href="../style.css">
+<title>test_prog</title></head>
+<body>
+<div class='container-fluid'>
+ <div class='row'>
+  <nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>
+   <div class='container-fluid'>
+    <div class='navbar-header'>
+     <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='#topmenu-collapse'>
+      <span class='sr-only'>Toggle menu</span>
+      <span class='icon-bar'></span>
+      <span class='icon-bar'></span>
+      <span class='icon-bar'></span>
+     </button>
+     <span class='navbar-brand'><a href="http://nitlanguage.org/">Nitlanguage.org</a></span>
+    </div>
+    <div class='collapse navbar-collapse' id='topmenu-collapse'>
+     <ul class='nav navbar-nav'>
+      <li><a href="../index.html">Catalog</a></li>
+     </ul>
+    </div>
+   </div>
+  </nav>
+ </div>
+<div class="content">
+<h1 class="package-name">test_prog</h1>
+<div class="nitdoc"><p class="synopsys">Test program for model tools.</p><p>This program creates a fake model that can be used to test tools like:</p>
+<ul>
+<li><code class="nitcode"><span class="nitcode"><span class="line"><span class="nc_i">nitdoc</span></span></span></code></li>
+<li><code class="nitcode"><span class="nitcode"><span class="line"><span class="nc_i">nitmetrics</span></span></span></code></li>
+<li><code class="nitcode"><span class="nitcode"><span class="line"><span class="nc_i">nitx</span></span></span></code></li>
+<li>or others <code class="nitcode"><span class="nitcode"><span class="line"><span class="nc_i">modelbuilder</span></span></span></code>.</li>
+</ul>
+<p>An image:</p>
+<p><img src="../res/c94d8a73ccfe143ebde7599e88f5f5ce.png" alt="Tinks3D"/></p>
+</div><h2>Content</h2><ul>
+<li><strong>test_prog</strong>: <span class="synopsys nitdoc">Test program for model tools.</span> (test_prog)<ul>
+<li><strong>game</strong>: <span class="synopsys nitdoc">Gaming group</span> (test_prog/game)<ul>
+<li><strong>game</strong>: <span class="synopsys nitdoc">A game abstraction for RPG.</span> (test_prog/game/game.nit)</li>
+</ul>
+</li>
+<li><strong>platform</strong>: <span class="synopsys nitdoc">Fictive Crappy Platform.</span> (test_prog/platform)<ul>
+<li><strong>platform</strong>: <span class="synopsys nitdoc">Declares base types allowed on the platform.</span> (test_prog/platform/platform.nit)</li>
+</ul>
+</li>
+<li><strong>rpg</strong>: <span class="synopsys nitdoc">Role Playing Game group</span> (test_prog/rpg)<ul>
+<li><strong>careers</strong>: <span class="synopsys nitdoc">Careers of the game.</span> (test_prog/rpg/careers.nit)</li>
+<li><strong>character</strong>: <span class="synopsys nitdoc">Characters are playable entity in the world.</span> (test_prog/rpg/character.nit)</li>
+<li><strong>combat</strong>: <span class="synopsys nitdoc">COmbat interactions between characters.</span> (test_prog/rpg/combat.nit)</li>
+<li><strong>races</strong>: <span class="synopsys nitdoc">Races of the game.</span> (test_prog/rpg/races.nit)</li>
+<li><strong>rpg</strong>: <span class="synopsys nitdoc">A worlg RPG abstraction.</span> (test_prog/rpg/rpg.nit)</li>
+</ul>
+</li>
+<li><strong>test_prog</strong>: <span class="synopsys nitdoc">A test program with a fake model to check model tools.</span> (test_prog/test_prog.nit)</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="sidebar">
+<ul class="box">
+</ul>
+<ul class="box">
+<li><a href="http:&#47;&#47;nitlanguage.org">http:&#47;&#47;nitlanguage.org</a></li>
+<li><a href="http:&#47;&#47;www.example.com&#47;~jdoe"><img src="https://secure.gravatar.com/avatar/694ea0904ceaf766c6738166ed89bafb?size=20&amp;default=retro">&nbsp;John Doe</a></li><li><a href="http://opensource.org/licenses/Apache-2.0">Apache-2.0</a> license</li>
+</ul>
+<h3>Source Code</h3>
+<ul class="box">
+<li><a href="https:&#47;&#47;github.com&#47;nitlang&#47;nit&#47;tree&#47;master&#47;tests&#47;test_prog">https:&#47;&#47;github.com&#47;nitlang&#47;nit&#47;tree&#47;master&#47;tests&#47;test_prog</a></li>
+<li><tt>https:&#47;&#47;github.com&#47;nitlang&#47;nit.git</tt></li>
+</ul>
+<h3>Quality</h3>
+<ul class="box">
+<li>28 warnings (63/kloc)</li>
+<li>95% documented</li>
+</ul>
+<h3>Tags</h3>
+<a href="../index.html#tag_test">test</a>, <a href="../index.html#tag_game">game</a><h3>Requirements</h3>
+none<h3>Clients</h3>
+none<h3>Contributors</h3>
+<ul class="box"><li><a href="http:&#47;&#47;www.example.com&#47;~jdoe"><img src="https://secure.gravatar.com/avatar/694ea0904ceaf766c6738166ed89bafb?size=20&amp;default=retro">&nbsp;John Doe</a></li><li><img src="https://secure.gravatar.com/avatar/db3f2909768694ad2bb6409b44627182?size=20&amp;default=retro">&nbsp;Riri</li><li>Fifi (http:&#47;&#47;www.example.com&#47;~fifi)</li><li>Loulou</li></ul><h3>Stats</h3>
+<ul class="box">
+<li>8 modules</li>
+<li>22 classes</li>
+<li>68 methods</li>
+<li>439 lines of code</li>
+</ul>
+</div>
+</div> <!-- container-fluid -->
+<script src='https://code.jquery.com/jquery-latest.min.js'></script>
+<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js'></script>
+<script src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table-all.min.js'></script>
+
+</body>
+</html>
+index.html
+p/
+people.html
+res/
+style.css
+table.html
diff --git a/tests/sav/nitce/base_gen_infinite2.res b/tests/sav/nitce/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..223b783
--- /dev/null
@@ -0,0 +1 @@
+B
index 4000211..33a285a 100644 (file)
@@ -1,4 +1,7 @@
 base_isa_gen1.nit:62,8--14: Warning: expression is already a `A` since it is a `F`.
+base_isa_gen1.nit:63,8--14: Warning: expression is already a `C` since it is a `F`.
 base_isa_gen1.nit:65,8--30: Warning: expression is already a `D[Object, Object]` since it is a `G[Object]`.
 base_isa_gen1.nit:66,8--30: Warning: expression is already a `D[Object, Object]` since it is a `E[F]`.
+base_isa_gen1.nit:67,8--25: Warning: expression is already a `D[A, Object]` since it is a `E[F]`.
+base_isa_gen1.nit:69,8--25: Warning: expression is already a `D[F, Object]` since it is a `E[F]`.
 Runtime error: Assert failed (base_isa_gen1.nit:68)
index 203c263..0e06154 100644 (file)
@@ -1,4 +1,5 @@
 base_isa_gen4.nit:34,8--15: Warning: expression is already a `A` since it is a `B[Canard]`.
+base_isa_gen4.nit:35,8--23: Warning: expression is already a `B[Canard]`.
 base_isa_gen4.nit:36,8--23: Warning: expression is already a `B[Animal]` since it is a `B[Canard]`.
 base_isa_gen4.nit:40,8--26: Warning: expression is already a `B[B[Canard]]`.
 base_isa_gen4.nit:42,8--26: Warning: expression is already a `B[B[Animal]]` since it is a `B[B[Canard]]`.
index 26754cf..6b34919 100644 (file)
@@ -1,4 +1,5 @@
 base_isa_gen5.nit:39,8--15: Warning: expression is already a `A` since it is a `B[Canard]`.
+base_isa_gen5.nit:40,8--23: Warning: expression is already a `B[Canard]`.
 base_isa_gen5.nit:41,8--23: Warning: expression is already a `B[Animal]` since it is a `B[Canard]`.
 base_isa_gen5.nit:46,8--26: Warning: expression is already a `B[B[Canard]]`.
 base_isa_gen5.nit:48,8--26: Warning: expression is already a `B[B[Animal]]` since it is a `B[B[Canard]]`.
index ff43ba5..89a56f6 100644 (file)
@@ -1,6 +1,7 @@
 base_isa_nullable1.nit:39,8--15: Warning: expression is already a `A` since it is a `B[Integer]`.
+base_isa_nullable1.nit:40,8--24: Warning: expression is already a `B[Integer]`.
 base_isa_nullable1.nit:41,8--25: Warning: expression is already a `B[Discrete]` since it is a `B[Integer]`.
 base_isa_nullable1.nit:46,8--27: Warning: expression is already a `B[B[Integer]]`.
 base_isa_nullable1.nit:48,8--28: Warning: expression is already a `B[B[Discrete]]` since it is a `B[B[Integer]]`.
-base_isa_nullable1.nit:50,8--34: Warning: expression is already a `B[nullable Discrete]` since it is a `B[Discrete]`.
+base_isa_nullable1.nit:50,8--34: Warning: expression is already a `B[nullable Discrete]` since it is a `B[Integer]`.
 Runtime error: Assert failed (base_isa_nullable1.nit:42)
index 84216d5..d8ca02f 100644 (file)
@@ -1,5 +1,5 @@
 base_isa_nullable2.nit:27,8--23: Warning: expression is already a `nullable A` since it is a `A`.
 base_isa_nullable2.nit:29,8--31: Warning: expression is already a `nullable B[Object]` since it is a `B[Object]`.
-base_isa_nullable2.nit:30,8--40: Warning: expression is already a `nullable B[nullable Object]` since it is a `nullable B[Object]`.
+base_isa_nullable2.nit:30,8--40: Warning: expression is already a `nullable B[nullable Object]` since it is a `B[Object]`.
 base_isa_nullable2.nit:33,8--31: Warning: expression is already a `C[nullable Object]`.
 Runtime error: Assert failed (base_isa_nullable2.nit:32)
index d556892..e131b2e 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:725)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:728)
 11
 21
 31
index d556892..e131b2e 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:725)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:728)
 11
 21
 31
index d556892..e131b2e 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:725)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/core/kernel.nit:728)
 11
 21
 31
index 8e58cac..dd26cc0 100644 (file)
@@ -2,7 +2,7 @@
 <A: true a 0.123 1234 asdf false p4ssw0rd>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null}
 
 # Back in Nit:
 <A: true a 0.123 1234 asdf false p4ssw0rd>
@@ -11,7 +11,7 @@
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"}
 
 # Back in Nit:
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
@@ -20,7 +20,7 @@
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null},"b": {"__kind": "obj", "__id": 2, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Back in Nit:
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
@@ -30,7 +30,7 @@
 <- false p4ssw0rd> 1111        f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "new line ->\n<-","n": null,"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
 # Back in Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
@@ -40,7 +40,7 @@
 <E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "E", "a": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": ["hello", 1234, 123.4]}, "b": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": ["hella", 2345, 234.5]}}
+{"__kind": "obj", "__id": 0, "__class": "E","a": {"__kind": "obj", "__id": 1, "__class": "Array","__items": ["hello",1234,123.4]},"b": {"__kind": "obj", "__id": 2, "__class": "Array","__items": ["hella",2345,234.5]}}
 
 # Back in Nit:
 <E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
@@ -49,7 +49,7 @@
 <E: 2222>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "F", "n": 2222}
+{"__kind": "obj", "__id": 0, "__class": "F","n": 2222}
 
 # Back in Nit:
 null
@@ -58,7 +58,7 @@ null
 <E: 33.33>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "F", "n": 33.33}
+{"__kind": "obj", "__id": 0, "__class": "F","n": 33.33}
 
 # Back in Nit:
 null
@@ -67,7 +67,7 @@ null
 <G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "G", "hs": {"__kind": "obj", "__id": 1, "__class": "HashSet", "__items": [-1, 0]}, "s": {"__kind": "obj", "__id": 2, "__class": "ArraySet", "__items": ["one", "two"]}, "hm": {"__kind": "obj", "__id": 3, "__class": "HashMap", "__length": 2, "__keys": ["one", "two"], "__values": [1, 2]}, "am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap", "__length": 2, "__keys": ["three", "four"], "__values": ["3", "4"]}}
+{"__kind": "obj", "__id": 0, "__class": "G","hs": {"__kind": "obj", "__id": 1, "__class": "HashSet","__items": [-1,0]},"s": {"__kind": "obj", "__id": 2, "__class": "ArraySet","__items": ["one","two"]},"hm": {"__kind": "obj", "__id": 3, "__class": "HashMap", "__length": 2,"__keys": ["one","two"],"__values": [1,2]},"am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap", "__length": 2,"__keys": ["three","four"],"__values": ["3","4"]}}
 
 # Back in Nit:
 <G: hs: ; s: ; hm: ; am: >
diff --git a/tests/sav/nitce/test_json_deserialization_alt3.res b/tests/sav/nitce/test_json_deserialization_alt3.res
new file mode 100644 (file)
index 0000000..b269caf
--- /dev/null
@@ -0,0 +1,192 @@
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "A",
+       "b": true,
+       "c": {"__kind": "char", "__val": "a"},
+       "f": 0.123,
+       "i": 1234,
+       "serialization_specific_name": "asdf",
+       "n": null
+}
+
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "B",
+       "b": false,
+       "c": {"__kind": "char", "__val": "b"},
+       "f": 123.123,
+       "i": 2345,
+       "serialization_specific_name": "hjkl",
+       "n": 12,
+       "ii": 1111,
+       "ss": "qwer"
+}
+
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "C",
+       "a": {
+               "__kind": "obj", "__id": 1, "__class": "A",
+               "b": true,
+               "c": {"__kind": "char", "__val": "a"},
+               "f": 0.123,
+               "i": 1234,
+               "serialization_specific_name": "asdf",
+               "n": null
+       },
+       "b": {
+               "__kind": "obj", "__id": 2, "__class": "B",
+               "b": false,
+               "c": {"__kind": "char", "__val": "b"},
+               "f": 123.123,
+               "i": 2345,
+               "serialization_specific_name": "hjkl",
+               "n": 12,
+               "ii": 1111,
+               "ss": "qwer"
+       },
+       "aa": {
+               "__kind": "ref", "__id": 1
+       }
+}
+
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111        f"\r\/> true>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "D",
+       "b": false,
+       "c": {"__kind": "char", "__val": "b"},
+       "f": 123.123,
+       "i": 2345,
+       "serialization_specific_name": "new line ->\n<-",
+       "n": null,
+       "ii": 1111,
+       "ss": "\tf\"\r\\/",
+       "d": {
+               "__kind": "ref", "__id": 0
+       }
+}
+
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111        f"\r\/> true>
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "E",
+       "a": {
+               "__kind": "obj", "__id": 1, "__class": "Array",
+               "__items": [
+                       "hello",
+                       1234,
+                       123.4
+               ]
+       },
+       "b": {
+               "__kind": "obj", "__id": 2, "__class": "Array",
+               "__items": [
+                       "hella",
+                       2345,
+                       234.5
+               ]
+       }
+}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<E: 2222>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "F",
+       "n": 2222
+}
+
+# Back in Nit:
+null
+
+# Nit:
+<E: 33.33>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "F",
+       "n": 33.33
+}
+
+# Back in Nit:
+null
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "G",
+       "hs": {
+               "__kind": "obj", "__id": 1, "__class": "HashSet",
+               "__items": [
+                       -1,
+                       0
+               ]
+       },
+       "s": {
+               "__kind": "obj", "__id": 2, "__class": "ArraySet",
+               "__items": [
+                       "one",
+                       "two"
+               ]
+       },
+       "hm": {
+               "__kind": "obj", "__id": 3, "__class": "HashMap", "__length": 2,
+               "__keys": [
+                       "one",
+                       "two"
+               ],
+               "__values": [
+                       1,
+                       2
+               ]
+       },
+       "am": {
+               "__kind": "obj", "__id": 4, "__class": "ArrayMap", "__length": 2,
+               "__keys": [
+                       "three",
+                       "four"
+               ],
+               "__values": [
+                       "3",
+                       "4"
+               ]
+       }
+}
+
+# Back in Nit:
+<G: hs: ; s: ; hm: ; am: >
+
index ad6cdbf..13a7fa8 100644 (file)
@@ -1,5 +1,5 @@
-FlatString
-FlatString
+ASCIIFlatString
+ASCIIFlatString
 Class
 Class
 
index 38a1cb2..67a1674 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 37fe5b4..d3fb349 100644 (file)
@@ -5,24 +5,24 @@ alt/test_serialization_alt2.nit:64,1--72,3: Warning: superfluous use of `auto_se
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]}},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"d": {"__kind": "ref", "__id": 0}}
 
index be2dadd..3b72eda 100644 (file)
@@ -3,24 +3,24 @@ alt/test_serialization_alt3.nit:40,1--51,3: Warning: superfluous use of `noseria
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]}},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"d": {"__kind": "ref", "__id": 0}}
 
index 26a805a..5f40834 100644 (file)
@@ -3,24 +3,24 @@ alt/test_serialization_alt4.nit:29,2--31,26: Warning: superfluous use of `serial
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index ba3196e..cc17087 100644 (file)
@@ -3,24 +3,24 @@ alt/test_serialization_alt5.nit:22,1--38,3: Warning: duplicated annotation `seri
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 38a1cb2..67a1674 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index d380e42..a6a77fd 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 4e37506..ec4a7d9 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "ffff": 6.789, "bbbb": false, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","ffff": 6.789,"bbbb": false,"d": {"__kind": "ref", "__id": 0}}
 
index 106a664..48b21cc 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "\tf\"\r\\/", "ffff": 6.789, "bbbb": false, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "\tf\"\r\\/","ffff": 6.789,"bbbb": false,"d": {"__kind": "ref", "__id": 0}}
 
diff --git a/tests/sav/nitcg/fixme/base_gen_infinite2.res b/tests/sav/nitcg/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
diff --git a/tests/sav/nitcs/fixme/base_gen_infinite2.res b/tests/sav/nitcs/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
diff --git a/tests/sav/nitcsg/fixme/base_gen_infinite2.res b/tests/sav/nitcsg/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
index c1ed475..53f4e84 100644 (file)
@@ -1,8 +1,35 @@
+Empty README for group `excluded` (readme-warning)
+Errors: 0. Warnings: 1.
+MGroupPage excluded
+       # excluded.section
+               ## excluded.intro
+               ## excluded.concerns
+               ## excluded.concern
+               ## excluded.concern
+               ## excluded-.concern
+                       ### excluded-.definition
+                               #### excluded-.intros_redefs
+                                       ##### list.group
+                                               ###### excluded-.intros
+                                               ###### excluded-.redefs
+
+MModulePage excluded
+       # excluded.section
+               ## excluded-.intro
+               ## excluded-.importation
+                       ### excluded-.graph
+                       ### list.group
+                               #### excluded-.imports
+                               #### excluded-.clients
+
 OverviewPage Overview
        # home.article
                ## packages.section
+                       ### excluded.definition
                        ### test_prog.definition
 
+ReadmePage excluded
+
 ReadmePage test_prog
        # mdarticle-0
 
@@ -1110,24 +1137,24 @@ MModulePage rpg
                                #### test_prog__rpg__rpg.imports
                                #### test_prog__rpg__rpg.clients
 
-Generated 115 pages
+Generated 118 pages
  list:
-  MPropertyPage: 77 (66.95%)
-  MClassPage: 20 (17.39%)
-  MModulePage: 8 (6.95%)
-  MGroupPage: 4 (3.47%)
-  ReadmePage: 4 (3.47%)
-  SearchPage: 1 (0.86%)
-  OverviewPage: 1 (0.86%)
-Found 219 mentities
+  MPropertyPage: 77 (65.25%)
+  MClassPage: 20 (16.94%)
+  MModulePage: 9 (7.62%)
+  ReadmePage: 5 (4.23%)
+  MGroupPage: 5 (4.23%)
+  SearchPage: 1 (0.84%)
+  OverviewPage: 1 (0.84%)
+Found 222 mentities
  list:
-  MMethodDef: 86 (39.26%)
-  MMethod: 76 (34.70%)
-  MClassDef: 22 (10.04%)
-  MClass: 20 (9.13%)
-  MModule: 8 (3.65%)
-  MGroup: 4 (1.82%)
+  MMethodDef: 86 (38.73%)
+  MMethod: 76 (34.23%)
+  MClassDef: 22 (9.90%)
+  MClass: 20 (9.00%)
+  MModule: 9 (4.05%)
+  MGroup: 5 (2.25%)
+  MPackage: 2 (0.90%)
   MVirtualTypeDef: 1 (0.45%)
   MVirtualTypeProp: 1 (0.45%)
-  MPackage: 1 (0.45%)
 quicksearch-list.js
index 4a24924..00f8ce6 100644 (file)
 </span></span><span class="line" id="L16">
 </span><span class="line" id="L17"><span class="nc_k">import</span> <span class="nc_k">end</span>
 </span><span class="line" id="L18">
-</span><span class="nc_cdef foldable" id="base_simple3#Object"><span class="line" id="L19"><span class="nc_k">interface</span> <span class="nc_t">Object</span>
+</span><span class="nc_cdef foldable" id="base_simple3$Object"><span class="line" id="L19"><span class="nc_k">interface</span> <span class="nc_t nc_def popupable" title="class Object" data-title="&lt;a href=&#34;#base_simple3$Object&#34;&gt;class Object&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Object&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; sub-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Bool&#34;&gt;base_simple3$Bool&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Sys&#34;&gt;base_simple3$Sys&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Object</span>
 </span><span class="line" id="L20"><span class="nc_k">end</span>
 </span></span><span class="line" id="L21">
-</span><span class="nc_cdef foldable" id="base_simple3#Bool"><span class="line" id="L22"><span class="nc_k">enum</span> <span class="nc_t">Bool</span>
+</span><span class="nc_cdef foldable" id="base_simple3$Bool"><span class="line" id="L22"><span class="nc_k">enum</span> <span class="nc_t nc_def popupable" title="class Bool" data-title="&lt;a href=&#34;#base_simple3$Bool&#34;&gt;class Bool&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Bool&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Bool</span>
 </span><span class="line" id="L23"><span class="nc_k">end</span>
 </span></span><span class="line" id="L24">
-</span><span class="nc_cdef foldable" id="base_simple3#Int"><span class="line" id="L25"><span class="nc_k">enum</span> <span class="nc_t">Int</span>
-</span><span class="nc_pdef foldable" id="base_simple3#Int#output"><span class="line" id="L26">        <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Int#output" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Int#output&#34;&gt;base_simple3#Int#output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span>
+</span><span class="nc_cdef foldable" id="base_simple3$Int"><span class="line" id="L25"><span class="nc_k">enum</span> <span class="nc_t nc_def popupable" title="class Int" data-title="&lt;a href=&#34;#base_simple3$Int&#34;&gt;class Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Int&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span>
+</span><span class="nc_pdef foldable" id="base_simple3$Int$output"><span class="line" id="L26">        <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span>
 </span></span><span class="line" id="L27"><span class="nc_k">end</span>
 </span></span><span class="line" id="L28">
-</span><span class="nc_cdef foldable" id="base_simple3#A"><span class="line" id="L29"><span class="nc_k">class</span> <span class="nc_t">A</span>
-</span><span class="nc_pdef foldable" id="base_simple3#A#init"><span class="line" id="L30">    <span class="nc_k">init</span> <span class="nc_k">do</span> <span class="nc_l">5</span><span>.</span><span class="nc_i">output</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#A#run"><span class="line" id="L31">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#A#run" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#A#run&#34;&gt;base_simple3#A#run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_l">6</span><span>.</span><span class="nc_i">output</span>
+</span><span class="nc_cdef foldable" id="base_simple3$A"><span class="line" id="L29"><span class="nc_k">class</span> <span class="nc_t nc_def popupable" title="class A" data-title="&lt;a href=&#34;#base_simple3$A&#34;&gt;class A&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;A&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">A</span>
+</span><span class="nc_pdef foldable" id="base_simple3$A$init"><span class="line" id="L30">    <span class="nc_k">init</span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">5</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$A$run"><span class="line" id="L31">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3$A$run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">6</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span></span><span class="line" id="L32"><span class="nc_k">end</span>
 </span></span><span class="line" id="L33">
-</span><span class="nc_cdef foldable" id="base_simple3#B"><span class="line" id="L34"><span class="nc_k">class</span> <span class="nc_t">B</span>
-</span><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35">     <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#B#autoinit"><span class="line" id="L36"> <span class="nc_k">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
+</span><span class="nc_cdef foldable" id="base_simple3$B"><span class="line" id="L34"><span class="nc_k">class</span> <span class="nc_t nc_def popupable" title="class B" data-title="&lt;a href=&#34;#base_simple3$B&#34;&gt;class B&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;B&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">B</span>
+</span><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35">     <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3$B$val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$B$autoinit"><span class="line" id="L36"> <span class="nc_k popupable" style="border-bottom: solid 2px red" title="Messages" data-content="&lt;div&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;1 message(s)&lt;&#47;b&gt; &lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;Warning: init with signature in base_simple3$B&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">init</span><span>(</span><span class="nc_i nc_v popupable" title="v: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;v:&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">v</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span><span>)</span>
 </span><span class="line" id="L37">    <span class="nc_k">do</span>
-</span><span class="line" id="L38">            <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L39">            <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
+</span><span class="line" id="L38">            <span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">7</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L39">            <span class="nc_k popupable" title="B" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">self</span><span class="popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3$B$val=&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$val=&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3$B$val=&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$val=&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span> <span class="popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3$B$val=&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$val=&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">=</span> <span class="nc_i nc_v popupable" title="v: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;v:&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">v</span>
 </span><span class="line" id="L40">    <span class="nc_k">end</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#B#run"><span class="line" id="L41">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#B#run" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#B#run&#34;&gt;base_simple3#B#run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_i">val</span><span>.</span><span class="nc_i">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$B$run"><span class="line" id="L41">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3$B$run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_i popupable" title="call base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3$B$val&#34;&gt;call base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span></span><span class="line" id="L42"><span class="nc_k">end</span>
 </span></span><span class="line" id="L43">
-</span><span class="nc_cdef foldable" id="base_simple3#C"><span class="line" id="L44"><span class="nc_k">class</span> <span class="nc_t">C</span>
-</span><span class="nc_pdef foldable" id="base_simple3#C#_val1"><a id="base_simple3#C#val1"></a><a id="base_simple3#C#val1="></a><span class="line" id="L45">  <span class="nc_k">var</span> <span class="nc_def nc_i">val1</span><span>:</span> <span class="nc_t">Int</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#C#_val2"><a id="base_simple3#C#val2"></a><a id="base_simple3#C#val2="></a><span class="line" id="L46">   <span class="nc_k">var</span> <span class="nc_def nc_i">val2</span><span>:</span> <span class="nc_t">Int</span> <span>=</span> <span class="nc_l">10</span>
+</span><span class="nc_cdef foldable" id="base_simple3$C"><span class="line" id="L44"><span class="nc_k">class</span> <span class="nc_t nc_def popupable" title="class C" data-title="&lt;a href=&#34;#base_simple3$C&#34;&gt;class C&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;C&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3$Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">C</span>
+</span><span class="nc_pdef foldable" id="base_simple3$C$_val1"><a id="base_simple3$C$val1"></a><a id="base_simple3$C$val1="></a><span class="line" id="L45">  <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3$C$val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val1&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$C$_val2"><a id="base_simple3$C$val2"></a><a id="base_simple3$C$val2="></a><span class="line" id="L46">   <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3$C$val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val2&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">10</span>
 </span></span><span class="line" id="L47"><span class="nc_k">end</span>
 </span></span><span class="line" id="L48">
-</span><span class="nc_pdef foldable" id="base_simple3#Sys#foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#foo" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#foo&#34;&gt;base_simple3#Sys#foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l">2</span><span>.</span><span class="nc_i">output</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#Sys#bar"><span class="line" id="L50"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#bar" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#bar&#34;&gt;base_simple3#Sys#bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;bar&lt;span&gt;(i: &lt;a href=&#34;base_simple3.html#base_simple3#Int&#34;&gt;Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_v nc_i">i</span><span>:</span> <span class="nc_t">Int</span><span>)</span> <span class="nc_k">do</span> <span class="nc_v nc_i">i</span><span>.</span><span class="nc_i">output</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#Sys#baz"><span class="line" id="L51"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#baz" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#baz&#34;&gt;base_simple3#Sys#baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;baz&lt;span&gt;: &lt;a href=&#34;base_simple3.html#base_simple3#Int&#34;&gt;Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t">Int</span> <span class="nc_k">do</span> <span class="nc_k">return</span> <span class="nc_l">4</span>
+</span><span class="nc_pdef foldable" id="base_simple3$Sys$foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$foo" data-title="&lt;a href=&#34;#base_simple3$Sys$foo&#34;&gt;base_simple3$Sys$foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">2</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$Sys$bar"><span class="line" id="L50"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$bar" data-title="&lt;a href=&#34;#base_simple3$Sys$bar&#34;&gt;base_simple3$Sys$bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;bar&lt;span&gt;(i: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_i nc_v popupable" title="i: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;i:&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">i</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span><span>)</span> <span class="nc_k">do</span> <span class="nc_i nc_v popupable" title="i: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;i:&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">i</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$Sys$baz"><span class="line" id="L51"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$baz" data-title="&lt;a href=&#34;#base_simple3$Sys$baz&#34;&gt;base_simple3$Sys$baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;baz&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span class="nc_k">do</span> <span class="nc_k">return</span> <span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">4</span>
 </span></span><span class="line" id="L52">
-</span><span class="nc_pdef foldable" id="base_simple3#Sys#main"><span class="line" id="L53"><span class="nc_l">1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L54"><span class="nc_i">foo</span>
-</span><span class="line" id="L55"><span class="nc_i">bar</span><span>(</span><span class="nc_l">3</span><span>)</span>
-</span><span class="line" id="L56"><span class="nc_i">baz</span><span>.</span><span class="nc_i">output</span>
+</span><span class="nc_pdef foldable" id="base_simple3$Sys$main"><span class="line" id="L53"><span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">1</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L54"><span class="nc_i popupable" title="call base_simple3$Sys$foo" data-title="&lt;a href=&#34;#base_simple3$Sys$foo&#34;&gt;call base_simple3$Sys$foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Sys$foo&#34;&gt;base_simple3$Sys$foo&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">foo</span>
+</span><span class="line" id="L55"><span class="nc_i popupable" title="call base_simple3$Sys$bar" data-title="&lt;a href=&#34;#base_simple3$Sys$bar&#34;&gt;call base_simple3$Sys$bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Sys$bar&#34;&gt;base_simple3$Sys$bar&lt;&#47;a&gt;&lt;span&gt;(i: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">bar</span><span>(</span><span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">3</span><span>)</span>
+</span><span class="line" id="L56"><span class="nc_i popupable" title="call base_simple3$Sys$baz" data-title="&lt;a href=&#34;#base_simple3$Sys$baz&#34;&gt;call base_simple3$Sys$baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Sys$baz&#34;&gt;base_simple3$Sys$baz&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">baz</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span><span class="line" id="L57">
-</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_v nc_i">a</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">A</span>
-</span><span class="line" id="L59"><span class="nc_v nc_i">a</span><span>.</span><span class="nc_i">run</span>
+</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="a: A" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;a:&lt;a href=&#34;#base_simple3$A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">a</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$A$autoinit" data-title="&lt;a href=&#34;#base_simple3$A$autoinit&#34;&gt;call base_simple3$A$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$A$autoinit&#34;&gt;base_simple3$A$autoinit&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="A" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">A</span>
+</span><span class="line" id="L59"><span class="nc_i nc_v popupable" title="a: A" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;a:&lt;a href=&#34;#base_simple3$A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">a</span><span class="popupable" title="call base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3$A$run&#34;&gt;call base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$A$run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3$A$run&#34;&gt;call base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$A$run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">run</span>
 </span><span class="line" id="L60">
-</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_v nc_i">b</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">B</span><span>(</span><span class="nc_l">8</span><span>)</span>
-</span><span class="line" id="L62"><span class="nc_v nc_i">b</span><span>.</span><span class="nc_i">run</span>
+</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="b: B" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;b:&lt;a href=&#34;#base_simple3$B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">b</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$B$autoinit" data-title="&lt;a href=&#34;#base_simple3$B$autoinit&#34;&gt;call base_simple3$B$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$autoinit&#34;&gt;base_simple3$B$autoinit&lt;&#47;a&gt;&lt;span&gt;(v: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="B" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">B</span><span>(</span><span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">8</span><span>)</span>
+</span><span class="line" id="L62"><span class="nc_i nc_v popupable" title="b: B" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;b:&lt;a href=&#34;#base_simple3$B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">b</span><span class="popupable" title="call base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3$B$run&#34;&gt;call base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3$B$run&#34;&gt;call base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$B$run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">run</span>
 </span><span class="line" id="L63">
-</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_v nc_i">c</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">C</span><span>(</span><span class="nc_l">9</span><span>)</span>
-</span><span class="line" id="L65"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L66"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val2</span><span>.</span><span class="nc_i">output</span>
+</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3$C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$C$autoinit" data-title="&lt;a href=&#34;#base_simple3$C$autoinit&#34;&gt;call base_simple3$C$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$C$autoinit&#34;&gt;base_simple3$C$autoinit&lt;&#47;a&gt;&lt;span&gt;(val1: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="C" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">C</span><span>(</span><span class="nc_l popupable" title="Int" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">9</span><span>)</span>
+</span><span class="line" id="L65"><span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3$C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3$C$val1&#34;&gt;call base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$C$val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3$C$val1&#34;&gt;call base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$C$val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L66"><span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3$C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3$C$val2&#34;&gt;call base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$C$val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3$C$val2&#34;&gt;call base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$C$val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3$Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3$Int$output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span></span></span>
\ No newline at end of file
index eca970e..2e6f89d 100644 (file)
@@ -9,4 +9,7 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 `--subdir (\e[33mproject1/subdir\e[m)
    |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
-   `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+   |--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+   `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+      `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+         `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
index eca970e..2e6f89d 100644 (file)
@@ -9,4 +9,7 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 `--subdir (\e[33mproject1/subdir\e[m)
    |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
-   `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+   |--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+   `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+      `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+         `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
index c5ec486..ed5df5e 100644 (file)
@@ -9,7 +9,10 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
 `--subdir (\e[33mproject1/subdir\e[m)
    |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
-   `--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+   |--\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+   `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+      `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+         `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
 project2 (\e[33mproject1/project2\e[m)
 |--\e[1mfoo\e[m (\e[33mproject1/project2/foo.nit\e[m)
 `--\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index 267bbdb..64895a3 100644 (file)
@@ -1,10 +1,11 @@
-base_simple3/\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
-project1/\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
-project1/\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
-project1/\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
-project1/subdir/\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
-project1/\e[1mmodule_0\e[m (\e[33mproject1/module_0.nit\e[m)
-project1/subdir/\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
-project1/\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
-project1/\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
-project1/\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+base_simple3>\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
+project1>\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
+project1>\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
+project1>\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+project1>subdir>\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+project1>\e[1mmodule_0\e[m (\e[33mproject1/module_0.nit\e[m)
+project1>subdir>\e[1mmodule_0\e[m (\e[33mproject1/subdir/module_0.nit\e[m)
+project1>\e[1mmodule_01\e[m (\e[33mproject1/module_01.nit\e[m)
+project1>\e[1mmodule_02\e[m (\e[33mproject1/module_02.nit\e[m)
+project1>\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+project1>subdir>subdir2>subdir3>\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
index 8ebf96a..7cf6fd3 100644 (file)
@@ -2,4 +2,7 @@ project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)\e[37m (module4)\e[m
 `--subdir (\e[33mproject1/subdir\e[m)
-   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)\e[37m (module1)\e[m
+   |--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)\e[37m (module1)\e[m
+   `--subdir2 (\e[33mproject1/subdir/subdir2\e[m)
+      `--subdir3 (\e[33mproject1/subdir/subdir2/subdir3\e[m)
+         `--\e[1msubmodule\e[m (\e[33mproject1/subdir/subdir2/subdir3/submodule.nit\e[m)
diff --git a/tests/sav/nitls_args7.res b/tests/sav/nitls_args7.res
new file mode 100644 (file)
index 0000000..a0a686e
--- /dev/null
@@ -0,0 +1,12 @@
+test_prog: Test program for model tools. (test_prog)
+|--game: Gaming group (test_prog/game)
+|  `--\e[1mgame\e[m: A game abstraction for RPG. (test_prog/game/game.nit)
+|--platform: Fictive Crappy Platform. (test_prog/platform)
+|  `--\e[1mplatform\e[m: Declares base types allowed on the platform. (test_prog/platform/platform.nit)
+|--rpg: Role Playing Game group (test_prog/rpg)
+|  |--\e[1mcareers\e[m: Careers of the game. (test_prog/rpg/careers.nit)
+|  |--\e[1mcharacter\e[m: Characters are playable entity in the world. (test_prog/rpg/character.nit)
+|  |--\e[1mcombat\e[m: COmbat interactions between characters. (test_prog/rpg/combat.nit)
+|  |--\e[1mraces\e[m: Races of the game. (test_prog/rpg/races.nit)
+|  `--\e[1mrpg\e[m: A worlg RPG abstraction. (test_prog/rpg/rpg.nit)
+`--\e[1mtest_prog\e[m: A test program with a fake model to check model tools. (test_prog/test_prog.nit)
diff --git a/tests/sav/nitls_args8.res b/tests/sav/nitls_args8.res
new file mode 100644 (file)
index 0000000..b80e7ce
--- /dev/null
@@ -0,0 +1,2 @@
+\e[1mexcluded\e[m (test_prog/game/excluded.nit)
+\e[1mmore\e[m (test_prog/game/excluded_dir/more.nit)
index f6ad37b..7e642cc 100644 (file)
@@ -91,7 +91,7 @@
 # MModules metrics
 
  ## package base_simple3
-  `- group base_simple3
+  `- group base_simple3>
        mnoa: number of ancestor modules
          avg: 0.0
          max: base_simple3 (0)
@@ -367,7 +367,7 @@ generating module_hierarchy.dot
 # MClasses metrics
 
  ## package base_simple3
-  `- group base_simple3
+  `- group base_simple3>
        cnoa: number of ancestor classes
          avg: 0.0
          max: Bool (1)
@@ -494,7 +494,7 @@ generating module_hierarchy.dot
 # Inheritance metrics
 
  ## package base_simple3
-  `- group base_simple3
+  `- group base_simple3>
        cnoac: number of class_kind ancestor
          avg: 0.0
          max: Object (0)
@@ -751,7 +751,7 @@ Statistics of type usage:
 # Nullable metrics
 
  ## package base_simple3
-  `- group base_simple3
+  `- group base_simple3>
        cnba: number of accessible attributes (inherited + local)
          avg: 0.0
          max: C (2)
@@ -868,17 +868,17 @@ MMethodDef locally designated (by number of CallSites)
   <=1: sub-population=13 (92.85%); cumulated value=13 (56.52%)
   <=16: sub-population=1 (7.14%); cumulated value=10 (43.47%)
  list:
-  base_simple3#Int#output: 10 (43.47%)
-  base_simple3#B#val: 1 (4.34%)
-  base_simple3#B#val=: 1 (4.34%)
-  base_simple3#Object#init: 1 (4.34%)
-  base_simple3#C#val2: 1 (4.34%)
+  base_simple3$Int$output: 10 (43.47%)
+  base_simple3$B$val: 1 (4.34%)
+  base_simple3$B$val=: 1 (4.34%)
+  base_simple3$Object$init: 1 (4.34%)
+  base_simple3$C$val2: 1 (4.34%)
   ...
-  base_simple3#A#autoinit: 1 (4.34%)
-  base_simple3#Sys#baz: 1 (4.34%)
-  base_simple3#Sys#bar: 1 (4.34%)
-  base_simple3#Sys#foo: 1 (4.34%)
-  base_simple3#C#autoinit: 1 (4.34%)
+  base_simple3$A$autoinit: 1 (4.34%)
+  base_simple3$Sys$baz: 1 (4.34%)
+  base_simple3$Sys$bar: 1 (4.34%)
+  base_simple3$Sys$foo: 1 (4.34%)
+  base_simple3$C$autoinit: 1 (4.34%)
 MMethodDef possibly invoked at runtime (by number of CallSites)
  population: 14
  minimum value: 1
@@ -889,17 +889,17 @@ MMethodDef possibly invoked at runtime (by number of CallSites)
   <=1: sub-population=13 (92.85%); cumulated value=13 (56.52%)
   <=16: sub-population=1 (7.14%); cumulated value=10 (43.47%)
  list:
-  base_simple3#Int#output: 10 (43.47%)
-  base_simple3#B#val: 1 (4.34%)
-  base_simple3#B#val=: 1 (4.34%)
-  base_simple3#Object#init: 1 (4.34%)
-  base_simple3#C#val2: 1 (4.34%)
+  base_simple3$Int$output: 10 (43.47%)
+  base_simple3$B$val: 1 (4.34%)
+  base_simple3$B$val=: 1 (4.34%)
+  base_simple3$Object$init: 1 (4.34%)
+  base_simple3$C$val2: 1 (4.34%)
   ...
-  base_simple3#A#autoinit: 1 (4.34%)
-  base_simple3#Sys#baz: 1 (4.34%)
-  base_simple3#Sys#bar: 1 (4.34%)
-  base_simple3#Sys#foo: 1 (4.34%)
-  base_simple3#C#autoinit: 1 (4.34%)
+  base_simple3$A$autoinit: 1 (4.34%)
+  base_simple3$Sys$baz: 1 (4.34%)
+  base_simple3$Sys$bar: 1 (4.34%)
+  base_simple3$Sys$foo: 1 (4.34%)
+  base_simple3$C$autoinit: 1 (4.34%)
 class_hierarchy.dot
 classdef_hierarchy.dot
 inheritance/
index 338a3d9..4e62b3e 100644 (file)
@@ -51,7 +51,7 @@ Float [
 Numeric -> Float [dir=back arrowtail=open style=dashed];
 
 Byte [
- label = "{Byte||+ %(i: Byte): Byte\l+ \<\<(i: Int): Byte\l+ \>\>(i: Int): Byte\l+ ascii(): Char\l+ autoinit()\l}"
+ label = "{Byte||+ %(i: Byte): Byte\l+ \<\<(i: Int): Byte\l+ \>\>(i: Int): Byte\l+ ascii(): Char\l+ is_whitespace(): Bool\l+ autoinit()\l}"
 ]
 Discrete -> Byte [dir=back arrowtail=open style=dashed];
 Numeric -> Byte [dir=back arrowtail=open style=dashed];
index de63dda..c79556c 100644 (file)
@@ -51,7 +51,7 @@ Float [
 Numeric -> Float [dir=back arrowtail=open style=dashed];
 
 Byte [
- label = "{Byte||+ %(i: Byte): Byte\l+ \<\<(i: Int): Byte\l+ \>\>(i: Int): Byte\l+ ascii(): Char\l+ autoinit()\l}"
+ label = "{Byte||+ %(i: Byte): Byte\l+ \<\<(i: Int): Byte\l+ \>\>(i: Int): Byte\l+ ascii(): Char\l+ is_whitespace(): Bool\l+ autoinit()\l}"
 ]
 Discrete -> Byte [dir=back arrowtail=open style=dashed];
 Numeric -> Byte [dir=back arrowtail=open style=dashed];
index e1439ca..a71e35b 100644 (file)
@@ -1,18 +1,36 @@
-test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit-2.nit): Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
+==== Docunits of module test_nitunit::test_nitunit | tests: 4
+[OK] test_nitunit::test_nitunit
+[KO] test_nitunit$X
+     test_nitunit.nit:21,7--22,0: Runtime error in nitunit.out/test_nitunit-2.nit
+     Output
+       Runtime error: Assert failed (nitunit.out/test_nitunit-2.nit:5)
 
-test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit-3.nit): .nitunit/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+[KO] test_nitunit$X$foo
+     test_nitunit.nit:24,8--25,0: Compilation error in nitunit.out/test_nitunit-3.nit
+     Output
+       nitunit.out/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
 
-test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/gen_test_test_nitunit.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
+[KO] test_nitunit$X$foo1
+     test_nitunit.nit:28,15: Syntax Error: unexpected operator '!'.
 
-DocUnits:
-Entities: 27; Documented ones: 3; With nitunits: 3; Failures: 2
+==== Test-suite of module test_test_nitunit::test_test_nitunit | tests: 3
+[OK] test_test_nitunit$TestX$test_foo
+[KO] test_test_nitunit$TestX$test_foo1
+     test_test_nitunit.nit:36,2--40,4: Runtime Error in file nitunit.out/gen_test_test_nitunit.nit
+     Output
+       Runtime error: Assert failed (test_test_nitunit.nit:39)
 
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 1
-<testsuites><testsuite package="test_nitunit"><testcase classname="nitunit.test_nitunit.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
-</system-out></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="&lt;class&gt;"><system-err></system-err><system-out>assert false
-</system-out><error message="Runtime error: Assert failed (.nitunit&#47;test_nitunit-2.nit:5)
-"></error></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><system-err></system-err><system-out>assert undefined_identifier
-</system-out><failure message=".nitunit&#47;test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
-"></failure></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"><system-err></system-err><system-out>out</system-out></testcase><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><system-err></system-err><system-out>out</system-out><error message="Runtime error: Assert failed (test_test_nitunit.nit:39)
-"></error></testcase><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"><system-err></system-err><system-out>out</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
+[OK] test_test_nitunit$TestX$test_foo2
+
+Docunits: Entities: 34; Documented ones: 6; With nitunits: 4; Failures: 3
+Test suites: Classes: 1; Test Cases: 3; Failures: 1
+[FAILURE] 4/7 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit::test_nitunit"><testcase classname="nitunit.test_nitunit::test_nitunit.&lt;module&gt;" name="&lt;module&gt;" time="0.0"><system-err></system-err><system-out>assert true
+</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="&lt;class&gt;" time="0.0"><error message="Runtime error in nitunit.out&#47;test_nitunit-2.nit">Runtime error: Assert failed (nitunit.out&#47;test_nitunit-2.nit:5)
+</error><system-out>assert false
+</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo" time="0.0"><failure message="Compilation error in nitunit.out&#47;test_nitunit-3.nit">nitunit.out&#47;test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+</failure><system-out>assert undefined_identifier
+</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo1" time="0.0"><failure message="Syntax Error: unexpected operator &#39;!&#39;."></failure><system-out>assert !@#$%^&amp;*()
+</system-out></testcase></testsuite><testsuite package="test_test_nitunit::test_test_nitunit"></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out&#47;gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:39)
+</error></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo2" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index 5d5471f..332fbea 100644 (file)
@@ -1,21 +1,22 @@
-DocUnits:
-DocUnits Success
-Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+==== Docunits of module test_nitunit2::test_nitunit2 | tests: 3
+[OK] test_nitunit2::test_nitunit2$core::Sys$foo1
+[OK] test_nitunit2::test_nitunit2$core::Sys$bar2
+[OK] test_nitunit2::test_nitunit2$core::Sys$foo3
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit2"><testcase classname="nitunit.test_nitunit2.core::Sys" name="test_nitunit2::Sys::foo1"><system-err></system-err><system-out>if true then
+Docunits: Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+Test suites: Classes: 0; Test Cases: 0
+[SUCCESS] All 3 tests passed.
+<testsuites><testsuite package="test_nitunit2::test_nitunit2"><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="foo1" time="0.0"><system-err></system-err><system-out>if true then
 
    assert true
 
 end
-</system-out></testcase><testcase classname="nitunit.test_nitunit2.core::Sys" name="test_nitunit2::Sys::bar2"><system-err></system-err><system-out>if true then
+</system-out></testcase><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="bar2" time="0.0"><system-err></system-err><system-out>if true then
 
     assert true
 
 end
-</system-out></testcase><testcase classname="nitunit.test_nitunit2.core::Sys" name="test_nitunit2::Sys::foo3"><system-err></system-err><system-out>var a = 1
+</system-out></testcase><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="foo3" time="0.0"><system-err></system-err><system-out>var a = 1
 assert a == 1
 assert a == 1
-</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
index e89f593..6671579 100644 (file)
@@ -1,11 +1,12 @@
-DocUnits:
-DocUnits Success
-Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+==== Docunits of module test_doc2::test_doc2 | tests: 3
+[OK] test_doc2::test_doc2$core::Sys$foo1
+[OK] test_doc2::test_doc2$core::Sys$foo2
+[OK] test_doc2::test_doc2$core::Sys$foo3
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_doc2"><testcase classname="nitunit.test_doc2.core::Sys" name="test_doc2::Sys::foo1"><system-err></system-err><system-out>assert true # tested
-</system-out></testcase><testcase classname="nitunit.test_doc2.core::Sys" name="test_doc2::Sys::foo2"><system-err></system-err><system-out>assert true # tested
-</system-out></testcase><testcase classname="nitunit.test_doc2.core::Sys" name="test_doc2::Sys::foo3"><system-err></system-err><system-out>assert true # tested
-</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+Docunits: Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+Test suites: Classes: 0; Test Cases: 0
+[SUCCESS] All 3 tests passed.
+<testsuites><testsuite package="test_doc2::test_doc2"><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="foo1" time="0.0"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="foo2" time="0.0"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="foo3" time="0.0"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
index 8971f9e..30f2862 100644 (file)
@@ -1,14 +1,22 @@
-test_nitunit3/README.md:1,0--13,0: Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`). At 1,2--4: Syntax Error: unexpected malformed character '\]..
-test_nitunit3/README.md:1,0--13,0: ERROR: nitunit.test_nitunit3.<group> (in .nitunit/test_nitunit3-0.nit): Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
+==== Docunits of group test_nitunit3> | tests: 2
+[KO] test_nitunit3>
+     test_nitunit3/README.md:4,2--15,0: Runtime error in nitunit.out/test_nitunit3-0.nit with argument 1
+     Output
+       Runtime error: Assert failed (nitunit.out/test_nitunit3-0.nit:7)
 
-DocUnits:
-Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+[KO] test_nitunit3>#2
+     test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\].
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit3"><testcase classname="nitunit.test_nitunit3" name="&lt;group&gt;"><failure message="test_nitunit3&#47;README.md:1,0--13,0: Invalid block of code. At 1,2--4: Syntax Error: unexpected malformed character &#39;\].."></failure><system-err></system-err><system-out>assert false
+==== Docunits of module test_nitunit3::test_nitunit3 | tests: 1
+[OK] test_nitunit3::test_nitunit3
+
+Docunits: Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 2/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit3&gt;"><testcase classname="nitunit.test_nitunit3.test_nitunit3.&lt;group&gt;" name="&lt;group&gt;" time="0.0"><error message="Runtime error in nitunit.out&#47;test_nitunit3-0.nit with argument 1">Runtime error: Assert failed (nitunit.out&#47;test_nitunit3-0.nit:7)
+</error><system-out>assert false
 assert true
-</system-out><error message="Runtime error: Assert failed (.nitunit&#47;test_nitunit3-0.nit:7)
-"></error></testcase></testsuite><testsuite package="test_nitunit3"><testcase classname="nitunit.test_nitunit3.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
-</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+</system-out></testcase><testcase classname="nitunit.test_nitunit3.test_nitunit3.&lt;group&gt;" name="&lt;group&gt;#2" time="0.0"><failure message="Syntax Error: unexpected malformed character &#39;\]."></failure><system-out>;&#39;\][]
+</system-out></testcase></testsuite><testsuite package="test_nitunit3::test_nitunit3"><testcase classname="nitunit.test_nitunit3::test_nitunit3.&lt;module&gt;" name="&lt;module&gt;" time="0.0"><system-err></system-err><system-out>assert true
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
index fc20a76..bda2580 100644 (file)
@@ -1,13 +1,16 @@
-test_nitunit_md.md:1,0--15,0: ERROR: nitunit.<file>.test_nitunit_md.md:1,0--15,0 (in .nitunit/file-0.nit): Runtime error: Assert failed (.nitunit/file-0.nit:8)
+==== Docunits of file test_nitunit_md.md | tests: 1
+[KO] nitunit.<file>.test_nitunit_md.md
+     test_nitunit_md.md:4,2--16,0: Runtime error in nitunit.out/file-0.nit with argument 1
+     Output
+       Runtime error: Assert failed (nitunit.out/file-0.nit:8)
 
-DocUnits:
-Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit_md.md:1,0--15,0"><testcase classname="nitunit.&lt;file&gt;" name="test_nitunit_md.md:1,0--15,0"><system-err></system-err><system-out>var a = 1
+Docunits: Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 1/1 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit_md.md"><testcase classname="nitunit.&lt;file&gt;" name="test_nitunit_md.md" time="0.0"><error message="Runtime error in nitunit.out&#47;file-0.nit with argument 1">Runtime error: Assert failed (nitunit.out&#47;file-0.nit:8)
+</error><system-out>var a = 1
 assert 1 == 1
 assert false
-</system-out><error message="Runtime error: Assert failed (.nitunit&#47;file-0.nit:8)
-"></error></testcase></testsuite></testsuites>
\ No newline at end of file
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
index 3f42f57..e3ad8c7 100644 (file)
@@ -1,10 +1,16 @@
-test_doc3.nit:15,1--18,0: Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`). At 1,3--9: Syntax Error: unexpected identifier 'garbage'..
-test_doc3.nit:20,1--25,0: Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`). At 1,2--8: Syntax Error: unexpected identifier 'garbage'..
-test_doc3.nit:27,1--32,0: Error: there is a block of invalid Nit code, thus not considered a nitunit. To suppress this warning, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`). At 1,2--8: Syntax Error: unexpected identifier 'garbage'..
-DocUnits:
-Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 3
+==== Docunits of module test_doc3::test_doc3 | tests: 3
+[KO] test_doc3::test_doc3$core::Sys$foo1
+     test_doc3.nit:17,9--15: Syntax Error: unexpected identifier 'garbage'.
+[KO] test_doc3::test_doc3$core::Sys$foo2
+     test_doc3.nit:23,4--10: Syntax Error: unexpected identifier 'garbage'.
+[KO] test_doc3::test_doc3$core::Sys$foo3
+     test_doc3.nit:30,4--10: Syntax Error: unexpected identifier 'garbage'.
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_doc3"><testcase classname="nitunit.test_doc3.core::Sys" name="test_doc3::Sys::foo1"><failure message="test_doc3.nit:15,1--18,0: Invalid block of code. At 1,3--9: Syntax Error: unexpected identifier &#39;garbage&#39;.."></failure></testcase><testcase classname="nitunit.test_doc3.core::Sys" name="test_doc3::Sys::foo2"><failure message="test_doc3.nit:20,1--25,0: Invalid block of code. At 1,2--8: Syntax Error: unexpected identifier &#39;garbage&#39;.."></failure></testcase><testcase classname="nitunit.test_doc3.core::Sys" name="test_doc3::Sys::foo3"><failure message="test_doc3.nit:27,1--32,0: Invalid block of code. At 1,2--8: Syntax Error: unexpected identifier &#39;garbage&#39;.."></failure></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+Docunits: Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 3
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 3/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_doc3::test_doc3"><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="foo1" time="0.0"><failure message="Syntax Error: unexpected identifier &#39;garbage&#39;."></failure><system-out> *garbage*
+</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="foo2" time="0.0"><failure message="Syntax Error: unexpected identifier &#39;garbage&#39;."></failure><system-out>*garbage*
+</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="foo3" time="0.0"><failure message="Syntax Error: unexpected identifier &#39;garbage&#39;."></failure><system-out>*garbage*
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
index 55f831f..72ec224 100644 (file)
@@ -1,16 +1,85 @@
-test_nitunit4/test_nitunit4.nit:22,2--25,4: ERROR: test_foo (in file .nitunit/gen_test_nitunit4.nit): Before Test
-Tested method
-After Test
-Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
+test_nitunit4/test_bad_comp2.nit:19,7--22: Error: a class named `test_nitunit4::TestSuiteBadComp` is already defined in module `test_bad_comp` at test_nitunit4/test_bad_comp.nit:19,1--29,3.
+==== Test-suite of module test_nitunit4::test_bad_comp | tests: 2
+[KO] test_nitunit4$TestSuiteBadComp$test_good
+     test_nitunit4/test_bad_comp.nit:22,2--24,4: Compilation Error
+     Output
+       test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+
+[KO] test_nitunit4$TestSuiteBadComp$test_bad
+     test_nitunit4/test_bad_comp.nit:26,2--28,4: Compilation Error
+     Output
+       test_nitunit4/test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+
+
+==== Test-suite of module test_nitunit4::test_bad_comp2 | tests: 2
+[KO] test_nitunit4$TestSuiteBadComp$test_good
+     test_nitunit4/test_bad_comp2.nit:22,2--24,4: Compilation Error
+     Output
+       nitunit.out/gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+
+[KO] test_nitunit4$TestSuiteBadComp$test_bad
+     test_nitunit4/test_bad_comp2.nit:26,2--28,4: Compilation Error
+     Output
+       nitunit.out/gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+
+
+==== Test-suite of module test_nitunit4::test_nitunit4 | tests: 4
+[KO] test_nitunit4$TestTestSuite$test_foo
+     test_nitunit4/test_nitunit4.nit:22,2--26,4: Runtime Error in file nitunit.out/gen_test_nitunit4.nit
+     Output
+       Before Test
+       Tested method
+       After Test
+       Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
 
-DocUnits:
-No doc units found
-Entities: 10; Documented ones: 0; With nitunits: 0; Failures: 0
+[OK] test_nitunit4$TestTestSuite$test_bar
+[KO] test_nitunit4$TestTestSuite$test_baz
+     test_nitunit4/test_nitunit4.nit:32,2--34,4: Difference with expected output: diff -u test_nitunit4/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1
+     Output
+       Diff
+       --- expected:test_nitunit4/test_baz.res
+       +++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
+       @@ -1 +1,3 @@
+       -Bad result file
+       +Before Test
+       +Tested method
+       +After Test
 
-TestSuites:
-Class suites: 1; Test Cases: 1; Failures: 1
-<testsuites><testsuite package="test_nitunit4"></testsuite><testsuite package="test_nitunit4"></testsuite><testsuite></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_foo"><system-err></system-err><system-out>out</system-out><error message="Before Test
+[KO] test_nitunit4$TestTestSuite$test_sav_conflict
+     test_nitunit4/test_nitunit4.nit:36,2--38,4: Conflicting expected output: test_nitunit4/test_nitunit4.sav/test_sav_conflict.res, test_nitunit4/sav/test_sav_conflict.res and test_nitunit4/test_sav_conflict.res all exist
+     Output
+       Before Test
+       Tested method
+       After Test
+
+
+==== Test-suite of module test_nitunit4::test_nitunit4_base | tests: 0
+==== Test-suite of module test_nitunit4::test_nitunit4_base | tests: 0
+
+Docunits: Entities: 21; Documented ones: 0; With nitunits: 0
+Test suites: Classes: 4; Test Cases: 8; Failures: 7
+[FAILURE] 7/8 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit4&gt;"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4::test_bad_comp"></testsuite><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4&#47;test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">test_nitunit4&#47;test_bad_comp.nit:27,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+</failure></testcase></testsuite><testsuite package="test_nitunit4::test_bad_comp2"></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out&#47;gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+</failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">nitunit.out&#47;gen_test_bad_comp2.nit:14,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+</failure></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_foo" time="0.0"><error message="Runtime Error in file nitunit.out&#47;gen_test_nitunit4.nit">Before Test
 Tested method
 After Test
 Runtime error: Assert failed (test_nitunit4&#47;test_nitunit4_base.nit:31)
-"></error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+</error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_bar" time="0.0"><system-err>Before Test
+Tested method
+After Test
+</system-err></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_baz" time="0.0"><error message="Difference with expected output: diff -u test_nitunit4&#47;test_baz.res nitunit.out&#47;gen_test_nitunit4_test_baz.out1">Diff
+--- expected:test_nitunit4&#47;test_baz.res
++++ got:nitunit.out&#47;gen_test_nitunit4_test_baz.out1
+@@ -1 +1,3 @@
+-Bad result file
++Before Test
++Tested method
++After Test
+</error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_sav_conflict" time="0.0"><error message="Conflicting expected output: test_nitunit4&#47;test_nitunit4.sav&#47;test_sav_conflict.res, test_nitunit4&#47;sav&#47;test_sav_conflict.res and test_nitunit4&#47;test_sav_conflict.res all exist">Before Test
+Tested method
+After Test
+</error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite><testsuite package="test_nitunit4_base"></testsuite></testsuites>
\ No newline at end of file
diff --git a/tests/sav/repeating_key_xor_solve.res b/tests/sav/repeating_key_xor_solve.res
new file mode 100644 (file)
index 0000000..8a61309
--- /dev/null
@@ -0,0 +1 @@
+Usage: repeating_key_xor_solve <cipher_file>
diff --git a/tests/sav/repeating_key_xor_solve_args1.res b/tests/sav/repeating_key_xor_solve_args1.res
new file mode 100644 (file)
index 0000000..f81264e
--- /dev/null
@@ -0,0 +1,80 @@
+I'm back and I'm ringin' the bell 
+A rockin' on the mike while the fly girls yell 
+In ecstasy in the back of me 
+Well that's my DJ Deshay cuttin' all them Z's 
+Hittin' hard and the girlies goin' crazy 
+Vanilla's on the mike, man I'm not lazy. 
+
+I'm lettin' my drug kick in 
+It controls my mouth and I begin 
+To just let it flow, let my concepts go 
+My posse's to the side yellin', Go Vanilla Go! 
+
+Smooth 'cause that's the way I will be 
+And if you don't give a damn, then 
+Why you starin' at me 
+So get off 'cause I control the stage 
+There's no dissin' allowed 
+I'm in my own phase 
+The girlies sa y they love me and that is ok 
+And I can dance better than any kid n' play 
+
+Stage 2 -- Yea the one ya' wanna listen to 
+It's off my head so let the beat play through 
+So I can funk it up and make it sound good 
+1-2-3 Yo -- Knock on some wood 
+For good luck, I like my rhymes atrocious 
+Supercalafragilisticexpialidocious 
+I'm an effect and that you can bet 
+I can take a fly girl and make her wet. 
+
+I'm like Samson -- Samson to Delilah 
+There's no denyin', You can try to hang 
+But you'll keep tryin' to get my style 
+Over and over, practice makes perfect 
+But not if you're a loafer. 
+
+You'll get nowhere, no place, no time, no girls 
+Soon -- Oh my God, homebody, you probably eat 
+Spaghetti with a spoon! Come on and say it! 
+
+VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino 
+Intoxicating so you stagger like a wino 
+So punks stop trying and girl stop cryin' 
+Vanilla Ice is sellin' and you people are buyin' 
+'Cause why the freaks are jockin' like Crazy Glue 
+Movin' and groovin' trying to sing along 
+All through the ghetto groovin' this here song 
+Now you're amazed by the VIP posse. 
+
+Steppin' so hard like a German Nazi 
+Startled by the bases hittin' ground 
+There's no trippin' on mine, I'm just gettin' down 
+Sparkamatic, I'm hangin' tight like a fanatic 
+You trapped me once and I thought that 
+You might have it 
+So step down and lend me your ear 
+'89 in my time! You, '90 is my year. 
+
+You're weakenin' fast, YO! and I can tell it 
+Your body's gettin' hot, so, so I can smell it 
+So don't be mad and don't be sad 
+'Cause the lyrics belong to ICE, You can call me Dad 
+You're pitchin' a fit, so step back and endure 
+Let the witch doctor, Ice, do the dance to cure 
+So come up close and don't be square 
+You wanna battle me -- Anytime, anywhere 
+
+You thought that I was weak, Boy, you're dead wrong 
+So come on, everybody and sing this song 
+
+Say -- Play that funky music Say, go white boy, go white boy go 
+play that funky music Go white boy, go white boy, go 
+Lay down and boogie and play that funky music till you die. 
+
+Play that funky music Come on, Come on, let me hear 
+Play that funky music white boy you say it, say it 
+Play that funky music A little louder now 
+Play that funky music, white boy Come on, Come on, Come on 
+Play that funky music 
+
diff --git a/tests/sav/rope_substring_test.res b/tests/sav/rope_substring_test.res
new file mode 100644 (file)
index 0000000..0b3ba6e
--- /dev/null
@@ -0,0 +1,5 @@
+40
+20
+20
+a
+either Rop
index 7a9a2cb..2afbdc5 100644 (file)
@@ -1,7 +1,7 @@
 s isa Bytes
 StringAB
 537472696E674142
-s2 isa FlatString
+s2 isa UnicodeFlatString
 String𐏓
 s3 isa Bytes
 StringA�
@@ -11,13 +11,13 @@ s4 isa Regex
 true
 true
 false
-s5 isa FlatString
+s5 isa UnicodeFlatString
 String�
-s6 isa FlatString
+s6 isa ASCIIFlatString
 \nStr\x00
-s7 isa FlatString
+s7 isa ASCIIFlatString
 \nString66515\x41
-s8 isa FlatString
+s8 isa ASCIIFlatString
 
 String66515A
 s9 isa Regex
index 01698e8..06bc37f 100644 (file)
@@ -12,3 +12,16 @@ Zm9v:     foo
 Zm9vYg==: foob
 Zm9vYmE=: fooba
 Zm9vYmFy: foobar
+Zm9vYg: foob
+Zm9vYmE: fooba
+Zm9v*Yg: foob
+:
+Znm=.is_base64? true
+Znm===.is_base64? false
+Z.sd=.is_base64? false
+Z==D.is_base64? false
+:
+Znm=: No error
+Znm===: Invalid padding length
+Z.sd=: Invalid Base64 character at position 1: .
+Z==D: Invalid padding character D at position 3
index 25574f7..e0353bf 100644 (file)
@@ -1,13 +1,13 @@
  population: 3
  minimum value: 2
- maximum value: 11
- total value: 16
- average value: 5.33
+ maximum value: 12
+ total value: 22
+ average value: 7.33
  distribution:
-  <=2: sub-population=1 (33.33%); cumulated value=2 (12.50%)
-  <=4: sub-population=1 (33.33%); cumulated value=3 (18.75%)
-  <=16: sub-population=1 (33.33%); cumulated value=11 (68.75%)
+  <=2: sub-population=1 (33.33%); cumulated value=2 (9.09%)
+  <=8: sub-population=1 (33.33%); cumulated value=8 (36.36%)
+  <=16: sub-population=1 (33.33%); cumulated value=12 (54.54%)
  list:
-  nit: 11 (68.75%)
-  : 3 (18.75%)
-  ini: 2 (12.50%)
+  nit: 12 (54.54%)
+  : 8 (36.36%)
+  ini: 2 (9.09%)
diff --git a/tests/sav/test_copy_to_native.res b/tests/sav/test_copy_to_native.res
new file mode 100644 (file)
index 0000000..764d170
--- /dev/null
@@ -0,0 +1 @@
+gâštr
diff --git a/tests/sav/test_copy_to_native_alt1.res b/tests/sav/test_copy_to_native_alt1.res
new file mode 100644 (file)
index 0000000..785cb5d
--- /dev/null
@@ -0,0 +1 @@
+gâštr%D
diff --git a/tests/sav/test_copy_to_native_alt2.res b/tests/sav/test_copy_to_native_alt2.res
new file mode 100644 (file)
index 0000000..ec88d9a
--- /dev/null
@@ -0,0 +1 @@
+gâštré
diff --git a/tests/sav/test_csv.res b/tests/sav/test_csv.res
new file mode 100644 (file)
index 0000000..e69de29
index 6020b5f..a69d953 100644 (file)
@@ -1,4 +1,4 @@
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
 i=5 s=Hello
 i:5; s:Hello
 
@@ -6,7 +6,7 @@ true
 true
 true
 
-<B i: <Int> s: <FlatString> a: <A i: <Int> s: <FlatString>>>
+<B i: <Int> s: <ASCIIFlatString> a: <A i: <Int> s: <ASCIIFlatString>>>
 i=100 s=World a=i:5; s:Hello
 i:100; s:World; a:i:5; s:Hello
 
index 88d1f35..c3e02fb 100644 (file)
@@ -1,14 +1,14 @@
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
 i=5 s=Hello
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
 
 true
 true
 true
 
-<B i: <Int> s: <FlatString> a: <A i: <Int> s: <FlatString>>>
-i=100 s=World a=<A i: <Int> s: <FlatString>>
-<B i: <Int> s: <FlatString> a: <A i: <Int> s: <FlatString>>>
+<B i: <Int> s: <ASCIIFlatString> a: <A i: <Int> s: <ASCIIFlatString>>>
+i=100 s=World a=<A i: <Int> s: <ASCIIFlatString>>
+<B i: <Int> s: <ASCIIFlatString> a: <A i: <Int> s: <ASCIIFlatString>>>
 
 true
 true
index 7af6a70..febe39e 100644 (file)
@@ -1,4 +1,4 @@
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
 i=5 s=Hello
 i:5; s:Hello
 
@@ -6,7 +6,7 @@ false
 false
 true
 
-<B i: <Int> s: <FlatString> a: <A i: <Int> s: <FlatString>>>
+<B i: <Int> s: <ASCIIFlatString> a: <A i: <Int> s: <ASCIIFlatString>>>
 i=100 s=World a=i:5; s:Hello
 i:100; s:World; a:i:5; s:Hello
 
index 9e385ed..1a65e90 100644 (file)
@@ -1,4 +1,4 @@
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
 i=5 s=Hello
 i:5; s:Hello
 
@@ -6,7 +6,7 @@ true
 true
 true
 
-<B i: <Int> s: <FlatString> a: <A i: <Int> s: <FlatString>>>
+<B i: <Int> s: <ASCIIFlatString> a: <A i: <Int> s: <ASCIIFlatString>>>
 i=100 s=World
 i:100; s:World
 
index 16cf4a0..490666d 100644 (file)
@@ -1,4 +1,4 @@
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
 i=5 s=Hello
 i:5; s:Hello
 
@@ -6,7 +6,7 @@ true
 true
 true
 
-<B a: <A i: <Int> s: <FlatString>>>
+<B a: <A i: <Int> s: <ASCIIFlatString>>>
 string=World a=i:5; s:Hello
 string:World; a:i:5; s:Hello
 
diff --git a/tests/sav/test_flatbuf_from.res b/tests/sav/test_flatbuf_from.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/test_flatbuf_from_alt1.res b/tests/sav/test_flatbuf_from_alt1.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/test_flatbuf_from_alt2.res b/tests/sav/test_flatbuf_from_alt2.res
new file mode 100644 (file)
index 0000000..e69de29
index 6919258..8faceb9 100644 (file)
@@ -2,6 +2,7 @@
        <meta charset="utf-8">
        <style type="text/css">
        .nitcode a { color: inherit; cursor:pointer; }
+.nitcode .titled:hover { text-decoration: underline; } /* underline titles */
 .nitcode .popupable:hover { text-decoration: underline; cursor:help; } /* underline titles */
 .nitcode .foldable { display: block } /* for block productions*/
 .nitcode .line{ display: block } /* for lines */
 
        </head><body>
        
-<h1>base_simple3#Int#output</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Int#output"><span class="line" id="L26">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Int#output" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Int#output&#34;&gt;base_simple3#Int#output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span></span></span></span></code></pre>
-<h1>base_simple3#A#Object::init</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#A#init"><span class="line" id="L30">  <span class="nc_k">init</span> <span class="nc_k">do</span> <span class="nc_l">5</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h1>base_simple3#A#run</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#A#run"><span class="line" id="L31">   <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#A#run" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#A#run&#34;&gt;base_simple3#A#run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_l">6</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h1>base_simple3#B#_val</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h1>base_simple3#B#val</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h1>base_simple3#B#val=</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h1>base_simple3#B#autoinit</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#B#autoinit"><span class="line" id="L36">      <span class="nc_k">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
+<h1 id="base_simple3___base_simple3__Int___output">base_simple3$Int$output</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$Int$output"><span class="line" id="L26">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__A___Object__init">base_simple3$A$Object::init</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$A$init"><span class="line" id="L30">  <span class="nc_k">init</span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">5</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__A___run">base_simple3$A$run</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$A$run"><span class="line" id="L31">   <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">6</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__B____val">base_simple3$B$_val</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__B___val">base_simple3$B$val</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__B___autoinit">base_simple3$B$autoinit</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$autoinit"><span class="line" id="L36">      <span class="nc_k popupable" style="border-bottom: solid 2px red" title="Messages" data-content="&lt;div&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;1 message(s)&lt;&#47;b&gt; &lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;Warning: init with signature in base_simple3$B&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">init</span><span>(</span><span class="nc_i nc_v popupable" title="v: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;v:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">v</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span><span>)</span>
 </span><span class="line" id="L37">    <span class="nc_k">do</span>
-</span><span class="line" id="L38">            <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L39">            <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
+</span><span class="line" id="L38">            <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">7</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L39">            <span class="nc_k popupable" title="B" data-title="&lt;a href=&#34;#base_simple3__B&#34;&gt;B&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">self</span><span class="popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span> <span class="popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">=</span> <span class="nc_i nc_v popupable" title="v: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;v:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">v</span>
 </span><span class="line" id="L40">    <span class="nc_k">end</span></span></span></span></code></pre>
-<h1>base_simple3#B#run</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#B#run"><span class="line" id="L41">   <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#B#run" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#B#run&#34;&gt;base_simple3#B#run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_i">val</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h1>base_simple3#C#_val1</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#C#_val1"><a id="base_simple3#C#val1"></a><a id="base_simple3#C#val1="></a><span class="line" id="L45">        <span class="nc_k">var</span> <span class="nc_def nc_i">val1</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h1>base_simple3#C#val1</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#C#_val1"><a id="base_simple3#C#val1"></a><a id="base_simple3#C#val1="></a><span class="line" id="L45">        <span class="nc_k">var</span> <span class="nc_def nc_i">val1</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h1>base_simple3#C#val1=</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#C#_val1"><a id="base_simple3#C#val1"></a><a id="base_simple3#C#val1="></a><span class="line" id="L45">        <span class="nc_k">var</span> <span class="nc_def nc_i">val1</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h1>base_simple3#C#_val2</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#C#_val2"><a id="base_simple3#C#val2"></a><a id="base_simple3#C#val2="></a><span class="line" id="L46">        <span class="nc_k">var</span> <span class="nc_def nc_i">val2</span><span>:</span> <span class="nc_t">Int</span> <span>=</span> <span class="nc_l">10</span></span></span></span></code></pre>
-<h1>base_simple3#C#val2</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#C#_val2"><a id="base_simple3#C#val2"></a><a id="base_simple3#C#val2="></a><span class="line" id="L46">        <span class="nc_k">var</span> <span class="nc_def nc_i">val2</span><span>:</span> <span class="nc_t">Int</span> <span>=</span> <span class="nc_l">10</span></span></span></span></code></pre>
-<h1>base_simple3#C#val2=</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#C#_val2"><a id="base_simple3#C#val2"></a><a id="base_simple3#C#val2="></a><span class="line" id="L46">        <span class="nc_k">var</span> <span class="nc_def nc_i">val2</span><span>:</span> <span class="nc_t">Int</span> <span>=</span> <span class="nc_l">10</span></span></span></span></code></pre>
-<h1>base_simple3#Sys#foo</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#foo" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#foo&#34;&gt;base_simple3#Sys#foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l">2</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h1>base_simple3#Sys#bar</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#bar"><span class="line" id="L50"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#bar" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#bar&#34;&gt;base_simple3#Sys#bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;bar&lt;span&gt;(i: &lt;a href=&#34;base_simple3.html#base_simple3#Int&#34;&gt;Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_v nc_i">i</span><span>:</span> <span class="nc_t">Int</span><span>)</span> <span class="nc_k">do</span> <span class="nc_v nc_i">i</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h1>base_simple3#Sys#baz</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#baz"><span class="line" id="L51"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#baz" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#baz&#34;&gt;base_simple3#Sys#baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;baz&lt;span&gt;: &lt;a href=&#34;base_simple3.html#base_simple3#Int&#34;&gt;Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t">Int</span> <span class="nc_k">do</span> <span class="nc_k">return</span> <span class="nc_l">4</span></span></span></span></code></pre>
-<h1>base_simple3#Sys#main</h1>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#main"><span class="line" id="L53"><span class="nc_l">1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L54"><span class="nc_i">foo</span>
-</span><span class="line" id="L55"><span class="nc_i">bar</span><span>(</span><span class="nc_l">3</span><span>)</span>
-</span><span class="line" id="L56"><span class="nc_i">baz</span><span>.</span><span class="nc_i">output</span>
+<h1 id="base_simple3___base_simple3__B___run">base_simple3$B$run</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$B$run"><span class="line" id="L41">   <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_i popupable" title="call base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;call base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__C____val1">base_simple3$C$_val1</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$C$_val1"><a id="base_simple3$C$val1"></a><a id="base_simple3$C$val1="></a><span class="line" id="L45">        <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val1&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__C___val1">base_simple3$C$val1</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$C$_val1"><a id="base_simple3$C$val1"></a><a id="base_simple3$C$val1="></a><span class="line" id="L45">        <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val1&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__C___val1_61d">base_simple3$C$val1=</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$C$_val1"><a id="base_simple3$C$val1"></a><a id="base_simple3$C$val1="></a><span class="line" id="L45">        <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val1&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__C____val2">base_simple3$C$_val2</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$C$_val2"><a id="base_simple3$C$val2"></a><a id="base_simple3$C$val2="></a><span class="line" id="L46">        <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val2&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">10</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__C___val2">base_simple3$C$val2</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$C$_val2"><a id="base_simple3$C$val2"></a><a id="base_simple3$C$val2="></a><span class="line" id="L46">        <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val2&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">10</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__C___val2_61d">base_simple3$C$val2=</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$C$_val2"><a id="base_simple3$C$val2"></a><a id="base_simple3$C$val2="></a><span class="line" id="L46">        <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val2&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">10</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__Sys___foo">base_simple3$Sys$foo</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$Sys$foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$foo" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___foo&#34;&gt;base_simple3$Sys$foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">2</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__Sys___bar">base_simple3$Sys$bar</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$Sys$bar"><span class="line" id="L50"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$bar" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___bar&#34;&gt;base_simple3$Sys$bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;bar&lt;span&gt;(i: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_i nc_v popupable" title="i: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;i:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">i</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span><span>)</span> <span class="nc_k">do</span> <span class="nc_i nc_v popupable" title="i: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;i:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">i</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__Sys___baz">base_simple3$Sys$baz</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$Sys$baz"><span class="line" id="L51"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$baz" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___baz&#34;&gt;base_simple3$Sys$baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;baz&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span class="nc_k">do</span> <span class="nc_k">return</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">4</span></span></span></span></code></pre>
+<h1 id="base_simple3___base_simple3__Sys___main">base_simple3$Sys$main</h1>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$Sys$main"><span class="line" id="L53"><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">1</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L54"><span class="nc_i popupable" title="call base_simple3$Sys$foo" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___foo&#34;&gt;call base_simple3$Sys$foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys___foo&#34;&gt;base_simple3$Sys$foo&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">foo</span>
+</span><span class="line" id="L55"><span class="nc_i popupable" title="call base_simple3$Sys$bar" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___bar&#34;&gt;call base_simple3$Sys$bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys___bar&#34;&gt;base_simple3$Sys$bar&lt;&#47;a&gt;&lt;span&gt;(i: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">bar</span><span>(</span><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">3</span><span>)</span>
+</span><span class="line" id="L56"><span class="nc_i popupable" title="call base_simple3$Sys$baz" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___baz&#34;&gt;call base_simple3$Sys$baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys___baz&#34;&gt;base_simple3$Sys$baz&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">baz</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span><span class="line" id="L57">
-</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_v nc_i">a</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">A</span>
-</span><span class="line" id="L59"><span class="nc_v nc_i">a</span><span>.</span><span class="nc_i">run</span>
+</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="a: A" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;a:&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">a</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$A$autoinit" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___autoinit&#34;&gt;call base_simple3$A$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A___autoinit&#34;&gt;base_simple3$A$autoinit&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="A" data-title="&lt;a href=&#34;#base_simple3__A&#34;&gt;A&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">A</span>
+</span><span class="line" id="L59"><span class="nc_i nc_v popupable" title="a: A" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;a:&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">a</span><span class="popupable" title="call base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;call base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;call base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">run</span>
 </span><span class="line" id="L60">
-</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_v nc_i">b</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">B</span><span>(</span><span class="nc_l">8</span><span>)</span>
-</span><span class="line" id="L62"><span class="nc_v nc_i">b</span><span>.</span><span class="nc_i">run</span>
+</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="b: B" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;b:&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">b</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$B$autoinit" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___autoinit&#34;&gt;call base_simple3$B$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___autoinit&#34;&gt;base_simple3$B$autoinit&lt;&#47;a&gt;&lt;span&gt;(v: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="B" data-title="&lt;a href=&#34;#base_simple3__B&#34;&gt;B&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">B</span><span>(</span><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">8</span><span>)</span>
+</span><span class="line" id="L62"><span class="nc_i nc_v popupable" title="b: B" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;b:&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">b</span><span class="popupable" title="call base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;call base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;call base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">run</span>
 </span><span class="line" id="L63">
-</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_v nc_i">c</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">C</span><span>(</span><span class="nc_l">9</span><span>)</span>
-</span><span class="line" id="L65"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L66"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val2</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
+</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$C$autoinit" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___autoinit&#34;&gt;call base_simple3$C$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___autoinit&#34;&gt;base_simple3$C$autoinit&lt;&#47;a&gt;&lt;span&gt;(val1: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="C" data-title="&lt;a href=&#34;#base_simple3__C&#34;&gt;C&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">C</span><span>(</span><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">9</span><span>)</span>
+</span><span class="line" id="L65"><span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;call base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;call base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L66"><span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;call base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;call base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
 <h2>AST node: AModule at base_simple3.nit:17,1--66,13</h2>
 <pre><code><span class="nitcode"><span class="line" id="L17"><span class="nc_k">import</span> <span class="nc_k">end</span>
 </span><span class="line" id="L18">
-</span><span class="nc_cdef foldable" id="base_simple3#Object"><span class="line" id="L19"><span class="nc_k">interface</span> <span class="nc_t">Object</span>
+</span><span class="nc_cdef foldable" id="base_simple3$Object"><span class="line" id="L19"><span class="nc_k">interface</span> <span class="nc_t nc_def popupable" title="class Object" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;class Object&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Object&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; sub-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Bool&#34;&gt;base_simple3$Bool&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys&#34;&gt;base_simple3$Sys&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Object</span>
 </span><span class="line" id="L20"><span class="nc_k">end</span>
 </span></span><span class="line" id="L21">
-</span><span class="nc_cdef foldable" id="base_simple3#Bool"><span class="line" id="L22"><span class="nc_k">enum</span> <span class="nc_t">Bool</span>
+</span><span class="nc_cdef foldable" id="base_simple3$Bool"><span class="line" id="L22"><span class="nc_k">enum</span> <span class="nc_t nc_def popupable" title="class Bool" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Bool&#34;&gt;class Bool&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Bool&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Bool</span>
 </span><span class="line" id="L23"><span class="nc_k">end</span>
 </span></span><span class="line" id="L24">
-</span><span class="nc_cdef foldable" id="base_simple3#Int"><span class="line" id="L25"><span class="nc_k">enum</span> <span class="nc_t">Int</span>
-</span><span class="nc_pdef foldable" id="base_simple3#Int#output"><span class="line" id="L26">        <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Int#output" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Int#output&#34;&gt;base_simple3#Int#output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span>
+</span><span class="nc_cdef foldable" id="base_simple3$Int"><span class="line" id="L25"><span class="nc_k">enum</span> <span class="nc_t nc_def popupable" title="class Int" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;class Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Int&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span>
+</span><span class="nc_pdef foldable" id="base_simple3$Int$output"><span class="line" id="L26">        <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span>
 </span></span><span class="line" id="L27"><span class="nc_k">end</span>
 </span></span><span class="line" id="L28">
-</span><span class="nc_cdef foldable" id="base_simple3#A"><span class="line" id="L29"><span class="nc_k">class</span> <span class="nc_t">A</span>
-</span><span class="nc_pdef foldable" id="base_simple3#A#init"><span class="line" id="L30">    <span class="nc_k">init</span> <span class="nc_k">do</span> <span class="nc_l">5</span><span>.</span><span class="nc_i">output</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#A#run"><span class="line" id="L31">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#A#run" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#A#run&#34;&gt;base_simple3#A#run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_l">6</span><span>.</span><span class="nc_i">output</span>
+</span><span class="nc_cdef foldable" id="base_simple3$A"><span class="line" id="L29"><span class="nc_k">class</span> <span class="nc_t nc_def popupable" title="class A" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;class A&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;A&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">A</span>
+</span><span class="nc_pdef foldable" id="base_simple3$A$init"><span class="line" id="L30">    <span class="nc_k">init</span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">5</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$A$run"><span class="line" id="L31">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">6</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span></span><span class="line" id="L32"><span class="nc_k">end</span>
 </span></span><span class="line" id="L33">
-</span><span class="nc_cdef foldable" id="base_simple3#B"><span class="line" id="L34"><span class="nc_k">class</span> <span class="nc_t">B</span>
-</span><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35">     <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#B#autoinit"><span class="line" id="L36"> <span class="nc_k">init</span><span>(</span><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span><span>)</span>
+</span><span class="nc_cdef foldable" id="base_simple3$B"><span class="line" id="L34"><span class="nc_k">class</span> <span class="nc_t nc_def popupable" title="class B" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;class B&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;B&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">B</span>
+</span><span class="nc_pdef foldable" id="base_simple3$B$_val"><a id="base_simple3$B$val"></a><a id="base_simple3$B$val="></a><span class="line" id="L35">     <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$B$autoinit"><span class="line" id="L36"> <span class="nc_k popupable" style="border-bottom: solid 2px red" title="Messages" data-content="&lt;div&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;1 message(s)&lt;&#47;b&gt; &lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;Warning: init with signature in base_simple3$B&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">init</span><span>(</span><span class="nc_i nc_v popupable" title="v: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;v:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">v</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span><span>)</span>
 </span><span class="line" id="L37">    <span class="nc_k">do</span>
-</span><span class="line" id="L38">            <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L39">            <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
+</span><span class="line" id="L38">            <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">7</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L39">            <span class="nc_k popupable" title="B" data-title="&lt;a href=&#34;#base_simple3__B&#34;&gt;B&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">self</span><span class="popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span> <span class="popupable" title="call base_simple3$B$val=" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;call base_simple3$B$val=&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val_61d&#34;&gt;base_simple3$B$val=&lt;&#47;a&gt;&lt;span&gt;(val: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">=</span> <span class="nc_i nc_v popupable" title="v: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;v:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">v</span>
 </span><span class="line" id="L40">    <span class="nc_k">end</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#B#run"><span class="line" id="L41">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#B#run" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#B#run&#34;&gt;base_simple3#B#run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_i">val</span><span>.</span><span class="nc_i">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$B$run"><span class="line" id="L41">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;run&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">run</span></span> <span class="nc_k">do</span> <span class="nc_i popupable" title="call base_simple3$B$val" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;call base_simple3$B$val&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___val&#34;&gt;base_simple3$B$val&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span></span><span class="line" id="L42"><span class="nc_k">end</span>
 </span></span><span class="line" id="L43">
-</span><span class="nc_cdef foldable" id="base_simple3#C"><span class="line" id="L44"><span class="nc_k">class</span> <span class="nc_t">C</span>
-</span><span class="nc_pdef foldable" id="base_simple3#C#_val1"><a id="base_simple3#C#val1"></a><a id="base_simple3#C#val1="></a><span class="line" id="L45">  <span class="nc_k">var</span> <span class="nc_def nc_i">val1</span><span>:</span> <span class="nc_t">Int</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#C#_val2"><a id="base_simple3#C#val2"></a><a id="base_simple3#C#val2="></a><span class="line" id="L46">   <span class="nc_k">var</span> <span class="nc_def nc_i">val2</span><span>:</span> <span class="nc_t">Int</span> <span>=</span> <span class="nc_l">10</span>
+</span><span class="nc_cdef foldable" id="base_simple3$C"><span class="line" id="L44"><span class="nc_k">class</span> <span class="nc_t nc_def popupable" title="class C" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;class C&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;C&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; super-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;base_simple3$Object&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">C</span>
+</span><span class="nc_pdef foldable" id="base_simple3$C$_val1"><a id="base_simple3$C$val1"></a><a id="base_simple3$C$val1="></a><span class="line" id="L45">  <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val1&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$C$_val2"><a id="base_simple3$C$val2"></a><a id="base_simple3$C$val2="></a><span class="line" id="L46">   <span class="nc_k">var</span> <span class="nc_i nc_def popupable" title="base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;val2&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">10</span>
 </span></span><span class="line" id="L47"><span class="nc_k">end</span>
 </span></span><span class="line" id="L48">
-</span><span class="nc_pdef foldable" id="base_simple3#Sys#foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#foo" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#foo&#34;&gt;base_simple3#Sys#foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l">2</span><span>.</span><span class="nc_i">output</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#Sys#bar"><span class="line" id="L50"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#bar" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#bar&#34;&gt;base_simple3#Sys#bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;bar&lt;span&gt;(i: &lt;a href=&#34;base_simple3.html#base_simple3#Int&#34;&gt;Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_v nc_i">i</span><span>:</span> <span class="nc_t">Int</span><span>)</span> <span class="nc_k">do</span> <span class="nc_v nc_i">i</span><span>.</span><span class="nc_i">output</span>
-</span></span><span class="nc_pdef foldable" id="base_simple3#Sys#baz"><span class="line" id="L51"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#baz" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#baz&#34;&gt;base_simple3#Sys#baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;baz&lt;span&gt;: &lt;a href=&#34;base_simple3.html#base_simple3#Int&#34;&gt;Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t">Int</span> <span class="nc_k">do</span> <span class="nc_k">return</span> <span class="nc_l">4</span>
+</span><span class="nc_pdef foldable" id="base_simple3$Sys$foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$foo" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___foo&#34;&gt;base_simple3$Sys$foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">2</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$Sys$bar"><span class="line" id="L50"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$bar" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___bar&#34;&gt;base_simple3$Sys$bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;bar&lt;span&gt;(i: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_i nc_v popupable" title="i: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;i:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">i</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span><span>)</span> <span class="nc_k">do</span> <span class="nc_i nc_v popupable" title="i: Int" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;i:&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">i</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span></span><span class="nc_pdef foldable" id="base_simple3$Sys$baz"><span class="line" id="L51"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Sys$baz" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___baz&#34;&gt;base_simple3$Sys$baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;baz&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">Int</span> <span class="nc_k">do</span> <span class="nc_k">return</span> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">4</span>
 </span></span><span class="line" id="L52">
-</span><span class="nc_pdef foldable" id="base_simple3#Sys#main"><span class="line" id="L53"><span class="nc_l">1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L54"><span class="nc_i">foo</span>
-</span><span class="line" id="L55"><span class="nc_i">bar</span><span>(</span><span class="nc_l">3</span><span>)</span>
-</span><span class="line" id="L56"><span class="nc_i">baz</span><span>.</span><span class="nc_i">output</span>
+</span><span class="nc_pdef foldable" id="base_simple3$Sys$main"><span class="line" id="L53"><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">1</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L54"><span class="nc_i popupable" title="call base_simple3$Sys$foo" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___foo&#34;&gt;call base_simple3$Sys$foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys___foo&#34;&gt;base_simple3$Sys$foo&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">foo</span>
+</span><span class="line" id="L55"><span class="nc_i popupable" title="call base_simple3$Sys$bar" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___bar&#34;&gt;call base_simple3$Sys$bar&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys___bar&#34;&gt;base_simple3$Sys$bar&lt;&#47;a&gt;&lt;span&gt;(i: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">bar</span><span>(</span><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">3</span><span>)</span>
+</span><span class="line" id="L56"><span class="nc_i popupable" title="call base_simple3$Sys$baz" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Sys___baz&#34;&gt;call base_simple3$Sys$baz&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys___baz&#34;&gt;base_simple3$Sys$baz&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">baz</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
 </span><span class="line" id="L57">
-</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_v nc_i">a</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">A</span>
-</span><span class="line" id="L59"><span class="nc_v nc_i">a</span><span>.</span><span class="nc_i">run</span>
+</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="a: A" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;a:&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">a</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$A$autoinit" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___autoinit&#34;&gt;call base_simple3$A$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A___autoinit&#34;&gt;base_simple3$A$autoinit&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="A" data-title="&lt;a href=&#34;#base_simple3__A&#34;&gt;A&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">A</span>
+</span><span class="line" id="L59"><span class="nc_i nc_v popupable" title="a: A" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;a:&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">a</span><span class="popupable" title="call base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;call base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$A$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;call base_simple3$A$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__A___run&#34;&gt;base_simple3$A$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">run</span>
 </span><span class="line" id="L60">
-</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_v nc_i">b</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">B</span><span>(</span><span class="nc_l">8</span><span>)</span>
-</span><span class="line" id="L62"><span class="nc_v nc_i">b</span><span>.</span><span class="nc_i">run</span>
+</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="b: B" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;b:&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">b</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$B$autoinit" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___autoinit&#34;&gt;call base_simple3$B$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___autoinit&#34;&gt;base_simple3$B$autoinit&lt;&#47;a&gt;&lt;span&gt;(v: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="B" data-title="&lt;a href=&#34;#base_simple3__B&#34;&gt;B&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">B</span><span>(</span><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">8</span><span>)</span>
+</span><span class="line" id="L62"><span class="nc_i nc_v popupable" title="b: B" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;b:&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">b</span><span class="popupable" title="call base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;call base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$run" data-title="&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;call base_simple3$B$run&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__B___run&#34;&gt;base_simple3$B$run&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">run</span>
 </span><span class="line" id="L63">
-</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_v nc_i">c</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">C</span><span>(</span><span class="nc_l">9</span><span>)</span>
-</span><span class="line" id="L65"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L66"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val2</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
+</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$C$autoinit" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___autoinit&#34;&gt;call base_simple3$C$autoinit&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___autoinit&#34;&gt;base_simple3$C$autoinit&lt;&#47;a&gt;&lt;span&gt;(val1: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;)&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">new</span> <span class="nc_t popupable" title="C" data-title="&lt;a href=&#34;#base_simple3__C&#34;&gt;C&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">C</span><span>(</span><span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">9</span><span>)</span>
+</span><span class="line" id="L65"><span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;call base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val1" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;call base_simple3$C$val1&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val1&#34;&gt;base_simple3$C$val1&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val1</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span>
+</span><span class="line" id="L66"><span class="nc_i nc_v popupable" title="c: C" data-content="&lt;div&gt;&lt;b&gt;local var&lt;&#47;b&gt; &lt;span&gt;c:&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;call base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val2" data-title="&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;call base_simple3$C$val2&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__C___val2&#34;&gt;base_simple3$C$val2&lt;&#47;a&gt;&lt;span&gt;: &lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">val2</span><span class="popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;call base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;call&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">output</span></span></span></span></code></pre>
 <h2>AST node: ANoImport at base_simple3.nit:17,1--10</h2>
 <pre><code><span class="nitcode"><span class="line" id="L17"><span class="nc_k">import</span> <span class="nc_k">end</span></span></span></code></pre>
 <h2>AST node: APublicVisibility at base_simple3.nit:17,1</h2>
 <h2>AST node: TKwend at base_simple3.nit:17,8--10</h2>
 <pre><code><span class="nitcode"><span class="line" id="L17"> <span class="nc_k">end</span></span></span></code></pre>
 <h2>AST node: AStdClassdef at base_simple3.nit:19,1--20,3</h2>
-<pre><code><span class="nitcode"><span class="nc_cdef foldable" id="base_simple3#Object"><span class="line" id="L19"><span class="nc_k">interface</span> <span class="nc_t">Object</span>
+<pre><code><span class="nitcode"><span class="nc_cdef foldable" id="base_simple3$Object"><span class="line" id="L19"><span class="nc_k">interface</span> <span class="nc_t nc_def popupable" title="class Object" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;class Object&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Object&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; sub-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Bool&#34;&gt;base_simple3$Bool&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys&#34;&gt;base_simple3$Sys&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Object</span>
 </span><span class="line" id="L20"><span class="nc_k">end</span></span></span></span></code></pre>
 <h2>AST node: AInterfaceClasskind at base_simple3.nit:19,1--9</h2>
 <pre><code><span class="nitcode"><span class="line" id="L19"><span class="nc_k">interface</span></span></span></code></pre>
 <h2>AST node: TKwinterface at base_simple3.nit:19,1--9</h2>
 <pre><code><span class="nitcode"><span class="line" id="L19"><span class="nc_k">interface</span></span></span></code></pre>
 <h2>AST node: AQclassid at base_simple3.nit:19,11--16</h2>
-<pre><code><span class="nitcode"><span class="line" id="L19"> <span class="nc_t">Object</span></span></span></code></pre>
+<pre><code><span class="nitcode"><span class="line" id="L19"> <span class="nc_t nc_def popupable" title="class Object" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;class Object&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Object&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; sub-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Bool&#34;&gt;base_simple3$Bool&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys&#34;&gt;base_simple3$Sys&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Object</span></span></span></code></pre>
 <h2>AST node: TClassid at base_simple3.nit:19,11--16</h2>
-<pre><code><span class="nitcode"><span class="line" id="L19"> <span class="nc_t">Object</span></span></span></code></pre>
+<pre><code><span class="nitcode"><span class="line" id="L19"> <span class="nc_t nc_def popupable" title="class Object" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Object&#34;&gt;class Object&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;Object&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;div class=&#34;dropdown&#34;&gt; &lt;a data-toggle=&#34;dropdown&#34; href=&#34;#&#34;&gt;&lt;b&gt;hier&lt;&#47;b&gt; sub-classes&lt;span class=&#34;caret&#34;&gt;&lt;&#47;span&gt;&lt;&#47;a&gt;&lt;ul class=&#34;dropdown-menu&#34; role=&#34;menu&#34; aria-labelledby=&#34;dLabel&#34;&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Bool&#34;&gt;base_simple3$Bool&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__A&#34;&gt;base_simple3$A&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__B&#34;&gt;base_simple3$B&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__C&#34;&gt;base_simple3$C&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;li&gt;&lt;a href=&#34;#base_simple3___base_simple3__Sys&#34;&gt;base_simple3$Sys&lt;&#47;a&gt;&lt;&#47;li&gt;&lt;&#47;ul&gt;&lt;&#47;div&gt;&lt;&#47;div&gt;" data-toggle="popover">Object</span></span></span></code></pre>
 <h2>AST node: AEnumClasskind at base_simple3.nit:22,1--4</h2>
 <pre><code><span class="nitcode"><span class="line" id="L22"><span class="nc_k">enum</span></span></span></code></pre>
 <h2>AST node: TKwenum at base_simple3.nit:22,1--4</h2>
 <pre><code><span class="nitcode"><span class="line" id="L22"><span class="nc_k">enum</span></span></span></code></pre>
 <h2>AST node: AMethPropdef at base_simple3.nit:26,2--21</h2>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Int#output"><span class="line" id="L26">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Int#output" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Int#output&#34;&gt;base_simple3#Int#output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span></span></span></span></code></pre>
+<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3$Int$output"><span class="line" id="L26">      <span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">output</span></span> <span class="nc_k">is</span> <span class="nc_i">intern</span></span></span></span></code></pre>
 <h2>AST node: TKwmeth at base_simple3.nit:26,2--4</h2>
-<pre><code><span class="nc_pdef foldable" id="base_simple3#Int#output"><span class="line" id="L26">    <span class="nc_k">fun</span></span></span></code></pre>
+<pre><code><span class="nc_pdef foldable" id="base_simple3$Int$output"><span class="line" id="L26">    <span class="nc_k">fun</span></span></span></code></pre>
 <h2>AST node: AIdMethid at base_simple3.nit:26,6--11</h2>
-<pre><code><span class="nitcode"><span class="nc_def popupable foldable" title="base_simple3#Int#output" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Int#output&#34;&gt;base_simple3#Int#output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="line" id="L26"> <span class="nc_i">output</span></span></span></span></code></pre>
+<pre><code><span class="nitcode"><span class="nc_def popupable foldable" title="base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="line" id="L26"> <span class="nc_i">output</span></span></span></span></code></pre>
 <h2>AST node: TId at base_simple3.nit:26,6--11</h2>
-<pre><code><span class="nitcode"><span class="nc_def popupable foldable" title="base_simple3#Int#output" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Int#output&#34;&gt;base_simple3#Int#output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="line" id="L26"> <span class="nc_i">output</span></span></span></span></code></pre>
+<pre><code><span class="nitcode"><span class="nc_def popupable foldable" title="base_simple3$Int$output" data-title="&lt;a href=&#34;#base_simple3___base_simple3__Int___output&#34;&gt;base_simple3$Int$output&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;output&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="line" id="L26"> <span class="nc_i">output</span></span></span></span></code></pre>
 <h2>AST node: ASignature at base_simple3.nit:26,13</h2>
 <pre><code><span class="nitcode"></span></code></pre>
 <h2>AST node: AAnnotations at base_simple3.nit:26,13--21</h2>
 <h2>AST node: TKwclass at base_simple3.nit:29,1--5</h2>
 <pre><code><span class="nitcode"><span class="line" id="L29"><span class="nc_k">class</span></span></span></code></pre>
 <h2>AST node: TKwinit at base_simple3.nit:30,2--5</h2>
-<pre><code><span class="nc_pdef foldable" id="base_simple3#A#init"><span class="line" id="L30">        <span class="nc_k">init</span></span></span></code></pre>
+<pre><code><span class="nc_pdef foldable" id="base_simple3$A$init"><span class="line" id="L30">        <span class="nc_k">init</span></span></span></code></pre>
 <h2>AST node: TKwdo at base_simple3.nit:30,7--8</h2>
 <pre><code><span class="nitcode"><span class="line" id="L30"> <span class="nc_k">do</span></span></span></code></pre>
 <h2>AST node: ACallExpr at base_simple3.nit:30,10--17</h2>
-<pre><code><span class="nitcode"><span class="line" id="L30"> <span class="nc_l">5</span><span>.</span><span class="nc_i">output</span></span></span></code></pre>
-<h2>AST node: AIntegerExpr at base_simple3.nit:30,10</h2>
-<pre><code><span class="nitcode"><span class="line" id="L30"> <span class="nc_l">5</span></span></span></code></pre>
-<h2>AST node: TInteger at base_simple3.nit:30,10</h2>
-<pre><code><span class="nitcode"><span class="line" id="L30"> <span class="nc_l">5</span></span></span></code></pre>
-<h2>AST node: AQid at base_simple3.nit:30,12--17</h2>
-<pre><code><span class="nitcode"><span class="line" id="L30"><span class="nc_i">output</span></span></span></code></pre>
-<h2>AST node: AListExprs at base_simple3.nit:30,17</h2>
-<pre><code><span class="nitcode"></span></code></pre>
-<h2>AST node: AAttrPropdef at base_simple3.nit:35,2--13</h2>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35">   <span class="nc_k">var</span> <span class="nc_def nc_i">val</span><span>:</span> <span class="nc_t">Int</span></span></span></span></code></pre>
-<h2>AST node: TKwvar at base_simple3.nit:35,2--4</h2>
-<pre><code><span class="nc_pdef foldable" id="base_simple3#B#_val"><a id="base_simple3#B#val"></a><a id="base_simple3#B#val="></a><span class="line" id="L35"> <span class="nc_k">var</span></span></span></code></pre>
-<h2>AST node: AType at base_simple3.nit:35,11--13</h2>
-<pre><code><span class="nitcode"><span class="line" id="L35"> <span class="nc_t">Int</span></span></span></code></pre>
-<h2>AST node: TOpar at base_simple3.nit:36,6</h2>
-<pre><code><span class="nitcode"><span class="line" id="L36"><span>(</span></span></span></code></pre>
-<h2>AST node: AParam at base_simple3.nit:36,7--12</h2>
-<pre><code><span class="nitcode"><span class="line" id="L36"><span class="nc_v nc_i">v</span><span>:</span> <span class="nc_t">Int</span></span></span></code></pre>
-<h2>AST node: TCpar at base_simple3.nit:36,13</h2>
-<pre><code><span class="nitcode"><span class="line" id="L36"><span>)</span></span></span></code></pre>
-<h2>AST node: ABlockExpr at base_simple3.nit:38,3--40,4</h2>
-<pre><code><span class="nitcode"><span class="line" id="L38">          <span class="nc_l">7</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L39">            <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span>
-</span><span class="line" id="L40">    <span class="nc_k">end</span></span></span></code></pre>
-<h2>AST node: ACallAssignExpr at base_simple3.nit:39,3--14</h2>
-<pre><code><span class="nitcode"><span class="line" id="L39">          <span class="nc_k">self</span><span>.</span><span class="nc_i">val</span> <span>=</span> <span class="nc_v nc_i">v</span></span></span></code></pre>
-<h2>AST node: ASelfExpr at base_simple3.nit:39,3--6</h2>
-<pre><code><span class="nitcode"><span class="line" id="L39">          <span class="nc_k">self</span></span></span></code></pre>
-<h2>AST node: TKwself at base_simple3.nit:39,3--6</h2>
-<pre><code><span class="nitcode"><span class="line" id="L39">          <span class="nc_k">self</span></span></span></code></pre>
-<h2>AST node: TAssign at base_simple3.nit:39,12</h2>
-<pre><code><span class="nitcode"><span class="line" id="L39"> <span>=</span></span></span></code></pre>
-<h2>AST node: AVarExpr at base_simple3.nit:39,14</h2>
-<pre><code><span class="nitcode"><span class="line" id="L39"> <span class="nc_v nc_i">v</span></span></span></code></pre>
-<h2>AST node: AImplicitSelfExpr at base_simple3.nit:41,13</h2>
-<pre><code><span class="nitcode"></span></code></pre>
-<h2>AST node: ATopClassdef at base_simple3.nit:49,1--19</h2>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#foo"><span class="line" id="L49"><span class="nc_k">fun</span> <span class="nc_def popupable" title="base_simple3#Sys#foo" data-title="&lt;a href=&#34;base_simple3.html#base_simple3#Sys#foo&#34;&gt;base_simple3#Sys#foo&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;fun&lt;&#47;b&gt; &lt;span&gt;foo&lt;span&gt;&lt;&#47;span&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover"><span class="nc_i">foo</span></span> <span class="nc_k">do</span> <span class="nc_l">2</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h2>AST node: AReturnExpr at base_simple3.nit:51,17--24</h2>
-<pre><code><span class="nitcode"><span class="line" id="L51"> <span class="nc_k">return</span> <span class="nc_l">4</span></span></span></code></pre>
-<h2>AST node: TKwreturn at base_simple3.nit:51,17--22</h2>
-<pre><code><span class="nitcode"><span class="line" id="L51"> <span class="nc_k">return</span></span></span></code></pre>
-<h2>AST node: AMainClassdef at base_simple3.nit:53,1--66,13</h2>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#main"><span class="line" id="L53"><span class="nc_l">1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L54"><span class="nc_i">foo</span>
-</span><span class="line" id="L55"><span class="nc_i">bar</span><span>(</span><span class="nc_l">3</span><span>)</span>
-</span><span class="line" id="L56"><span class="nc_i">baz</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L57">
-</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_v nc_i">a</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">A</span>
-</span><span class="line" id="L59"><span class="nc_v nc_i">a</span><span>.</span><span class="nc_i">run</span>
-</span><span class="line" id="L60">
-</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_v nc_i">b</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">B</span><span>(</span><span class="nc_l">8</span><span>)</span>
-</span><span class="line" id="L62"><span class="nc_v nc_i">b</span><span>.</span><span class="nc_i">run</span>
-</span><span class="line" id="L63">
-</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_v nc_i">c</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">C</span><span>(</span><span class="nc_l">9</span><span>)</span>
-</span><span class="line" id="L65"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L66"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val2</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h2>AST node: AMainMethPropdef at base_simple3.nit:53,1--66,13</h2>
-<pre><code><span class="nitcode"><span class="nc_pdef foldable" id="base_simple3#Sys#main"><span class="line" id="L53"><span class="nc_l">1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L54"><span class="nc_i">foo</span>
-</span><span class="line" id="L55"><span class="nc_i">bar</span><span>(</span><span class="nc_l">3</span><span>)</span>
-</span><span class="line" id="L56"><span class="nc_i">baz</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L57">
-</span><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_v nc_i">a</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">A</span>
-</span><span class="line" id="L59"><span class="nc_v nc_i">a</span><span>.</span><span class="nc_i">run</span>
-</span><span class="line" id="L60">
-</span><span class="line" id="L61"><span class="nc_k">var</span> <span class="nc_v nc_i">b</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">B</span><span>(</span><span class="nc_l">8</span><span>)</span>
-</span><span class="line" id="L62"><span class="nc_v nc_i">b</span><span>.</span><span class="nc_i">run</span>
-</span><span class="line" id="L63">
-</span><span class="line" id="L64"><span class="nc_k">var</span> <span class="nc_v nc_i">c</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">C</span><span>(</span><span class="nc_l">9</span><span>)</span>
-</span><span class="line" id="L65"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val1</span><span>.</span><span class="nc_i">output</span>
-</span><span class="line" id="L66"><span class="nc_v nc_i">c</span><span>.</span><span class="nc_i">val2</span><span>.</span><span class="nc_i">output</span></span></span></span></code></pre>
-<h2>AST node: AParExprs at base_simple3.nit:55,4--6</h2>
-<pre><code><span class="nitcode"><span class="line" id="L55"><span>(</span><span class="nc_l">3</span><span>)</span></span></span></code></pre>
-<h2>AST node: AVardeclExpr at base_simple3.nit:58,1--13</h2>
-<pre><code><span class="nitcode"><span class="line" id="L58"><span class="nc_k">var</span> <span class="nc_v nc_i">a</span> <span>=</span> <span class="nc_k">new</span> <span class="nc_t">A</span></span></span></code></pre>
-<h2>AST node: ANewExpr at base_simple3.nit:58,9--13</h2>
-<pre><code><span class="nitcode"><span class="line" id="L58"> <span class="nc_k">new</span> <span class="nc_t">A</span></span></span></code></pre>
-<h2>AST node: TKwnew at base_simple3.nit:58,9--11</h2>
-<pre><code><span class="nitcode"><span class="line" id="L58"> <span class="nc_k">new</span></span></span></code></pre>
-<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
-<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
-<script>$(".popupable").popover({html:true, placement:'top'})/*initialize bootstrap popover*/</script>
-</body></html>
+<pre><code><span class="nitcode"><span class="line" id="L30"> <span class="nc_l popupable" title="Int" data-title="&lt;a href=&#34;#base_simple3__Int&#34;&gt;Int&lt;&#47;a&gt;" data-content="&lt;div&gt;&lt;b&gt;class&lt;&#47;b&gt; &lt;span&gt;&lt;a href=&#34;#base_simple3___base_simple3__Int&#34;&gt;base_simple3$Int&lt;&#47;a&gt;&lt;&#47;span&gt;&lt;br&#47;&gt;&lt;&#47;div&gt;" data-toggle="popover">5</span><span class="popupable***TRUNCATED***
index 95a4dc1..5ab004a 100644 (file)
@@ -2,7 +2,7 @@
 <A: true a 0.123 1234 asdf false p4ssw0rd>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null}
 
 # Back in Nit:
 <A: true a 0.123 1234 asdf false p4ssw0rd>
@@ -11,7 +11,7 @@
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"}
 
 # Back in Nit:
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
@@ -20,7 +20,7 @@
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null},"b": {"__kind": "obj", "__id": 2, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Back in Nit:
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
@@ -30,7 +30,7 @@
 <- false p4ssw0rd> 1111        f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "new line ->\n<-","n": null,"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
 # Back in Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index 84e2d3d..161fb58 100644 (file)
@@ -2,7 +2,7 @@
 <A: true a 0.123 1234 asdf false p4ssw0rd>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null}
 
 # Back in Nit:
 <A: true a 0.123 1234 asdf false p4ssw0rd>
@@ -11,7 +11,7 @@
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"}
 
 # Back in Nit:
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
@@ -20,7 +20,7 @@
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null},"b": {"__kind": "obj", "__id": 2, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Back in Nit:
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
@@ -30,7 +30,7 @@
 <- false p4ssw0rd> 1111        f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "serialization_specific_name": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"serialization_specific_name": "new line ->\n<-","n": null,"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
 # Back in Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
@@ -40,7 +40,7 @@
 <E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "E", "a": {"__kind": "obj", "__id": 1, "__class": "Array[Object]", "__items": ["hello", 1234, 123.4]}, "b": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Serializable]", "__items": ["hella", 2345, 234.5]}}
+{"__kind": "obj", "__id": 0, "__class": "E","a": {"__kind": "obj", "__id": 1, "__class": "Array[Object]","__items": ["hello",1234,123.4]},"b": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Serializable]","__items": ["hella",2345,234.5]}}
 
 # Back in Nit:
 <E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
@@ -49,7 +49,7 @@
 <E: 2222>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "F[Int]", "n": 2222}
+{"__kind": "obj", "__id": 0, "__class": "F[Int]","n": 2222}
 
 # Back in Nit:
 <E: 2222>
@@ -58,7 +58,7 @@
 <E: 33.33>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "F[Float]", "n": 33.33}
+{"__kind": "obj", "__id": 0, "__class": "F[Float]","n": 33.33}
 
 # Back in Nit:
 <E: 33.33>
@@ -67,7 +67,7 @@
 <G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "G", "hs": {"__kind": "obj", "__id": 1, "__class": "HashSet[Int]", "__items": [-1, 0]}, "s": {"__kind": "obj", "__id": 2, "__class": "ArraySet[String]", "__items": ["one", "two"]}, "hm": {"__kind": "obj", "__id": 3, "__class": "HashMap[String, Int]", "__length": 2, "__keys": ["one", "two"], "__values": [1, 2]}, "am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap[String, String]", "__length": 2, "__keys": ["three", "four"], "__values": ["3", "4"]}}
+{"__kind": "obj", "__id": 0, "__class": "G","hs": {"__kind": "obj", "__id": 1, "__class": "HashSet[Int]","__items": [-1,0]},"s": {"__kind": "obj", "__id": 2, "__class": "ArraySet[String]","__items": ["one","two"]},"hm": {"__kind": "obj", "__id": 3, "__class": "HashMap[String, Int]", "__length": 2,"__keys": ["one","two"],"__values": [1,2]},"am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap[String, String]", "__length": 2,"__keys": ["three","four"],"__values": ["3","4"]}}
 
 # Back in Nit:
 <G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
index 86c613b..48005ec 100644 (file)
@@ -2,32 +2,33 @@
 <A: true a 0.123 1234 asdf false p4ssw0rd>
 
 # Json:
-{"b": true, "c": "a", "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}
+{"b": true,"c": "a","f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
 
 # Json:
-{"b": false, "c": "b", "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}
+{"b": false,"c": "b","f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
 
 # Json:
-{"a": {"b": true, "c": "a", "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}, "b": {"b": false, "c": "b", "f": 123.123, "i": 2345, "serialization_specific_name": "hjkl", "n": 12, "ii": 1111, "ss": "qwer"}, "aa": {"b": true, "c": "a", "f": 0.123, "i": 1234, "serialization_specific_name": "asdf", "n": null}}
+{"a": {"b": true,"c": "a","f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null},"b": {"b": false,"c": "b","f": 123.123,"i": 2345,"serialization_specific_name": "hjkl","n": 12,"ii": 1111,"ss": "qwer"},"aa": {"b": true,"c": "a","f": 0.123,"i": 1234,"serialization_specific_name": "asdf","n": null}}
 
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false p4ssw0rd> 1111        f"\r\/> true>
 
 # Json:
-{"b": false, "c": "b", "f": 123.123, "i": 2345, "serialization_specific_name": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\/", "d": null}
+{"b": false,"c": "b","f": 123.123,"i": 2345,"serialization_specific_name": "new line ->\n<-","n": null,"ii": 1111,"ss": "\tf\"\r\\/","d": null}
 
 # Nit:
 <E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
 
 # Json:
-{"a": ["hello", 1234, 123.4], "b": ["hella", 2345, 234.5]}
+{"a": ["hello",1234,123.4],"b": ["hella",2345,234.5]}
 
 # Nit:
 <E: 2222>
@@ -45,5 +46,5 @@
 <G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
 
 # Json:
-{"hs": [-1, 0], "s": ["one", "two"], "hm": {"one": 1, "two": 2}, "am": {"three": "3", "four": "4"}}
+{"hs": [-1,0],"s": ["one","two"],"hm": {"one": 1,"two": 2},"am": {"three": "3","four": "4"}}
 
diff --git a/tests/sav/test_json_deserialization_alt3.res b/tests/sav/test_json_deserialization_alt3.res
new file mode 100644 (file)
index 0000000..c49a52a
--- /dev/null
@@ -0,0 +1,192 @@
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "A",
+       "b": true,
+       "c": {"__kind": "char", "__val": "a"},
+       "f": 0.123,
+       "i": 1234,
+       "serialization_specific_name": "asdf",
+       "n": null
+}
+
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "B",
+       "b": false,
+       "c": {"__kind": "char", "__val": "b"},
+       "f": 123.123,
+       "i": 2345,
+       "serialization_specific_name": "hjkl",
+       "n": 12,
+       "ii": 1111,
+       "ss": "qwer"
+}
+
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "C",
+       "a": {
+               "__kind": "obj", "__id": 1, "__class": "A",
+               "b": true,
+               "c": {"__kind": "char", "__val": "a"},
+               "f": 0.123,
+               "i": 1234,
+               "serialization_specific_name": "asdf",
+               "n": null
+       },
+       "b": {
+               "__kind": "obj", "__id": 2, "__class": "B",
+               "b": false,
+               "c": {"__kind": "char", "__val": "b"},
+               "f": 123.123,
+               "i": 2345,
+               "serialization_specific_name": "hjkl",
+               "n": 12,
+               "ii": 1111,
+               "ss": "qwer"
+       },
+       "aa": {
+               "__kind": "ref", "__id": 1
+       }
+}
+
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111        f"\r\/> true>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "D",
+       "b": false,
+       "c": {"__kind": "char", "__val": "b"},
+       "f": 123.123,
+       "i": 2345,
+       "serialization_specific_name": "new line ->\n<-",
+       "n": null,
+       "ii": 1111,
+       "ss": "\tf\"\r\\/",
+       "d": {
+               "__kind": "ref", "__id": 0
+       }
+}
+
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111        f"\r\/> true>
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "E",
+       "a": {
+               "__kind": "obj", "__id": 1, "__class": "Array[Object]",
+               "__items": [
+                       "hello",
+                       1234,
+                       123.4
+               ]
+       },
+       "b": {
+               "__kind": "obj", "__id": 2, "__class": "Array[nullable Serializable]",
+               "__items": [
+                       "hella",
+                       2345,
+                       234.5
+               ]
+       }
+}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<E: 2222>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "F[Int]",
+       "n": 2222
+}
+
+# Back in Nit:
+null
+
+# Nit:
+<E: 33.33>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "F[Float]",
+       "n": 33.33
+}
+
+# Back in Nit:
+null
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{
+       "__kind": "obj", "__id": 0, "__class": "G",
+       "hs": {
+               "__kind": "obj", "__id": 1, "__class": "HashSet[Int]",
+               "__items": [
+                       -1,
+                       0
+               ]
+       },
+       "s": {
+               "__kind": "obj", "__id": 2, "__class": "ArraySet[String]",
+               "__items": [
+                       "one",
+                       "two"
+               ]
+       },
+       "hm": {
+               "__kind": "obj", "__id": 3, "__class": "HashMap[String, Int]", "__length": 2,
+               "__keys": [
+                       "one",
+                       "two"
+               ],
+               "__values": [
+                       1,
+                       2
+               ]
+       },
+       "am": {
+               "__kind": "obj", "__id": 4, "__class": "ArrayMap[String, String]", "__length": 2,
+               "__keys": [
+                       "three",
+                       "four"
+               ],
+               "__values": [
+                       "3",
+                       "4"
+               ]
+       }
+}
+
+# Back in Nit:
+<G: hs: ; s: ; hm: ; am: >
+
diff --git a/tests/sav/test_json_deserialization_alt4.res b/tests/sav/test_json_deserialization_alt4.res
new file mode 100644 (file)
index 0000000..5527174
--- /dev/null
@@ -0,0 +1,135 @@
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{
+       "b": true,
+       "c": "a",
+       "f": 0.123,
+       "i": 1234,
+       "serialization_specific_name": "asdf",
+       "n": null
+}
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{
+       "b": false,
+       "c": "b",
+       "f": 123.123,
+       "i": 2345,
+       "serialization_specific_name": "hjkl",
+       "n": 12,
+       "ii": 1111,
+       "ss": "qwer"
+}
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{
+       "a": {
+               "b": true,
+               "c": "a",
+               "f": 0.123,
+               "i": 1234,
+               "serialization_specific_name": "asdf",
+               "n": null
+       },
+       "b": {
+               "b": false,
+               "c": "b",
+               "f": 123.123,
+               "i": 2345,
+               "serialization_specific_name": "hjkl",
+               "n": 12,
+               "ii": 1111,
+               "ss": "qwer"
+       },
+       "aa": {
+               "b": true,
+               "c": "a",
+               "f": 0.123,
+               "i": 1234,
+               "serialization_specific_name": "asdf",
+               "n": null
+       }
+}
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111        f"\r\/> true>
+
+# Json:
+{
+       "b": false,
+       "c": "b",
+       "f": 123.123,
+       "i": 2345,
+       "serialization_specific_name": "new line ->\n<-",
+       "n": null,
+       "ii": 1111,
+       "ss": "\tf\"\r\\/",
+       "d": null
+}
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{
+       "a": [
+               "hello",
+               1234,
+               123.4
+       ],
+       "b": [
+               "hella",
+               2345,
+               234.5
+       ]
+}
+
+# Nit:
+<E: 2222>
+
+# Json:
+{
+       "n": 2222
+}
+
+# Nit:
+<E: 33.33>
+
+# Json:
+{
+       "n": 33.33
+}
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{
+       "hs": [
+               -1,
+               0
+       ],
+       "s": [
+               "one",
+               "two"
+       ],
+       "hm": {
+               "one": 1,
+               "two": 2
+       },
+       "am": {
+               "three": "3",
+               "four": "4"
+       }
+}
+
index ef8399f..71dc9e7 100644 (file)
 # Nit: <JsonObject i:123, s:hello, f:123.456, a:[one,two], o:<null>>
 
 # JSON: {"__class": "MyClass", "i": 123, "s": "hello", "f": 123.456, "a": ["one", "two"], "o": "Not the right type"}
-# Errors: 'Deserialization Error: Wrong type on `MyClass::o` expected `nullable MyClass`, got `FlatString`'
+# Errors: 'Deserialization Error: Wrong type on `MyClass::o` expected `nullable MyClass`, got `ASCIIFlatString`'
 # Nit: <MyClass i:123 s:hello f:123.456 a:[one, two] o:<null>>
 
 # JSON: not valid json
-# Errors: 'Error Parsing JSON: [1:1-1:2] Unexpected character 'n'; is acceptable instead: value'
+# Errors: 'Parsing error at line 1, position 1: Error: bad JSON entity'
 # Nit: null
 
diff --git a/tests/sav/test_loader.res b/tests/sav/test_loader.res
new file mode 100644 (file)
index 0000000..e50ad70
--- /dev/null
@@ -0,0 +1,2 @@
+Usage: [OPTION]... [ARG]...
+Use --help for help
diff --git a/tests/sav/test_loader_args1.res b/tests/sav/test_loader_args1.res
new file mode 100644 (file)
index 0000000..1b169e2
--- /dev/null
@@ -0,0 +1,21 @@
+test_prog/rpg/races.nit: module?
+       module test_prog::races at test_prog/rpg/races.nit
+test_prog/rpg/races.nit: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+test_prog/rpg: module?
+       module test_prog::rpg at test_prog/rpg/rpg.nit
+test_prog/rpg: group?
+       group test_prog>rpg> at test_prog/rpg
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+scan_full found 6 modules
+  model: mpackages=1 mmodules=5
+  mb: identified modules=5; parsed modules=0
+parse found 2 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=6
+parse_full found 5 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=6
diff --git a/tests/sav/test_loader_args2.res b/tests/sav/test_loader_args2.res
new file mode 100644 (file)
index 0000000..5b3c4f0
--- /dev/null
@@ -0,0 +1,41 @@
+Error: cannot find module `test_prog/test_prog`.
+Error: cannot find module `test_prog/test_prog`.
+test_prog/test_prog.nit: module?
+       module test_prog::test_prog at test_prog/test_prog.nit
+test_prog/test_prog.nit: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+test_prog/test_prog: module?
+       nothing
+test_prog/test_prog: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+test_prog: module?
+       module test_prog::test_prog at test_prog/test_prog.nit
+test_prog: group?
+       group test_prog> at test_prog
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+test_prog::test_prog: module?
+       module test_prog::test_prog at test_prog/test_prog.nit
+test_prog::test_prog: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+test_prog/: module?
+       module test_prog::test_prog at test_prog/test_prog.nit
+test_prog/: group?
+       group test_prog> at test_prog
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+scan_full found 18 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+parse found 1 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=8
+parse_full found 8 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=8
diff --git a/tests/sav/test_loader_args3.res b/tests/sav/test_loader_args3.res
new file mode 100644 (file)
index 0000000..940b6bf
--- /dev/null
@@ -0,0 +1,31 @@
+Error: cannot find module `test_prog::races:rpg`. Did you mean test_prog::races?
+Error: cannot find module `test_prog::races::combat`. Did you mean test_prog::combat?
+Error: cannot find module `test_prog::races:rpg`. Did you mean test_prog::races?
+Error: cannot find module `test_prog::races::combat`. Did you mean test_prog::combat?
+test_prog::races: module?
+       module test_prog::races at test_prog/rpg/races.nit
+test_prog::races: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+test_prog::races:rpg: module?
+       Error: cannot find module `test_prog::races:rpg`. Did you mean test_prog::races?
+test_prog::races:rpg: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+test_prog::races::combat: module?
+       Error: cannot find module `test_prog::races::combat`. Did you mean test_prog::combat?
+test_prog::races::combat: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+scan_full found 1 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+parse found 1 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=2
+parse_full found 1 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=2
diff --git a/tests/sav/test_loader_args4.res b/tests/sav/test_loader_args4.res
new file mode 100644 (file)
index 0000000..74dfacb
--- /dev/null
@@ -0,0 +1,33 @@
+Error: cannot find module `test_prog::fail::races`. Did you mean test_prog::races?
+Error: cannot find module `test_prog::fail`. Did you mean test_prog::game?
+Error: cannot find module `fail::fail`.
+Error: cannot find module `test_prog::fail::races`. Did you mean test_prog::races?
+Error: cannot find module `test_prog::fail`. Did you mean test_prog::game?
+Error: cannot find module `fail::fail`.
+test_prog::fail::races: module?
+       Error: cannot find module `test_prog::fail::races`. Did you mean test_prog::races?
+test_prog::fail::races: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+test_prog::fail: module?
+       Error: cannot find module `test_prog::fail`. Did you mean test_prog::game?
+test_prog::fail: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+fail::fail: module?
+       nothing
+fail::fail: group?
+       nothing
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+scan_full found 0 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+parse found 0 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
+parse_full found 0 modules
+  model: mpackages=2 mmodules=9
+  mb: identified modules=9; parsed modules=0
diff --git a/tests/sav/test_loader_args5.res b/tests/sav/test_loader_args5.res
new file mode 100644 (file)
index 0000000..8c087fa
--- /dev/null
@@ -0,0 +1,31 @@
+Error: cannot find module `/fail`.
+Error: `/lib` is not a Nit source file.
+Error: `sublib` is not a Nit source file.
+Error: cannot find module `/fail`.
+/lib: module?
+       Error: `/lib` is not a Nit package.
+/lib: group?
+       nothing
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
+/fail: module?
+       nothing
+/fail: group?
+       nothing
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
+sublib: module?
+       Error: `sublib` is not a Nit package.
+sublib: group?
+       nothing
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
+scan_full found 3 modules
+  model: mpackages=2 mmodules=3
+  mb: identified modules=3; parsed modules=0
+parse found 0 modules
+  model: mpackages=2 mmodules=3
+  mb: identified modules=3; parsed modules=0
+parse_full found 3 modules
+  model: mpackages=2 mmodules=3
+  mb: identified modules=3; parsed modules=3
diff --git a/tests/sav/test_loader_args6.res b/tests/sav/test_loader_args6.res
new file mode 100644 (file)
index 0000000..c3553f5
--- /dev/null
@@ -0,0 +1,61 @@
+Error: cannot find module `sublib/foo`.
+Error: cannot find module `foo.nit`.
+Error: cannot find module `./foo`.
+Error: cannot find module `foo/`.
+Error: cannot find module `foo::foo`.
+Error: cannot find module `sublib/foo`.
+Error: cannot find module `foo.nit`.
+Error: cannot find module `./foo`.
+Error: cannot find module `foo/`.
+Error: cannot find module `foo::foo`.
+foo: module?
+       module foo::foo at sublib/foo.nit
+foo: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+sublib/foo: module?
+       nothing
+sublib/foo: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+sublib/foo.nit: module?
+       module foo::foo at sublib/foo.nit
+sublib/foo.nit: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+foo.nit: module?
+       nothing
+foo.nit: group?
+       Error: `sublib/foo.nit` is not a directory.
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+./foo: module?
+       nothing
+./foo: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+foo/: module?
+       nothing
+foo/: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+foo::foo: module?
+       nothing
+foo::foo: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+scan_full found 2 modules
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+parse found 1 modules
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=1
+parse_full found 1 modules
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=1
diff --git a/tests/sav/test_loader_args7.res b/tests/sav/test_loader_args7.res
new file mode 100644 (file)
index 0000000..fbf0445
--- /dev/null
@@ -0,0 +1,65 @@
+Error: cannot find module `sublib/bar.nit`.
+Error: cannot find module `bar.nit`.
+Error: cannot find module `./bar`.
+Error: cannot find module `bar/`.
+Error: cannot find module `sublib/bar.nit`.
+Error: cannot find module `bar.nit`.
+Error: cannot find module `./bar`.
+Error: cannot find module `bar/`.
+bar: module?
+       module bar::bar at sublib/bar/bar.nit
+bar: group?
+       group bar> at sublib/bar
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+sublib/bar: module?
+       module bar::bar at sublib/bar/bar.nit
+sublib/bar: group?
+       group bar> at sublib/bar
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+sublib/bar.nit: module?
+       nothing
+sublib/bar.nit: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+sublib/bar/bar.nit: module?
+       module bar::bar at sublib/bar/bar.nit
+sublib/bar/bar.nit: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+bar.nit: module?
+       nothing
+bar.nit: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+./bar: module?
+       nothing
+./bar: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+bar/: module?
+       nothing
+bar/: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+bar::bar: module?
+       module bar::bar at sublib/bar/bar.nit
+bar::bar: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+scan_full found 6 modules
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+parse found 1 modules
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=1
+parse_full found 2 modules
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=2
diff --git a/tests/sav/test_loader_args8.res b/tests/sav/test_loader_args8.res
new file mode 100644 (file)
index 0000000..d13776b
--- /dev/null
@@ -0,0 +1,37 @@
+Error: cannot find module `sublib/bar/foo`.
+Error: cannot find module `bar/foo.nit`.
+Error: cannot find module `sublib/bar/foo`.
+Error: cannot find module `bar/foo.nit`.
+sublib/bar/foo.nit: module?
+       module bar::foo at sublib/bar/foo.nit
+sublib/bar/foo.nit: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+sublib/bar/foo: module?
+       nothing
+sublib/bar/foo: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+bar/foo.nit: module?
+       nothing
+bar/foo.nit: group?
+       nothing
+  model: mpackages=1 mmodules=1
+  mb: identified modules=1; parsed modules=0
+bar::foo: module?
+       module bar::foo at sublib/bar/foo.nit
+bar::foo: group?
+       nothing
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+scan_full found 2 modules
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=0
+parse found 1 modules
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=1
+parse_full found 1 modules
+  model: mpackages=1 mmodules=2
+  mb: identified modules=2; parsed modules=1
diff --git a/tests/sav/test_loader_args9.res b/tests/sav/test_loader_args9.res
new file mode 100644 (file)
index 0000000..6882434
--- /dev/null
@@ -0,0 +1,18 @@
+Error: cannot find module `fail::fail`.
+Error: cannot find module `fail::fail`.
+qnames? fail::fail fail
+fail::fail: not found
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
+qnames? fail::fail fail
+scan_full found 0 modules
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
+qnames? fail::fail fail
+parse found 0 modules
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
+qnames? fail::fail fail
+parse_full found 0 modules
+  model: mpackages=0 mmodules=0
+  mb: identified modules=0; parsed modules=0
index 6f23d37..dfb6534 100644 (file)
@@ -1,7 +1,7 @@
-FlatString
-FlatString
-Class[FlatString]
-Class[Class[FlatString]]
+ASCIIFlatString
+ASCIIFlatString
+Class[ASCIIFlatString]
+Class[Class[ASCIIFlatString]]
 
 XObject
 XObject
index 78cc43f..327b4a6 100644 (file)
@@ -50,3 +50,118 @@ All public entities:
 
 All documented public entities:
  list:
+
+Names:
+
+# Classes of entities
+ population: 9
+ minimum value: 1
+ maximum value: 17
+ total value: 55
+ average value: 6.11
+ distribution:
+  <=1: sub-population=3 (33.33%); cumulated value=3 (5.45%)
+  <=4: sub-population=2 (22.22%); cumulated value=6 (10.90%)
+  <=8: sub-population=2 (22.22%); cumulated value=14 (25.45%)
+  <=16: sub-population=1 (11.11%); cumulated value=15 (27.27%)
+  <=32: sub-population=1 (11.11%); cumulated value=17 (30.90%)
+ list:
+  MMethodDef: 17 (30.90%)
+  MMethod: 15 (27.27%)
+  MClassDef: 7 (12.72%)
+  MClass: 7 (12.72%)
+  MAttributeDef: 3 (5.45%)
+  MAttribute: 3 (5.45%)
+  MPackage: 1 (1.81%)
+  MGroup: 1 (1.81%)
+  MModule: 1 (1.81%)
+
+# Name length of entities
+ population: 55
+ minimum value: 12
+ maximum value: 36
+ total value: 1124
+ average value: 20.43
+ distribution:
+  <=12: sub-population=1 (1.81%); cumulated value=12 (1.06%)
+  <=24: sub-population=46 (83.63%); cumulated value=874 (77.75%)
+  <=48: sub-population=8 (14.54%); cumulated value=238 (21.17%)
+ list:
+  base_simple3::base_simple3::C::_val2: 36 (3.20%)
+  base_simple3::base_simple3::C::_val1: 36 (3.20%)
+  base_simple3::base_simple3::B::_val: 35 (3.11%)
+  base_simple3$A$Object::init: 27 (2.40%)
+  base_simple3$C$Object::init: 27 (2.40%)
+  base_simple3::base_simple3: 26 (2.31%)
+  base_simple3::Object::init: 26 (2.31%)
+  base_simple3::Int::output: 25 (2.22%)
+  base_simple3$Object$init: 24 (2.13%)
+  base_simple3::Sys::main: 23 (2.04%)
+  ...
+  base_simple3$Sys: 16 (1.42%)
+  base_simple3$Int: 16 (1.42%)
+  base_simple3::A: 15 (1.33%)
+  base_simple3::B: 15 (1.33%)
+  base_simple3::C: 15 (1.33%)
+  base_simple3$B: 14 (1.24%)
+  base_simple3$C: 14 (1.24%)
+  base_simple3$A: 14 (1.24%)
+  base_simple3>: 13 (1.15%)
+  base_simple3: 12 (1.06%)
+
+# All entities
+base_simple3   MPackage        base_simple3.nit        
+base_simple3>  MGroup  base_simple3.nit        
+base_simple3::base_simple3     MModule base_simple3.nit:17,1--66,13    
+base_simple3::Object   MClass  base_simple3.nit:19,1--20,3     
+base_simple3$Object    MClassDef       base_simple3.nit:19,1--20,3     
+base_simple3::Object::init     MMethod base_simple3.nit:19,1--20,3     
+base_simple3$Object$init       MMethodDef      base_simple3.nit:19,1--20,3     
+base_simple3::Bool     MClass  base_simple3.nit:22,1--23,3     
+base_simple3$Bool      MClassDef       base_simple3.nit:22,1--23,3     
+base_simple3::Int      MClass  base_simple3.nit:25,1--27,3     
+base_simple3$Int       MClassDef       base_simple3.nit:25,1--27,3     
+base_simple3::Int::output      MMethod base_simple3.nit:26,2--21       
+base_simple3$Int$output        MMethodDef      base_simple3.nit:26,2--21       
+base_simple3::A        MClass  base_simple3.nit:29,1--32,3     
+base_simple3$A MClassDef       base_simple3.nit:29,1--32,3     
+base_simple3$A$Object::init    MMethodDef      base_simple3.nit:30,2--17       
+base_simple3::A::run   MMethod base_simple3.nit:31,2--20       
+base_simple3$A$run     MMethodDef      base_simple3.nit:31,2--20       
+base_simple3::B        MClass  base_simple3.nit:34,1--42,3     
+base_simple3$B MClassDef       base_simple3.nit:34,1--42,3     
+base_simple3::base_simple3::B::_val    MAttribute      base_simple3.nit:35,2--13       
+base_simple3$B$_val    MAttributeDef   base_simple3.nit:35,2--13       
+base_simple3::B::val   MMethod base_simple3.nit:35,2--13       
+base_simple3$B$val     MMethodDef      base_simple3.nit:35,2--13       
+base_simple3::B::val=  MMethod base_simple3.nit:35,2--13       
+base_simple3$B$val=    MMethodDef      base_simple3.nit:35,2--13       
+base_simple3::B::init  MMethod base_simple3.nit:36,2--40,4     
+base_simple3$B$init    MMethodDef      base_simple3.nit:36,2--40,4     
+base_simple3::B::run   MMethod base_simple3.nit:41,2--22       
+base_simple3$B$run     MMethodDef      base_simple3.nit:41,2--22       
+base_simple3::C        MClass  base_simple3.nit:44,1--47,3     
+base_simple3$C MClassDef       base_simple3.nit:44,1--47,3     
+base_simple3::base_simple3::C::_val1   MAttribute      base_simple3.nit:45,2--14       
+base_simple3$C$_val1   MAttributeDef   base_simple3.nit:45,2--14       
+base_simple3::C::val1  MMethod base_simple3.nit:45,2--14       
+base_simple3$C$val1    MMethodDef      base_simple3.nit:45,2--14       
+base_simple3::C::val1= MMethod base_simple3.nit:45,2--14       
+base_simple3$C$val1=   MMethodDef      base_simple3.nit:45,2--14       
+base_simple3::base_simple3::C::_val2   MAttribute      base_simple3.nit:46,2--19       
+base_simple3$C$_val2   MAttributeDef   base_simple3.nit:46,2--19       
+base_simple3::C::val2  MMethod base_simple3.nit:46,2--19       
+base_simple3$C$val2    MMethodDef      base_simple3.nit:46,2--19       
+base_simple3::C::val2= MMethod base_simple3.nit:46,2--19       
+base_simple3$C$val2=   MMethodDef      base_simple3.nit:46,2--19       
+base_simple3$C$Object::init    MMethodDef      base_simple3.nit:44,1--47,3     
+base_simple3::Sys      MClass  base_simple3.nit:49,1--19       
+base_simple3$Sys       MClassDef       base_simple3.nit:49,1--19       
+base_simple3::Sys::foo MMethod base_simple3.nit:49,1--19       
+base_simple3$Sys$foo   MMethodDef      base_simple3.nit:49,1--19       
+base_simple3::Sys::bar MMethod base_simple3.nit:50,1--27       
+base_simple3$Sys$bar   MMethodDef      base_simple3.nit:50,1--27       
+base_simple3::Sys::baz MMethod base_simple3.nit:51,1--24       
+base_simple3$Sys$baz   MMethodDef      base_simple3.nit:51,1--24       
+base_simple3::Sys::main        MMethod base_simple3.nit:53,1--66,13    
+base_simple3$Sys$main  MMethodDef      base_simple3.nit:53,1--66,13    
diff --git a/tests/sav/test_model_visitor_args2.res b/tests/sav/test_model_visitor_args2.res
new file mode 100644 (file)
index 0000000..445de88
--- /dev/null
@@ -0,0 +1,206 @@
+All entities, including fictive ones:
+ list:
+  MMethodDef: 49 (49.49%)
+  MClassDef: 21 (21.21%)
+  MClass: 10 (10.10%)
+  MMethod: 9 (9.09%)
+  MModule: 5 (5.05%)
+  MGroup: 2 (2.02%)
+  MPackage: 2 (2.02%)
+  Model: 1 (1.01%)
+All entities:
+ list:
+  MMethodDef: 49 (49.49%)
+  MClassDef: 21 (21.21%)
+  MClass: 10 (10.10%)
+  MMethod: 9 (9.09%)
+  MModule: 5 (5.05%)
+  MGroup: 2 (2.02%)
+  MPackage: 2 (2.02%)
+  Model: 1 (1.01%)
+
+All non-private entities:
+ list:
+  MMethodDef: 14 (30.43%)
+  MClassDef: 11 (23.91%)
+  MMethod: 6 (13.04%)
+  MClass: 5 (10.86%)
+  MModule: 5 (10.86%)
+  MGroup: 2 (4.34%)
+  MPackage: 2 (4.34%)
+  Model: 1 (2.17%)
+
+All documented non-private entities:
+ list:
+
+All public entities:
+ list:
+  MMethodDef: 14 (30.43%)
+  MClassDef: 11 (23.91%)
+  MMethod: 6 (13.04%)
+  MClass: 5 (10.86%)
+  MModule: 5 (10.86%)
+  MGroup: 2 (4.34%)
+  MPackage: 2 (4.34%)
+  Model: 1 (2.17%)
+
+All documented public entities:
+ list:
+
+Names:
+
+# Classes of entities
+ population: 7
+ minimum value: 2
+ maximum value: 49
+ total value: 98
+ average value: 14.00
+ distribution:
+  <=2: sub-population=2 (28.57%); cumulated value=4 (4.08%)
+  <=8: sub-population=1 (14.28%); cumulated value=5 (5.10%)
+  <=16: sub-population=2 (28.57%); cumulated value=19 (19.38%)
+  <=32: sub-population=1 (14.28%); cumulated value=21 (21.42%)
+  <=64: sub-population=1 (14.28%); cumulated value=49 (50.00%)
+ list:
+  MMethodDef: 49 (50.00%)
+  MClassDef: 21 (21.42%)
+  MClass: 10 (10.20%)
+  MMethod: 9 (9.18%)
+  MModule: 5 (5.10%)
+  MGroup: 2 (2.04%)
+  MPackage: 2 (2.04%)
+
+# Name length of entities
+ population: 98
+ minimum value: 5
+ maximum value: 44
+ total value: 1762
+ average value: 17.97
+ distribution:
+  <=5: sub-population=1 (1.02%); cumulated value=5 (0.28%)
+  <=10: sub-population=17 (17.34%); cumulated value=141 (8.00%)
+  <=20: sub-population=48 (48.97%); cumulated value=706 (40.06%)
+  <=40: sub-population=30 (30.61%); cumulated value=822 (46.65%)
+  <=80: sub-population=2 (2.04%); cumulated value=88 (4.99%)
+ list:
+  names1::names1$names::n0::P0$names::n0::P::p: 44 (2.49%)
+  names1::names1$names::n0::P0$names::n0::A::z: 44 (2.49%)
+  names1::names1$names::A0$names::n0::A::z: 40 (2.27%)
+  names1::names1$names::A0$names::n0::P::p: 40 (2.27%)
+  names1::names1$names::n0::P0$names::A::a: 40 (2.27%)
+  names1::names1$names::A0$names::A::a: 36 (2.04%)
+  names1::names1$P1$names::n0::P::p: 33 (1.87%)
+  names1::names1$P1$names::n0::A::z: 33 (1.87%)
+  names::n1$::n0::P0$::n0::A::z: 29 (1.64%)
+  names1::names1$P1$names::A::a: 29 (1.64%)
+  ...
+  names::n3: 9 (0.51%)
+  names::n0: 9 (0.51%)
+  names::A: 8 (0.45%)
+  names$A0: 8 (0.45%)
+  names$A1: 8 (0.45%)
+  names$A: 7 (0.39%)
+  names1>: 7 (0.39%)
+  names1: 6 (0.34%)
+  names>: 6 (0.34%)
+  names: 5 (0.28%)
+
+# All entities
+names  MPackage        names   Group of modules used to test various full_name configurations and conflicts.
+names> MGroup  names   Group of modules used to test various full_name configurations and conflicts.
+names::n3      MModule names/n3.nit:15,1--35,3 The bottom module
+names::n3$A1   MClassDef       names/n3.nit:21,1--27,3 a refinement of a subclass in a submodule
+names::n3$A1$A::a      MMethodDef      names/n3.nit:23,2--24,19        a refinement (3 distinct modules)
+names::n3$A1$::n0::P::p        MMethodDef      names/n3.nit:25,2--26,19        a refinement (3 distinct modules)
+names::n3$::n1::P1     MClassDef       names/n3.nit:29,1--35,3 a refinement of a subclass in a submodule
+names::n3$::n1::P1$A::a        MMethodDef      names/n3.nit:31,2--32,19        a refinement (3 distinct modules)
+names::n3$::n1::P1$::n0::P::p  MMethodDef      names/n3.nit:33,2--34,19        a refinement (3 distinct modules)
+names::n0      MModule names/n0.nit:15,1--67,3 Root module
+names::Object  MClass  names/n0.nit:20,1--22,3 Root interface
+names$Object   MClassDef       names/n0.nit:20,1--22,3 Root interface
+names::Object::init    MMethod names/n0.nit:20,1--22,3 
+names$Object$init      MMethodDef      names/n0.nit:20,1--22,3 
+names::A       MClass  names/n0.nit:24,1--31,3 A public class
+names$A        MClassDef       names/n0.nit:24,1--31,3 A public class
+names::A::a    MMethod names/n0.nit:26,2--27,13        A public method in a public class
+names$A$a      MMethodDef      names/n0.nit:26,2--27,13        A public method in a public class
+names::n0::A::z        MMethod names/n0.nit:29,2--30,21        A private method in a public class
+names$A$z      MMethodDef      names/n0.nit:29,2--30,21        A private method in a public class
+names::A0      MClass  names/n0.nit:33,1--46,3 A public subclass in the same module
+names$A0       MClassDef       names/n0.nit:33,1--46,3 A public subclass in the same module
+names$A0$A::a  MMethodDef      names/n0.nit:38,2--39,19        Redefinition it the same module of a public method
+names$A0$::n0::A::z    MMethodDef      names/n0.nit:41,2--42,19        Redefinition it the same module of a private method
+names$A0$::n0::P::p    MMethodDef      names/n0.nit:44,2--45,19        Redefinition it the same module of a private method
+names::n0::P   MClass  names/n0.nit:48,1--52,3 A private class
+names::n0$P    MClassDef       names/n0.nit:48,1--52,3 A private class
+names::n0::P::p        MMethod names/n0.nit:50,2--51,13        A private method in a private class
+names::n0$P$p  MMethodDef      names/n0.nit:50,2--51,13        A private method in a private class
+names::n0::P0  MClass  names/n0.nit:54,1--67,3 A private subclass introduced in the same module
+names::n0$P0   MClassDef       names/n0.nit:54,1--67,3 A private subclass introduced in the same module
+names::n0$P0$A::a      MMethodDef      names/n0.nit:59,2--60,19        Redefinition it the same module of a public method
+names::n0$P0$::n0::A::z        MMethodDef      names/n0.nit:62,2--63,19        Redefinition it the same module of a private method
+names::n0$P0$::n0::P::p        MMethodDef      names/n0.nit:65,2--66,19        Redefinition it the same module of a private method
+names::n1      MModule names/n1.nit:15,1--90,3 Second module
+names::n1$A    MClassDef       names/n1.nit:20,1--30,3 A refinement of a class
+names::n1$A$a  MMethodDef      names/n1.nit:22,2--23,19        A refinement in the same class
+names::n1$A$z  MMethodDef      names/n1.nit:25,2--26,19        A refinement in the same class
+names::n1::A::b        MMethod names/n1.nit:28,2--29,13        A public method introduced in a refinement
+names::n1$A$b  MMethodDef      names/n1.nit:28,2--29,13        A public method introduced in a refinement
+names::n1$A0   MClassDef       names/n1.nit:32,1--42,3 A refinement of a subclass
+names::n1$A0$A::a      MMethodDef      names/n1.nit:34,2--35,19        A refinement+redefinition
+names::n1$A0$::n0::A::z        MMethodDef      names/n1.nit:37,2--38,19        A refinement+redefinition
+names::n1$A0$::n0::P::p        MMethodDef      names/n1.nit:40,2--41,19        A refinement+redefinition
+names::A1      MClass  names/n1.nit:44,1--57,3 A subclass introduced in a submodule
+names$A1       MClassDef       names/n1.nit:44,1--57,3 A subclass introduced in a submodule
+names$A1$A::a  MMethodDef      names/n1.nit:49,2--50,19        A redefinition in a subclass from a different module
+names$A1$::n0::A::z    MMethodDef      names/n1.nit:52,2--53,19        A redefinition in a subclass from a different module
+names$A1$::n0::P::p    MMethodDef      names/n1.nit:55,2--56,19        A redefinition in a subclass from a different module
+names::n1$::n0::P      MClassDef       names/n1.nit:59,1--63,3 A refinement of a class
+names::n1$::n0::P$p    MMethodDef      names/n1.nit:61,2--62,19        A refinement in the same class
+names::n1$::n0::P0     MClassDef       names/n1.nit:65,1--75,3 A refinement of a subclass
+names::n1$::n0::P0$A::a        MMethodDef      names/n1.nit:67,2--68,19        A refinement+redefinition
+names::n1$::n0::P0$::n0::A::z  MMethodDef      names/n1.nit:70,2--71,19        A refinement+redefinition
+names::n1$::n0::P0$::n0::P::p  MMethodDef      names/n1.nit:73,2--74,19        A refinement+redefinition
+names::n1::P1  MClass  names/n1.nit:77,1--90,3 A private subclass introduced in a different module
+names::n1$P1   MClassDef       names/n1.nit:77,1--90,3 A private subclass introduced in a different module
+names::n1$P1$A::a      MMethodDef      names/n1.nit:82,2--83,19        A redefinition in a subclass from a different module
+names::n1$P1$::n0::A::z        MMethodDef      names/n1.nit:85,2--86,19        A redefinition in a subclass from a different module
+names::n1$P1$::n0::P::p        MMethodDef      names/n1.nit:88,2--89,19        A redefinition in a subclass from a different module
+names::n2      MModule names/n2.nit:15,1--33,3 A alternative second module, used to make name conflicts
+names::n2$A    MClassDef       names/n2.nit:20,1--27,3 A refinement of a class
+names::n2::A::b        MMethod names/n2.nit:22,2--23,13        Name conflict? A second public method
+names::n2$A$b  MMethodDef      names/n2.nit:22,2--23,13        Name conflict? A second public method
+names::n2::A::z        MMethod names/n2.nit:25,2--26,13        Name conflict? A second private method
+names::n2$A$z  MMethodDef      names/n2.nit:25,2--26,13        Name conflict? A second private method
+names::n2::P   MClass  names/n2.nit:29,1--33,3 Name conflict? A second private class
+names::n2$P    MClassDef       names/n2.nit:29,1--33,3 Name conflict? A second private class
+names::n2::P::p        MMethod names/n2.nit:31,2--32,13        Name conflict? A private method in an homonym class.
+names::n2$P$p  MMethodDef      names/n2.nit:31,2--32,13        Name conflict? A private method in an homonym class.
+names1 MPackage        names1.nit      An alternative second module in a distinct package
+names1>        MGroup  names1.nit      An alternative second module in a distinct package
+names1::names1 MModule names1.nit:15,1--90,3   An alternative second module in a distinct package
+names1::names1$names::A        MClassDef       names1.nit:20,1--30,3   A refinement of a class
+names1::names1$names::A$a      MMethodDef      names1.nit:22,2--23,19  A refinement in the same class
+names1::names1$names::A$z      MMethodDef      names1.nit:25,2--26,19  A refinement in the same class
+names1::names1::A::b   MMethod names1.nit:28,2--29,13  A public method introduced in a refinement
+names1::names1$names::A$b      MMethodDef      names1.nit:28,2--29,13  A public method introduced in a refinement
+names1::names1$names::A0       MClassDef       names1.nit:32,1--42,3   A refinement of a subclass
+names1::names1$names::A0$names::A::a   MMethodDef      names1.nit:34,2--35,19  A refinement+redefinition
+names1::names1$names::A0$names::n0::A::z       MMethodDef      names1.nit:37,2--38,19  A refinement+redefinition
+names1::names1$names::A0$names::n0::P::p       MMethodDef      names1.nit:40,2--41,19  A refinement+redefinition
+names1::A1     MClass  names1.nit:44,1--57,3   A subclass introduced in a submodule
+names1$A1      MClassDef       names1.nit:44,1--57,3   A subclass introduced in a submodule
+names1$A1$names::A::a  MMethodDef      names1.nit:49,2--50,19  A redefinition in a subclass from a different module
+names1$A1$names::n0::A::z      MMethodDef      names1.nit:52,2--53,19  A redefinition in a subclass from a different module
+names1$A1$names::n0::P::p      MMethodDef      names1.nit:55,2--56,19  A redefinition in a subclass from a different module
+names1::names1$names::n0::P    MClassDef       names1.nit:59,1--63,3   A refinement of a class
+names1::names1$names::n0::P$p  MMethodDef      names1.nit:61,2--62,19  A refinement in the same class
+names1::names1$names::n0::P0   MClassDef       names1.nit:65,1--75,3   A refinement of a subclass
+names1::names1$names::n0::P0$names::A::a       MMethodDef      names1.nit:67,2--68,19  A refinement+redefinition
+names1::names1$names::n0::P0$names::n0::A::z   MMethodDef      names1.nit:70,2--71,19  A refinement+redefinition
+names1::names1$names::n0::P0$names::n0::P::p   MMethodDef      names1.nit:73,2--74,19  A refinement+redefinition
+names1::names1::P1     MClass  names1.nit:77,1--90,3   A private subclass introduced in a different module
+names1::names1$P1      MClassDef       names1.nit:77,1--90,3   A private subclass introduced in a different module
+names1::names1$P1$names::A::a  MMethodDef      names1.nit:82,2--83,19  A redefinition in a subclass from a different module
+names1::names1$P1$names::n0::A::z      MMethodDef      names1.nit:85,2--86,19  A redefinition in a subclass from a different module
+names1::names1$P1$names::n0::P::p      MMethodDef      names1.nit:88,2--89,19  A redefinition in a subclass from a different module
diff --git a/tests/sav/test_nativestring_fill_from.res b/tests/sav/test_nativestring_fill_from.res
new file mode 100644 (file)
index 0000000..2c26c70
--- /dev/null
@@ -0,0 +1 @@
+S&éstr
diff --git a/tests/sav/test_nativestring_fill_from_alt1.res b/tests/sav/test_nativestring_fill_from_alt1.res
new file mode 100644 (file)
index 0000000..0667158
--- /dev/null
@@ -0,0 +1 @@
+S&éstrS&éstr
diff --git a/tests/sav/test_nativestring_fill_from_alt2.res b/tests/sav/test_nativestring_fill_from_alt2.res
new file mode 100644 (file)
index 0000000..2c26c70
--- /dev/null
@@ -0,0 +1 @@
+S&éstr
diff --git a/tests/sav/test_nativestring_fill_from_alt3.res b/tests/sav/test_nativestring_fill_from_alt3.res
new file mode 100644 (file)
index 0000000..f28809f
--- /dev/null
@@ -0,0 +1 @@
+&éstr
index 4ac86da..fdc3be7 100644 (file)
@@ -1,12 +1,12 @@
 # mpackages:
-test_prog
+excluded test_prog
 ------------------------------------
-test_prog
+excluded test_prog
 
 # mmodules:
-careers character combat game platform races rpg test_prog
+careers character combat excluded game platform races rpg test_prog
 ------------------------------------
-careers character combat game platform races rpg test_prog
+careers character combat excluded game platform races rpg test_prog
 
 # mclasses:
 Alcoholic Bool Career Character Combatable Dwarf Elf Float Game Human Int List Magician Object Race Starter String Sys Warrior Weapon
index 9e5ceab..38c4890 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/core/collection/array.nit:989)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/core/collection/array.nit:991)
 NativeString
 0x4e
 Nit
diff --git a/tests/sav/test_nitcorn.res b/tests/sav/test_nitcorn.res
new file mode 100644 (file)
index 0000000..6e77d2e
--- /dev/null
@@ -0,0 +1,125 @@
+
+[Client] curl -s localhost:*****/simple_answer
+[Response] Simple answer
+Method: GET, URI: /simple_answer, trailing: /
+
+[Client] curl -s localhost:*****/simple_answer/
+[Response] Simple answer
+Method: GET, URI: /simple_answer/, trailing: /
+
+[Client] curl -s localhost:*****/simple_answer/trailing/path
+[Response] Simple answer
+Method: GET, URI: /simple_answer/trailing/path, trailing: /trailing/path
+
+[Client] curl -s 'localhost:*****/simple_answer?i=0123&s=asdf'
+[Response] Simple answer
+Method: GET, URI: /simple_answer, trailing: /
+GET args: i:0123, s:asdf
+
+[Client] curl -s localhost:*****/simple_answer --data 'i=0123&s=asdf'
+[Response] Simple answer
+Method: POST, URI: /simple_answer, trailing: /
+POST args: i:0123, s:asdf
+
+[Client] curl -s localhost:*****/simple_answer --cookie 'i=0123; s=asdf'
+[Response] Simple answer
+Method: GET, URI: /simple_answer, trailing: /
+Cookie: i:0123, s:asdf
+
+[Client] curl -s localhost:*****/params_answer/0123/asdf
+[Response] Simple answer
+Method: GET, URI: /params_answer/0123/asdf, trailing: /
+Params args: i:0123, s:asdf
+
+[Client] curl -s localhost:*****/params_answer/0123/
+[Response] Simple answer
+Method: GET, URI: /params_answer/0123/, trailing: /
+Params args: i:0123, s:
+
+[Client] curl -s localhost:*****/params_answer/0123/asdf/trailing/path
+[Response] Simple answer
+Method: GET, URI: /params_answer/0123/asdf/trailing/path, trailing: /trailing/path
+Params args: i:0123, s:asdf
+
+[Client] curl -s localhost:*****/params_answer/0123 --head
+HTTP/1.0 404 Not Found\r
+Content-Length: 0\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
+
+[Client] curl -s localhost:*****/file_server/
+<!DOCTYPE html>
+<head>
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <script>
+               
+       </script>
+       <title>/</title>
+</head>
+<body>
+       
+       <div class="container">
+               <h1>/</h1>
+               <ul>
+                       <li><a href="a.txt">a.txt</a></li>
+                       <li><a href="b.txt">b.txt</a></li>
+                       <li><a href="binary_file.png">binary_file.png</a></li>
+               </ul>
+       </div>
+</body>
+</html>
+[Client] curl -s localhost:*****/file_server/ --head
+HTTP/1.0 200 OK\r
+Content-Type: text/html\r
+Content-Length: 467\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
+
+[Client] curl -s localhost:*****/file_server --head
+HTTP/1.0 303 See Other\r
+Location: /file_server/\r
+Content-Length: 0\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
+
+[Client] curl -s localhost:*****/file_server/a.txt
+aaaAAAAAaaaa
+
+[Client] curl -s localhost:*****/file_server/a.txt --head
+HTTP/1.0 200 OK\r
+Content-Type: text/plain\r
+cache-control: public, max-age=360\r
+Content-Length: 13\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
+
+[Client] curl -s localhost:*****/file_server/binary_file.png --head
+HTTP/1.0 200 OK\r
+Content-Type: image/png\r
+cache-control: public, max-age=360\r
+Content-Length: 2503\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
+
+[Client] curl -s localhost:*****/file_server/binary_file.png | diff - .../binary_file.png
+
+[Client] curl -s localhost:*****/file_server/unknown_file.txt --head
+HTTP/1.0 404 Not Found\r
+Content-Length: 329\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
+
+[Client] curl -s localhost:*****/invalid_route --head
+HTTP/1.0 404 Not Found\r
+Content-Length: 0\r
+Server: nitcorn\r
+Set-Cookie: nitcorn_session=; HttpOnly; expires=Thu, 01 Jan 1970 00:00:00 GMT\r
+\r
diff --git a/tests/sav/test_postgres_native.res b/tests/sav/test_postgres_native.res
new file mode 100644 (file)
index 0000000..0c511fe
--- /dev/null
@@ -0,0 +1,3 @@
+aname   class   sex   
+Whale   mammal   1   
+Snake   reptile   0   
diff --git a/tests/sav/test_postgres_nity.res b/tests/sav/test_postgres_nity.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/test_readline.res b/tests/sav/test_readline.res
new file mode 100644 (file)
index 0000000..666312a
--- /dev/null
@@ -0,0 +1,9 @@
+prompt>line 1
+line 1
+prompt>line 2
+line 2
+prompt>line 2bis
+line 2bis
+prompt>line 3 \b \bine\b \b\b \b\b \b3\b \b
+line 3
+prompt>
\ No newline at end of file
diff --git a/tests/sav/test_realtime.res b/tests/sav/test_realtime.res
deleted file mode 100644 (file)
index 65b7078..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-sleeping 1s
-true
-true
-sleeping 5000ns
-true
-true
-true
diff --git a/tests/sav/test_rope_bytes.res b/tests/sav/test_rope_bytes.res
new file mode 100644 (file)
index 0000000..a61d3e4
--- /dev/null
@@ -0,0 +1,2 @@
+à1111111111111111111111111111111111111111éć2222222222222222222222222222222222222222ç
+0xc3 0xa0 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0xc3 0xa9 0xc4 0x87 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0xc3 0xa7
diff --git a/tests/sav/test_rubix_cube.res b/tests/sav/test_rubix_cube.res
new file mode 100644 (file)
index 0000000..a97fc9b
--- /dev/null
@@ -0,0 +1,1664 @@
+Trying combination [U,U']
+Combination [U,U'] is OK.
+Trying combination [L,L']
+Combination [L,L'] is OK.
+Trying combination [F,F']
+Combination [F,F'] is OK.
+Trying combination [R,R']
+Combination [R,R'] is OK.
+Trying combination [B,B']
+Combination [B,B'] is OK.
+Trying combination [D,D']
+Combination [D,D'] is OK.
+Trying combination [M,M']
+Combination [M,M'] is OK.
+Trying combination [E,E']
+Combination [E,E'] is OK.
+Trying combination [S,S']
+Combination [S,S'] is OK.
+Trying combination [u,u']
+Combination [u,u'] is OK.
+Trying combination [l,l']
+Combination [l,l'] is OK.
+Trying combination [r,r']
+Combination [r,r'] is OK.
+Trying combination [b,b']
+Combination [b,b'] is OK.
+Trying combination [d,d']
+Combination [d,d'] is OK.
+Trying combination [X,X']
+Combination [X,X'] is OK.
+Trying combination [Y,Y']
+Combination [Y,Y'] is OK.
+Trying combination [Z,Z']
+Combination [Z,Z'] is OK.
+Trying combination [U,L,L',U']
+Combination [U,L,L',U'] is OK.
+Trying combination [U,F,F',U']
+Combination [U,F,F',U'] is OK.
+Trying combination [U,R,R',U']
+Combination [U,R,R',U'] is OK.
+Trying combination [U,B,B',U']
+Combination [U,B,B',U'] is OK.
+Trying combination [U,D,D',U']
+Combination [U,D,D',U'] is OK.
+Trying combination [U,M,M',U']
+Combination [U,M,M',U'] is OK.
+Trying combination [U,E,E',U']
+Combination [U,E,E',U'] is OK.
+Trying combination [U,S,S',U']
+Combination [U,S,S',U'] is OK.
+Trying combination [U,u,u',U']
+Combination [U,u,u',U'] is OK.
+Trying combination [U,l,l',U']
+Combination [U,l,l',U'] is OK.
+Trying combination [U,r,r',U']
+Combination [U,r,r',U'] is OK.
+Trying combination [U,b,b',U']
+Combination [U,b,b',U'] is OK.
+Trying combination [U,d,d',U']
+Combination [U,d,d',U'] is OK.
+Trying combination [U,X,X',U']
+Combination [U,X,X',U'] is OK.
+Trying combination [U,Y,Y',U']
+Combination [U,Y,Y',U'] is OK.
+Trying combination [U,Z,Z',U']
+Combination [U,Z,Z',U'] is OK.
+Trying combination [L,F,F',L']
+Combination [L,F,F',L'] is OK.
+Trying combination [L,R,R',L']
+Combination [L,R,R',L'] is OK.
+Trying combination [L,B,B',L']
+Combination [L,B,B',L'] is OK.
+Trying combination [L,D,D',L']
+Combination [L,D,D',L'] is OK.
+Trying combination [L,M,M',L']
+Combination [L,M,M',L'] is OK.
+Trying combination [L,E,E',L']
+Combination [L,E,E',L'] is OK.
+Trying combination [L,S,S',L']
+Combination [L,S,S',L'] is OK.
+Trying combination [L,u,u',L']
+Combination [L,u,u',L'] is OK.
+Trying combination [L,l,l',L']
+Combination [L,l,l',L'] is OK.
+Trying combination [L,r,r',L']
+Combination [L,r,r',L'] is OK.
+Trying combination [L,b,b',L']
+Combination [L,b,b',L'] is OK.
+Trying combination [L,d,d',L']
+Combination [L,d,d',L'] is OK.
+Trying combination [L,X,X',L']
+Combination [L,X,X',L'] is OK.
+Trying combination [L,Y,Y',L']
+Combination [L,Y,Y',L'] is OK.
+Trying combination [L,Z,Z',L']
+Combination [L,Z,Z',L'] is OK.
+Trying combination [F,R,R',F']
+Combination [F,R,R',F'] is OK.
+Trying combination [F,B,B',F']
+Combination [F,B,B',F'] is OK.
+Trying combination [F,D,D',F']
+Combination [F,D,D',F'] is OK.
+Trying combination [F,M,M',F']
+Combination [F,M,M',F'] is OK.
+Trying combination [F,E,E',F']
+Combination [F,E,E',F'] is OK.
+Trying combination [F,S,S',F']
+Combination [F,S,S',F'] is OK.
+Trying combination [F,u,u',F']
+Combination [F,u,u',F'] is OK.
+Trying combination [F,l,l',F']
+Combination [F,l,l',F'] is OK.
+Trying combination [F,r,r',F']
+Combination [F,r,r',F'] is OK.
+Trying combination [F,b,b',F']
+Combination [F,b,b',F'] is OK.
+Trying combination [F,d,d',F']
+Combination [F,d,d',F'] is OK.
+Trying combination [F,X,X',F']
+Combination [F,X,X',F'] is OK.
+Trying combination [F,Y,Y',F']
+Combination [F,Y,Y',F'] is OK.
+Trying combination [F,Z,Z',F']
+Combination [F,Z,Z',F'] is OK.
+Trying combination [R,B,B',R']
+Combination [R,B,B',R'] is OK.
+Trying combination [R,D,D',R']
+Combination [R,D,D',R'] is OK.
+Trying combination [R,M,M',R']
+Combination [R,M,M',R'] is OK.
+Trying combination [R,E,E',R']
+Combination [R,E,E',R'] is OK.
+Trying combination [R,S,S',R']
+Combination [R,S,S',R'] is OK.
+Trying combination [R,u,u',R']
+Combination [R,u,u',R'] is OK.
+Trying combination [R,l,l',R']
+Combination [R,l,l',R'] is OK.
+Trying combination [R,r,r',R']
+Combination [R,r,r',R'] is OK.
+Trying combination [R,b,b',R']
+Combination [R,b,b',R'] is OK.
+Trying combination [R,d,d',R']
+Combination [R,d,d',R'] is OK.
+Trying combination [R,X,X',R']
+Combination [R,X,X',R'] is OK.
+Trying combination [R,Y,Y',R']
+Combination [R,Y,Y',R'] is OK.
+Trying combination [R,Z,Z',R']
+Combination [R,Z,Z',R'] is OK.
+Trying combination [B,D,D',B']
+Combination [B,D,D',B'] is OK.
+Trying combination [B,M,M',B']
+Combination [B,M,M',B'] is OK.
+Trying combination [B,E,E',B']
+Combination [B,E,E',B'] is OK.
+Trying combination [B,S,S',B']
+Combination [B,S,S',B'] is OK.
+Trying combination [B,u,u',B']
+Combination [B,u,u',B'] is OK.
+Trying combination [B,l,l',B']
+Combination [B,l,l',B'] is OK.
+Trying combination [B,r,r',B']
+Combination [B,r,r',B'] is OK.
+Trying combination [B,b,b',B']
+Combination [B,b,b',B'] is OK.
+Trying combination [B,d,d',B']
+Combination [B,d,d',B'] is OK.
+Trying combination [B,X,X',B']
+Combination [B,X,X',B'] is OK.
+Trying combination [B,Y,Y',B']
+Combination [B,Y,Y',B'] is OK.
+Trying combination [B,Z,Z',B']
+Combination [B,Z,Z',B'] is OK.
+Trying combination [D,M,M',D']
+Combination [D,M,M',D'] is OK.
+Trying combination [D,E,E',D']
+Combination [D,E,E',D'] is OK.
+Trying combination [D,S,S',D']
+Combination [D,S,S',D'] is OK.
+Trying combination [D,u,u',D']
+Combination [D,u,u',D'] is OK.
+Trying combination [D,l,l',D']
+Combination [D,l,l',D'] is OK.
+Trying combination [D,r,r',D']
+Combination [D,r,r',D'] is OK.
+Trying combination [D,b,b',D']
+Combination [D,b,b',D'] is OK.
+Trying combination [D,d,d',D']
+Combination [D,d,d',D'] is OK.
+Trying combination [D,X,X',D']
+Combination [D,X,X',D'] is OK.
+Trying combination [D,Y,Y',D']
+Combination [D,Y,Y',D'] is OK.
+Trying combination [D,Z,Z',D']
+Combination [D,Z,Z',D'] is OK.
+Trying combination [M,E,E',M']
+Combination [M,E,E',M'] is OK.
+Trying combination [M,S,S',M']
+Combination [M,S,S',M'] is OK.
+Trying combination [M,u,u',M']
+Combination [M,u,u',M'] is OK.
+Trying combination [M,l,l',M']
+Combination [M,l,l',M'] is OK.
+Trying combination [M,r,r',M']
+Combination [M,r,r',M'] is OK.
+Trying combination [M,b,b',M']
+Combination [M,b,b',M'] is OK.
+Trying combination [M,d,d',M']
+Combination [M,d,d',M'] is OK.
+Trying combination [M,X,X',M']
+Combination [M,X,X',M'] is OK.
+Trying combination [M,Y,Y',M']
+Combination [M,Y,Y',M'] is OK.
+Trying combination [M,Z,Z',M']
+Combination [M,Z,Z',M'] is OK.
+Trying combination [E,S,S',E']
+Combination [E,S,S',E'] is OK.
+Trying combination [E,u,u',E']
+Combination [E,u,u',E'] is OK.
+Trying combination [E,l,l',E']
+Combination [E,l,l',E'] is OK.
+Trying combination [E,r,r',E']
+Combination [E,r,r',E'] is OK.
+Trying combination [E,b,b',E']
+Combination [E,b,b',E'] is OK.
+Trying combination [E,d,d',E']
+Combination [E,d,d',E'] is OK.
+Trying combination [E,X,X',E']
+Combination [E,X,X',E'] is OK.
+Trying combination [E,Y,Y',E']
+Combination [E,Y,Y',E'] is OK.
+Trying combination [E,Z,Z',E']
+Combination [E,Z,Z',E'] is OK.
+Trying combination [S,u,u',S']
+Combination [S,u,u',S'] is OK.
+Trying combination [S,l,l',S']
+Combination [S,l,l',S'] is OK.
+Trying combination [S,r,r',S']
+Combination [S,r,r',S'] is OK.
+Trying combination [S,b,b',S']
+Combination [S,b,b',S'] is OK.
+Trying combination [S,d,d',S']
+Combination [S,d,d',S'] is OK.
+Trying combination [S,X,X',S']
+Combination [S,X,X',S'] is OK.
+Trying combination [S,Y,Y',S']
+Combination [S,Y,Y',S'] is OK.
+Trying combination [S,Z,Z',S']
+Combination [S,Z,Z',S'] is OK.
+Trying combination [u,l,l',u']
+Combination [u,l,l',u'] is OK.
+Trying combination [u,r,r',u']
+Combination [u,r,r',u'] is OK.
+Trying combination [u,b,b',u']
+Combination [u,b,b',u'] is OK.
+Trying combination [u,d,d',u']
+Combination [u,d,d',u'] is OK.
+Trying combination [u,X,X',u']
+Combination [u,X,X',u'] is OK.
+Trying combination [u,Y,Y',u']
+Combination [u,Y,Y',u'] is OK.
+Trying combination [u,Z,Z',u']
+Combination [u,Z,Z',u'] is OK.
+Trying combination [l,r,r',l']
+Combination [l,r,r',l'] is OK.
+Trying combination [l,b,b',l']
+Combination [l,b,b',l'] is OK.
+Trying combination [l,d,d',l']
+Combination [l,d,d',l'] is OK.
+Trying combination [l,X,X',l']
+Combination [l,X,X',l'] is OK.
+Trying combination [l,Y,Y',l']
+Combination [l,Y,Y',l'] is OK.
+Trying combination [l,Z,Z',l']
+Combination [l,Z,Z',l'] is OK.
+Trying combination [r,b,b',r']
+Combination [r,b,b',r'] is OK.
+Trying combination [r,d,d',r']
+Combination [r,d,d',r'] is OK.
+Trying combination [r,X,X',r']
+Combination [r,X,X',r'] is OK.
+Trying combination [r,Y,Y',r']
+Combination [r,Y,Y',r'] is OK.
+Trying combination [r,Z,Z',r']
+Combination [r,Z,Z',r'] is OK.
+Trying combination [b,d,d',b']
+Combination [b,d,d',b'] is OK.
+Trying combination [b,X,X',b']
+Combination [b,X,X',b'] is OK.
+Trying combination [b,Y,Y',b']
+Combination [b,Y,Y',b'] is OK.
+Trying combination [b,Z,Z',b']
+Combination [b,Z,Z',b'] is OK.
+Trying combination [d,X,X',d']
+Combination [d,X,X',d'] is OK.
+Trying combination [d,Y,Y',d']
+Combination [d,Y,Y',d'] is OK.
+Trying combination [d,Z,Z',d']
+Combination [d,Z,Z',d'] is OK.
+Trying combination [X,Y,Y',X']
+Combination [X,Y,Y',X'] is OK.
+Trying combination [X,Z,Z',X']
+Combination [X,Z,Z',X'] is OK.
+Trying combination [Y,Z,Z',Y']
+Combination [Y,Z,Z',Y'] is OK.
+Trying combination [U,L,F,F',L',U']
+Combination [U,L,F,F',L',U'] is OK.
+Trying combination [U,L,R,R',L',U']
+Combination [U,L,R,R',L',U'] is OK.
+Trying combination [U,L,B,B',L',U']
+Combination [U,L,B,B',L',U'] is OK.
+Trying combination [U,L,D,D',L',U']
+Combination [U,L,D,D',L',U'] is OK.
+Trying combination [U,L,M,M',L',U']
+Combination [U,L,M,M',L',U'] is OK.
+Trying combination [U,L,E,E',L',U']
+Combination [U,L,E,E',L',U'] is OK.
+Trying combination [U,L,S,S',L',U']
+Combination [U,L,S,S',L',U'] is OK.
+Trying combination [U,L,u,u',L',U']
+Combination [U,L,u,u',L',U'] is OK.
+Trying combination [U,L,l,l',L',U']
+Combination [U,L,l,l',L',U'] is OK.
+Trying combination [U,L,r,r',L',U']
+Combination [U,L,r,r',L',U'] is OK.
+Trying combination [U,L,b,b',L',U']
+Combination [U,L,b,b',L',U'] is OK.
+Trying combination [U,L,d,d',L',U']
+Combination [U,L,d,d',L',U'] is OK.
+Trying combination [U,L,X,X',L',U']
+Combination [U,L,X,X',L',U'] is OK.
+Trying combination [U,L,Y,Y',L',U']
+Combination [U,L,Y,Y',L',U'] is OK.
+Trying combination [U,L,Z,Z',L',U']
+Combination [U,L,Z,Z',L',U'] is OK.
+Trying combination [U,F,R,R',F',U']
+Combination [U,F,R,R',F',U'] is OK.
+Trying combination [U,F,B,B',F',U']
+Combination [U,F,B,B',F',U'] is OK.
+Trying combination [U,F,D,D',F',U']
+Combination [U,F,D,D',F',U'] is OK.
+Trying combination [U,F,M,M',F',U']
+Combination [U,F,M,M',F',U'] is OK.
+Trying combination [U,F,E,E',F',U']
+Combination [U,F,E,E',F',U'] is OK.
+Trying combination [U,F,S,S',F',U']
+Combination [U,F,S,S',F',U'] is OK.
+Trying combination [U,F,u,u',F',U']
+Combination [U,F,u,u',F',U'] is OK.
+Trying combination [U,F,l,l',F',U']
+Combination [U,F,l,l',F',U'] is OK.
+Trying combination [U,F,r,r',F',U']
+Combination [U,F,r,r',F',U'] is OK.
+Trying combination [U,F,b,b',F',U']
+Combination [U,F,b,b',F',U'] is OK.
+Trying combination [U,F,d,d',F',U']
+Combination [U,F,d,d',F',U'] is OK.
+Trying combination [U,F,X,X',F',U']
+Combination [U,F,X,X',F',U'] is OK.
+Trying combination [U,F,Y,Y',F',U']
+Combination [U,F,Y,Y',F',U'] is OK.
+Trying combination [U,F,Z,Z',F',U']
+Combination [U,F,Z,Z',F',U'] is OK.
+Trying combination [U,R,B,B',R',U']
+Combination [U,R,B,B',R',U'] is OK.
+Trying combination [U,R,D,D',R',U']
+Combination [U,R,D,D',R',U'] is OK.
+Trying combination [U,R,M,M',R',U']
+Combination [U,R,M,M',R',U'] is OK.
+Trying combination [U,R,E,E',R',U']
+Combination [U,R,E,E',R',U'] is OK.
+Trying combination [U,R,S,S',R',U']
+Combination [U,R,S,S',R',U'] is OK.
+Trying combination [U,R,u,u',R',U']
+Combination [U,R,u,u',R',U'] is OK.
+Trying combination [U,R,l,l',R',U']
+Combination [U,R,l,l',R',U'] is OK.
+Trying combination [U,R,r,r',R',U']
+Combination [U,R,r,r',R',U'] is OK.
+Trying combination [U,R,b,b',R',U']
+Combination [U,R,b,b',R',U'] is OK.
+Trying combination [U,R,d,d',R',U']
+Combination [U,R,d,d',R',U'] is OK.
+Trying combination [U,R,X,X',R',U']
+Combination [U,R,X,X',R',U'] is OK.
+Trying combination [U,R,Y,Y',R',U']
+Combination [U,R,Y,Y',R',U'] is OK.
+Trying combination [U,R,Z,Z',R',U']
+Combination [U,R,Z,Z',R',U'] is OK.
+Trying combination [U,B,D,D',B',U']
+Combination [U,B,D,D',B',U'] is OK.
+Trying combination [U,B,M,M',B',U']
+Combination [U,B,M,M',B',U'] is OK.
+Trying combination [U,B,E,E',B',U']
+Combination [U,B,E,E',B',U'] is OK.
+Trying combination [U,B,S,S',B',U']
+Combination [U,B,S,S',B',U'] is OK.
+Trying combination [U,B,u,u',B',U']
+Combination [U,B,u,u',B',U'] is OK.
+Trying combination [U,B,l,l',B',U']
+Combination [U,B,l,l',B',U'] is OK.
+Trying combination [U,B,r,r',B',U']
+Combination [U,B,r,r',B',U'] is OK.
+Trying combination [U,B,b,b',B',U']
+Combination [U,B,b,b',B',U'] is OK.
+Trying combination [U,B,d,d',B',U']
+Combination [U,B,d,d',B',U'] is OK.
+Trying combination [U,B,X,X',B',U']
+Combination [U,B,X,X',B',U'] is OK.
+Trying combination [U,B,Y,Y',B',U']
+Combination [U,B,Y,Y',B',U'] is OK.
+Trying combination [U,B,Z,Z',B',U']
+Combination [U,B,Z,Z',B',U'] is OK.
+Trying combination [U,D,M,M',D',U']
+Combination [U,D,M,M',D',U'] is OK.
+Trying combination [U,D,E,E',D',U']
+Combination [U,D,E,E',D',U'] is OK.
+Trying combination [U,D,S,S',D',U']
+Combination [U,D,S,S',D',U'] is OK.
+Trying combination [U,D,u,u',D',U']
+Combination [U,D,u,u',D',U'] is OK.
+Trying combination [U,D,l,l',D',U']
+Combination [U,D,l,l',D',U'] is OK.
+Trying combination [U,D,r,r',D',U']
+Combination [U,D,r,r',D',U'] is OK.
+Trying combination [U,D,b,b',D',U']
+Combination [U,D,b,b',D',U'] is OK.
+Trying combination [U,D,d,d',D',U']
+Combination [U,D,d,d',D',U'] is OK.
+Trying combination [U,D,X,X',D',U']
+Combination [U,D,X,X',D',U'] is OK.
+Trying combination [U,D,Y,Y',D',U']
+Combination [U,D,Y,Y',D',U'] is OK.
+Trying combination [U,D,Z,Z',D',U']
+Combination [U,D,Z,Z',D',U'] is OK.
+Trying combination [U,M,E,E',M',U']
+Combination [U,M,E,E',M',U'] is OK.
+Trying combination [U,M,S,S',M',U']
+Combination [U,M,S,S',M',U'] is OK.
+Trying combination [U,M,u,u',M',U']
+Combination [U,M,u,u',M',U'] is OK.
+Trying combination [U,M,l,l',M',U']
+Combination [U,M,l,l',M',U'] is OK.
+Trying combination [U,M,r,r',M',U']
+Combination [U,M,r,r',M',U'] is OK.
+Trying combination [U,M,b,b',M',U']
+Combination [U,M,b,b',M',U'] is OK.
+Trying combination [U,M,d,d',M',U']
+Combination [U,M,d,d',M',U'] is OK.
+Trying combination [U,M,X,X',M',U']
+Combination [U,M,X,X',M',U'] is OK.
+Trying combination [U,M,Y,Y',M',U']
+Combination [U,M,Y,Y',M',U'] is OK.
+Trying combination [U,M,Z,Z',M',U']
+Combination [U,M,Z,Z',M',U'] is OK.
+Trying combination [U,E,S,S',E',U']
+Combination [U,E,S,S',E',U'] is OK.
+Trying combination [U,E,u,u',E',U']
+Combination [U,E,u,u',E',U'] is OK.
+Trying combination [U,E,l,l',E',U']
+Combination [U,E,l,l',E',U'] is OK.
+Trying combination [U,E,r,r',E',U']
+Combination [U,E,r,r',E',U'] is OK.
+Trying combination [U,E,b,b',E',U']
+Combination [U,E,b,b',E',U'] is OK.
+Trying combination [U,E,d,d',E',U']
+Combination [U,E,d,d',E',U'] is OK.
+Trying combination [U,E,X,X',E',U']
+Combination [U,E,X,X',E',U'] is OK.
+Trying combination [U,E,Y,Y',E',U']
+Combination [U,E,Y,Y',E',U'] is OK.
+Trying combination [U,E,Z,Z',E',U']
+Combination [U,E,Z,Z',E',U'] is OK.
+Trying combination [U,S,u,u',S',U']
+Combination [U,S,u,u',S',U'] is OK.
+Trying combination [U,S,l,l',S',U']
+Combination [U,S,l,l',S',U'] is OK.
+Trying combination [U,S,r,r',S',U']
+Combination [U,S,r,r',S',U'] is OK.
+Trying combination [U,S,b,b',S',U']
+Combination [U,S,b,b',S',U'] is OK.
+Trying combination [U,S,d,d',S',U']
+Combination [U,S,d,d',S',U'] is OK.
+Trying combination [U,S,X,X',S',U']
+Combination [U,S,X,X',S',U'] is OK.
+Trying combination [U,S,Y,Y',S',U']
+Combination [U,S,Y,Y',S',U'] is OK.
+Trying combination [U,S,Z,Z',S',U']
+Combination [U,S,Z,Z',S',U'] is OK.
+Trying combination [U,u,l,l',u',U']
+Combination [U,u,l,l',u',U'] is OK.
+Trying combination [U,u,r,r',u',U']
+Combination [U,u,r,r',u',U'] is OK.
+Trying combination [U,u,b,b',u',U']
+Combination [U,u,b,b',u',U'] is OK.
+Trying combination [U,u,d,d',u',U']
+Combination [U,u,d,d',u',U'] is OK.
+Trying combination [U,u,X,X',u',U']
+Combination [U,u,X,X',u',U'] is OK.
+Trying combination [U,u,Y,Y',u',U']
+Combination [U,u,Y,Y',u',U'] is OK.
+Trying combination [U,u,Z,Z',u',U']
+Combination [U,u,Z,Z',u',U'] is OK.
+Trying combination [U,l,r,r',l',U']
+Combination [U,l,r,r',l',U'] is OK.
+Trying combination [U,l,b,b',l',U']
+Combination [U,l,b,b',l',U'] is OK.
+Trying combination [U,l,d,d',l',U']
+Combination [U,l,d,d',l',U'] is OK.
+Trying combination [U,l,X,X',l',U']
+Combination [U,l,X,X',l',U'] is OK.
+Trying combination [U,l,Y,Y',l',U']
+Combination [U,l,Y,Y',l',U'] is OK.
+Trying combination [U,l,Z,Z',l',U']
+Combination [U,l,Z,Z',l',U'] is OK.
+Trying combination [U,r,b,b',r',U']
+Combination [U,r,b,b',r',U'] is OK.
+Trying combination [U,r,d,d',r',U']
+Combination [U,r,d,d',r',U'] is OK.
+Trying combination [U,r,X,X',r',U']
+Combination [U,r,X,X',r',U'] is OK.
+Trying combination [U,r,Y,Y',r',U']
+Combination [U,r,Y,Y',r',U'] is OK.
+Trying combination [U,r,Z,Z',r',U']
+Combination [U,r,Z,Z',r',U'] is OK.
+Trying combination [U,b,d,d',b',U']
+Combination [U,b,d,d',b',U'] is OK.
+Trying combination [U,b,X,X',b',U']
+Combination [U,b,X,X',b',U'] is OK.
+Trying combination [U,b,Y,Y',b',U']
+Combination [U,b,Y,Y',b',U'] is OK.
+Trying combination [U,b,Z,Z',b',U']
+Combination [U,b,Z,Z',b',U'] is OK.
+Trying combination [U,d,X,X',d',U']
+Combination [U,d,X,X',d',U'] is OK.
+Trying combination [U,d,Y,Y',d',U']
+Combination [U,d,Y,Y',d',U'] is OK.
+Trying combination [U,d,Z,Z',d',U']
+Combination [U,d,Z,Z',d',U'] is OK.
+Trying combination [U,X,Y,Y',X',U']
+Combination [U,X,Y,Y',X',U'] is OK.
+Trying combination [U,X,Z,Z',X',U']
+Combination [U,X,Z,Z',X',U'] is OK.
+Trying combination [U,Y,Z,Z',Y',U']
+Combination [U,Y,Z,Z',Y',U'] is OK.
+Trying combination [U,L,F,R,R',F',L',U']
+Combination [U,L,F,R,R',F',L',U'] is OK.
+Trying combination [U,L,F,B,B',F',L',U']
+Combination [U,L,F,B,B',F',L',U'] is OK.
+Trying combination [U,L,F,D,D',F',L',U']
+Combination [U,L,F,D,D',F',L',U'] is OK.
+Trying combination [U,L,F,M,M',F',L',U']
+Combination [U,L,F,M,M',F',L',U'] is OK.
+Trying combination [U,L,F,E,E',F',L',U']
+Combination [U,L,F,E,E',F',L',U'] is OK.
+Trying combination [U,L,F,S,S',F',L',U']
+Combination [U,L,F,S,S',F',L',U'] is OK.
+Trying combination [U,L,F,u,u',F',L',U']
+Combination [U,L,F,u,u',F',L',U'] is OK.
+Trying combination [U,L,F,l,l',F',L',U']
+Combination [U,L,F,l,l',F',L',U'] is OK.
+Trying combination [U,L,F,r,r',F',L',U']
+Combination [U,L,F,r,r',F',L',U'] is OK.
+Trying combination [U,L,F,b,b',F',L',U']
+Combination [U,L,F,b,b',F',L',U'] is OK.
+Trying combination [U,L,F,d,d',F',L',U']
+Combination [U,L,F,d,d',F',L',U'] is OK.
+Trying combination [U,L,F,X,X',F',L',U']
+Combination [U,L,F,X,X',F',L',U'] is OK.
+Trying combination [U,L,F,Y,Y',F',L',U']
+Combination [U,L,F,Y,Y',F',L',U'] is OK.
+Trying combination [U,L,F,Z,Z',F',L',U']
+Combination [U,L,F,Z,Z',F',L',U'] is OK.
+Trying combination [U,L,R,B,B',R',L',U']
+Combination [U,L,R,B,B',R',L',U'] is OK.
+Trying combination [U,L,R,D,D',R',L',U']
+Combination [U,L,R,D,D',R',L',U'] is OK.
+Trying combination [U,L,R,M,M',R',L',U']
+Combination [U,L,R,M,M',R',L',U'] is OK.
+Trying combination [U,L,R,E,E',R',L',U']
+Combination [U,L,R,E,E',R',L',U'] is OK.
+Trying combination [U,L,R,S,S',R',L',U']
+Combination [U,L,R,S,S',R',L',U'] is OK.
+Trying combination [U,L,R,u,u',R',L',U']
+Combination [U,L,R,u,u',R',L',U'] is OK.
+Trying combination [U,L,R,l,l',R',L',U']
+Combination [U,L,R,l,l',R',L',U'] is OK.
+Trying combination [U,L,R,r,r',R',L',U']
+Combination [U,L,R,r,r',R',L',U'] is OK.
+Trying combination [U,L,R,b,b',R',L',U']
+Combination [U,L,R,b,b',R',L',U'] is OK.
+Trying combination [U,L,R,d,d',R',L',U']
+Combination [U,L,R,d,d',R',L',U'] is OK.
+Trying combination [U,L,R,X,X',R',L',U']
+Combination [U,L,R,X,X',R',L',U'] is OK.
+Trying combination [U,L,R,Y,Y',R',L',U']
+Combination [U,L,R,Y,Y',R',L',U'] is OK.
+Trying combination [U,L,R,Z,Z',R',L',U']
+Combination [U,L,R,Z,Z',R',L',U'] is OK.
+Trying combination [U,L,B,D,D',B',L',U']
+Combination [U,L,B,D,D',B',L',U'] is OK.
+Trying combination [U,L,B,M,M',B',L',U']
+Combination [U,L,B,M,M',B',L',U'] is OK.
+Trying combination [U,L,B,E,E',B',L',U']
+Combination [U,L,B,E,E',B',L',U'] is OK.
+Trying combination [U,L,B,S,S',B',L',U']
+Combination [U,L,B,S,S',B',L',U'] is OK.
+Trying combination [U,L,B,u,u',B',L',U']
+Combination [U,L,B,u,u',B',L',U'] is OK.
+Trying combination [U,L,B,l,l',B',L',U']
+Combination [U,L,B,l,l',B',L',U'] is OK.
+Trying combination [U,L,B,r,r',B',L',U']
+Combination [U,L,B,r,r',B',L',U'] is OK.
+Trying combination [U,L,B,b,b',B',L',U']
+Combination [U,L,B,b,b',B',L',U'] is OK.
+Trying combination [U,L,B,d,d',B',L',U']
+Combination [U,L,B,d,d',B',L',U'] is OK.
+Trying combination [U,L,B,X,X',B',L',U']
+Combination [U,L,B,X,X',B',L',U'] is OK.
+Trying combination [U,L,B,Y,Y',B',L',U']
+Combination [U,L,B,Y,Y',B',L',U'] is OK.
+Trying combination [U,L,B,Z,Z',B',L',U']
+Combination [U,L,B,Z,Z',B',L',U'] is OK.
+Trying combination [U,L,D,M,M',D',L',U']
+Combination [U,L,D,M,M',D',L',U'] is OK.
+Trying combination [U,L,D,E,E',D',L',U']
+Combination [U,L,D,E,E',D',L',U'] is OK.
+Trying combination [U,L,D,S,S',D',L',U']
+Combination [U,L,D,S,S',D',L',U'] is OK.
+Trying combination [U,L,D,u,u',D',L',U']
+Combination [U,L,D,u,u',D',L',U'] is OK.
+Trying combination [U,L,D,l,l',D',L',U']
+Combination [U,L,D,l,l',D',L',U'] is OK.
+Trying combination [U,L,D,r,r',D',L',U']
+Combination [U,L,D,r,r',D',L',U'] is OK.
+Trying combination [U,L,D,b,b',D',L',U']
+Combination [U,L,D,b,b',D',L',U'] is OK.
+Trying combination [U,L,D,d,d',D',L',U']
+Combination [U,L,D,d,d',D',L',U'] is OK.
+Trying combination [U,L,D,X,X',D',L',U']
+Combination [U,L,D,X,X',D',L',U'] is OK.
+Trying combination [U,L,D,Y,Y',D',L',U']
+Combination [U,L,D,Y,Y',D',L',U'] is OK.
+Trying combination [U,L,D,Z,Z',D',L',U']
+Combination [U,L,D,Z,Z',D',L',U'] is OK.
+Trying combination [U,L,M,E,E',M',L',U']
+Combination [U,L,M,E,E',M',L',U'] is OK.
+Trying combination [U,L,M,S,S',M',L',U']
+Combination [U,L,M,S,S',M',L',U'] is OK.
+Trying combination [U,L,M,u,u',M',L',U']
+Combination [U,L,M,u,u',M',L',U'] is OK.
+Trying combination [U,L,M,l,l',M',L',U']
+Combination [U,L,M,l,l',M',L',U'] is OK.
+Trying combination [U,L,M,r,r',M',L',U']
+Combination [U,L,M,r,r',M',L',U'] is OK.
+Trying combination [U,L,M,b,b',M',L',U']
+Combination [U,L,M,b,b',M',L',U'] is OK.
+Trying combination [U,L,M,d,d',M',L',U']
+Combination [U,L,M,d,d',M',L',U'] is OK.
+Trying combination [U,L,M,X,X',M',L',U']
+Combination [U,L,M,X,X',M',L',U'] is OK.
+Trying combination [U,L,M,Y,Y',M',L',U']
+Combination [U,L,M,Y,Y',M',L',U'] is OK.
+Trying combination [U,L,M,Z,Z',M',L',U']
+Combination [U,L,M,Z,Z',M',L',U'] is OK.
+Trying combination [U,L,E,S,S',E',L',U']
+Combination [U,L,E,S,S',E',L',U'] is OK.
+Trying combination [U,L,E,u,u',E',L',U']
+Combination [U,L,E,u,u',E',L',U'] is OK.
+Trying combination [U,L,E,l,l',E',L',U']
+Combination [U,L,E,l,l',E',L',U'] is OK.
+Trying combination [U,L,E,r,r',E',L',U']
+Combination [U,L,E,r,r',E',L',U'] is OK.
+Trying combination [U,L,E,b,b',E',L',U']
+Combination [U,L,E,b,b',E',L',U'] is OK.
+Trying combination [U,L,E,d,d',E',L',U']
+Combination [U,L,E,d,d',E',L',U'] is OK.
+Trying combination [U,L,E,X,X',E',L',U']
+Combination [U,L,E,X,X',E',L',U'] is OK.
+Trying combination [U,L,E,Y,Y',E',L',U']
+Combination [U,L,E,Y,Y',E',L',U'] is OK.
+Trying combination [U,L,E,Z,Z',E',L',U']
+Combination [U,L,E,Z,Z',E',L',U'] is OK.
+Trying combination [U,L,S,u,u',S',L',U']
+Combination [U,L,S,u,u',S',L',U'] is OK.
+Trying combination [U,L,S,l,l',S',L',U']
+Combination [U,L,S,l,l',S',L',U'] is OK.
+Trying combination [U,L,S,r,r',S',L',U']
+Combination [U,L,S,r,r',S',L',U'] is OK.
+Trying combination [U,L,S,b,b',S',L',U']
+Combination [U,L,S,b,b',S',L',U'] is OK.
+Trying combination [U,L,S,d,d',S',L',U']
+Combination [U,L,S,d,d',S',L',U'] is OK.
+Trying combination [U,L,S,X,X',S',L',U']
+Combination [U,L,S,X,X',S',L',U'] is OK.
+Trying combination [U,L,S,Y,Y',S',L',U']
+Combination [U,L,S,Y,Y',S',L',U'] is OK.
+Trying combination [U,L,S,Z,Z',S',L',U']
+Combination [U,L,S,Z,Z',S',L',U'] is OK.
+Trying combination [U,L,u,l,l',u',L',U']
+Combination [U,L,u,l,l',u',L',U'] is OK.
+Trying combination [U,L,u,r,r',u',L',U']
+Combination [U,L,u,r,r',u',L',U'] is OK.
+Trying combination [U,L,u,b,b',u',L',U']
+Combination [U,L,u,b,b',u',L',U'] is OK.
+Trying combination [U,L,u,d,d',u',L',U']
+Combination [U,L,u,d,d',u',L',U'] is OK.
+Trying combination [U,L,u,X,X',u',L',U']
+Combination [U,L,u,X,X',u',L',U'] is OK.
+Trying combination [U,L,u,Y,Y',u',L',U']
+Combination [U,L,u,Y,Y',u',L',U'] is OK.
+Trying combination [U,L,u,Z,Z',u',L',U']
+Combination [U,L,u,Z,Z',u',L',U'] is OK.
+Trying combination [U,L,l,r,r',l',L',U']
+Combination [U,L,l,r,r',l',L',U'] is OK.
+Trying combination [U,L,l,b,b',l',L',U']
+Combination [U,L,l,b,b',l',L',U'] is OK.
+Trying combination [U,L,l,d,d',l',L',U']
+Combination [U,L,l,d,d',l',L',U'] is OK.
+Trying combination [U,L,l,X,X',l',L',U']
+Combination [U,L,l,X,X',l',L',U'] is OK.
+Trying combination [U,L,l,Y,Y',l',L',U']
+Combination [U,L,l,Y,Y',l',L',U'] is OK.
+Trying combination [U,L,l,Z,Z',l',L',U']
+Combination [U,L,l,Z,Z',l',L',U'] is OK.
+Trying combination [U,L,r,b,b',r',L',U']
+Combination [U,L,r,b,b',r',L',U'] is OK.
+Trying combination [U,L,r,d,d',r',L',U']
+Combination [U,L,r,d,d',r',L',U'] is OK.
+Trying combination [U,L,r,X,X',r',L',U']
+Combination [U,L,r,X,X',r',L',U'] is OK.
+Trying combination [U,L,r,Y,Y',r',L',U']
+Combination [U,L,r,Y,Y',r',L',U'] is OK.
+Trying combination [U,L,r,Z,Z',r',L',U']
+Combination [U,L,r,Z,Z',r',L',U'] is OK.
+Trying combination [U,L,b,d,d',b',L',U']
+Combination [U,L,b,d,d',b',L',U'] is OK.
+Trying combination [U,L,b,X,X',b',L',U']
+Combination [U,L,b,X,X',b',L',U'] is OK.
+Trying combination [U,L,b,Y,Y',b',L',U']
+Combination [U,L,b,Y,Y',b',L',U'] is OK.
+Trying combination [U,L,b,Z,Z',b',L',U']
+Combination [U,L,b,Z,Z',b',L',U'] is OK.
+Trying combination [U,L,d,X,X',d',L',U']
+Combination [U,L,d,X,X',d',L',U'] is OK.
+Trying combination [U,L,d,Y,Y',d',L',U']
+Combination [U,L,d,Y,Y',d',L',U'] is OK.
+Trying combination [U,L,d,Z,Z',d',L',U']
+Combination [U,L,d,Z,Z',d',L',U'] is OK.
+Trying combination [U,L,X,Y,Y',X',L',U']
+Combination [U,L,X,Y,Y',X',L',U'] is OK.
+Trying combination [U,L,X,Z,Z',X',L',U']
+Combination [U,L,X,Z,Z',X',L',U'] is OK.
+Trying combination [U,L,Y,Z,Z',Y',L',U']
+Combination [U,L,Y,Z,Z',Y',L',U'] is OK.
+Trying combination [U,L,F,R,B,B',R',F',L',U']
+Combination [U,L,F,R,B,B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,D',R',F',L',U']
+Combination [U,L,F,R,D,D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,M',R',F',L',U']
+Combination [U,L,F,R,M,M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,E',R',F',L',U']
+Combination [U,L,F,R,E,E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,S',R',F',L',U']
+Combination [U,L,F,R,S,S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,u',R',F',L',U']
+Combination [U,L,F,R,u,u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,l',R',F',L',U']
+Combination [U,L,F,R,l,l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,r',R',F',L',U']
+Combination [U,L,F,R,r,r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,b',R',F',L',U']
+Combination [U,L,F,R,b,b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,d',R',F',L',U']
+Combination [U,L,F,R,d,d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,X,X',R',F',L',U']
+Combination [U,L,F,R,X,X',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,Y,Y',R',F',L',U']
+Combination [U,L,F,R,Y,Y',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,Z,Z',R',F',L',U']
+Combination [U,L,F,R,Z,Z',R',F',L',U'] is OK.
+Trying combination [U,L,F,B,D,D',B',F',L',U']
+Combination [U,L,F,B,D,D',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,M,M',B',F',L',U']
+Combination [U,L,F,B,M,M',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,E,E',B',F',L',U']
+Combination [U,L,F,B,E,E',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,S,S',B',F',L',U']
+Combination [U,L,F,B,S,S',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,u,u',B',F',L',U']
+Combination [U,L,F,B,u,u',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,l,l',B',F',L',U']
+Combination [U,L,F,B,l,l',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,r,r',B',F',L',U']
+Combination [U,L,F,B,r,r',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,b,b',B',F',L',U']
+Combination [U,L,F,B,b,b',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,d,d',B',F',L',U']
+Combination [U,L,F,B,d,d',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,X,X',B',F',L',U']
+Combination [U,L,F,B,X,X',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,Y,Y',B',F',L',U']
+Combination [U,L,F,B,Y,Y',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,Z,Z',B',F',L',U']
+Combination [U,L,F,B,Z,Z',B',F',L',U'] is OK.
+Trying combination [U,L,F,D,M,M',D',F',L',U']
+Combination [U,L,F,D,M,M',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,E,E',D',F',L',U']
+Combination [U,L,F,D,E,E',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,S,S',D',F',L',U']
+Combination [U,L,F,D,S,S',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,u,u',D',F',L',U']
+Combination [U,L,F,D,u,u',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,l,l',D',F',L',U']
+Combination [U,L,F,D,l,l',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,r,r',D',F',L',U']
+Combination [U,L,F,D,r,r',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,b,b',D',F',L',U']
+Combination [U,L,F,D,b,b',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,d,d',D',F',L',U']
+Combination [U,L,F,D,d,d',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,X,X',D',F',L',U']
+Combination [U,L,F,D,X,X',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,Y,Y',D',F',L',U']
+Combination [U,L,F,D,Y,Y',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,Z,Z',D',F',L',U']
+Combination [U,L,F,D,Z,Z',D',F',L',U'] is OK.
+Trying combination [U,L,F,M,E,E',M',F',L',U']
+Combination [U,L,F,M,E,E',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,S,S',M',F',L',U']
+Combination [U,L,F,M,S,S',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,u,u',M',F',L',U']
+Combination [U,L,F,M,u,u',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,l,l',M',F',L',U']
+Combination [U,L,F,M,l,l',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,r,r',M',F',L',U']
+Combination [U,L,F,M,r,r',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,b,b',M',F',L',U']
+Combination [U,L,F,M,b,b',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,d,d',M',F',L',U']
+Combination [U,L,F,M,d,d',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,X,X',M',F',L',U']
+Combination [U,L,F,M,X,X',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,Y,Y',M',F',L',U']
+Combination [U,L,F,M,Y,Y',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,Z,Z',M',F',L',U']
+Combination [U,L,F,M,Z,Z',M',F',L',U'] is OK.
+Trying combination [U,L,F,E,S,S',E',F',L',U']
+Combination [U,L,F,E,S,S',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,u,u',E',F',L',U']
+Combination [U,L,F,E,u,u',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,l,l',E',F',L',U']
+Combination [U,L,F,E,l,l',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,r,r',E',F',L',U']
+Combination [U,L,F,E,r,r',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,b,b',E',F',L',U']
+Combination [U,L,F,E,b,b',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,d,d',E',F',L',U']
+Combination [U,L,F,E,d,d',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,X,X',E',F',L',U']
+Combination [U,L,F,E,X,X',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,Y,Y',E',F',L',U']
+Combination [U,L,F,E,Y,Y',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,Z,Z',E',F',L',U']
+Combination [U,L,F,E,Z,Z',E',F',L',U'] is OK.
+Trying combination [U,L,F,S,u,u',S',F',L',U']
+Combination [U,L,F,S,u,u',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,l,l',S',F',L',U']
+Combination [U,L,F,S,l,l',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,r,r',S',F',L',U']
+Combination [U,L,F,S,r,r',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,b,b',S',F',L',U']
+Combination [U,L,F,S,b,b',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,d,d',S',F',L',U']
+Combination [U,L,F,S,d,d',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,X,X',S',F',L',U']
+Combination [U,L,F,S,X,X',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,Y,Y',S',F',L',U']
+Combination [U,L,F,S,Y,Y',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,Z,Z',S',F',L',U']
+Combination [U,L,F,S,Z,Z',S',F',L',U'] is OK.
+Trying combination [U,L,F,u,l,l',u',F',L',U']
+Combination [U,L,F,u,l,l',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,r,r',u',F',L',U']
+Combination [U,L,F,u,r,r',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,b,b',u',F',L',U']
+Combination [U,L,F,u,b,b',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,d,d',u',F',L',U']
+Combination [U,L,F,u,d,d',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,X,X',u',F',L',U']
+Combination [U,L,F,u,X,X',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,Y,Y',u',F',L',U']
+Combination [U,L,F,u,Y,Y',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,Z,Z',u',F',L',U']
+Combination [U,L,F,u,Z,Z',u',F',L',U'] is OK.
+Trying combination [U,L,F,l,r,r',l',F',L',U']
+Combination [U,L,F,l,r,r',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,b,b',l',F',L',U']
+Combination [U,L,F,l,b,b',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,d,d',l',F',L',U']
+Combination [U,L,F,l,d,d',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,X,X',l',F',L',U']
+Combination [U,L,F,l,X,X',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,Y,Y',l',F',L',U']
+Combination [U,L,F,l,Y,Y',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,Z,Z',l',F',L',U']
+Combination [U,L,F,l,Z,Z',l',F',L',U'] is OK.
+Trying combination [U,L,F,r,b,b',r',F',L',U']
+Combination [U,L,F,r,b,b',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,d,d',r',F',L',U']
+Combination [U,L,F,r,d,d',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,X,X',r',F',L',U']
+Combination [U,L,F,r,X,X',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,Y,Y',r',F',L',U']
+Combination [U,L,F,r,Y,Y',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,Z,Z',r',F',L',U']
+Combination [U,L,F,r,Z,Z',r',F',L',U'] is OK.
+Trying combination [U,L,F,b,d,d',b',F',L',U']
+Combination [U,L,F,b,d,d',b',F',L',U'] is OK.
+Trying combination [U,L,F,b,X,X',b',F',L',U']
+Combination [U,L,F,b,X,X',b',F',L',U'] is OK.
+Trying combination [U,L,F,b,Y,Y',b',F',L',U']
+Combination [U,L,F,b,Y,Y',b',F',L',U'] is OK.
+Trying combination [U,L,F,b,Z,Z',b',F',L',U']
+Combination [U,L,F,b,Z,Z',b',F',L',U'] is OK.
+Trying combination [U,L,F,d,X,X',d',F',L',U']
+Combination [U,L,F,d,X,X',d',F',L',U'] is OK.
+Trying combination [U,L,F,d,Y,Y',d',F',L',U']
+Combination [U,L,F,d,Y,Y',d',F',L',U'] is OK.
+Trying combination [U,L,F,d,Z,Z',d',F',L',U']
+Combination [U,L,F,d,Z,Z',d',F',L',U'] is OK.
+Trying combination [U,L,F,X,Y,Y',X',F',L',U']
+Combination [U,L,F,X,Y,Y',X',F',L',U'] is OK.
+Trying combination [U,L,F,X,Z,Z',X',F',L',U']
+Combination [U,L,F,X,Z,Z',X',F',L',U'] is OK.
+Trying combination [U,L,F,Y,Z,Z',Y',F',L',U']
+Combination [U,L,F,Y,Z,Z',Y',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,X,X',B',R',F',L',U']
+Combination [U,L,F,R,B,X,X',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,Y,Y',B',R',F',L',U']
+Combination [U,L,F,R,B,Y,Y',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,Z,Z',B',R',F',L',U']
+Combination [U,L,F,R,B,Z,Z',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,M,M',D',R',F',L',U']
+Combination [U,L,F,R,D,M,M',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,E,E',D',R',F',L',U']
+Combination [U,L,F,R,D,E,E',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,S,S',D',R',F',L',U']
+Combination [U,L,F,R,D,S,S',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,u,u',D',R',F',L',U']
+Combination [U,L,F,R,D,u,u',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,l,l',D',R',F',L',U']
+Combination [U,L,F,R,D,l,l',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,r,r',D',R',F',L',U']
+Combination [U,L,F,R,D,r,r',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,b,b',D',R',F',L',U']
+Combination [U,L,F,R,D,b,b',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,d,d',D',R',F',L',U']
+Combination [U,L,F,R,D,d,d',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,X,X',D',R',F',L',U']
+Combination [U,L,F,R,D,X,X',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,Y,Y',D',R',F',L',U']
+Combination [U,L,F,R,D,Y,Y',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,Z,Z',D',R',F',L',U']
+Combination [U,L,F,R,D,Z,Z',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,E,E',M',R',F',L',U']
+Combination [U,L,F,R,M,E,E',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,S,S',M',R',F',L',U']
+Combination [U,L,F,R,M,S,S',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,u,u',M',R',F',L',U']
+Combination [U,L,F,R,M,u,u',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,l,l',M',R',F',L',U']
+Combination [U,L,F,R,M,l,l',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,r,r',M',R',F',L',U']
+Combination [U,L,F,R,M,r,r',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,b,b',M',R',F',L',U']
+Combination [U,L,F,R,M,b,b',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,d,d',M',R',F',L',U']
+Combination [U,L,F,R,M,d,d',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,X,X',M',R',F',L',U']
+Combination [U,L,F,R,M,X,X',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,Y,Y',M',R',F',L',U']
+Combination [U,L,F,R,M,Y,Y',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,Z,Z',M',R',F',L',U']
+Combination [U,L,F,R,M,Z,Z',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,S,S',E',R',F',L',U']
+Combination [U,L,F,R,E,S,S',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,u,u',E',R',F',L',U']
+Combination [U,L,F,R,E,u,u',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,l,l',E',R',F',L',U']
+Combination [U,L,F,R,E,l,l',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,r,r',E',R',F',L',U']
+Combination [U,L,F,R,E,r,r',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,b,b',E',R',F',L',U']
+Combination [U,L,F,R,E,b,b',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,d,d',E',R',F',L',U']
+Combination [U,L,F,R,E,d,d',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,X,X',E',R',F',L',U']
+Combination [U,L,F,R,E,X,X',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,Y,Y',E',R',F',L',U']
+Combination [U,L,F,R,E,Y,Y',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,Z,Z',E',R',F',L',U']
+Combination [U,L,F,R,E,Z,Z',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,u,u',S',R',F',L',U']
+Combination [U,L,F,R,S,u,u',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,l,l',S',R',F',L',U']
+Combination [U,L,F,R,S,l,l',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,r,r',S',R',F',L',U']
+Combination [U,L,F,R,S,r,r',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,b,b',S',R',F',L',U']
+Combination [U,L,F,R,S,b,b',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,d,d',S',R',F',L',U']
+Combination [U,L,F,R,S,d,d',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,X,X',S',R',F',L',U']
+Combination [U,L,F,R,S,X,X',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,Y,Y',S',R',F',L',U']
+Combination [U,L,F,R,S,Y,Y',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,Z,Z',S',R',F',L',U']
+Combination [U,L,F,R,S,Z,Z',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,l,l',u',R',F',L',U']
+Combination [U,L,F,R,u,l,l',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,r,r',u',R',F',L',U']
+Combination [U,L,F,R,u,r,r',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,b,b',u',R',F',L',U']
+Combination [U,L,F,R,u,b,b',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,d,d',u',R',F',L',U']
+Combination [U,L,F,R,u,d,d',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,X,X',u',R',F',L',U']
+Combination [U,L,F,R,u,X,X',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,Y,Y',u',R',F',L',U']
+Combination [U,L,F,R,u,Y,Y',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,Z,Z',u',R',F',L',U']
+Combination [U,L,F,R,u,Z,Z',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,r,r',l',R',F',L',U']
+Combination [U,L,F,R,l,r,r',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,b,b',l',R',F',L',U']
+Combination [U,L,F,R,l,b,b',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,d,d',l',R',F',L',U']
+Combination [U,L,F,R,l,d,d',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,X,X',l',R',F',L',U']
+Combination [U,L,F,R,l,X,X',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,Y,Y',l',R',F',L',U']
+Combination [U,L,F,R,l,Y,Y',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,Z,Z',l',R',F',L',U']
+Combination [U,L,F,R,l,Z,Z',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,b,b',r',R',F',L',U']
+Combination [U,L,F,R,r,b,b',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,d,d',r',R',F',L',U']
+Combination [U,L,F,R,r,d,d',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,X,X',r',R',F',L',U']
+Combination [U,L,F,R,r,X,X',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,Y,Y',r',R',F',L',U']
+Combination [U,L,F,R,r,Y,Y',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,Z,Z',r',R',F',L',U']
+Combination [U,L,F,R,r,Z,Z',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,d,d',b',R',F',L',U']
+Combination [U,L,F,R,b,d,d',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,X,X',b',R',F',L',U']
+Combination [U,L,F,R,b,X,X',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,Y,Y',b',R',F',L',U']
+Combination [U,L,F,R,b,Y,Y',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,Z,Z',b',R',F',L',U']
+Combination [U,L,F,R,b,Z,Z',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,X,X',d',R',F',L',U']
+Combination [U,L,F,R,d,X,X',d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,Y,Y',d',R',F',L',U']
+Combination [U,L,F,R,d,Y,Y',d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,Z,Z',d',R',F',L',U']
+Combination [U,L,F,R,d,Z,Z',d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,X,Y,Y',X',R',F',L',U']
+Combination [U,L,F,R,X,Y,Y',X',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,X,Z,Z',X',R',F',L',U']
+Combination [U,L,F,R,X,Z,Z',X',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,Y,Z,Z',Y',R',F',L',U']
+Combination [U,L,F,R,Y,Z,Z',Y',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,X,X',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,X,X',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,Y,Y',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,Y,Y',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,Z,Z',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,Z,Z',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,E,E',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,E,E',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,S,S',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,S,S',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,u,u',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,u,u',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,l,l',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,l,l',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,r,r',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,r,r',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,b,b',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,b,b',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,d,d',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,d,d',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,X,X',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,X,X',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,Y,Y',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,Y,Y',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,Z,Z',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,Z,Z',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,S,S',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,S,S',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,u,u',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,u,u',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,l,l',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,l,l',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,r,r',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,r,r',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,b,b',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,b,b',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,d,d',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,d,d',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,X,X',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,X,X',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,Y,Y',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,Y,Y',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,Z,Z',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,Z,Z',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,u,u',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,u,u',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,l,l',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,l,l',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,r,r',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,r,r',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,b,b',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,b,b',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,d,d',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,d,d',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,X,X',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,X,X',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,Y,Y',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,Y,Y',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,Z,Z',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,Z,Z',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,l,l',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,l,l',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,r,r',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,r,r',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,b,b',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,b,b',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,d,d',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,d,d',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,X,X',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,X,X',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,Y,Y',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,Y,Y',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,Z,Z',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,Z,Z',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,r,r',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,r,r',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,b,b',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,b,b',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,d,d',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,d,d',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,X,X',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,X,X',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,Y,Y',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,Y,Y',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,Z,Z',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,Z,Z',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,b,b',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,b,b',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,d,d',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,d,d',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,X,X',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,X,X',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,Y,Y',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,Y,Y',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,Z,Z',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,Z,Z',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,d,d',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,d,d',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,X,X',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,X,X',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,Y,Y',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,Y,Y',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,Z,Z',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,Z,Z',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,X,X',d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,X,X',d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,Y,Y',d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,Y,Y',d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,Z,Z',d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,Z,Z',d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,X,Y,Y',X',B',R',F',L',U']
+Combination [U,L,F,R,B,X,Y,Y',X',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,X,Z,Z',X',B',R',F',L',U']
+Combination [U,L,F,R,B,X,Z,Z',X',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,Y,Z,Z',Y',B',R',F',L',U']
+Combination [U,L,F,R,B,Y,Z,Z',Y',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,X,X',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,X,X',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,Y,Y',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,Y,Y',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,Z,Z',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,Z,Z',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,S,S',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,S,S',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,u,u',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,u,u',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,l,l',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,l,l',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,r,r',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,r,r',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,b,b',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,b,b',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,d,d',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,d,d',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,X,X',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,X,X',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,Y,Y',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,Y,Y',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,Z,Z',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,Z,Z',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,u,u',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,u,u',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,l,l',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,l,l',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,r,r',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,r,r',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,b,b',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,b,b',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,d,d',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,d,d',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,X,X',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,X,X',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,Y,Y',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,Y,Y',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,Z,Z',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,Z,Z',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,l,l',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,l,l',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,r,r',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,r,r',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,b,b',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,b,b',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,d,d',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,d,d',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,X,X',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,X,X',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,Y,Y',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,Y,Y',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,Z,Z',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,Z,Z',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,r,r',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,r,r',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,b,b',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,b,b',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,d,d',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,d,d',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,X,X',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,X,X',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,Y,Y',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,Y,Y',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,Z,Z',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,Z,Z',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,b,b',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,b,b',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,d,d',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,d,d',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,X,X',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,X,X',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,Y,Y',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,Y,Y',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,Z,Z',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,Z,Z',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,d,d',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,d,d',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,X,X',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,X,X',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,Y,Y',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,Y,Y',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,Z,Z',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,Z,Z',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,X,X',d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,X,X',d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,Y,Y',d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,Y,Y',d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,Z,Z',d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,Z,Z',d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,X,Y,Y',X',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,X,Y,Y',X',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,X,Z,Z',X',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,X,Z,Z',X',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,Y,Z,Z',Y',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,Y,Z,Z',Y',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,X,X',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,X,X',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,Y,Y',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,Y,Y',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,Z,Z',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,Z,Z',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,u,u',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,u,u',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,l,l',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,l,l',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,r,r',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,r,r',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,b,b',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,b,b',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,d,d',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,d,d',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,X,X',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,X,X',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,Y,Y',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,Y,Y',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,Z,Z',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,Z,Z',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,l,l',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,l,l',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,r,r',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,r,r',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,b,b',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,b,b',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,d,d',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,d,d',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,X,X',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,X,X',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,Y,Y',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,Y,Y',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,Z,Z',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,Z,Z',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,r,r',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,r,r',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,b,b',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,b,b',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,d,d',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,d,d',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,X,X',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,X,X',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,Y,Y',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,Y,Y',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,Z,Z',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,Z,Z',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,b,b',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,b,b',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,d,d',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,d,d',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,X,X',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,X,X',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,Y,Y',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,Y,Y',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,Z,Z',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,Z,Z',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,d,d',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,d,d',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,X,X',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,X,X',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,Y,Y',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,Y,Y',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,Z,Z',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,Z,Z',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,X,X',d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,X,X',d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,Y,Y',d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,Y,Y',d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,Z,Z',d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,Z,Z',d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,X,Y,Y',X',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,X,Y,Y',X',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,X,Z,Z',X',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,X,Z,Z',X',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,Y,Z,Z',Y',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,Y,Z,Z',Y',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,X,X',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,X,X',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,Y,Y',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,Y,Y',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,Z,Z',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,Z,Z',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,l,l',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,l,l',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,r,r',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,r,r',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,b,b',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,b,b',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,d,d',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,d,d',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,X,X',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,X,X',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,Y,Y',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,Y,Y',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,Z,Z',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,Z,Z',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,r,r',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,r,r',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,b,b',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,b,b',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,d,d',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,d,d',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,X,X',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,X,X',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,Y,Y',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,Y,Y',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,Z,Z',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,Z,Z',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,b,b',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,b,b',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,d,d',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,d,d',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,X,X',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,X,X',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,Y,Y',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,Y,Y',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,Z,Z',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,Z,Z',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,d,d',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,d,d',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,X,X',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,X,X',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,Y,Y',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,Y,Y',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,Z,Z',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,Z,Z',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,X,X',d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,X,X',d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,Y,Y',d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,Y,Y',d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,Z,Z',d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,Z,Z',d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,X,Y,Y',X',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,X,Y,Y',X',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,X,Z,Z',X',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,X,Z,Z',X',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,Y,Z,Z',Y',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,Y,Z,Z',Y',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,X,X',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,X,X',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,Y,Y',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,Y,Y',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,Z,Z',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,Z,Z',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,r,r',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,r,r',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,b,b',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,b,b',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,d,d',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,d,d',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,X,X',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,X,X',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,Y,Y',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,Y,Y',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,Z,Z',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,Z,Z',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,b,b',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,b,b',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,d,d',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,d,d',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,X,X',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,X,X',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,Y,Y',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,Y,Y',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,Z,Z',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,Z,Z',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,d,d',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,d,d',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,X,X',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,X,X',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,Y,Y',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,Y,Y',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,Z,Z',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,Z,Z',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,X,X',d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,X,X',d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,Y,Y',d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,Y,Y',d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,Z,Z',d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,Z,Z',d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,X,Y,Y',X',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,X,Y,Y',X',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,X,Z,Z',X',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,X,Z,Z',X',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,Y,Z,Z',Y',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,Y,Z,Z',Y',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,X,X',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,X,X',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,Y,Y',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,Y,Y',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,Z,Z',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,Z,Z',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,b,b',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,b,b',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,d,d',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,d,d',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,X,X',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,X,X',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,Y,Y',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,Y,Y',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,Z,Z',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,Z,Z',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,d,d',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,d,d',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,X,X',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,X,X',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,Y,Y',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,Y,Y',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,Z,Z',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,Z,Z',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,X,X',d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,X,X',d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,Y,Y',d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,Y,Y',d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,Z,Z',d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,Z,Z',d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,X,Y,Y',X',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,X,Y,Y',X',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,X,Z,Z',X',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,X,Z,Z',X',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,Y,Z,Z',Y',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,Y,Z,Z',Y',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,X,X',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,X,X',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Y',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Y',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,Z,Z',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,Z,Z',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,d,d',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,d,d',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,X,X',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,X,X',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,Y,Y',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,Y,Y',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,Z,Z',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,Z,Z',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,X,X',d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,X,X',d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,Y,Y',d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,Y,Y',d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,Z,Z',d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,Z,Z',d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,X,Y,Y',X',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,X,Y,Y',X',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,X,Z,Z',X',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,X,Z,Z',X',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,Y,Z,Z',Y',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,Y,Z,Z',Y',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,X',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,X',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Y',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Y',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Z,Z',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Z,Z',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,X,X',d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,X,X',d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Y,Y',d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Y,Y',d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Z,Z',d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Z,Z',d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Y,Y',X',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Y,Y',X',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Z,Z',X',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Z,Z',X',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Z,Z',Y',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Z,Z',Y',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,X',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,X',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Z,Z',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Z,Z',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Y,Y',X',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Y,Y',X',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Z,Z',X',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Z,Z',X',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Z,Z',Y',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Z,Z',Y',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Y,Y',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Y,Y',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Z,Z',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Z,Z',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Z,Z',Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Z,Z',Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
diff --git a/tests/sav/test_rubix_cube_alt1.res b/tests/sav/test_rubix_cube_alt1.res
new file mode 100644 (file)
index 0000000..a97fc9b
--- /dev/null
@@ -0,0 +1,1664 @@
+Trying combination [U,U']
+Combination [U,U'] is OK.
+Trying combination [L,L']
+Combination [L,L'] is OK.
+Trying combination [F,F']
+Combination [F,F'] is OK.
+Trying combination [R,R']
+Combination [R,R'] is OK.
+Trying combination [B,B']
+Combination [B,B'] is OK.
+Trying combination [D,D']
+Combination [D,D'] is OK.
+Trying combination [M,M']
+Combination [M,M'] is OK.
+Trying combination [E,E']
+Combination [E,E'] is OK.
+Trying combination [S,S']
+Combination [S,S'] is OK.
+Trying combination [u,u']
+Combination [u,u'] is OK.
+Trying combination [l,l']
+Combination [l,l'] is OK.
+Trying combination [r,r']
+Combination [r,r'] is OK.
+Trying combination [b,b']
+Combination [b,b'] is OK.
+Trying combination [d,d']
+Combination [d,d'] is OK.
+Trying combination [X,X']
+Combination [X,X'] is OK.
+Trying combination [Y,Y']
+Combination [Y,Y'] is OK.
+Trying combination [Z,Z']
+Combination [Z,Z'] is OK.
+Trying combination [U,L,L',U']
+Combination [U,L,L',U'] is OK.
+Trying combination [U,F,F',U']
+Combination [U,F,F',U'] is OK.
+Trying combination [U,R,R',U']
+Combination [U,R,R',U'] is OK.
+Trying combination [U,B,B',U']
+Combination [U,B,B',U'] is OK.
+Trying combination [U,D,D',U']
+Combination [U,D,D',U'] is OK.
+Trying combination [U,M,M',U']
+Combination [U,M,M',U'] is OK.
+Trying combination [U,E,E',U']
+Combination [U,E,E',U'] is OK.
+Trying combination [U,S,S',U']
+Combination [U,S,S',U'] is OK.
+Trying combination [U,u,u',U']
+Combination [U,u,u',U'] is OK.
+Trying combination [U,l,l',U']
+Combination [U,l,l',U'] is OK.
+Trying combination [U,r,r',U']
+Combination [U,r,r',U'] is OK.
+Trying combination [U,b,b',U']
+Combination [U,b,b',U'] is OK.
+Trying combination [U,d,d',U']
+Combination [U,d,d',U'] is OK.
+Trying combination [U,X,X',U']
+Combination [U,X,X',U'] is OK.
+Trying combination [U,Y,Y',U']
+Combination [U,Y,Y',U'] is OK.
+Trying combination [U,Z,Z',U']
+Combination [U,Z,Z',U'] is OK.
+Trying combination [L,F,F',L']
+Combination [L,F,F',L'] is OK.
+Trying combination [L,R,R',L']
+Combination [L,R,R',L'] is OK.
+Trying combination [L,B,B',L']
+Combination [L,B,B',L'] is OK.
+Trying combination [L,D,D',L']
+Combination [L,D,D',L'] is OK.
+Trying combination [L,M,M',L']
+Combination [L,M,M',L'] is OK.
+Trying combination [L,E,E',L']
+Combination [L,E,E',L'] is OK.
+Trying combination [L,S,S',L']
+Combination [L,S,S',L'] is OK.
+Trying combination [L,u,u',L']
+Combination [L,u,u',L'] is OK.
+Trying combination [L,l,l',L']
+Combination [L,l,l',L'] is OK.
+Trying combination [L,r,r',L']
+Combination [L,r,r',L'] is OK.
+Trying combination [L,b,b',L']
+Combination [L,b,b',L'] is OK.
+Trying combination [L,d,d',L']
+Combination [L,d,d',L'] is OK.
+Trying combination [L,X,X',L']
+Combination [L,X,X',L'] is OK.
+Trying combination [L,Y,Y',L']
+Combination [L,Y,Y',L'] is OK.
+Trying combination [L,Z,Z',L']
+Combination [L,Z,Z',L'] is OK.
+Trying combination [F,R,R',F']
+Combination [F,R,R',F'] is OK.
+Trying combination [F,B,B',F']
+Combination [F,B,B',F'] is OK.
+Trying combination [F,D,D',F']
+Combination [F,D,D',F'] is OK.
+Trying combination [F,M,M',F']
+Combination [F,M,M',F'] is OK.
+Trying combination [F,E,E',F']
+Combination [F,E,E',F'] is OK.
+Trying combination [F,S,S',F']
+Combination [F,S,S',F'] is OK.
+Trying combination [F,u,u',F']
+Combination [F,u,u',F'] is OK.
+Trying combination [F,l,l',F']
+Combination [F,l,l',F'] is OK.
+Trying combination [F,r,r',F']
+Combination [F,r,r',F'] is OK.
+Trying combination [F,b,b',F']
+Combination [F,b,b',F'] is OK.
+Trying combination [F,d,d',F']
+Combination [F,d,d',F'] is OK.
+Trying combination [F,X,X',F']
+Combination [F,X,X',F'] is OK.
+Trying combination [F,Y,Y',F']
+Combination [F,Y,Y',F'] is OK.
+Trying combination [F,Z,Z',F']
+Combination [F,Z,Z',F'] is OK.
+Trying combination [R,B,B',R']
+Combination [R,B,B',R'] is OK.
+Trying combination [R,D,D',R']
+Combination [R,D,D',R'] is OK.
+Trying combination [R,M,M',R']
+Combination [R,M,M',R'] is OK.
+Trying combination [R,E,E',R']
+Combination [R,E,E',R'] is OK.
+Trying combination [R,S,S',R']
+Combination [R,S,S',R'] is OK.
+Trying combination [R,u,u',R']
+Combination [R,u,u',R'] is OK.
+Trying combination [R,l,l',R']
+Combination [R,l,l',R'] is OK.
+Trying combination [R,r,r',R']
+Combination [R,r,r',R'] is OK.
+Trying combination [R,b,b',R']
+Combination [R,b,b',R'] is OK.
+Trying combination [R,d,d',R']
+Combination [R,d,d',R'] is OK.
+Trying combination [R,X,X',R']
+Combination [R,X,X',R'] is OK.
+Trying combination [R,Y,Y',R']
+Combination [R,Y,Y',R'] is OK.
+Trying combination [R,Z,Z',R']
+Combination [R,Z,Z',R'] is OK.
+Trying combination [B,D,D',B']
+Combination [B,D,D',B'] is OK.
+Trying combination [B,M,M',B']
+Combination [B,M,M',B'] is OK.
+Trying combination [B,E,E',B']
+Combination [B,E,E',B'] is OK.
+Trying combination [B,S,S',B']
+Combination [B,S,S',B'] is OK.
+Trying combination [B,u,u',B']
+Combination [B,u,u',B'] is OK.
+Trying combination [B,l,l',B']
+Combination [B,l,l',B'] is OK.
+Trying combination [B,r,r',B']
+Combination [B,r,r',B'] is OK.
+Trying combination [B,b,b',B']
+Combination [B,b,b',B'] is OK.
+Trying combination [B,d,d',B']
+Combination [B,d,d',B'] is OK.
+Trying combination [B,X,X',B']
+Combination [B,X,X',B'] is OK.
+Trying combination [B,Y,Y',B']
+Combination [B,Y,Y',B'] is OK.
+Trying combination [B,Z,Z',B']
+Combination [B,Z,Z',B'] is OK.
+Trying combination [D,M,M',D']
+Combination [D,M,M',D'] is OK.
+Trying combination [D,E,E',D']
+Combination [D,E,E',D'] is OK.
+Trying combination [D,S,S',D']
+Combination [D,S,S',D'] is OK.
+Trying combination [D,u,u',D']
+Combination [D,u,u',D'] is OK.
+Trying combination [D,l,l',D']
+Combination [D,l,l',D'] is OK.
+Trying combination [D,r,r',D']
+Combination [D,r,r',D'] is OK.
+Trying combination [D,b,b',D']
+Combination [D,b,b',D'] is OK.
+Trying combination [D,d,d',D']
+Combination [D,d,d',D'] is OK.
+Trying combination [D,X,X',D']
+Combination [D,X,X',D'] is OK.
+Trying combination [D,Y,Y',D']
+Combination [D,Y,Y',D'] is OK.
+Trying combination [D,Z,Z',D']
+Combination [D,Z,Z',D'] is OK.
+Trying combination [M,E,E',M']
+Combination [M,E,E',M'] is OK.
+Trying combination [M,S,S',M']
+Combination [M,S,S',M'] is OK.
+Trying combination [M,u,u',M']
+Combination [M,u,u',M'] is OK.
+Trying combination [M,l,l',M']
+Combination [M,l,l',M'] is OK.
+Trying combination [M,r,r',M']
+Combination [M,r,r',M'] is OK.
+Trying combination [M,b,b',M']
+Combination [M,b,b',M'] is OK.
+Trying combination [M,d,d',M']
+Combination [M,d,d',M'] is OK.
+Trying combination [M,X,X',M']
+Combination [M,X,X',M'] is OK.
+Trying combination [M,Y,Y',M']
+Combination [M,Y,Y',M'] is OK.
+Trying combination [M,Z,Z',M']
+Combination [M,Z,Z',M'] is OK.
+Trying combination [E,S,S',E']
+Combination [E,S,S',E'] is OK.
+Trying combination [E,u,u',E']
+Combination [E,u,u',E'] is OK.
+Trying combination [E,l,l',E']
+Combination [E,l,l',E'] is OK.
+Trying combination [E,r,r',E']
+Combination [E,r,r',E'] is OK.
+Trying combination [E,b,b',E']
+Combination [E,b,b',E'] is OK.
+Trying combination [E,d,d',E']
+Combination [E,d,d',E'] is OK.
+Trying combination [E,X,X',E']
+Combination [E,X,X',E'] is OK.
+Trying combination [E,Y,Y',E']
+Combination [E,Y,Y',E'] is OK.
+Trying combination [E,Z,Z',E']
+Combination [E,Z,Z',E'] is OK.
+Trying combination [S,u,u',S']
+Combination [S,u,u',S'] is OK.
+Trying combination [S,l,l',S']
+Combination [S,l,l',S'] is OK.
+Trying combination [S,r,r',S']
+Combination [S,r,r',S'] is OK.
+Trying combination [S,b,b',S']
+Combination [S,b,b',S'] is OK.
+Trying combination [S,d,d',S']
+Combination [S,d,d',S'] is OK.
+Trying combination [S,X,X',S']
+Combination [S,X,X',S'] is OK.
+Trying combination [S,Y,Y',S']
+Combination [S,Y,Y',S'] is OK.
+Trying combination [S,Z,Z',S']
+Combination [S,Z,Z',S'] is OK.
+Trying combination [u,l,l',u']
+Combination [u,l,l',u'] is OK.
+Trying combination [u,r,r',u']
+Combination [u,r,r',u'] is OK.
+Trying combination [u,b,b',u']
+Combination [u,b,b',u'] is OK.
+Trying combination [u,d,d',u']
+Combination [u,d,d',u'] is OK.
+Trying combination [u,X,X',u']
+Combination [u,X,X',u'] is OK.
+Trying combination [u,Y,Y',u']
+Combination [u,Y,Y',u'] is OK.
+Trying combination [u,Z,Z',u']
+Combination [u,Z,Z',u'] is OK.
+Trying combination [l,r,r',l']
+Combination [l,r,r',l'] is OK.
+Trying combination [l,b,b',l']
+Combination [l,b,b',l'] is OK.
+Trying combination [l,d,d',l']
+Combination [l,d,d',l'] is OK.
+Trying combination [l,X,X',l']
+Combination [l,X,X',l'] is OK.
+Trying combination [l,Y,Y',l']
+Combination [l,Y,Y',l'] is OK.
+Trying combination [l,Z,Z',l']
+Combination [l,Z,Z',l'] is OK.
+Trying combination [r,b,b',r']
+Combination [r,b,b',r'] is OK.
+Trying combination [r,d,d',r']
+Combination [r,d,d',r'] is OK.
+Trying combination [r,X,X',r']
+Combination [r,X,X',r'] is OK.
+Trying combination [r,Y,Y',r']
+Combination [r,Y,Y',r'] is OK.
+Trying combination [r,Z,Z',r']
+Combination [r,Z,Z',r'] is OK.
+Trying combination [b,d,d',b']
+Combination [b,d,d',b'] is OK.
+Trying combination [b,X,X',b']
+Combination [b,X,X',b'] is OK.
+Trying combination [b,Y,Y',b']
+Combination [b,Y,Y',b'] is OK.
+Trying combination [b,Z,Z',b']
+Combination [b,Z,Z',b'] is OK.
+Trying combination [d,X,X',d']
+Combination [d,X,X',d'] is OK.
+Trying combination [d,Y,Y',d']
+Combination [d,Y,Y',d'] is OK.
+Trying combination [d,Z,Z',d']
+Combination [d,Z,Z',d'] is OK.
+Trying combination [X,Y,Y',X']
+Combination [X,Y,Y',X'] is OK.
+Trying combination [X,Z,Z',X']
+Combination [X,Z,Z',X'] is OK.
+Trying combination [Y,Z,Z',Y']
+Combination [Y,Z,Z',Y'] is OK.
+Trying combination [U,L,F,F',L',U']
+Combination [U,L,F,F',L',U'] is OK.
+Trying combination [U,L,R,R',L',U']
+Combination [U,L,R,R',L',U'] is OK.
+Trying combination [U,L,B,B',L',U']
+Combination [U,L,B,B',L',U'] is OK.
+Trying combination [U,L,D,D',L',U']
+Combination [U,L,D,D',L',U'] is OK.
+Trying combination [U,L,M,M',L',U']
+Combination [U,L,M,M',L',U'] is OK.
+Trying combination [U,L,E,E',L',U']
+Combination [U,L,E,E',L',U'] is OK.
+Trying combination [U,L,S,S',L',U']
+Combination [U,L,S,S',L',U'] is OK.
+Trying combination [U,L,u,u',L',U']
+Combination [U,L,u,u',L',U'] is OK.
+Trying combination [U,L,l,l',L',U']
+Combination [U,L,l,l',L',U'] is OK.
+Trying combination [U,L,r,r',L',U']
+Combination [U,L,r,r',L',U'] is OK.
+Trying combination [U,L,b,b',L',U']
+Combination [U,L,b,b',L',U'] is OK.
+Trying combination [U,L,d,d',L',U']
+Combination [U,L,d,d',L',U'] is OK.
+Trying combination [U,L,X,X',L',U']
+Combination [U,L,X,X',L',U'] is OK.
+Trying combination [U,L,Y,Y',L',U']
+Combination [U,L,Y,Y',L',U'] is OK.
+Trying combination [U,L,Z,Z',L',U']
+Combination [U,L,Z,Z',L',U'] is OK.
+Trying combination [U,F,R,R',F',U']
+Combination [U,F,R,R',F',U'] is OK.
+Trying combination [U,F,B,B',F',U']
+Combination [U,F,B,B',F',U'] is OK.
+Trying combination [U,F,D,D',F',U']
+Combination [U,F,D,D',F',U'] is OK.
+Trying combination [U,F,M,M',F',U']
+Combination [U,F,M,M',F',U'] is OK.
+Trying combination [U,F,E,E',F',U']
+Combination [U,F,E,E',F',U'] is OK.
+Trying combination [U,F,S,S',F',U']
+Combination [U,F,S,S',F',U'] is OK.
+Trying combination [U,F,u,u',F',U']
+Combination [U,F,u,u',F',U'] is OK.
+Trying combination [U,F,l,l',F',U']
+Combination [U,F,l,l',F',U'] is OK.
+Trying combination [U,F,r,r',F',U']
+Combination [U,F,r,r',F',U'] is OK.
+Trying combination [U,F,b,b',F',U']
+Combination [U,F,b,b',F',U'] is OK.
+Trying combination [U,F,d,d',F',U']
+Combination [U,F,d,d',F',U'] is OK.
+Trying combination [U,F,X,X',F',U']
+Combination [U,F,X,X',F',U'] is OK.
+Trying combination [U,F,Y,Y',F',U']
+Combination [U,F,Y,Y',F',U'] is OK.
+Trying combination [U,F,Z,Z',F',U']
+Combination [U,F,Z,Z',F',U'] is OK.
+Trying combination [U,R,B,B',R',U']
+Combination [U,R,B,B',R',U'] is OK.
+Trying combination [U,R,D,D',R',U']
+Combination [U,R,D,D',R',U'] is OK.
+Trying combination [U,R,M,M',R',U']
+Combination [U,R,M,M',R',U'] is OK.
+Trying combination [U,R,E,E',R',U']
+Combination [U,R,E,E',R',U'] is OK.
+Trying combination [U,R,S,S',R',U']
+Combination [U,R,S,S',R',U'] is OK.
+Trying combination [U,R,u,u',R',U']
+Combination [U,R,u,u',R',U'] is OK.
+Trying combination [U,R,l,l',R',U']
+Combination [U,R,l,l',R',U'] is OK.
+Trying combination [U,R,r,r',R',U']
+Combination [U,R,r,r',R',U'] is OK.
+Trying combination [U,R,b,b',R',U']
+Combination [U,R,b,b',R',U'] is OK.
+Trying combination [U,R,d,d',R',U']
+Combination [U,R,d,d',R',U'] is OK.
+Trying combination [U,R,X,X',R',U']
+Combination [U,R,X,X',R',U'] is OK.
+Trying combination [U,R,Y,Y',R',U']
+Combination [U,R,Y,Y',R',U'] is OK.
+Trying combination [U,R,Z,Z',R',U']
+Combination [U,R,Z,Z',R',U'] is OK.
+Trying combination [U,B,D,D',B',U']
+Combination [U,B,D,D',B',U'] is OK.
+Trying combination [U,B,M,M',B',U']
+Combination [U,B,M,M',B',U'] is OK.
+Trying combination [U,B,E,E',B',U']
+Combination [U,B,E,E',B',U'] is OK.
+Trying combination [U,B,S,S',B',U']
+Combination [U,B,S,S',B',U'] is OK.
+Trying combination [U,B,u,u',B',U']
+Combination [U,B,u,u',B',U'] is OK.
+Trying combination [U,B,l,l',B',U']
+Combination [U,B,l,l',B',U'] is OK.
+Trying combination [U,B,r,r',B',U']
+Combination [U,B,r,r',B',U'] is OK.
+Trying combination [U,B,b,b',B',U']
+Combination [U,B,b,b',B',U'] is OK.
+Trying combination [U,B,d,d',B',U']
+Combination [U,B,d,d',B',U'] is OK.
+Trying combination [U,B,X,X',B',U']
+Combination [U,B,X,X',B',U'] is OK.
+Trying combination [U,B,Y,Y',B',U']
+Combination [U,B,Y,Y',B',U'] is OK.
+Trying combination [U,B,Z,Z',B',U']
+Combination [U,B,Z,Z',B',U'] is OK.
+Trying combination [U,D,M,M',D',U']
+Combination [U,D,M,M',D',U'] is OK.
+Trying combination [U,D,E,E',D',U']
+Combination [U,D,E,E',D',U'] is OK.
+Trying combination [U,D,S,S',D',U']
+Combination [U,D,S,S',D',U'] is OK.
+Trying combination [U,D,u,u',D',U']
+Combination [U,D,u,u',D',U'] is OK.
+Trying combination [U,D,l,l',D',U']
+Combination [U,D,l,l',D',U'] is OK.
+Trying combination [U,D,r,r',D',U']
+Combination [U,D,r,r',D',U'] is OK.
+Trying combination [U,D,b,b',D',U']
+Combination [U,D,b,b',D',U'] is OK.
+Trying combination [U,D,d,d',D',U']
+Combination [U,D,d,d',D',U'] is OK.
+Trying combination [U,D,X,X',D',U']
+Combination [U,D,X,X',D',U'] is OK.
+Trying combination [U,D,Y,Y',D',U']
+Combination [U,D,Y,Y',D',U'] is OK.
+Trying combination [U,D,Z,Z',D',U']
+Combination [U,D,Z,Z',D',U'] is OK.
+Trying combination [U,M,E,E',M',U']
+Combination [U,M,E,E',M',U'] is OK.
+Trying combination [U,M,S,S',M',U']
+Combination [U,M,S,S',M',U'] is OK.
+Trying combination [U,M,u,u',M',U']
+Combination [U,M,u,u',M',U'] is OK.
+Trying combination [U,M,l,l',M',U']
+Combination [U,M,l,l',M',U'] is OK.
+Trying combination [U,M,r,r',M',U']
+Combination [U,M,r,r',M',U'] is OK.
+Trying combination [U,M,b,b',M',U']
+Combination [U,M,b,b',M',U'] is OK.
+Trying combination [U,M,d,d',M',U']
+Combination [U,M,d,d',M',U'] is OK.
+Trying combination [U,M,X,X',M',U']
+Combination [U,M,X,X',M',U'] is OK.
+Trying combination [U,M,Y,Y',M',U']
+Combination [U,M,Y,Y',M',U'] is OK.
+Trying combination [U,M,Z,Z',M',U']
+Combination [U,M,Z,Z',M',U'] is OK.
+Trying combination [U,E,S,S',E',U']
+Combination [U,E,S,S',E',U'] is OK.
+Trying combination [U,E,u,u',E',U']
+Combination [U,E,u,u',E',U'] is OK.
+Trying combination [U,E,l,l',E',U']
+Combination [U,E,l,l',E',U'] is OK.
+Trying combination [U,E,r,r',E',U']
+Combination [U,E,r,r',E',U'] is OK.
+Trying combination [U,E,b,b',E',U']
+Combination [U,E,b,b',E',U'] is OK.
+Trying combination [U,E,d,d',E',U']
+Combination [U,E,d,d',E',U'] is OK.
+Trying combination [U,E,X,X',E',U']
+Combination [U,E,X,X',E',U'] is OK.
+Trying combination [U,E,Y,Y',E',U']
+Combination [U,E,Y,Y',E',U'] is OK.
+Trying combination [U,E,Z,Z',E',U']
+Combination [U,E,Z,Z',E',U'] is OK.
+Trying combination [U,S,u,u',S',U']
+Combination [U,S,u,u',S',U'] is OK.
+Trying combination [U,S,l,l',S',U']
+Combination [U,S,l,l',S',U'] is OK.
+Trying combination [U,S,r,r',S',U']
+Combination [U,S,r,r',S',U'] is OK.
+Trying combination [U,S,b,b',S',U']
+Combination [U,S,b,b',S',U'] is OK.
+Trying combination [U,S,d,d',S',U']
+Combination [U,S,d,d',S',U'] is OK.
+Trying combination [U,S,X,X',S',U']
+Combination [U,S,X,X',S',U'] is OK.
+Trying combination [U,S,Y,Y',S',U']
+Combination [U,S,Y,Y',S',U'] is OK.
+Trying combination [U,S,Z,Z',S',U']
+Combination [U,S,Z,Z',S',U'] is OK.
+Trying combination [U,u,l,l',u',U']
+Combination [U,u,l,l',u',U'] is OK.
+Trying combination [U,u,r,r',u',U']
+Combination [U,u,r,r',u',U'] is OK.
+Trying combination [U,u,b,b',u',U']
+Combination [U,u,b,b',u',U'] is OK.
+Trying combination [U,u,d,d',u',U']
+Combination [U,u,d,d',u',U'] is OK.
+Trying combination [U,u,X,X',u',U']
+Combination [U,u,X,X',u',U'] is OK.
+Trying combination [U,u,Y,Y',u',U']
+Combination [U,u,Y,Y',u',U'] is OK.
+Trying combination [U,u,Z,Z',u',U']
+Combination [U,u,Z,Z',u',U'] is OK.
+Trying combination [U,l,r,r',l',U']
+Combination [U,l,r,r',l',U'] is OK.
+Trying combination [U,l,b,b',l',U']
+Combination [U,l,b,b',l',U'] is OK.
+Trying combination [U,l,d,d',l',U']
+Combination [U,l,d,d',l',U'] is OK.
+Trying combination [U,l,X,X',l',U']
+Combination [U,l,X,X',l',U'] is OK.
+Trying combination [U,l,Y,Y',l',U']
+Combination [U,l,Y,Y',l',U'] is OK.
+Trying combination [U,l,Z,Z',l',U']
+Combination [U,l,Z,Z',l',U'] is OK.
+Trying combination [U,r,b,b',r',U']
+Combination [U,r,b,b',r',U'] is OK.
+Trying combination [U,r,d,d',r',U']
+Combination [U,r,d,d',r',U'] is OK.
+Trying combination [U,r,X,X',r',U']
+Combination [U,r,X,X',r',U'] is OK.
+Trying combination [U,r,Y,Y',r',U']
+Combination [U,r,Y,Y',r',U'] is OK.
+Trying combination [U,r,Z,Z',r',U']
+Combination [U,r,Z,Z',r',U'] is OK.
+Trying combination [U,b,d,d',b',U']
+Combination [U,b,d,d',b',U'] is OK.
+Trying combination [U,b,X,X',b',U']
+Combination [U,b,X,X',b',U'] is OK.
+Trying combination [U,b,Y,Y',b',U']
+Combination [U,b,Y,Y',b',U'] is OK.
+Trying combination [U,b,Z,Z',b',U']
+Combination [U,b,Z,Z',b',U'] is OK.
+Trying combination [U,d,X,X',d',U']
+Combination [U,d,X,X',d',U'] is OK.
+Trying combination [U,d,Y,Y',d',U']
+Combination [U,d,Y,Y',d',U'] is OK.
+Trying combination [U,d,Z,Z',d',U']
+Combination [U,d,Z,Z',d',U'] is OK.
+Trying combination [U,X,Y,Y',X',U']
+Combination [U,X,Y,Y',X',U'] is OK.
+Trying combination [U,X,Z,Z',X',U']
+Combination [U,X,Z,Z',X',U'] is OK.
+Trying combination [U,Y,Z,Z',Y',U']
+Combination [U,Y,Z,Z',Y',U'] is OK.
+Trying combination [U,L,F,R,R',F',L',U']
+Combination [U,L,F,R,R',F',L',U'] is OK.
+Trying combination [U,L,F,B,B',F',L',U']
+Combination [U,L,F,B,B',F',L',U'] is OK.
+Trying combination [U,L,F,D,D',F',L',U']
+Combination [U,L,F,D,D',F',L',U'] is OK.
+Trying combination [U,L,F,M,M',F',L',U']
+Combination [U,L,F,M,M',F',L',U'] is OK.
+Trying combination [U,L,F,E,E',F',L',U']
+Combination [U,L,F,E,E',F',L',U'] is OK.
+Trying combination [U,L,F,S,S',F',L',U']
+Combination [U,L,F,S,S',F',L',U'] is OK.
+Trying combination [U,L,F,u,u',F',L',U']
+Combination [U,L,F,u,u',F',L',U'] is OK.
+Trying combination [U,L,F,l,l',F',L',U']
+Combination [U,L,F,l,l',F',L',U'] is OK.
+Trying combination [U,L,F,r,r',F',L',U']
+Combination [U,L,F,r,r',F',L',U'] is OK.
+Trying combination [U,L,F,b,b',F',L',U']
+Combination [U,L,F,b,b',F',L',U'] is OK.
+Trying combination [U,L,F,d,d',F',L',U']
+Combination [U,L,F,d,d',F',L',U'] is OK.
+Trying combination [U,L,F,X,X',F',L',U']
+Combination [U,L,F,X,X',F',L',U'] is OK.
+Trying combination [U,L,F,Y,Y',F',L',U']
+Combination [U,L,F,Y,Y',F',L',U'] is OK.
+Trying combination [U,L,F,Z,Z',F',L',U']
+Combination [U,L,F,Z,Z',F',L',U'] is OK.
+Trying combination [U,L,R,B,B',R',L',U']
+Combination [U,L,R,B,B',R',L',U'] is OK.
+Trying combination [U,L,R,D,D',R',L',U']
+Combination [U,L,R,D,D',R',L',U'] is OK.
+Trying combination [U,L,R,M,M',R',L',U']
+Combination [U,L,R,M,M',R',L',U'] is OK.
+Trying combination [U,L,R,E,E',R',L',U']
+Combination [U,L,R,E,E',R',L',U'] is OK.
+Trying combination [U,L,R,S,S',R',L',U']
+Combination [U,L,R,S,S',R',L',U'] is OK.
+Trying combination [U,L,R,u,u',R',L',U']
+Combination [U,L,R,u,u',R',L',U'] is OK.
+Trying combination [U,L,R,l,l',R',L',U']
+Combination [U,L,R,l,l',R',L',U'] is OK.
+Trying combination [U,L,R,r,r',R',L',U']
+Combination [U,L,R,r,r',R',L',U'] is OK.
+Trying combination [U,L,R,b,b',R',L',U']
+Combination [U,L,R,b,b',R',L',U'] is OK.
+Trying combination [U,L,R,d,d',R',L',U']
+Combination [U,L,R,d,d',R',L',U'] is OK.
+Trying combination [U,L,R,X,X',R',L',U']
+Combination [U,L,R,X,X',R',L',U'] is OK.
+Trying combination [U,L,R,Y,Y',R',L',U']
+Combination [U,L,R,Y,Y',R',L',U'] is OK.
+Trying combination [U,L,R,Z,Z',R',L',U']
+Combination [U,L,R,Z,Z',R',L',U'] is OK.
+Trying combination [U,L,B,D,D',B',L',U']
+Combination [U,L,B,D,D',B',L',U'] is OK.
+Trying combination [U,L,B,M,M',B',L',U']
+Combination [U,L,B,M,M',B',L',U'] is OK.
+Trying combination [U,L,B,E,E',B',L',U']
+Combination [U,L,B,E,E',B',L',U'] is OK.
+Trying combination [U,L,B,S,S',B',L',U']
+Combination [U,L,B,S,S',B',L',U'] is OK.
+Trying combination [U,L,B,u,u',B',L',U']
+Combination [U,L,B,u,u',B',L',U'] is OK.
+Trying combination [U,L,B,l,l',B',L',U']
+Combination [U,L,B,l,l',B',L',U'] is OK.
+Trying combination [U,L,B,r,r',B',L',U']
+Combination [U,L,B,r,r',B',L',U'] is OK.
+Trying combination [U,L,B,b,b',B',L',U']
+Combination [U,L,B,b,b',B',L',U'] is OK.
+Trying combination [U,L,B,d,d',B',L',U']
+Combination [U,L,B,d,d',B',L',U'] is OK.
+Trying combination [U,L,B,X,X',B',L',U']
+Combination [U,L,B,X,X',B',L',U'] is OK.
+Trying combination [U,L,B,Y,Y',B',L',U']
+Combination [U,L,B,Y,Y',B',L',U'] is OK.
+Trying combination [U,L,B,Z,Z',B',L',U']
+Combination [U,L,B,Z,Z',B',L',U'] is OK.
+Trying combination [U,L,D,M,M',D',L',U']
+Combination [U,L,D,M,M',D',L',U'] is OK.
+Trying combination [U,L,D,E,E',D',L',U']
+Combination [U,L,D,E,E',D',L',U'] is OK.
+Trying combination [U,L,D,S,S',D',L',U']
+Combination [U,L,D,S,S',D',L',U'] is OK.
+Trying combination [U,L,D,u,u',D',L',U']
+Combination [U,L,D,u,u',D',L',U'] is OK.
+Trying combination [U,L,D,l,l',D',L',U']
+Combination [U,L,D,l,l',D',L',U'] is OK.
+Trying combination [U,L,D,r,r',D',L',U']
+Combination [U,L,D,r,r',D',L',U'] is OK.
+Trying combination [U,L,D,b,b',D',L',U']
+Combination [U,L,D,b,b',D',L',U'] is OK.
+Trying combination [U,L,D,d,d',D',L',U']
+Combination [U,L,D,d,d',D',L',U'] is OK.
+Trying combination [U,L,D,X,X',D',L',U']
+Combination [U,L,D,X,X',D',L',U'] is OK.
+Trying combination [U,L,D,Y,Y',D',L',U']
+Combination [U,L,D,Y,Y',D',L',U'] is OK.
+Trying combination [U,L,D,Z,Z',D',L',U']
+Combination [U,L,D,Z,Z',D',L',U'] is OK.
+Trying combination [U,L,M,E,E',M',L',U']
+Combination [U,L,M,E,E',M',L',U'] is OK.
+Trying combination [U,L,M,S,S',M',L',U']
+Combination [U,L,M,S,S',M',L',U'] is OK.
+Trying combination [U,L,M,u,u',M',L',U']
+Combination [U,L,M,u,u',M',L',U'] is OK.
+Trying combination [U,L,M,l,l',M',L',U']
+Combination [U,L,M,l,l',M',L',U'] is OK.
+Trying combination [U,L,M,r,r',M',L',U']
+Combination [U,L,M,r,r',M',L',U'] is OK.
+Trying combination [U,L,M,b,b',M',L',U']
+Combination [U,L,M,b,b',M',L',U'] is OK.
+Trying combination [U,L,M,d,d',M',L',U']
+Combination [U,L,M,d,d',M',L',U'] is OK.
+Trying combination [U,L,M,X,X',M',L',U']
+Combination [U,L,M,X,X',M',L',U'] is OK.
+Trying combination [U,L,M,Y,Y',M',L',U']
+Combination [U,L,M,Y,Y',M',L',U'] is OK.
+Trying combination [U,L,M,Z,Z',M',L',U']
+Combination [U,L,M,Z,Z',M',L',U'] is OK.
+Trying combination [U,L,E,S,S',E',L',U']
+Combination [U,L,E,S,S',E',L',U'] is OK.
+Trying combination [U,L,E,u,u',E',L',U']
+Combination [U,L,E,u,u',E',L',U'] is OK.
+Trying combination [U,L,E,l,l',E',L',U']
+Combination [U,L,E,l,l',E',L',U'] is OK.
+Trying combination [U,L,E,r,r',E',L',U']
+Combination [U,L,E,r,r',E',L',U'] is OK.
+Trying combination [U,L,E,b,b',E',L',U']
+Combination [U,L,E,b,b',E',L',U'] is OK.
+Trying combination [U,L,E,d,d',E',L',U']
+Combination [U,L,E,d,d',E',L',U'] is OK.
+Trying combination [U,L,E,X,X',E',L',U']
+Combination [U,L,E,X,X',E',L',U'] is OK.
+Trying combination [U,L,E,Y,Y',E',L',U']
+Combination [U,L,E,Y,Y',E',L',U'] is OK.
+Trying combination [U,L,E,Z,Z',E',L',U']
+Combination [U,L,E,Z,Z',E',L',U'] is OK.
+Trying combination [U,L,S,u,u',S',L',U']
+Combination [U,L,S,u,u',S',L',U'] is OK.
+Trying combination [U,L,S,l,l',S',L',U']
+Combination [U,L,S,l,l',S',L',U'] is OK.
+Trying combination [U,L,S,r,r',S',L',U']
+Combination [U,L,S,r,r',S',L',U'] is OK.
+Trying combination [U,L,S,b,b',S',L',U']
+Combination [U,L,S,b,b',S',L',U'] is OK.
+Trying combination [U,L,S,d,d',S',L',U']
+Combination [U,L,S,d,d',S',L',U'] is OK.
+Trying combination [U,L,S,X,X',S',L',U']
+Combination [U,L,S,X,X',S',L',U'] is OK.
+Trying combination [U,L,S,Y,Y',S',L',U']
+Combination [U,L,S,Y,Y',S',L',U'] is OK.
+Trying combination [U,L,S,Z,Z',S',L',U']
+Combination [U,L,S,Z,Z',S',L',U'] is OK.
+Trying combination [U,L,u,l,l',u',L',U']
+Combination [U,L,u,l,l',u',L',U'] is OK.
+Trying combination [U,L,u,r,r',u',L',U']
+Combination [U,L,u,r,r',u',L',U'] is OK.
+Trying combination [U,L,u,b,b',u',L',U']
+Combination [U,L,u,b,b',u',L',U'] is OK.
+Trying combination [U,L,u,d,d',u',L',U']
+Combination [U,L,u,d,d',u',L',U'] is OK.
+Trying combination [U,L,u,X,X',u',L',U']
+Combination [U,L,u,X,X',u',L',U'] is OK.
+Trying combination [U,L,u,Y,Y',u',L',U']
+Combination [U,L,u,Y,Y',u',L',U'] is OK.
+Trying combination [U,L,u,Z,Z',u',L',U']
+Combination [U,L,u,Z,Z',u',L',U'] is OK.
+Trying combination [U,L,l,r,r',l',L',U']
+Combination [U,L,l,r,r',l',L',U'] is OK.
+Trying combination [U,L,l,b,b',l',L',U']
+Combination [U,L,l,b,b',l',L',U'] is OK.
+Trying combination [U,L,l,d,d',l',L',U']
+Combination [U,L,l,d,d',l',L',U'] is OK.
+Trying combination [U,L,l,X,X',l',L',U']
+Combination [U,L,l,X,X',l',L',U'] is OK.
+Trying combination [U,L,l,Y,Y',l',L',U']
+Combination [U,L,l,Y,Y',l',L',U'] is OK.
+Trying combination [U,L,l,Z,Z',l',L',U']
+Combination [U,L,l,Z,Z',l',L',U'] is OK.
+Trying combination [U,L,r,b,b',r',L',U']
+Combination [U,L,r,b,b',r',L',U'] is OK.
+Trying combination [U,L,r,d,d',r',L',U']
+Combination [U,L,r,d,d',r',L',U'] is OK.
+Trying combination [U,L,r,X,X',r',L',U']
+Combination [U,L,r,X,X',r',L',U'] is OK.
+Trying combination [U,L,r,Y,Y',r',L',U']
+Combination [U,L,r,Y,Y',r',L',U'] is OK.
+Trying combination [U,L,r,Z,Z',r',L',U']
+Combination [U,L,r,Z,Z',r',L',U'] is OK.
+Trying combination [U,L,b,d,d',b',L',U']
+Combination [U,L,b,d,d',b',L',U'] is OK.
+Trying combination [U,L,b,X,X',b',L',U']
+Combination [U,L,b,X,X',b',L',U'] is OK.
+Trying combination [U,L,b,Y,Y',b',L',U']
+Combination [U,L,b,Y,Y',b',L',U'] is OK.
+Trying combination [U,L,b,Z,Z',b',L',U']
+Combination [U,L,b,Z,Z',b',L',U'] is OK.
+Trying combination [U,L,d,X,X',d',L',U']
+Combination [U,L,d,X,X',d',L',U'] is OK.
+Trying combination [U,L,d,Y,Y',d',L',U']
+Combination [U,L,d,Y,Y',d',L',U'] is OK.
+Trying combination [U,L,d,Z,Z',d',L',U']
+Combination [U,L,d,Z,Z',d',L',U'] is OK.
+Trying combination [U,L,X,Y,Y',X',L',U']
+Combination [U,L,X,Y,Y',X',L',U'] is OK.
+Trying combination [U,L,X,Z,Z',X',L',U']
+Combination [U,L,X,Z,Z',X',L',U'] is OK.
+Trying combination [U,L,Y,Z,Z',Y',L',U']
+Combination [U,L,Y,Z,Z',Y',L',U'] is OK.
+Trying combination [U,L,F,R,B,B',R',F',L',U']
+Combination [U,L,F,R,B,B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,D',R',F',L',U']
+Combination [U,L,F,R,D,D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,M',R',F',L',U']
+Combination [U,L,F,R,M,M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,E',R',F',L',U']
+Combination [U,L,F,R,E,E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,S',R',F',L',U']
+Combination [U,L,F,R,S,S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,u',R',F',L',U']
+Combination [U,L,F,R,u,u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,l',R',F',L',U']
+Combination [U,L,F,R,l,l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,r',R',F',L',U']
+Combination [U,L,F,R,r,r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,b',R',F',L',U']
+Combination [U,L,F,R,b,b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,d',R',F',L',U']
+Combination [U,L,F,R,d,d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,X,X',R',F',L',U']
+Combination [U,L,F,R,X,X',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,Y,Y',R',F',L',U']
+Combination [U,L,F,R,Y,Y',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,Z,Z',R',F',L',U']
+Combination [U,L,F,R,Z,Z',R',F',L',U'] is OK.
+Trying combination [U,L,F,B,D,D',B',F',L',U']
+Combination [U,L,F,B,D,D',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,M,M',B',F',L',U']
+Combination [U,L,F,B,M,M',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,E,E',B',F',L',U']
+Combination [U,L,F,B,E,E',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,S,S',B',F',L',U']
+Combination [U,L,F,B,S,S',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,u,u',B',F',L',U']
+Combination [U,L,F,B,u,u',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,l,l',B',F',L',U']
+Combination [U,L,F,B,l,l',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,r,r',B',F',L',U']
+Combination [U,L,F,B,r,r',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,b,b',B',F',L',U']
+Combination [U,L,F,B,b,b',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,d,d',B',F',L',U']
+Combination [U,L,F,B,d,d',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,X,X',B',F',L',U']
+Combination [U,L,F,B,X,X',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,Y,Y',B',F',L',U']
+Combination [U,L,F,B,Y,Y',B',F',L',U'] is OK.
+Trying combination [U,L,F,B,Z,Z',B',F',L',U']
+Combination [U,L,F,B,Z,Z',B',F',L',U'] is OK.
+Trying combination [U,L,F,D,M,M',D',F',L',U']
+Combination [U,L,F,D,M,M',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,E,E',D',F',L',U']
+Combination [U,L,F,D,E,E',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,S,S',D',F',L',U']
+Combination [U,L,F,D,S,S',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,u,u',D',F',L',U']
+Combination [U,L,F,D,u,u',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,l,l',D',F',L',U']
+Combination [U,L,F,D,l,l',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,r,r',D',F',L',U']
+Combination [U,L,F,D,r,r',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,b,b',D',F',L',U']
+Combination [U,L,F,D,b,b',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,d,d',D',F',L',U']
+Combination [U,L,F,D,d,d',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,X,X',D',F',L',U']
+Combination [U,L,F,D,X,X',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,Y,Y',D',F',L',U']
+Combination [U,L,F,D,Y,Y',D',F',L',U'] is OK.
+Trying combination [U,L,F,D,Z,Z',D',F',L',U']
+Combination [U,L,F,D,Z,Z',D',F',L',U'] is OK.
+Trying combination [U,L,F,M,E,E',M',F',L',U']
+Combination [U,L,F,M,E,E',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,S,S',M',F',L',U']
+Combination [U,L,F,M,S,S',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,u,u',M',F',L',U']
+Combination [U,L,F,M,u,u',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,l,l',M',F',L',U']
+Combination [U,L,F,M,l,l',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,r,r',M',F',L',U']
+Combination [U,L,F,M,r,r',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,b,b',M',F',L',U']
+Combination [U,L,F,M,b,b',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,d,d',M',F',L',U']
+Combination [U,L,F,M,d,d',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,X,X',M',F',L',U']
+Combination [U,L,F,M,X,X',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,Y,Y',M',F',L',U']
+Combination [U,L,F,M,Y,Y',M',F',L',U'] is OK.
+Trying combination [U,L,F,M,Z,Z',M',F',L',U']
+Combination [U,L,F,M,Z,Z',M',F',L',U'] is OK.
+Trying combination [U,L,F,E,S,S',E',F',L',U']
+Combination [U,L,F,E,S,S',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,u,u',E',F',L',U']
+Combination [U,L,F,E,u,u',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,l,l',E',F',L',U']
+Combination [U,L,F,E,l,l',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,r,r',E',F',L',U']
+Combination [U,L,F,E,r,r',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,b,b',E',F',L',U']
+Combination [U,L,F,E,b,b',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,d,d',E',F',L',U']
+Combination [U,L,F,E,d,d',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,X,X',E',F',L',U']
+Combination [U,L,F,E,X,X',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,Y,Y',E',F',L',U']
+Combination [U,L,F,E,Y,Y',E',F',L',U'] is OK.
+Trying combination [U,L,F,E,Z,Z',E',F',L',U']
+Combination [U,L,F,E,Z,Z',E',F',L',U'] is OK.
+Trying combination [U,L,F,S,u,u',S',F',L',U']
+Combination [U,L,F,S,u,u',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,l,l',S',F',L',U']
+Combination [U,L,F,S,l,l',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,r,r',S',F',L',U']
+Combination [U,L,F,S,r,r',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,b,b',S',F',L',U']
+Combination [U,L,F,S,b,b',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,d,d',S',F',L',U']
+Combination [U,L,F,S,d,d',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,X,X',S',F',L',U']
+Combination [U,L,F,S,X,X',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,Y,Y',S',F',L',U']
+Combination [U,L,F,S,Y,Y',S',F',L',U'] is OK.
+Trying combination [U,L,F,S,Z,Z',S',F',L',U']
+Combination [U,L,F,S,Z,Z',S',F',L',U'] is OK.
+Trying combination [U,L,F,u,l,l',u',F',L',U']
+Combination [U,L,F,u,l,l',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,r,r',u',F',L',U']
+Combination [U,L,F,u,r,r',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,b,b',u',F',L',U']
+Combination [U,L,F,u,b,b',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,d,d',u',F',L',U']
+Combination [U,L,F,u,d,d',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,X,X',u',F',L',U']
+Combination [U,L,F,u,X,X',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,Y,Y',u',F',L',U']
+Combination [U,L,F,u,Y,Y',u',F',L',U'] is OK.
+Trying combination [U,L,F,u,Z,Z',u',F',L',U']
+Combination [U,L,F,u,Z,Z',u',F',L',U'] is OK.
+Trying combination [U,L,F,l,r,r',l',F',L',U']
+Combination [U,L,F,l,r,r',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,b,b',l',F',L',U']
+Combination [U,L,F,l,b,b',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,d,d',l',F',L',U']
+Combination [U,L,F,l,d,d',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,X,X',l',F',L',U']
+Combination [U,L,F,l,X,X',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,Y,Y',l',F',L',U']
+Combination [U,L,F,l,Y,Y',l',F',L',U'] is OK.
+Trying combination [U,L,F,l,Z,Z',l',F',L',U']
+Combination [U,L,F,l,Z,Z',l',F',L',U'] is OK.
+Trying combination [U,L,F,r,b,b',r',F',L',U']
+Combination [U,L,F,r,b,b',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,d,d',r',F',L',U']
+Combination [U,L,F,r,d,d',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,X,X',r',F',L',U']
+Combination [U,L,F,r,X,X',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,Y,Y',r',F',L',U']
+Combination [U,L,F,r,Y,Y',r',F',L',U'] is OK.
+Trying combination [U,L,F,r,Z,Z',r',F',L',U']
+Combination [U,L,F,r,Z,Z',r',F',L',U'] is OK.
+Trying combination [U,L,F,b,d,d',b',F',L',U']
+Combination [U,L,F,b,d,d',b',F',L',U'] is OK.
+Trying combination [U,L,F,b,X,X',b',F',L',U']
+Combination [U,L,F,b,X,X',b',F',L',U'] is OK.
+Trying combination [U,L,F,b,Y,Y',b',F',L',U']
+Combination [U,L,F,b,Y,Y',b',F',L',U'] is OK.
+Trying combination [U,L,F,b,Z,Z',b',F',L',U']
+Combination [U,L,F,b,Z,Z',b',F',L',U'] is OK.
+Trying combination [U,L,F,d,X,X',d',F',L',U']
+Combination [U,L,F,d,X,X',d',F',L',U'] is OK.
+Trying combination [U,L,F,d,Y,Y',d',F',L',U']
+Combination [U,L,F,d,Y,Y',d',F',L',U'] is OK.
+Trying combination [U,L,F,d,Z,Z',d',F',L',U']
+Combination [U,L,F,d,Z,Z',d',F',L',U'] is OK.
+Trying combination [U,L,F,X,Y,Y',X',F',L',U']
+Combination [U,L,F,X,Y,Y',X',F',L',U'] is OK.
+Trying combination [U,L,F,X,Z,Z',X',F',L',U']
+Combination [U,L,F,X,Z,Z',X',F',L',U'] is OK.
+Trying combination [U,L,F,Y,Z,Z',Y',F',L',U']
+Combination [U,L,F,Y,Z,Z',Y',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,X,X',B',R',F',L',U']
+Combination [U,L,F,R,B,X,X',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,Y,Y',B',R',F',L',U']
+Combination [U,L,F,R,B,Y,Y',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,Z,Z',B',R',F',L',U']
+Combination [U,L,F,R,B,Z,Z',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,M,M',D',R',F',L',U']
+Combination [U,L,F,R,D,M,M',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,E,E',D',R',F',L',U']
+Combination [U,L,F,R,D,E,E',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,S,S',D',R',F',L',U']
+Combination [U,L,F,R,D,S,S',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,u,u',D',R',F',L',U']
+Combination [U,L,F,R,D,u,u',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,l,l',D',R',F',L',U']
+Combination [U,L,F,R,D,l,l',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,r,r',D',R',F',L',U']
+Combination [U,L,F,R,D,r,r',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,b,b',D',R',F',L',U']
+Combination [U,L,F,R,D,b,b',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,d,d',D',R',F',L',U']
+Combination [U,L,F,R,D,d,d',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,X,X',D',R',F',L',U']
+Combination [U,L,F,R,D,X,X',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,Y,Y',D',R',F',L',U']
+Combination [U,L,F,R,D,Y,Y',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,D,Z,Z',D',R',F',L',U']
+Combination [U,L,F,R,D,Z,Z',D',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,E,E',M',R',F',L',U']
+Combination [U,L,F,R,M,E,E',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,S,S',M',R',F',L',U']
+Combination [U,L,F,R,M,S,S',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,u,u',M',R',F',L',U']
+Combination [U,L,F,R,M,u,u',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,l,l',M',R',F',L',U']
+Combination [U,L,F,R,M,l,l',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,r,r',M',R',F',L',U']
+Combination [U,L,F,R,M,r,r',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,b,b',M',R',F',L',U']
+Combination [U,L,F,R,M,b,b',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,d,d',M',R',F',L',U']
+Combination [U,L,F,R,M,d,d',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,X,X',M',R',F',L',U']
+Combination [U,L,F,R,M,X,X',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,Y,Y',M',R',F',L',U']
+Combination [U,L,F,R,M,Y,Y',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,M,Z,Z',M',R',F',L',U']
+Combination [U,L,F,R,M,Z,Z',M',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,S,S',E',R',F',L',U']
+Combination [U,L,F,R,E,S,S',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,u,u',E',R',F',L',U']
+Combination [U,L,F,R,E,u,u',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,l,l',E',R',F',L',U']
+Combination [U,L,F,R,E,l,l',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,r,r',E',R',F',L',U']
+Combination [U,L,F,R,E,r,r',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,b,b',E',R',F',L',U']
+Combination [U,L,F,R,E,b,b',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,d,d',E',R',F',L',U']
+Combination [U,L,F,R,E,d,d',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,X,X',E',R',F',L',U']
+Combination [U,L,F,R,E,X,X',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,Y,Y',E',R',F',L',U']
+Combination [U,L,F,R,E,Y,Y',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,E,Z,Z',E',R',F',L',U']
+Combination [U,L,F,R,E,Z,Z',E',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,u,u',S',R',F',L',U']
+Combination [U,L,F,R,S,u,u',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,l,l',S',R',F',L',U']
+Combination [U,L,F,R,S,l,l',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,r,r',S',R',F',L',U']
+Combination [U,L,F,R,S,r,r',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,b,b',S',R',F',L',U']
+Combination [U,L,F,R,S,b,b',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,d,d',S',R',F',L',U']
+Combination [U,L,F,R,S,d,d',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,X,X',S',R',F',L',U']
+Combination [U,L,F,R,S,X,X',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,Y,Y',S',R',F',L',U']
+Combination [U,L,F,R,S,Y,Y',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,S,Z,Z',S',R',F',L',U']
+Combination [U,L,F,R,S,Z,Z',S',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,l,l',u',R',F',L',U']
+Combination [U,L,F,R,u,l,l',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,r,r',u',R',F',L',U']
+Combination [U,L,F,R,u,r,r',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,b,b',u',R',F',L',U']
+Combination [U,L,F,R,u,b,b',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,d,d',u',R',F',L',U']
+Combination [U,L,F,R,u,d,d',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,X,X',u',R',F',L',U']
+Combination [U,L,F,R,u,X,X',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,Y,Y',u',R',F',L',U']
+Combination [U,L,F,R,u,Y,Y',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,u,Z,Z',u',R',F',L',U']
+Combination [U,L,F,R,u,Z,Z',u',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,r,r',l',R',F',L',U']
+Combination [U,L,F,R,l,r,r',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,b,b',l',R',F',L',U']
+Combination [U,L,F,R,l,b,b',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,d,d',l',R',F',L',U']
+Combination [U,L,F,R,l,d,d',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,X,X',l',R',F',L',U']
+Combination [U,L,F,R,l,X,X',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,Y,Y',l',R',F',L',U']
+Combination [U,L,F,R,l,Y,Y',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,l,Z,Z',l',R',F',L',U']
+Combination [U,L,F,R,l,Z,Z',l',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,b,b',r',R',F',L',U']
+Combination [U,L,F,R,r,b,b',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,d,d',r',R',F',L',U']
+Combination [U,L,F,R,r,d,d',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,X,X',r',R',F',L',U']
+Combination [U,L,F,R,r,X,X',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,Y,Y',r',R',F',L',U']
+Combination [U,L,F,R,r,Y,Y',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,r,Z,Z',r',R',F',L',U']
+Combination [U,L,F,R,r,Z,Z',r',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,d,d',b',R',F',L',U']
+Combination [U,L,F,R,b,d,d',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,X,X',b',R',F',L',U']
+Combination [U,L,F,R,b,X,X',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,Y,Y',b',R',F',L',U']
+Combination [U,L,F,R,b,Y,Y',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,b,Z,Z',b',R',F',L',U']
+Combination [U,L,F,R,b,Z,Z',b',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,X,X',d',R',F',L',U']
+Combination [U,L,F,R,d,X,X',d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,Y,Y',d',R',F',L',U']
+Combination [U,L,F,R,d,Y,Y',d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,d,Z,Z',d',R',F',L',U']
+Combination [U,L,F,R,d,Z,Z',d',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,X,Y,Y',X',R',F',L',U']
+Combination [U,L,F,R,X,Y,Y',X',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,X,Z,Z',X',R',F',L',U']
+Combination [U,L,F,R,X,Z,Z',X',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,Y,Z,Z',Y',R',F',L',U']
+Combination [U,L,F,R,Y,Z,Z',Y',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,X,X',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,X,X',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,Y,Y',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,Y,Y',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,Z,Z',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,Z,Z',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,E,E',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,E,E',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,S,S',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,S,S',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,u,u',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,u,u',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,l,l',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,l,l',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,r,r',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,r,r',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,b,b',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,b,b',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,d,d',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,d,d',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,X,X',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,X,X',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,Y,Y',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,Y,Y',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,M,Z,Z',M',B',R',F',L',U']
+Combination [U,L,F,R,B,M,Z,Z',M',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,S,S',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,S,S',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,u,u',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,u,u',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,l,l',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,l,l',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,r,r',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,r,r',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,b,b',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,b,b',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,d,d',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,d,d',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,X,X',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,X,X',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,Y,Y',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,Y,Y',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,E,Z,Z',E',B',R',F',L',U']
+Combination [U,L,F,R,B,E,Z,Z',E',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,u,u',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,u,u',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,l,l',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,l,l',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,r,r',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,r,r',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,b,b',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,b,b',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,d,d',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,d,d',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,X,X',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,X,X',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,Y,Y',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,Y,Y',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,S,Z,Z',S',B',R',F',L',U']
+Combination [U,L,F,R,B,S,Z,Z',S',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,l,l',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,l,l',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,r,r',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,r,r',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,b,b',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,b,b',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,d,d',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,d,d',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,X,X',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,X,X',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,Y,Y',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,Y,Y',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,u,Z,Z',u',B',R',F',L',U']
+Combination [U,L,F,R,B,u,Z,Z',u',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,r,r',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,r,r',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,b,b',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,b,b',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,d,d',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,d,d',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,X,X',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,X,X',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,Y,Y',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,Y,Y',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,l,Z,Z',l',B',R',F',L',U']
+Combination [U,L,F,R,B,l,Z,Z',l',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,b,b',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,b,b',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,d,d',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,d,d',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,X,X',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,X,X',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,Y,Y',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,Y,Y',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,r,Z,Z',r',B',R',F',L',U']
+Combination [U,L,F,R,B,r,Z,Z',r',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,d,d',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,d,d',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,X,X',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,X,X',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,Y,Y',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,Y,Y',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,b,Z,Z',b',B',R',F',L',U']
+Combination [U,L,F,R,B,b,Z,Z',b',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,X,X',d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,X,X',d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,Y,Y',d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,Y,Y',d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,d,Z,Z',d',B',R',F',L',U']
+Combination [U,L,F,R,B,d,Z,Z',d',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,X,Y,Y',X',B',R',F',L',U']
+Combination [U,L,F,R,B,X,Y,Y',X',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,X,Z,Z',X',B',R',F',L',U']
+Combination [U,L,F,R,B,X,Z,Z',X',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,Y,Z,Z',Y',B',R',F',L',U']
+Combination [U,L,F,R,B,Y,Z,Z',Y',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,X,X',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,X,X',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,Y,Y',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,Y,Y',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,Z,Z',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,Z,Z',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,S,S',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,S,S',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,u,u',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,u,u',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,l,l',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,l,l',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,r,r',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,r,r',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,b,b',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,b,b',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,d,d',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,d,d',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,X,X',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,X,X',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,Y,Y',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,Y,Y',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,E,Z,Z',E',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,E,Z,Z',E',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,u,u',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,u,u',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,l,l',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,l,l',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,r,r',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,r,r',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,b,b',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,b,b',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,d,d',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,d,d',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,X,X',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,X,X',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,Y,Y',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,Y,Y',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,S,Z,Z',S',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,S,Z,Z',S',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,l,l',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,l,l',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,r,r',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,r,r',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,b,b',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,b,b',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,d,d',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,d,d',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,X,X',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,X,X',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,Y,Y',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,Y,Y',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,u,Z,Z',u',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,u,Z,Z',u',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,r,r',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,r,r',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,b,b',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,b,b',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,d,d',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,d,d',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,X,X',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,X,X',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,Y,Y',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,Y,Y',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,l,Z,Z',l',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,l,Z,Z',l',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,b,b',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,b,b',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,d,d',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,d,d',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,X,X',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,X,X',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,Y,Y',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,Y,Y',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,r,Z,Z',r',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,r,Z,Z',r',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,d,d',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,d,d',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,X,X',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,X,X',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,Y,Y',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,Y,Y',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,b,Z,Z',b',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,b,Z,Z',b',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,X,X',d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,X,X',d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,Y,Y',d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,Y,Y',d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,d,Z,Z',d',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,d,Z,Z',d',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,X,Y,Y',X',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,X,Y,Y',X',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,X,Z,Z',X',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,X,Z,Z',X',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,Y,Z,Z',Y',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,Y,Z,Z',Y',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,X,X',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,X,X',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,Y,Y',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,Y,Y',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,Z,Z',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,Z,Z',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,u,u',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,u,u',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,l,l',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,l,l',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,r,r',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,r,r',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,b,b',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,b,b',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,d,d',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,d,d',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,X,X',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,X,X',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,Y,Y',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,Y,Y',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,S,Z,Z',S',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,S,Z,Z',S',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,l,l',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,l,l',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,r,r',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,r,r',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,b,b',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,b,b',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,d,d',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,d,d',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,X,X',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,X,X',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,Y,Y',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,Y,Y',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,u,Z,Z',u',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,u,Z,Z',u',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,r,r',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,r,r',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,b,b',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,b,b',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,d,d',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,d,d',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,X,X',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,X,X',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,Y,Y',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,Y,Y',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,l,Z,Z',l',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,l,Z,Z',l',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,b,b',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,b,b',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,d,d',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,d,d',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,X,X',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,X,X',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,Y,Y',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,Y,Y',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,r,Z,Z',r',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,r,Z,Z',r',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,d,d',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,d,d',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,X,X',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,X,X',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,Y,Y',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,Y,Y',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,b,Z,Z',b',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,b,Z,Z',b',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,X,X',d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,X,X',d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,Y,Y',d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,Y,Y',d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,d,Z,Z',d',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,d,Z,Z',d',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,X,Y,Y',X',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,X,Y,Y',X',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,X,Z,Z',X',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,X,Z,Z',X',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,Y,Z,Z',Y',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,Y,Z,Z',Y',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,X,X',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,X,X',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,Y,Y',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,Y,Y',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,Z,Z',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,Z,Z',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,l,l',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,l,l',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,r,r',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,r,r',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,b,b',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,b,b',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,d,d',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,d,d',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,X,X',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,X,X',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,Y,Y',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,Y,Y',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,u,Z,Z',u',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,u,Z,Z',u',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,r,r',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,r,r',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,b,b',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,b,b',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,d,d',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,d,d',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,X,X',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,X,X',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,Y,Y',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,Y,Y',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,l,Z,Z',l',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,l,Z,Z',l',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,b,b',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,b,b',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,d,d',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,d,d',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,X,X',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,X,X',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,Y,Y',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,Y,Y',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,r,Z,Z',r',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,r,Z,Z',r',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,d,d',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,d,d',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,X,X',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,X,X',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,Y,Y',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,Y,Y',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,b,Z,Z',b',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,b,Z,Z',b',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,X,X',d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,X,X',d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,Y,Y',d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,Y,Y',d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,d,Z,Z',d',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,d,Z,Z',d',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,X,Y,Y',X',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,X,Y,Y',X',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,X,Z,Z',X',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,X,Z,Z',X',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,Y,Z,Z',Y',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,Y,Z,Z',Y',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,X,X',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,X,X',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,Y,Y',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,Y,Y',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,Z,Z',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,Z,Z',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,r,r',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,r,r',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,b,b',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,b,b',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,d,d',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,d,d',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,X,X',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,X,X',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,Y,Y',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,Y,Y',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,l,Z,Z',l',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,l,Z,Z',l',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,b,b',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,b,b',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,d,d',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,d,d',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,X,X',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,X,X',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,Y,Y',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,Y,Y',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,r,Z,Z',r',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,r,Z,Z',r',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,d,d',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,d,d',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,X,X',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,X,X',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,Y,Y',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,Y,Y',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,b,Z,Z',b',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,b,Z,Z',b',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,X,X',d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,X,X',d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,Y,Y',d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,Y,Y',d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,d,Z,Z',d',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,d,Z,Z',d',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,X,Y,Y',X',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,X,Y,Y',X',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,X,Z,Z',X',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,X,Z,Z',X',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,Y,Z,Z',Y',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,Y,Z,Z',Y',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,X,X',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,X,X',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,Y,Y',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,Y,Y',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,Z,Z',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,Z,Z',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,b,b',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,b,b',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,d,d',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,d,d',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,X,X',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,X,X',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,Y,Y',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,Y,Y',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,r,Z,Z',r',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,r,Z,Z',r',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,d,d',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,d,d',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,X,X',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,X,X',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,Y,Y',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,Y,Y',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,b,Z,Z',b',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,b,Z,Z',b',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,X,X',d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,X,X',d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,Y,Y',d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,Y,Y',d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,d,Z,Z',d',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,d,Z,Z',d',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,X,Y,Y',X',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,X,Y,Y',X',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,X,Z,Z',X',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,X,Z,Z',X',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,Y,Z,Z',Y',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,Y,Z,Z',Y',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,X,X',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,X,X',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Y',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Y',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,Z,Z',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,Z,Z',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,d,d',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,d,d',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,X,X',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,X,X',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,Y,Y',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,Y,Y',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,b,Z,Z',b',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,b,Z,Z',b',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,X,X',d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,X,X',d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,Y,Y',d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,Y,Y',d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,d,Z,Z',d',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,d,Z,Z',d',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,X,Y,Y',X',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,X,Y,Y',X',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,X,Z,Z',X',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,X,Z,Z',X',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,Y,Z,Z',Y',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,Y,Z,Z',Y',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,X',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,X',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Y',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Y',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Z,Z',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Z,Z',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,X,X',d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,X,X',d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Y,Y',d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Y,Y',d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Z,Z',d',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,d,Z,Z',d',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Y,Y',X',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Y,Y',X',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Z,Z',X',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,X,Z,Z',X',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Z,Z',Y',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,Y,Z,Z',Y',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,X',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,X',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Z,Z',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Z,Z',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Y,Y',X',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Y,Y',X',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Z,Z',X',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,X,Z,Z',X',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Z,Z',Y',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,Y,Z,Z',Y',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Y,Y',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Y,Y',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Z,Z',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,X,Z,Z',X',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
+Trying combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Z,Z',Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U']
+Combination [U,L,F,R,B,D,M,E,S,u,l,r,b,d,Y,Z,Z',Y',d',b',r',l',u',S',E',M',D',B',R',F',L',U'] is OK.
diff --git a/tests/sav/test_rubix_visual.res b/tests/sav/test_rubix_visual.res
new file mode 100644 (file)
index 0000000..d8fa8de
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1.res b/tests/sav/test_rubix_visual_alt1.res
new file mode 100644 (file)
index 0000000..e4e5c1d
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args1.res b/tests/sav/test_rubix_visual_alt1_args1.res
new file mode 100644 (file)
index 0000000..a6230a2
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[U]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args10.res b/tests/sav/test_rubix_visual_alt1_args10.res
new file mode 100644 (file)
index 0000000..be57829
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[M']'
+      \e[31m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[37m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args11.res b/tests/sav/test_rubix_visual_alt1_args11.res
new file mode 100644 (file)
index 0000000..5c6a2cc
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[R]'
+      \e[31m■\e[m \e[37m■\e[m \e[33m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[34m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[33m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args12.res b/tests/sav/test_rubix_visual_alt1_args12.res
new file mode 100644 (file)
index 0000000..01c1b6c
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[R']'
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[34m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[33m■\e[m \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args13.res b/tests/sav/test_rubix_visual_alt1_args13.res
new file mode 100644 (file)
index 0000000..a71b3eb
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[F]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args14.res b/tests/sav/test_rubix_visual_alt1_args14.res
new file mode 100644 (file)
index 0000000..6f9ad8b
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[F']'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m \e[31m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args15.res b/tests/sav/test_rubix_visual_alt1_args15.res
new file mode 100644 (file)
index 0000000..4275a84
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[S]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[33m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[31m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[35m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args16.res b/tests/sav/test_rubix_visual_alt1_args16.res
new file mode 100644 (file)
index 0000000..40de1a9
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[S']'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[33m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args17.res b/tests/sav/test_rubix_visual_alt1_args17.res
new file mode 100644 (file)
index 0000000..4ad64b4
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[u]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args18.res b/tests/sav/test_rubix_visual_alt1_args18.res
new file mode 100644 (file)
index 0000000..5561a0d
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[u']'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m 
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args19.res b/tests/sav/test_rubix_visual_alt1_args19.res
new file mode 100644 (file)
index 0000000..e58dfbb
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[l]'
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[33m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[31m■\e[m      
+\e[33m■\e[m \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m \e[35m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[37m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[34m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args2.res b/tests/sav/test_rubix_visual_alt1_args2.res
new file mode 100644 (file)
index 0000000..5500041
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[U']'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args20.res b/tests/sav/test_rubix_visual_alt1_args20.res
new file mode 100644 (file)
index 0000000..e1f40fe
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[l']'
+      \e[34m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[34m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[31m■\e[m      
+\e[32m■\e[m \e[31m■\e[m \e[34m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args21.res b/tests/sav/test_rubix_visual_alt1_args21.res
new file mode 100644 (file)
index 0000000..72619fd
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[f]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[31m■\e[m \e[33m■\e[m \e[32m■\e[m \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[35m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[34m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m 
+      \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args22.res b/tests/sav/test_rubix_visual_alt1_args22.res
new file mode 100644 (file)
index 0000000..0ce32a4
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[f']'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m \e[32m■\e[m \e[33m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args23.res b/tests/sav/test_rubix_visual_alt1_args23.res
new file mode 100644 (file)
index 0000000..e0593f8
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[r]'
+      \e[31m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args24.res b/tests/sav/test_rubix_visual_alt1_args24.res
new file mode 100644 (file)
index 0000000..6c8c2ab
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[r']'
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[34m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[34m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args25.res b/tests/sav/test_rubix_visual_alt1_args25.res
new file mode 100644 (file)
index 0000000..4660575
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[b]'
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+      \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[33m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[33m■\e[m 
+\e[35m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[34m■\e[m \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args26.res b/tests/sav/test_rubix_visual_alt1_args26.res
new file mode 100644 (file)
index 0000000..dcedd54
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[b']'
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[34m■\e[m \e[33m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[31m■\e[m \e[34m■\e[m 
+\e[35m■\e[m \e[35m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[35m■\e[m 
+\e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args27.res b/tests/sav/test_rubix_visual_alt1_args27.res
new file mode 100644 (file)
index 0000000..287cba2
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[d]'
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+      \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args28.res b/tests/sav/test_rubix_visual_alt1_args28.res
new file mode 100644 (file)
index 0000000..cccb868
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[d']'
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+\e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m 
+      \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args29.res b/tests/sav/test_rubix_visual_alt1_args29.res
new file mode 100644 (file)
index 0000000..85c8409
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[X]'
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+\e[32m■\e[m \e[31m■\e[m \e[34m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m 
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args3.res b/tests/sav/test_rubix_visual_alt1_args3.res
new file mode 100644 (file)
index 0000000..5cc23f2
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[E]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args30.res b/tests/sav/test_rubix_visual_alt1_args30.res
new file mode 100644 (file)
index 0000000..f8eb3ef
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[X']'
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+\e[33m■\e[m \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m 
+\e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m 
+\e[34m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args31.res b/tests/sav/test_rubix_visual_alt1_args31.res
new file mode 100644 (file)
index 0000000..c043226
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[Y]'
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+\e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m 
+      \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args32.res b/tests/sav/test_rubix_visual_alt1_args32.res
new file mode 100644 (file)
index 0000000..b10129e
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[Y']'
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m 
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+      \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args33.res b/tests/sav/test_rubix_visual_alt1_args33.res
new file mode 100644 (file)
index 0000000..06fad2d
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[Z]'
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[34m■\e[m \e[33m■\e[m \e[32m■\e[m \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m 
+\e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m 
+\e[33m■\e[m \e[34m■\e[m \e[31m■\e[m \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m 
+      \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args34.res b/tests/sav/test_rubix_visual_alt1_args34.res
new file mode 100644 (file)
index 0000000..0f18bfa
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[Z']'
+      \e[33m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[35m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[32m■\e[m      
+      \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m      
+\e[33m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m 
+\e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[34m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m 
+      \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args4.res b/tests/sav/test_rubix_visual_alt1_args4.res
new file mode 100644 (file)
index 0000000..7ee7e2f
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[E']'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args5.res b/tests/sav/test_rubix_visual_alt1_args5.res
new file mode 100644 (file)
index 0000000..056eeee
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[D]'
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+      \e[34m■\e[m \e[33m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[37m■\e[m      
+      \e[33m■\e[m \e[34m■\e[m \e[31m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args6.res b/tests/sav/test_rubix_visual_alt1_args6.res
new file mode 100644 (file)
index 0000000..decb58b
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[D']'
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m 
+      \e[31m■\e[m \e[34m■\e[m \e[33m■\e[m      
+      \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args7.res b/tests/sav/test_rubix_visual_alt1_args7.res
new file mode 100644 (file)
index 0000000..f08e7a3
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[L]'
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[33m■\e[m \e[32m■\e[m \e[31m■\e[m \e[34m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[35m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[34m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args8.res b/tests/sav/test_rubix_visual_alt1_args8.res
new file mode 100644 (file)
index 0000000..92d4fb6
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[L']'
+      \e[34m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[34m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[32m■\e[m \e[31m■\e[m \e[34m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[31m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_alt1_args9.res b/tests/sav/test_rubix_visual_alt1_args9.res
new file mode 100644 (file)
index 0000000..867d394
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
+After commands '[M]'
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[37m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[31m■\e[m \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[37m■\e[m \e[31m■\e[m \e[33m■\e[m \e[37m■\e[m \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[33m■\e[m \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[33m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m 
+      \e[32m■\e[m \e[37m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args1.res b/tests/sav/test_rubix_visual_args1.res
new file mode 100644 (file)
index 0000000..ba93848
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[U]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args10.res b/tests/sav/test_rubix_visual_args10.res
new file mode 100644 (file)
index 0000000..85745d0
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[M']'
+      \e[34m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[34m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args11.res b/tests/sav/test_rubix_visual_args11.res
new file mode 100644 (file)
index 0000000..04ed8cf
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[R]'
+      \e[34m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args12.res b/tests/sav/test_rubix_visual_args12.res
new file mode 100644 (file)
index 0000000..75d7b43
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[R']'
+      \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args13.res b/tests/sav/test_rubix_visual_args13.res
new file mode 100644 (file)
index 0000000..c5bce36
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[F]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args14.res b/tests/sav/test_rubix_visual_args14.res
new file mode 100644 (file)
index 0000000..e1f97de
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[F']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args15.res b/tests/sav/test_rubix_visual_args15.res
new file mode 100644 (file)
index 0000000..0f01bb8
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[S]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[35m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[31m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[35m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[31m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[35m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[31m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args16.res b/tests/sav/test_rubix_visual_args16.res
new file mode 100644 (file)
index 0000000..5ac4e9c
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[S']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[35m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args17.res b/tests/sav/test_rubix_visual_args17.res
new file mode 100644 (file)
index 0000000..64ffb3a
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[u]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args18.res b/tests/sav/test_rubix_visual_args18.res
new file mode 100644 (file)
index 0000000..fabcc9f
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[u']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args19.res b/tests/sav/test_rubix_visual_args19.res
new file mode 100644 (file)
index 0000000..9c8725f
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[l]'
+      \e[35m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args2.res b/tests/sav/test_rubix_visual_args2.res
new file mode 100644 (file)
index 0000000..c38adda
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[U']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args20.res b/tests/sav/test_rubix_visual_args20.res
new file mode 100644 (file)
index 0000000..7e59a3f
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[l']'
+      \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[34m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args21.res b/tests/sav/test_rubix_visual_args21.res
new file mode 100644 (file)
index 0000000..b4a7d00
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[f]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+\e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m 
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args22.res b/tests/sav/test_rubix_visual_args22.res
new file mode 100644 (file)
index 0000000..65e3bf7
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[f']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+\e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m 
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args23.res b/tests/sav/test_rubix_visual_args23.res
new file mode 100644 (file)
index 0000000..bbcd817
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[r]'
+      \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args24.res b/tests/sav/test_rubix_visual_args24.res
new file mode 100644 (file)
index 0000000..e415e18
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[r']'
+      \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args25.res b/tests/sav/test_rubix_visual_args25.res
new file mode 100644 (file)
index 0000000..da6e087
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[b]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[31m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[31m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[31m■\e[m \e[31m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[35m■\e[m \e[35m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args26.res b/tests/sav/test_rubix_visual_args26.res
new file mode 100644 (file)
index 0000000..b6f60f0
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[b']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[31m■\e[m \e[31m■\e[m 
+\e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[31m■\e[m \e[31m■\e[m 
+\e[35m■\e[m \e[35m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[31m■\e[m \e[31m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args27.res b/tests/sav/test_rubix_visual_args27.res
new file mode 100644 (file)
index 0000000..e588b52
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[d]'
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args28.res b/tests/sav/test_rubix_visual_args28.res
new file mode 100644 (file)
index 0000000..021ac04
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[d']'
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args29.res b/tests/sav/test_rubix_visual_args29.res
new file mode 100644 (file)
index 0000000..759cf50
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[X]'
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args3.res b/tests/sav/test_rubix_visual_args3.res
new file mode 100644 (file)
index 0000000..7fc13fb
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[E]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args30.res b/tests/sav/test_rubix_visual_args30.res
new file mode 100644 (file)
index 0000000..688d36e
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[X']'
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m      
+      \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args31.res b/tests/sav/test_rubix_visual_args31.res
new file mode 100644 (file)
index 0000000..02dc063
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[Y]'
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args32.res b/tests/sav/test_rubix_visual_args32.res
new file mode 100644 (file)
index 0000000..2565a60
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[Y']'
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args33.res b/tests/sav/test_rubix_visual_args33.res
new file mode 100644 (file)
index 0000000..9b62a10
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[Z]'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+\e[35m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m 
+\e[35m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m 
+\e[35m■\e[m \e[35m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m 
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args34.res b/tests/sav/test_rubix_visual_args34.res
new file mode 100644 (file)
index 0000000..1110f2a
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[Z']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+\e[31m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[31m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m 
+\e[31m■\e[m \e[31m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m 
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args4.res b/tests/sav/test_rubix_visual_args4.res
new file mode 100644 (file)
index 0000000..acfd35a
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[E']'
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args5.res b/tests/sav/test_rubix_visual_args5.res
new file mode 100644 (file)
index 0000000..4fa73e8
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[D]'
+      \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[34m■\e[m \e[34m■\e[m \e[34m■\e[m \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args6.res b/tests/sav/test_rubix_visual_args6.res
new file mode 100644 (file)
index 0000000..1fc1716
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[D']'
+      \e[37m■\e[m \e[37m■\e[m \e[37m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args7.res b/tests/sav/test_rubix_visual_args7.res
new file mode 100644 (file)
index 0000000..8bd9fdf
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[L]'
+      \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[35m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[34m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[31m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[32m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args8.res b/tests/sav/test_rubix_visual_args8.res
new file mode 100644 (file)
index 0000000..de04815
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[L']'
+      \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[32m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[35m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
diff --git a/tests/sav/test_rubix_visual_args9.res b/tests/sav/test_rubix_visual_args9.res
new file mode 100644 (file)
index 0000000..5915e1a
--- /dev/null
@@ -0,0 +1,28 @@
+At beginning
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[34m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[31m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[32m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[35m■\e[m \e[35m■\e[m      
+
+After commands '[M]'
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[34m■\e[m \e[35m■\e[m \e[34m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[31m■\e[m      
+      \e[31m■\e[m \e[34m■\e[m \e[31m■\e[m      
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+\e[37m■\e[m \e[37m■\e[m \e[37m■\e[m \e[32m■\e[m \e[31m■\e[m \e[32m■\e[m \e[33m■\e[m \e[33m■\e[m \e[33m■\e[m 
+      \e[35m■\e[m \e[32m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[32m■\e[m \e[35m■\e[m      
+      \e[35m■\e[m \e[32m■\e[m \e[35m■\e[m      
+
index 29b474e..e3b56a7 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 4fa0577..f275932 100644 (file)
@@ -2,24 +2,25 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}
+{"b": true,"c": "a","f": 0.123,"i": 1234,"s": "asdf","n": null,"array": [88,"hello",null]}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": [88, "hello", null], "ii": 1111, "ss": "qwer"}
+{"b": false,"c": "b","f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": [88,"hello",null],"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"a": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}, "b": {"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": [88, "hello", null], "ii": 1111, "ss": "qwer"}, "aa": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}}
+{"a": {"b": true,"c": "a","f": 0.123,"i": 1234,"s": "asdf","n": null,"array": [88,"hello",null]},"b": {"b": false,"c": "b","f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": [88,"hello",null],"ii": 1111,"ss": "qwer"},"aa": {"b": true,"c": "a","f": 0.123,"i": 1234,"s": "asdf","n": null,"array": [88,"hello",null]}}
 
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "\tf\"\r\\/", "d": null}
+{"b": false,"c": "b","f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": [88,"hello",null],"ii": 1111,"ss": "\tf\"\r\\/","d": null}
 
index aa15c5f..42ad47b 100644 (file)
@@ -5,24 +5,24 @@ alt/test_serialization_alt2.nit:64,1--72,3: Warning: superfluous use of `auto_se
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"d": {"__kind": "ref", "__id": 0}}
 
index 1e8de79..9ad4073 100644 (file)
@@ -3,24 +3,24 @@ alt/test_serialization_alt3.nit:40,1--51,3: Warning: superfluous use of `noseria
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"d": {"__kind": "ref", "__id": 0}}
 
index 1d9be55..039170f 100644 (file)
@@ -3,24 +3,24 @@ alt/test_serialization_alt4.nit:29,2--31,26: Warning: superfluous use of `serial
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index be11dba..6f763c2 100644 (file)
@@ -3,24 +3,24 @@ alt/test_serialization_alt5.nit:22,1--38,3: Warning: duplicated annotation `seri
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 29b474e..e3b56a7 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 4c298df..337cdaa 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer"}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer"},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "\tf\"\r\\/", "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "\tf\"\r\\/","d": {"__kind": "ref", "__id": 0}}
 
index 6e79e63..2deb5ce 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]}}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]}},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "ii": 1111, "ss": "\tf\"\r\\/", "ffff": 6.789, "bbbb": false, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"ii": 1111,"ss": "\tf\"\r\\/","ffff": 6.789,"bbbb": false,"d": {"__kind": "ref", "__id": 0}}
 
index e52136f..1c1850c 100644 (file)
@@ -2,24 +2,24 @@
 <A: true a 0.123 1234 asdf false>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}
+{"__kind": "obj", "__id": 0, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"}
 
 # Nit:
 <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}
+{"__kind": "obj", "__id": 0, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false}
 
 # Nit:
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl true> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": 12, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C","a": {"__kind": "obj", "__id": 1, "__class": "A","b": true,"c": {"__kind": "char", "__val": "a"},"f": 0.123,"i": 1234,"s": "asdf","n": null,"array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef"},"b": {"__kind": "obj", "__id": 3, "__class": "B","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "hjkl","n": 12,"array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "qwer","ffff": 6.789,"bbbb": false},"aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
 <- false> 1111         f"\r\/> true>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]", "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "\tf\"\r\\/", "ffff": 6.789, "bbbb": false, "d": {"__kind": "ref", "__id": 0}}
+{"__kind": "obj", "__id": 0, "__class": "D","b": false,"c": {"__kind": "char", "__val": "b"},"f": 123.123,"i": 2345,"s": "new line ->\n<-","n": null,"array": {"__kind": "obj", "__id": 1, "__class": "Array[nullable Object]","__items": [88,"hello",null]},"iii": 6789,"sss": "redef","ii": 1111,"ss": "\tf\"\r\\/","ffff": 6.789,"bbbb": false,"d": {"__kind": "ref", "__id": 0}}
 
index 05d8839..b1fe714 100644 (file)
@@ -2,7 +2,11 @@ test_prog/
 |--test_prog/README.md
 |--test_prog/game
 |  |--test_prog/game/README.md
+|  |--test_prog/game/excluded.nit
+|  |--test_prog/game/excluded_dir
+|  |  `--test_prog/game/excluded_dir/more.nit
 |  `--test_prog/game/game.nit
+|--test_prog/package.ini
 |--test_prog/platform
 |  |--test_prog/platform/README.md
 |  `--test_prog/platform/platform.nit
index f40c047..54cb7f8 100644 (file)
@@ -43,3 +43,6 @@ test.has_suffix("test") => true
 test.has_suffix("bt") => false
 test.has_suffix("bat") => false
 test.has_suffix("foot") => false
+........
+
+........
index b2b26fe..9fbf85a 100644 (file)
@@ -1 +1 @@
-test_super_param2.nit:20,10: Error: class `C` not found in module `test_super_param2`.
+test_super_param2.nit:20,10: Error: class `C` not found in module `test_super_param2`. Did you mean `test_super_param2::A` or `test_super_param2::B`?
index 702e2b2..4a8c500 100644 (file)
@@ -3,55 +3,20 @@ Usage of Strings:
 
 Allocations, by type:
                
-       -FlatString = 29
+       -UnicodeFlatString = 0
+       -ASCIIFlatString = 30
        -FlatBuffer = 2
        -Concat = 0
        -RopeBuffer = 0
 
 Calls to length, by type:
-       FlatString = 18 (cache misses 5, 27.77%)
+       FlatString = 19
 Indexed accesses, by type:
-       FlatString = 8
-Calls to bytelen for each type:
-       FlatString = 61
+Calls to byte_length for each type:
+       FlatString = 48
 Calls to position for each type:
-       FlatString = 17
 Calls to bytepos for each type:
-       FlatString = 9
-Calls to first_byte on FlatString 191
-Calls to last_byte on FlatString 19
-FlatStrings allocated with length 78 (86.813%)
+Calls to first_byte on FlatString 50
+Calls to last_byte on FlatString 10
 Length of travel for index distribution:
-* 0 = 11 => occurences 73.333%, cumulative 73.333% 
-* 1 = 8 => occurences 27.586%, cumulative 65.517% 
 Byte length of the FlatStrings created:
-* 0 = 6 => occurences 4.478%, cumulative 4.478% 
-* 1 = 24 => occurences 16.216%, cumulative 20.27% 
-* 2 = 30 => occurences 18.519%, cumulative 37.037% 
-* 3 = 29 => occurences 16.384%, cumulative 50.282% 
-* 4 = 3 => occurences 1.563%, cumulative 47.917% 
-* 5 = 20 => occurences 9.662%, cumulative 54.106% 
-* 6 = 26 => occurences 11.712%, cumulative 62.162% 
-* 9 = 1 => occurences 0.422%, cumulative 58.65% 
-* 10 = 9 => occurences 3.571%, cumulative 58.73% 
-* 11 = 2 => occurences 0.749%, cumulative 56.18% 
-* 12 = 1 => occurences 0.356%, cumulative 53.737% 
-* 13 = 1 => occurences 0.34%, cumulative 51.701% 
-* 14 = 1 => occurences 0.326%, cumulative 49.837% 
-* 15 = 7 => occurences 2.188%, cumulative 50.0% 
-* 16 = 5 => occurences 1.493%, cumulative 49.254% 
-* 17 = 1 => occurences 0.286%, cumulative 47.429% 
-* 25 = 2 => occurences 0.551%, cumulative 46.281% 
-* 26 = 1 => occurences 0.265%, cumulative 44.828% 
-* 31 = 2 => occurences 0.513%, cumulative 43.846% 
-* 32 = 1 => occurences 0.248%, cumulative 42.574% 
-* 33 = 1 => occurences 0.24%, cumulative 41.487% 
-* 34 = 2 => occurences 0.465%, cumulative 40.698% 
-* 35 = 1 => occurences 0.225%, cumulative 39.64% 
-* 37 = 1 => occurences 0.219%, cumulative 38.731% 
-* 39 = 1 => occurences 0.213%, cumulative 37.872% 
-* 40 = 1 => occurences 0.207%, cumulative 37.06% 
-* 43 = 1 => occurences 0.202%, cumulative 36.29% 
-* 46 = 1 => occurences 0.196%, cumulative 35.56% 
-* 51 = 14 => occurences 2.682%, cumulative 37.356% 
-* 52 = 5 => occurences 0.931%, cumulative 37.244% 
diff --git a/tests/sublib/bar/bar.nit b/tests/sublib/bar/bar.nit
new file mode 100644 (file)
index 0000000..cbbf19b
--- /dev/null
@@ -0,0 +1 @@
+import end
diff --git a/tests/sublib/bar/foo.nit b/tests/sublib/bar/foo.nit
new file mode 100644 (file)
index 0000000..cbbf19b
--- /dev/null
@@ -0,0 +1 @@
+import end
diff --git a/tests/sublib/foo.nit b/tests/sublib/foo.nit
new file mode 100644 (file)
index 0000000..cbbf19b
--- /dev/null
@@ -0,0 +1 @@
+import end
diff --git a/tests/sublib/packages.ini b/tests/sublib/packages.ini
new file mode 100644 (file)
index 0000000..e69de29
index af82e47..fb0d769 100644 (file)
@@ -24,10 +24,30 @@ print "foob:   " + "foob".encode_base64
 print "fooba:  " + "fooba".encode_base64
 print "foobar: " + "foobar".encode_base64
 
-print ":" + "".decode_base64
-print "Zg==:     " + "Zg==".decode_base64
-print "Zm8=:     " + "Zm8=".decode_base64
-print "Zm9v:     " + "Zm9v".decode_base64
-print "Zm9vYg==: " + "Zm9vYg==".decode_base64
-print "Zm9vYmE=: " + "Zm9vYmE=".decode_base64
-print "Zm9vYmFy: " + "Zm9vYmFy".decode_base64
+print ":" + "".decode_base64.to_s
+print "Zg==:     " + "Zg==".decode_base64.to_s
+print "Zm8=:     " + "Zm8=".decode_base64.to_s
+print "Zm9v:     " + "Zm9v".decode_base64.to_s
+print "Zm9vYg==: " + "Zm9vYg==".decode_base64.to_s
+print "Zm9vYmE=: " + "Zm9vYmE=".decode_base64.to_s
+print "Zm9vYmFy: " + "Zm9vYmFy".decode_base64.to_s
+
+print "Zm9vYg: " + "Zm9vYg".decode_base64.to_s
+print "Zm9vYmE: " + "Zm9vYmE".decode_base64.to_s
+print "Zm9v*Yg: " + "Zm9v*Yg".decode_base64.to_s
+
+print ":"
+print "Znm=.is_base64? " + "Znm=".is_base64.to_s
+print "Znm===.is_base64? " + "Znm===".is_base64.to_s
+print "Z.sd=.is_base64? " + "Z.sd=".is_base64.to_s
+print "Z==D.is_base64? " + "Z==D".is_base64.to_s
+
+print ":"
+printn "Znm=: "
+print "Znm=".check_base64 or else "No error"
+printn "Znm===: "
+print "Znm===".check_base64 or else "No error"
+printn "Z.sd=: "
+print "Z.sd=".check_base64 or else "No error"
+printn "Z==D: "
+print "Z==D".check_base64 or else "No error"
index f76ac5f..902993a 100644 (file)
@@ -46,7 +46,7 @@ for i in fb.chars.reverse_iterator do
        l -= 1
 end
 
-l = fb.bytelen - 1
+l = fb.byte_length - 1
 
 for i in fb.bytes.reverse_iterator do
        print "Byte {l} = {i}"
diff --git a/tests/test_copy_to_native.nit b/tests/test_copy_to_native.nit
new file mode 100644 (file)
index 0000000..0018f1d
--- /dev/null
@@ -0,0 +1,28 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core
+#alt1 intrude import core::text::ropes
+#alt2 intrude import core::text::ropes
+
+var ons = new NativeString(9)
+var base_str = "%Dégâštr"
+
+var str: String = base_str
+#alt1 str = new Concat(base_str, base_str)
+#alt2 str = new Concat(base_str, base_str.substring_from(2))
+
+var copy_len = (str.byte_length - 4).min(9)
+str.copy_to_native(ons, copy_len, 4, 0)
+print ons.to_s_with_length(copy_len)
diff --git a/tests/test_csv.nit b/tests/test_csv.nit
new file mode 100644 (file)
index 0000000..179405d
--- /dev/null
@@ -0,0 +1,48 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import csv
+
+var words = "../examples/rosettacode/unixdict.txt".to_path.read_all.split('\n')
+
+var doc = new CsvDocument
+for i in [0 .. 8[ do doc.header.add("Col{i}")
+
+var del = doc.delimiter.to_s
+var eol = doc.eol
+var sep = doc.separator.to_s
+for i in [0 .. 10000[ do
+       var ln = new Array[String]
+       for j in [0 .. 8[ do
+               var add_sep = 100.rand >= 70
+               var add_eol = 100.rand >= 70
+               var add_del = 100.rand >= 70
+               var el = "{words.rand}"
+               if add_sep then el += sep
+               if add_eol then el += eol
+               if add_del then el += del
+               ln.add el
+       end
+       doc.records.add ln
+end
+
+var refst = new StringWriter
+doc.write_to(refst)
+
+var csvd = new CsvReader.from_string(refst.to_s).read_all
+
+var prodst = new StringWriter
+csvd.write_to(prodst)
+
+assert refst.to_s.trim == prodst.to_s.trim
diff --git a/tests/test_flatbuf_from.nit b/tests/test_flatbuf_from.nit
new file mode 100644 (file)
index 0000000..a91883f
--- /dev/null
@@ -0,0 +1,25 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#alt2 intrude import core::text::ropes
+
+var a = "String"
+
+var s: Text = a
+#alt1 s = new FlatBuffer
+#alt1 s.append(a)
+#alt2 var b = a
+#alt2 s = new Concat(a, b)
+
+var buf = new FlatBuffer.from(s)
index 8baadb1..155a39c 100644 (file)
@@ -18,20 +18,25 @@ import json::serialization
 
 var entities = new TestEntities
 
-var tests = entities.without_generics#alt1##alt2#
+var tests = entities.without_generics#alt1##alt2##alt3##alt4#
 #alt1#var tests = entities.with_generics
 #alt2#var tests = entities.with_generics
+#alt3#var tests = entities.with_generics
+#alt4#var tests = entities.with_generics
 
 for o in tests do
        var stream = new StringWriter
        var serializer = new JsonSerializer(stream)
        #alt2#serializer.plain_json = true
+       #alt3#serializer.pretty_json = true
+       #alt4#serializer.plain_json = true
+       #alt4#serializer.pretty_json = true
        serializer.serialize(o)
 
-       var deserializer = new JsonDeserializer(stream.to_s)#alt2#
-       var deserialized = deserializer.deserialize#alt2#
+       var deserializer = new JsonDeserializer(stream.to_s)#alt2##alt4#
+       var deserialized = deserializer.deserialize#alt2##alt4#
 
        print "# Nit:\n{o}\n"
        print "# Json:\n{stream}\n"
-       print "# Back in Nit:\n{deserialized or else "null"}\n"#alt2#
+       print "# Back in Nit:\n{deserialized or else "null"}\n"#alt2##alt4#
 end
index 8b49b8b..d921bf4 100644 (file)
@@ -43,11 +43,10 @@ builder.options.add "-Djava.class.path=."
 #alt1#builder.options.clear
 #alt1#builder.options.add "-Djava.class.path=/tmp/"
 var jvm = builder.create_jvm
-var env = builder.jni_env
-assert env != null
+assert jvm != null
 
-# Test JavaVM::env
-assert not jvm.env.address_is_null
+var env = jvm.env
+assert not env.address_is_null
 
 print "---------------------Test 1----------------------"
 # get the class
index 21728c1..a9f3e61 100644 (file)
@@ -21,12 +21,11 @@ var clock = new Clock
 var p = new Process("sleep", "10")
 
 # Send some signals
-p.signal(sigalarm)
+p.signal sigalarm
 p.kill
 
 # Wait for it to die
 p.wait
-var lapse = clock.lapse
 
 # Let's be generous here, as long as it does not take 5 secs out of 10 it's OK
-print lapse.sec < 5
+print clock.lapse < 5.0
diff --git a/tests/test_loader.args b/tests/test_loader.args
new file mode 100644 (file)
index 0000000..52a76e8
--- /dev/null
@@ -0,0 +1,8 @@
+test_prog/rpg/races.nit test_prog/rpg
+test_prog/test_prog.nit test_prog/test_prog test_prog test_prog::test_prog test_prog/
+test_prog::races test_prog::races:rpg test_prog::races::combat
+test_prog::fail::races test_prog::fail fail::fail
+/lib /fail sublib
+foo sublib/foo sublib/foo.nit foo.nit ./foo foo/ foo::foo -I sublib
+bar sublib/bar sublib/bar.nit sublib/bar/bar.nit bar.nit ./bar bar/ bar::bar -I sublib
+sublib/bar/foo.nit sublib/bar/foo bar/foo.nit bar::foo -I sublib
index 5f3aa2c..11a4b28 100644 (file)
@@ -1 +1,2 @@
 base_simple3.nit
+names/n3.nit names1.nit -I .
diff --git a/tests/test_nativestring_fill_from.nit b/tests/test_nativestring_fill_from.nit
new file mode 100644 (file)
index 0000000..4a88e23
--- /dev/null
@@ -0,0 +1,27 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#alt1 intrude import core::text::ropes
+import core
+
+var src_s = "S&éstr"
+var cpstr: Text = src_s
+#alt1 cpstr = new Concat(src_s, src_s)
+#alt2 cpstr = new FlatBuffer.from(src_s)
+#alt3 cpstr = cpstr.substring(1, 5)
+
+var ns = new NativeString(cpstr.byte_length)
+ns.fill_from(cpstr)
+
+print ns.to_s_with_length(cpstr.byte_length)
diff --git a/tests/test_nitcorn.nit b/tests/test_nitcorn.nit
new file mode 100644 (file)
index 0000000..5f2ca1c
--- /dev/null
@@ -0,0 +1,133 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import nitcorn
+import pthreads
+
+redef class Sys
+       var iface: String is lazy do
+               var testid = "NIT_TESTING_ID".environ.to_i
+               return "localhost:{10000+testid}"
+       end
+
+       var fs_path: String = getcwd / "../lib/nitcorn/examples/www/hello_world/dir" is lazy
+end
+
+class MyAction
+       super Action
+
+       redef fun answer(request, turi)
+       do
+               var rep = new HttpResponse(200)
+               rep.body = """
+[Response] Simple answer
+Method: {{{request.method}}}, URI: {{{request.uri}}}, trailing: {{{turi}}}"""
+
+               if request.get_args.not_empty
+               then rep.body += "\nGET args: {request.get_args.join(", ", ":")}"
+
+               if request.post_args.not_empty
+               then rep.body += "\nPOST args: {request.post_args.join(", ", ":")}"
+
+               if request.uri_params.not_empty
+               then rep.body += "\nParams args: {request.uri_params.join(", ", ":")}"
+
+               if request.cookie.not_empty
+               then rep.body += "\nCookie: {request.cookie.join(", ", ":")}"
+
+               rep.body += "\n"
+               return rep
+       end
+end
+
+class ServerThread
+       super Thread
+
+       redef fun main
+       do
+               # Hide testing concept to force nitcorn to actually run
+               "NIT_TESTING".setenv("false")
+
+               # Setup
+               var vh = new VirtualHost(iface)
+               vh.routes.add new Route("file_server", new FileServer(fs_path.simplify_path))
+               vh.routes.add new Route("simple_answer", new MyAction)
+               vh.routes.add new Route("params_answer/:i/:s", new MyAction)
+
+               # Launch
+               var factory = new HttpFactory.and_libevent
+               factory.config.virtual_hosts.add vh
+               factory.run
+
+               return null
+       end
+end
+
+class ClientThread
+       super Thread
+
+       redef fun main
+       do
+               system "curl -s {iface}/simple_answer"
+               system "curl -s {iface}/simple_answer/"
+               system "curl -s {iface}/simple_answer/trailing/path"
+
+               system "curl -s '{iface}/simple_answer?i=0123&s=asdf'"
+               system "curl -s {iface}/simple_answer --data 'i=0123&s=asdf'"
+               system "curl -s {iface}/simple_answer --cookie 'i=0123; s=asdf'"
+
+               system "curl -s {iface}/params_answer/0123/asdf"
+               system "curl -s {iface}/params_answer/0123/"
+               system "curl -s {iface}/params_answer/0123/asdf/trailing/path"
+               system "curl -s {iface}/params_answer/0123 --head"
+
+               system "curl -s {iface}/file_server/"
+               system "curl -s {iface}/file_server/ --head"
+               system "curl -s {iface}/file_server --head"
+               system "curl -s {iface}/file_server/a.txt"
+               system "curl -s {iface}/file_server/a.txt --head"
+               system "curl -s {iface}/file_server/binary_file.png --head"
+               system("curl -s {iface}/file_server/binary_file.png | diff - {fs_path.escape_to_sh}/binary_file.png",
+                      "curl -s {iface}/file_server/binary_file.png | diff - .../binary_file.png")
+               system "curl -s {iface}/file_server/unknown_file.txt --head"
+
+               system "curl -s {iface}/invalid_route --head"
+               return null
+       end
+
+       # Regex to catch and hide the port from the output to get consistent results
+       var host_re: Regex = "localhost:\[0-9\]+".to_re
+
+       fun system(cmd: String, title: nullable String)
+       do
+               title = title or else cmd
+               title = title.replace(host_re, "localhost:*****")
+               print "\n[Client] {title}"
+               sys.system cmd
+       end
+end
+
+# First, launch a server in the background
+var server = new ServerThread
+server.start
+0.1.sleep
+
+# Then, launch a client running test requests
+var client = new ClientThread
+client.start
+client.join
+0.1.sleep
+
+# Force quit the server
+exit 0
index b583197..d260cd1 100644 (file)
@@ -24,6 +24,8 @@ class X
        #     assert undefined_identifier
        fun foo do end
 
+       # a 'failure' unit test (does not parse)
+       #     assert !@#$%^&*()
        fun foo1(a, b: Int) do end
 
        private fun foo2: Bool do return true
diff --git a/tests/test_nitunit4/sav/test_sav_conflict.res b/tests/test_nitunit4/sav/test_sav_conflict.res
new file mode 100644 (file)
index 0000000..7b3a785
--- /dev/null
@@ -0,0 +1 @@
+BAD
diff --git a/tests/test_nitunit4/test_bad_comp.nit b/tests/test_nitunit4/test_bad_comp.nit
new file mode 100644 (file)
index 0000000..f8d9b87
--- /dev/null
@@ -0,0 +1,29 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_bad_comp is test_suite
+
+import test_suite
+
+class TestSuiteBadComp
+       super TestSuite
+
+       fun test_good do
+               assert true
+       end
+
+       fun test_bad do
+               assert bad_method
+       end
+end
diff --git a/tests/test_nitunit4/test_bad_comp2.nit b/tests/test_nitunit4/test_bad_comp2.nit
new file mode 100644 (file)
index 0000000..aac1a09
--- /dev/null
@@ -0,0 +1,29 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_bad_comp2 is test_suite
+
+import test_suite
+
+class TestSuiteBadComp
+       super TestSuite
+
+       fun test_good do
+               assert true
+       end
+
+       fun test_bad(param: Bool) do
+               assert param
+       end
+end
diff --git a/tests/test_nitunit4/test_baz.res b/tests/test_nitunit4/test_baz.res
new file mode 100644 (file)
index 0000000..0e89bcd
--- /dev/null
@@ -0,0 +1 @@
+Bad result file
index db734a6..8864263 100644 (file)
@@ -22,5 +22,18 @@ class TestTestSuite
        fun test_foo do
                print "Tested method"
                assert before
+               before = false
+       end
+
+       fun test_bar do
+               print "Tested method"
+       end
+
+       fun test_baz do
+               print "Tested method"
+       end
+
+       fun test_sav_conflict do
+               print "Tested method"
        end
 end
diff --git a/tests/test_nitunit4/test_nitunit4.sav/test_bar.res b/tests/test_nitunit4/test_nitunit4.sav/test_bar.res
new file mode 100644 (file)
index 0000000..f6d97cf
--- /dev/null
@@ -0,0 +1,3 @@
+Before Test
+Tested method
+After Test
diff --git a/tests/test_nitunit4/test_nitunit4.sav/test_sav_conflict.res b/tests/test_nitunit4/test_nitunit4.sav/test_sav_conflict.res
new file mode 100644 (file)
index 0000000..7b3a785
--- /dev/null
@@ -0,0 +1 @@
+BAD
index 3e7a1e7..af21e73 100644 (file)
@@ -28,6 +28,6 @@ class SuperTestSuite
 
        redef fun after_test do
                print "After Test"
-               assert false
+               assert before
        end
 end
diff --git a/tests/test_nitunit4/test_sav_conflict.res b/tests/test_nitunit4/test_sav_conflict.res
new file mode 100644 (file)
index 0000000..7b3a785
--- /dev/null
@@ -0,0 +1 @@
+BAD
diff --git a/tests/test_postgres_native.nit b/tests/test_postgres_native.nit
new file mode 100644 (file)
index 0000000..ff3be6f
--- /dev/null
@@ -0,0 +1,73 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_postgres_native
+
+import postgresql::native_postgres
+
+var db = new NativePostgres.connectdb("dbname=postgres")
+assert postgres_open: db.status.is_ok else print_error db.error
+
+var result = db.exec("CREATE TABLE IF NOT EXISTS animals (aname TEXT PRIMARY KEY, class TEXT NOT NULL, sex INTEGER)")
+assert postgres_create_table: result.status.is_ok else print_error db.error
+
+result = db.exec("INSERT INTO animals VALUES('Whale', 'mammal', 1)")
+assert postgres_insert_1: result.status.is_ok else print_error db.error
+
+result = db.exec("INSERT INTO animals VALUES('Snake', 'reptile', 0)")
+assert postgres_insert_2: result.status.is_ok else print_error db.error
+
+result = db.exec("SELECT * FROM animals")
+assert postgres_select: result.status.is_ok else print_error db.error
+
+assert postgres_ntuples: result.ntuples == 2 else print_error db.error
+assert postgres_nfields: result.nfields == 3 else print_error db.error
+assert postgres_fname: result.fname(0) == "aname" else print_error db.error
+assert postgres_isnull: result.is_null(0,0) == false else print_error db.error
+assert postgres_value: result.value(0,0) == "Whale" else print_error db.error
+
+var cols: Int = result.nfields
+var rows: Int = result.ntuples
+var fields: String = ""
+for c in [0..cols[ do fields += result.fname(c) + "   "
+print fields
+for i in [0..rows[ do
+  fields = ""
+  for j in [0..cols[ do fields +=  result.value(i, j) + "   "
+  print fields
+end
+
+result = db.exec("DELETE FROM animals WHERE aname = 'Lioness'")
+assert postgres_delete_1: result.status.is_ok else print_error db.error
+
+result = db.exec("DELETE FROM animals WHERE aname = 'Snake'")
+assert postgres_delete_2: result.status.is_ok else print_error db.error
+
+result = db.prepare("PREPARED_INSERT", "INSERT INTO animals(aname, class, sex) VALUES ($1, $2, $3)", 3)
+assert postgres_prepare: result.status.is_ok else print_error db.error
+
+result = db.exec("DELETE FROM animals WHERE aname = 'Frog'")
+assert postgres_delete_3: result.status.is_ok else print_error db.error
+
+var values = ["Frog", "Anphibian", "1"]
+var lengths = [values[0].length, values[1].length, values[2].length]
+var formats = [0,0,0]
+result = db.exec_prepared("PREPARED_INSERT", 3, values, lengths, formats,0)
+assert postgres_exec_prepared: result.status.is_ok else print_error db.error
+
+result = db.exec("DROP TABLE animals")
+assert postgres_drop_table: result.status.is_ok else print_error db.error
+db.finish
diff --git a/tests/test_postgres_nity.nit b/tests/test_postgres_nity.nit
new file mode 100644 (file)
index 0000000..9565335
--- /dev/null
@@ -0,0 +1,49 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_postgres_nity
+
+import postgresql::postgres
+
+var db = new Postgres.open("dbname=postgres")
+assert open_db: not db.is_closed else print db.error
+
+assert create_table: db.create_table("IF NOT EXISTS users (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else
+  print db.error
+end
+
+assert insert1: db.insert("INTO users VALUES('Bob', 'zzz', 1, 77.7)") else
+  print db.error
+end
+
+assert insert2: db.insert("INTO users VALUES('Guilherme', 'xxx', 1, 88)") else
+  print db.error
+end
+
+var result = db.raw_execute("SELECT * FROM users")
+
+assert raw_exec: result.is_ok else print db.error
+
+assert postgres_nfields: result.nfields == 4 else print_error db.error
+assert postgres_fname: result.fname(0) == "uname" else print_error db.error
+assert postgres_isnull: result.is_null(0,0) == false else print_error db.error
+assert postgres_value: result.value(0,0) == "Bob" else print_error db.error
+
+assert drop_table: db.execute("DROP TABLE users") else print db.error
+
+db.finish
+
+assert db.is_closed else print db.error
index c300f42..4b65c8a 100644 (file)
@@ -6,3 +6,7 @@ This program creates a fake model that can be used to test tools like:
 * `nitmetrics`
 * `nitx`
 * or others `modelbuilder`.
+
+An image:
+
+![Tinks3D](../../contrib/tinks/doc/tinks3d.png)
diff --git a/tests/test_prog/game/excluded.nit b/tests/test_prog/game/excluded.nit
new file mode 100644 (file)
index 0000000..d296d4b
--- /dev/null
@@ -0,0 +1 @@
+This is not a valid Nit program
diff --git a/tests/test_prog/game/excluded_dir/more.nit b/tests/test_prog/game/excluded_dir/more.nit
new file mode 100644 (file)
index 0000000..d296d4b
--- /dev/null
@@ -0,0 +1 @@
+This is not a valid Nit program
diff --git a/tests/test_prog/package.ini b/tests/test_prog/package.ini
new file mode 100644 (file)
index 0000000..bfcd472
--- /dev/null
@@ -0,0 +1,15 @@
+[package]
+name=test_prog
+version=0.1
+tags=test,game
+maintainer=John Doe <jdoe@example.com> (http://www.example.com/~jdoe), Spider-Man
+more_contributors=Riri <riri@example.com>, Fifi (http://www.example.com/~fifi), Loulou
+license=Apache-2.0
+[source]
+exclude=game/excluded.nit:game/excluded_dir
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/tests/test_prog
+git=https://github.com/nitlang/nit.git
+git.directory=tests/test_prog
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/tests/test_readline.inputs b/tests/test_readline.inputs
new file mode 100644 (file)
index 0000000..59f6fe7
--- /dev/null
@@ -0,0 +1,4 @@
+line 1
+line 2
+line 2bis
+line 3 \bine\b\b\b3\b
diff --git a/tests/test_readline.nit b/tests/test_readline.nit
new file mode 100644 (file)
index 0000000..c254b24
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import readline
+
+loop
+       var line = readline("prompt>", true)
+       if line == null then break
+       print line
+end
diff --git a/tests/test_rope_bytes.nit b/tests/test_rope_bytes.nit
new file mode 100644 (file)
index 0000000..4685f18
--- /dev/null
@@ -0,0 +1,29 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+intrude import core::text::ropes
+import core
+
+var lft = "à" + "1" * 40 + "é"
+var rht = "ć" + "2" * 40 + "ç"
+
+var rp = new Concat(lft, rht)
+
+print rp
+
+var bts = rp.bytes.to_a
+
+assert bts.length == 88
+
+print bts.join(" ")
diff --git a/tests/test_rubix_cube.nit b/tests/test_rubix_cube.nit
new file mode 100644 (file)
index 0000000..d920cc5
--- /dev/null
@@ -0,0 +1,37 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import rubix
+import combinations
+
+var commands = ["U", "L", "F", "R", "B", "D", "M", "E", "S", "u", "l", "r", "b", "d", "X", "Y", "Z"]
+
+for iters in [1 .. commands.length[ do
+       var ref_rubix = new RubixCube.solved
+       #alt1 ref_rubix = new RubixCube.scrambled
+       var combin = new CombinationCollection[String](commands, iters)
+       combin.are_sorted = true
+       combin.are_unique = true
+       for cmd in combin do
+               var rubix = new RubixCube.solved
+               #alt1 rubix = new RubixCube.scrambled
+               var revcmd = new Array[String].with_capacity(cmd.length * 2)
+               for i in cmd do revcmd.add i
+               for i in cmd.reverse_iterator do revcmd.add("{i}'")
+               print "Trying combination {revcmd}"
+               for i in revcmd do rubix.do_cmd(i)
+               assert rubix == ref_rubix
+               print "Combination {revcmd} is OK."
+       end
+end
diff --git a/tests/test_rubix_visual.args b/tests/test_rubix_visual.args
new file mode 100644 (file)
index 0000000..76f62dd
--- /dev/null
@@ -0,0 +1,34 @@
+U
+U\\'
+E
+E\\'
+D
+D\\'
+L
+L\\'
+M
+M\\'
+R
+R\\'
+F
+F\\'
+S
+S\\'
+u
+u\\'
+l
+l\\'
+f
+f\\'
+r
+r\\'
+b
+b\\'
+d
+d\\'
+X
+X\\'
+Y
+Y\\'
+Z
+Z\\'
diff --git a/tests/test_rubix_visual.nit b/tests/test_rubix_visual.nit
new file mode 100644 (file)
index 0000000..cfb8191
--- /dev/null
@@ -0,0 +1,25 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import rubix
+
+var r = new RubixCube.solved
+#alt1 r = new RubixCube.scrambled
+
+print "At beginning"
+print r
+var cmds = args
+for i in args do r.do_cmd(i)
+print "After commands '{cmds}'"
+print r
index 0573a88..1e16375 100644 (file)
@@ -29,18 +29,18 @@ var select_req = "SELECT * FROM users"
 var db = new NativeSqlite3.open(filename.to_cstring)
 assert sqlite_open: db.error.is_ok
 
-db.exec(create_req)
+db.exec(create_req.to_cstring)
 assert sqlite_create_table: db.error.is_ok
 
-db.exec(insert_req_1)
+db.exec(insert_req_1.to_cstring)
 assert sqlite_insert_1: db.error.is_ok
 
-db.exec(insert_req_2)
+db.exec(insert_req_2.to_cstring)
 assert sqlite_insert_2: db.error.is_ok
 
-var stmt = db.prepare(select_req)
+var stmt = db.prepare(select_req.to_cstring)
 assert sqlite_select: db.error.is_ok
-if stmt == null then
+if stmt.address_is_null then
        print "Prepared failed got: {db.error.to_s}"
        abort
 end
@@ -56,8 +56,8 @@ db.close
 db = new NativeSqlite3.open(filename.to_cstring)
 assert sqlite_reopen: db.error.is_ok
 
-stmt = db.prepare(select_req)
+stmt = db.prepare(select_req.to_cstring)
 assert sqlite_reselect: db.error.is_ok
-assert stmt != null
+assert not stmt.address_is_null
 stmt.step
 assert sqlite_column_0_0_reopened: stmt.column_text(0).to_s == "Bob"
index c2393aa..ed7d73d 100644 (file)
@@ -75,3 +75,6 @@ print("test.has_suffix(\"bt\") => {test.has_suffix("bt")}")
 print("test.has_suffix(\"bat\") => {test.has_suffix("bat")}")
 print("test.has_suffix(\"foot\") => {test.has_suffix("foot")}")
 
+print "........"
+print "/".substring(7, 1)
+print "........"
index 84ba34a..d11c60a 100755 (executable)
@@ -21,6 +21,8 @@
 export LANG=C
 export LC_ALL=C
 export NIT_TESTING=true
+# Use the pid as a collision prevention
+export NIT_TESTING_ID=$$
 export NIT_SRAND=0
 
 unset NIT_DIR