c_src/** -diff
tests/sav/**/*.res -whitespace
+*.res -whitespace
*.patch -whitespace
*.bak
*.swp
+*.swo
*~
.project
EIFGENs
.metadata/*
.github_data
+.DS_Store
--- /dev/null
+# 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.
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>
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.
* 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
$ . 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:
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
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"
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
if test -z "$fast"; then
make -C markdown/benches
make -C ../contrib/nitcc
+ make pre-build -C ../contrib/jwrapper
fi
## EFFECTIVE BENCHS ##
--- /dev/null
+# 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
--- /dev/null
+#!/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
--- /dev/null
+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");
+ }
+ }
+}
--- /dev/null
+# 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)
--- /dev/null
+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) }
+}
--- /dev/null
+# 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
--- /dev/null
+import sys
+from pandas import read_csv
+
+csv = read_csv(sys.argv[1])
--- /dev/null
+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)
--- /dev/null
+require 'csv'
+
+CSV.read(ARGV.first)
--- /dev/null
+inputs/*.json
all:
./bench_json.sh
+
+check:
+ ./bench_json.sh -n 1 --fast
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
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"
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]}"
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
# 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
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
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
--- /dev/null
+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/
-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
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 = [
-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
# 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
# See the License for the specific language governing permissions and
# limitations under the License.
-import ::android::platform
+import ::android
import ::android::vibration
import asteronits
# 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
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
# 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
-src/benitlux_restful.nit
+src/server/benitlux_restful.nit
*.db
*.email
benitlux_corrections.txt
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/
-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/>
- [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
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="item_background">#000000</color>
+</resources>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+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.
+.
[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
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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
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
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
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
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
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
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
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`
# 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
# 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)")
# limitations under the License.
# Web server for Benitlux
-module benitlux_web
+module server
import benitlux_model
import benitlux_view
# 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)
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
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
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
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/
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)
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
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
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
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
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
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
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/
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
--- /dev/null
+# 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
--- /dev/null
+# 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")
--- /dev/null
+[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
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
-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
# 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
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
# 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
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
# 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
# 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}
# 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
# 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
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
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)
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)
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
# note: the DFA is not minimized.
fun to_dfa: Automaton
do
+ assert_valid
+
trim
var dfa = new Automaton.empty
{plus:} re3 '+' |
{shortest:} 'Shortest' '(' re ')' |
{longest:} 'Longest' '(' re ')' |
+ {prefixes:} 'Prefixes' '(' re ')' |
{id:} id |
{par:} '(' re ')' |
{class:} text '.' '.' text |
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,
t_except,
t_shortest,
t_longest,
+ t_prefixes,
t_ch_dec,
t_ch_hex])
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)
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
--- /dev/null
+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;
--- /dev/null
+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)=''
all:
mkdir -p bin/
- ../../bin/nitc src/nitester.nit -o bin/nitester
+ nitc src/nitester.nit -o bin/nitester
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
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
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`.
# 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
# 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.
# 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.
# 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
# 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`.
# 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`.
# 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`.
# 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`.
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
#
# 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
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
# 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
# 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
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
# 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
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
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
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
# 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
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
# 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
# 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
fun is_index: Bool do return name == "index"
redef fun href do
+ var parent = self.parent
if parent == null then
return "{name}.html"
else
# 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
# limitations under the License.
# Test tools for NitRPG.
-module test_helper is test_suite
+module test_helper
import test_suite
import game
# 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
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
# 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
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/
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
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/
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
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
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)
# 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
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
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
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
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
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
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
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
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
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
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
refund:
mkdir -p bin
- ../../bin/nitc src/refund.nit -o bin/refund
+ nitc src/refund.nit -o bin/refund
check: refund
cd tests; make
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
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
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
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
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
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
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
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
{
- "message": "Wrong type for `soin` (expected Int got FlatString)"
+ "message": "Wrong type for `soin` (expected Int got ASCIIFlatString)"
}
all:
mkdir -p bin/
- ../../bin/nitc --dir bin/ src/*.nit
+ nitc --dir bin/ src/*.nit
-NITC=../../bin/nitc
+NITC=nitc
NITCC=../nitcc/src/nitcc
all: simplan
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
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
# 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
# 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
# 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
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
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
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
build:
mkdir -p bin/
- ../../bin/nitc --dir bin/ src/*.nit
+ nitc --dir bin/ src/*.nit
install:
install bin/sort_downloads /usr/local/bin/
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/
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)
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)
# 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
--- /dev/null
+# 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
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
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
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
# 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
[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
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
# 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`
redef fun on_create
do
# Create the main window
- window = new TnitterWindow
+ push_window new TnitterWindow
super
end
end
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
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
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
import tnitter_app
-import android::ui
-import android::http_request
+import android
import android::portrait
redef class LabelAuthor
--- /dev/null
+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
--- /dev/null
+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/.
--- /dev/null
+[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
# 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.
# Use actions defined by contribs
import tnitter
import benitlux::benitlux_controller
+import benitlux::benitlux_restful
import opportunity::opportunity_controller
import nitiwiki::wiki_edit
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
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")
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
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
# ---
# 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
* `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
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)
[package]
name=calculator
-tags=example
+tags=example,mobile
maintainer=Alexis Laferrière <alexis.laf@xymus.net>
license=Apache-2.0
[upstream]
--- /dev/null
+# 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
# 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"
if debug then print "App::on_create"
# Create the main window
- window = new CalculatorWindow
+ push_window new CalculatorWindow
super
end
end
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
# 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
for op in row do
var but = new Button(parent=row_layout, text=op)
- but.observers.add self
buttons[op] = but
end
end
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
# 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 ' '
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
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
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 "-"
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 "-"
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 "-"
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
--- /dev/null
+# 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
--- /dev/null
+# 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
# 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.
# 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)
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.
import android.app.Activity;
import android.os.Bundle;
+import android.view.KeyEvent;
/*
* Entry point to Nit applications on Android, redirect most calls to Nit
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
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);
+ }
}
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
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
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.
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
--- /dev/null
+# 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
module input_events
import mnit::input
-import android
+import android::game
in "C header" `{
#include <android/log.h>
`}
fun action: AMotionEventAction `{ return AMotionEvent_getAction(self); `}
+
+ fun native_down_time: Int `{ return AMotionEvent_getDownTime(self); `}
end
private extern class AMotionEventAction `{ int32_t `}
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
--- /dev/null
+# 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;
+`}
android_manifest_activity """android:screenOrientation="sensorLandscape" """
end
-import platform
+import android
import platform
import log
import activities
+import key_event
import bundle
import dalvik
{
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`
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;
`}
# 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
android:screenOrientation="portrait"
"""
-import platform
+import android
# 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" `{
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); `}
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);
# 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:
#
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
else // if (align > 0.5d)
g = android.view.Gravity.RIGHT;
- view.setGravity(g);
+ view.setGravity(g | android.view.Gravity.CENTER_VERTICAL);
`}
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
};
`}
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
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
* 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:
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`.
* 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:
* `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
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.
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
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
+
+ # ...
~~~
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)
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)
--- /dev/null
+http_request_example
+ui_example
+*.apk
+*.app
--- /dev/null
+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
--- /dev/null
+# 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
--- /dev/null
+# 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
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
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
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
# 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`
#
var maybe_code: nullable Int
# The status code
+ #
# Require: `not is_error`
fun code: Int do return maybe_code.as(not null)
end
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
sender_thread.after
end
end
+
+private class RestRunnableJoin
+ super HttpRequestTask
+
+ redef fun main do sender_thread.join
+end
[package]
name=app
-tags=lib
+tags=lib,mobile
maintainer=Alexis Laferrière <alexis.laf@xymus.net>
license=Apache-2.0
[upstream]
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
# 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
# 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
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`
# 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`
# 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`
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
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
# 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.
# 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
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
# 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
# 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
module bucketed_game is serialize
import serialization
+import counter
# Something acting on the game
abstract class Turnable[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
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
# 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`.
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 )
# 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
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
# 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
# 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
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
# 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
#
# assert "<STRING/&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
# 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
fun binarydigest_to_bytes: Bytes
do
var b = bytes
- var max = bytelen
+ var max = byte_length
# Count bits
var bitlen = 0
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
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
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
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
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
#
# 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.
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)
if _length <= index then
_length = index + 1
end
- _items[index] = item
+ _items.as(not null)[index] = item
end
redef fun add(item)
enlarge(l + 1)
end
_length = l + 1
- _items[l] = item
+ _items.as(not null)[l] = item
end
# Slight optimization for arrays
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
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
# 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
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
# 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
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
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
# 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
# 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
# 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
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
# 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
redef fun next
do
- _node = _node.prev
+ _node = _node.as(not null).prev
_index -= 1
end
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
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
# ~~~
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
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
# 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
# * `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
# 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
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
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
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
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}")
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")
_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
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
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
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
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
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
# 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)
return self
end
end
+
+ # Is `self` an ASCII whitespace ?
+ fun is_whitespace: Bool do return self == 0x7Fu8 or self <= 0x20u8
end
# Native integer numbers.
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
# ~~~
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
# 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
# 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
# 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
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
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
# 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
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
# 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.
#
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('_')
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
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
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)
# 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
# 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
redef var length = 0
- redef var bytelen = 0
+ redef var byte_length = 0
redef fun output
do
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.
# 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
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
# 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")
# 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
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
# 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.
# 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,
# 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]
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
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
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
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
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
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
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)
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
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
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
#
# `_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)
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
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
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
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
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]
# 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
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)
# 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)
# 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
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
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)
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.
#
# 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
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
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)
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]
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
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
#
# 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
continue
end
var tmp = itsi.to_s
- sl += tmp.bytelen
+ sl += tmp.byte_length
na[mypos] = tmp
i += 1
mypos += 1
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
var sl = 0
var mypos = 0
while i < l do
- sl += na[i].bytelen
+ sl += na[i].byte_length
i += 1
mypos += 1
end
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
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
#
# 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
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)
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
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
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)
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
end
end
_flat_last_pos_start = st - pos
- _flat_last_pos_end = st - pos + s.length - 1
_flat_cache = s
return s
end
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
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`
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 = ""
end
# Length of the complete rope in bytes
- redef var bytelen = 0
+ redef var byte_length = 0
# Length of the mutable part (in bytes)
#
self.str = str
ns = new NativeString(maxlen)
buf_size = maxlen
- _bytelen = str.length
+ _byte_length = str.length
dumped = 0
end
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)
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)
redef fun clear do
str = ""
- _bytelen = 0
+ _byte_length = 0
rpos = 0
dumped = 0
if written then
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
end
ns.set_char_at(rp, c)
rp += cln
- _bytelen += cln
+ _byte_length += cln
rpos = rp
end
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
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
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
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]
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
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
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
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
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)
# 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
# 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
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
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
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
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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+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=
--- /dev/null
+# 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
--- /dev/null
+[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
--- /dev/null
+# 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
# 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`
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
--- /dev/null
+# 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
[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
--- /dev/null
+# 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
# 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.
# ~~~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")
+++ /dev/null
-# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+[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
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
intrude import more_materials
import more_models
+import model_dimensions
import particles
+import selection
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
# 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
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
--- /dev/null
+# 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
# 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
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)
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
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
import geometry
import depth_core
+import model_dimensions
# Simple flat mesh, sits on the axes X and Z, normal on Y
class Plane
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
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
# 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
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
--- /dev/null
+# 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
# 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.
android_manifest """<uses-feature android:glEsVersion="0x00020000"/>"""
end
-import ::android
+import ::android::game
intrude import android::load_image
private import gamnit::egl
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)
redef fun close do close_egl
end
-redef class GamnitAssetTexture
+redef class TextureAsset
redef fun load_from_platform
do
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
end
end
-redef class GamnitAssetTexture
+redef class TextureAsset
redef fun load_from_platform
do
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
# 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
# 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
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`
#
# 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
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`.
# 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
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
# 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
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
end
# Texture loaded from the assets folder
-class GamnitAssetTexture
+class TextureAsset
super GamnitRootTexture
# Path to this texture within the `assets` folder
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
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
# 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; `}
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 `{
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 `{
`}
# 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.
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 `{
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 `{
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 `{
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 `{
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 `{
# 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 `{
`}
# 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
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 `{
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 `{
@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
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
redef type NATIVE: UIView
redef var enabled = null is lazy
+
+ private fun on_ios_event do end
end
redef class CompositeControl
init
do
native.alignment = new UIStackViewAlignment.fill
- native.distribution = new UIStackViewDistribution.fill_equally
# TODO make customizable
native.spacing = 4.0
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
# `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
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
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:)
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
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
# ~~~
module collections
-import base
+import ffi_support
# Java primitive array
#
# 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
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`
# 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;
# 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
# 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
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
# 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:
#
# * 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.
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
end
first_attribute = true
- object.serialize_to_json self
+ object.accept_json_serializer self
first_attribute = false
if plain_json then open_objects.pop
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 "\": "
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.
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.
#
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
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"]
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]
# 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
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\": \""
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]
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
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
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
# 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
# "\"\\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
"\"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
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
# 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
# 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
args.options = c_options
- var jvm = new JavaVM(args, self)
+ var jvm = new JavaVM(args)
args.free
c_options.free
# 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);
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)
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
`}
end
-# used to initialize a JavaVM
-class JniEnvRef
- var jni_env: nullable JniEnv = null
-end
-
# Represents a jni jclass
extern class JClass `{jclass`}
end
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
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
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)
# 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`
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); `}
# 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`
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
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
app.on_start
app.on_resume
- native_window.show_all
gtk_main
app.on_pause
# 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
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("")
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
super
end
end
+
+redef class Text
+ redef fun open_in_browser do system("xdg-open '{self.escape_to_sh}' &")
+end
--- /dev/null
+# 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<rue) == 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
--- /dev/null
+# 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
--- /dev/null
+[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
-NITC=../../bin/nitc
+NITC=nitc
all: nitmd
import mnit
import mnit::opengles1
-import ::android
+import ::android::game
intrude import ::android::input_events
in "C" `{
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
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
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
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)
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_.
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
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
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)
# 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
# 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
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">
</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
# 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
# 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
# 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
# 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`
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"
end
end
+# MIME types list.
fun media_types: MediaTypes do return once new MediaTypes
# 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
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
# 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
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
# Parameters match everything.
redef fun match(part) do return true
+
+ redef fun to_s do return name
end
# A static uri string like `users`.
# 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
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
# 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
--- /dev/null
+# 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) .
--- /dev/null
+# 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.
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+<!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>
--- /dev/null
+/*
+ * 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);
+ });
+})();
--- /dev/null
+<h1>Nit ♥ 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>
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+# 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)
--- /dev/null
+# 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)
# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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
--- /dev/null
+
+[Client] curl -s localhost:*****/
+Hello World!
+[Client] curl -s localhost:*****/about
+An error occurred!
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <body>
+ <h1>Another Index</h1>
+ </body>
+</html>
--- /dev/null
+body {
+ color: blue;
+ padding: 20px;
+}
--- /dev/null
+<!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>
--- /dev/null
+<!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>
--- /dev/null
+alert("Hello World!");
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+# 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
--- /dev/null
+
+[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
--- /dev/null
+[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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+
+[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>
--- /dev/null
+
+[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>
--- /dev/null
+# 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
--- /dev/null
+[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
--- /dev/null
+# 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
# 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
`}
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".
--- /dev/null
+[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
--- /dev/null
+# 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
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.
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;
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
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
--- /dev/null
+[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
--- /dev/null
+# 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
# 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)
# 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)
# 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.
#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`}
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
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`
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
`}
# 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 `{
# 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
# 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
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
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
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 `'`
#
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
# 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.
#
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
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]
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}"
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:"
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
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
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
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
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
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
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
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
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
--- /dev/null
+# 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
--- /dev/null
+# 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>
--- /dev/null
+# 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" ]
--- /dev/null
+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"]
--- /dev/null
+print "hello"
nit [*options*] FILE [ARG]...
+nit [*options*] - [ARG]...
+
nit [*options*] -e COMMAND [ARG]...
# DESCRIPTION
$ 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.
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.
### `--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.
### `--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
### `--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.
## CUSTOMIZATION
-### `--sharedir`
-Directory containing nitdoc assets.
+### `--share-dir`
+Directory containing tools assets.
By default `$NIT_DIR/share/nitdoc/` is used.
$ 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
$ 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
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
### `-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.
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
### `--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>
--- /dev/null
+<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&default=retro">
+ {{contributor.name}}
+ </li>
+ </ul>
+</div>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<div class='card' ng-if='mentity.mdoc'>
+ <div class='card-body'>
+ <div ng-bind-html='mentity.mdoc.html_documentation'></div>
+ </div>
+</div>
--- /dev/null
+<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>
--- /dev/null
+<span>
+ <a ng-href='{{mentity.web_url}}'>{{mentity.name}}</a>
+</span>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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}}
--- /dev/null
+<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>
--- /dev/null
+<span class="glyphicon glyphicon-tag" ng-class='{
+ "text-success": mentity.visibility == "public",
+ "text-warning": mentity.visibility == "protected",
+ "text-danger": mentity.visibility == "private",
+}' />
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<form class='form-inline'>
+ <ui-filter-field property='searchFilter.$' />
+ <div class='pull-right'>
+ <ui-filter-group-vis property='visibilityFilter' />
+ </div>
+</form>
--- /dev/null
+<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>
--- /dev/null
+<!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>
--- /dev/null
+/*
+ * 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();
+ }])
+})();
--- /dev/null
+/*
+ * 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');
+ }
+ }
+ };
+ }
+ };
+ }])
+})();
--- /dev/null
+/*
+ * 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'
+ };
+ }])
+})();
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }])
+})();
--- /dev/null
+/*
+ * 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);
+ });
+})();
--- /dev/null
+/*
+ * 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;
+ }
+ }
+ };
+ })
+})();
--- /dev/null
+/* 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 */
--- /dev/null
+/*! 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
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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> <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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
# 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]
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
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
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
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
# 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
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:
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);")
# 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)
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.
# 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.
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
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)))
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
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
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
import separate_erasure_compiler
import global_compiler
import compiler_ffi
+import memory_logger
import platform::android
import platform::pnacl
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)
--- /dev/null
+# 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
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};")
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}")
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}")
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}")
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")
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
# * `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`.
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
class CodeCommand
super AbstractDocCommand
end
+
+# A `DocCommand` that display an graph for a `MEntity`.
+#
+# Syntax:
+# * `graph: MEntity::name`
+class GraphCommand
+ super AbstractDocCommand
+end
# 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
# 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"
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
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
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,
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}/")
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.
# * 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
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.
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
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
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.
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
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.
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
--- /dev/null
+# 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!)
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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)
# 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")
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")
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
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 */
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
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
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
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
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
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
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
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")
end
redef class MSignature
- redef fun linkto
+ redef fun linkto(v)
do
var res = new HTMLTag("span")
var first = true
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
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
return res
end
- redef fun linkto
+ redef fun linkto(v)
do
- return mpropdef.linkto
+ return mpropdef.linkto(v)
end
end
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
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
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)
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
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")
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")
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
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
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
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
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
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
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
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
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
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
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)
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
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
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
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
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
# Workaround to tag strings
res.classes.remove("nc_l")
res.add_class("nc_s")
- return null
+ return super
end
end
redef class AExpr
end
# External compiler used to generate the foreign code library
- private var c_compiler = "gcc"
+ private var c_compiler = "cc"
end
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
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
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}"
# 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
# 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
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
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)
# 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)
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
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
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.
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}`.")
# 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.
# 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)
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?
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)
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
# 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
# Filter out non-directories
if not stat.is_dir then
+ last_loader_error = "Error: `{dirpath}` is not a directory."
return null
end
# 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
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
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
mdoc.original_mentity = mgroup
end
- mgroup.filepath = dirpath
mgroups[rdp] = mgroup
return mgroup
end
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
# 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}`.")
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
#
# 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
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
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
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
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
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
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})")
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
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
# 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")
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
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
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
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)
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)
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
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
# modules and module hierarchies in the metamodel
module mmodule
-import location
import mpackage
private import more_collections
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`
# 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
# 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}"
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]
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
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`
# The visibility of the class
# In Nit, the visibility of a class cannot evolve in refinements
- var visibility: MVisibility
+ redef var visibility
init
do
# 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.
# 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
# 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
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
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
# 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
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
# 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
# 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
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
# 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
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`
# * 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
# 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,
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?
# 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.
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.
# 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
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
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
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
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
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]
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
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]
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
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
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
--- /dev/null
+# 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
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.
# 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
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.
#
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
# 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
# empty name for a default group in a single-module package
redef var name: String
+ redef var location
+
# The enclosing package
var mpackage: MPackage
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`)
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
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
# 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
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
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.
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
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
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.
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.")
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}.")
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
# 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]
# 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)
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
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
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
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
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
# 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)
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
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
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
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)
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
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
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]
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
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
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(",")}"
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"
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
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
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
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>"
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"
# 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
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
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 = """
# PAGES
-for p in model.mpackages do
+for p in mpackages do
# print p
var f = "p/{p.name}.html"
catalog.package_page(p)
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)
<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>
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")
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
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
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
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
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
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)
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"
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
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
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
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
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*.
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
# 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
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
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
add_cast(paramtype)
end
+ if mmethoddef.is_abstract then continue
+
var npropdef = modelbuilder.mpropdef2node(mmethoddef)
if npropdef isa AClassdef then
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(", ")}"
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
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)
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">
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>"
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>"
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.
v.include_fictive = true
v.enter_visit(model)
v.cpt.print_elements(10)
+ var names = v.names
print "All entities:"
v = new TestModelVisitor
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
import modelize
private import parser_util
+import html
+import console
redef class ToolContext
# opt --full
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
import testing_base
import markdown
import html
+import realtime
# Extractor, Executor and Reporter for the tests in a module
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
# 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.
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
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
# 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)
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`.
# 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
# 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
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
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
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
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
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
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
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.
# `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
# 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.
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
# 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
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.
# 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
# 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
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
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
# 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
# * 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
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
# 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
# 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
# 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
# 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")
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
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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
# 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
+++ /dev/null
-# 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
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
+++ /dev/null
-# 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
mpi
emscripten
ui_test
+readline
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
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
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)
--- /dev/null
+# 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
# 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
# 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
--- /dev/null
+# 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
--- /dev/null
+names A P P1 names::n0::ZZ
#!/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/
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
--- /dev/null
+# 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
--- /dev/null
+Group of modules used to test various full_name configurations and conflicts.
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
-e 'print "hello world"'
-n -e 'print line' test_prog/README.md test_prog/test_prog.nit
test_ffi_c_interpreter.nit
+-
--- /dev/null
+print "Hello!"
--- /dev/null
+test_prog --no-git -d "$WRITE" ; cat "$WRITE/p/test_prog.html"
nit_args4
nit_args5
nit_args6
+nit_args8
nitvm_args1
nitvm_args3
nitc_args1
test_text_stat
nitsaf_args
test_ffi_c_lots_of_refs
+test_rubix_cube
+test_rubix_visual
+test_csv
+repeating_key_xor_solve
-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
--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
-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
nit_args4
nit_args5
nit_args6
+nit_args8
nitvm_args1
nitvm_args3
nitc_args1
test_text_stat
nitsaf.args
test_ffi_c_lots_of_refs
+test_rubix_visual
+test_rubix_cube
+test_csv
+repeating_key_xor_solve
--- /dev/null
+import module0
--- /dev/null
+../lib/crapto/examples/repeating_key_xor_cipher.txt
--- /dev/null
+# 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)
-FlatString
+ASCIIFlatString
Int
Test
Test
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
--- /dev/null
+B[B[Object]]
-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=`
--- /dev/null
+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
+
--- /dev/null
+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
+
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
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
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
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]]`.
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]]`.
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
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
--- /dev/null
+1
+1
+100
+1
+10
+110
--- /dev/null
+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`.
--- /dev/null
+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`?
--- /dev/null
+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`.
--- /dev/null
+B1
+B5
+HB5
+HB1
+HB5
+HB1
+HB5
+HB1
+HHB5
+HHB1
+HHB5
+HHB1
--- /dev/null
+1
+-2
+2
+-3
+1
+10
+-2
+10
+100
+-2
+100
--- /dev/null
+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.
--- /dev/null
+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.
-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`?
-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`?
-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`?
-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`?
-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`?
-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`?
-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`?
-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`?
-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`?
-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`?
-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`?
-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`.
-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`.
-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`.
-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`.
-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`.
-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`.
-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`.
-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`.
-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`?
--- /dev/null
+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`?
-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
-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`.
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`.
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`.
-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`?
--- /dev/null
+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`?
-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`?
--- /dev/null
+Usage: get_mclasses file qualified_class_name...
+Use --help for help
--- /dev/null
+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
* `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");
--- /dev/null
+<!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://nitlanguage.org">http://nitlanguage.org</a></li>
+<li><a href="http://www.example.com/~jdoe"><img src="https://secure.gravatar.com/avatar/694ea0904ceaf766c6738166ed89bafb?size=20&default=retro"> 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://github.com/nitlang/nit/tree/master/tests/test_prog">https://github.com/nitlang/nit/tree/master/tests/test_prog</a></li>
+<li><tt>https://github.com/nitlang/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://www.example.com/~jdoe"><img src="https://secure.gravatar.com/avatar/694ea0904ceaf766c6738166ed89bafb?size=20&default=retro"> John Doe</a></li><li><img src="https://secure.gravatar.com/avatar/db3f2909768694ad2bb6409b44627182?size=20&default=retro"> Riri</li><li>Fifi (http://www.example.com/~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
-FlatString
+ASCIIFlatString
Int
Test
Test
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)
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]]`.
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]]`.
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)
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)
-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
-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
-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
<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>
<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>
<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>>
<- 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 ->
<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>
<E: 2222>
# Json:
-{"__kind": "obj", "__id": 0, "__class": "F", "n": 2222}
+{"__kind": "obj", "__id": 0, "__class": "F","n": 2222}
# Back in Nit:
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
<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: >
--- /dev/null
+# 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: >
+
-FlatString
-FlatString
+ASCIIFlatString
+ASCIIFlatString
Class
Class
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
--- /dev/null
+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]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
--- /dev/null
+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]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
--- /dev/null
+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]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
+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
#### 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
</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="<a href="#base_simple3$Object">class Object</a>" data-content="<div><b>class</b> <span>Object</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> sub-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3$Bool">base_simple3$Bool</a></li><li><a href="#base_simple3$Int">base_simple3$Int</a></li><li><a href="#base_simple3$A">base_simple3$A</a></li><li><a href="#base_simple3$B">base_simple3$B</a></li><li><a href="#base_simple3$C">base_simple3$C</a></li><li><a href="#base_simple3$Sys">base_simple3$Sys</a></li></ul></div></div>" 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="<a href="#base_simple3$Bool">class Bool</a>" data-content="<div><b>class</b> <span>Bool</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3$Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="base_simple3.html#base_simple3#Int#output">base_simple3#Int#output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="#base_simple3$Int">class Int</a>" data-content="<div><b>class</b> <span>Int</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3$Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3$Int$output">base_simple3$Int$output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#A#run">base_simple3#A#run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3$A">class A</a>" data-content="<div><b>class</b> <span>A</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3$Object">base_simple3$Object</a></li></ul></div></div>" 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="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">5</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3$A$run">base_simple3$A$run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">6</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3$B">class B</a>" data-content="<div><b>class</b> <span>B</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3$Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3$B$val">base_simple3$B$val</a>" data-content="<div><b>fun</b> <span>val<span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>1 message(s)</b> <span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li>Warning: init with signature in base_simple3$B</li></ul></div></div>" data-toggle="popover">init</span><span>(</span><span class="nc_i nc_v popupable" title="v: Int" data-content="<div><b>local var</b> <span>v:<a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">v</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">7</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L39"> <span class="nc_k popupable" title="B" data-content="<div><b>class</b> <span><a href="#base_simple3$B">base_simple3$B</a></span><br/></div>" data-toggle="popover">self</span><span class="popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3$B$val=">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$val=">base_simple3$B$val=</a><span>(val: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3$B$val=">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$val=">base_simple3$B$val=</a><span>(val: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">val</span> <span class="popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3$B$val=">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$val=">base_simple3$B$val=</a><span>(val: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">=</span> <span class="nc_i nc_v popupable" title="v: Int" data-content="<div><b>local var</b> <span>v:<a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#B#run">base_simple3#B#run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3$B$run">base_simple3$B$run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3$B$val">call base_simple3$B$val</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$val">base_simple3$B$val</a><span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3$C">class C</a>" data-content="<div><b>class</b> <span>C</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3$Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3$C$val1">base_simple3$C$val1</a>" data-content="<div><b>fun</b> <span>val1<span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3$C$val2">base_simple3$C$val2</a>" data-content="<div><b>fun</b> <span>val2<span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#foo">base_simple3#Sys#foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#bar">base_simple3#Sys#bar</a>" data-content="<div><b>fun</b> <span>bar<span>(i: <a href="base_simple3.html#base_simple3#Int">Int</a>)</span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#baz">base_simple3#Sys#baz</a>" data-content="<div><b>fun</b> <span>baz<span>: <a href="base_simple3.html#base_simple3#Int">Int</a></span></span><br/></div>" 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="<a href="#base_simple3$Sys$foo">base_simple3$Sys$foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">2</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3$Sys$bar">base_simple3$Sys$bar</a>" data-content="<div><b>fun</b> <span>bar<span>(i: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_i nc_v popupable" title="i: Int" data-content="<div><b>local var</b> <span>i:<a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">i</span><span>:</span> <span class="nc_t popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>i:<a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">i</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3$Sys$baz">base_simple3$Sys$baz</a>" data-content="<div><b>fun</b> <span>baz<span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">1</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L54"><span class="nc_i popupable" title="call base_simple3$Sys$foo" data-title="<a href="#base_simple3$Sys$foo">call base_simple3$Sys$foo</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Sys$foo">base_simple3$Sys$foo</a><span></span></span><br/></div>" data-toggle="popover">foo</span>
+</span><span class="line" id="L55"><span class="nc_i popupable" title="call base_simple3$Sys$bar" data-title="<a href="#base_simple3$Sys$bar">call base_simple3$Sys$bar</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Sys$bar">base_simple3$Sys$bar</a><span>(i: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">bar</span><span>(</span><span class="nc_l popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3$Sys$baz">call base_simple3$Sys$baz</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Sys$baz">base_simple3$Sys$baz</a><span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">baz</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>a:<a href="#base_simple3$A">base_simple3$A</a></span><br/></div>" data-toggle="popover">a</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$A$autoinit" data-title="<a href="#base_simple3$A$autoinit">call base_simple3$A$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$A$autoinit">base_simple3$A$autoinit</a><span></span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="A" data-content="<div><b>class</b> <span><a href="#base_simple3$A">base_simple3$A</a></span><br/></div>" data-toggle="popover">A</span>
+</span><span class="line" id="L59"><span class="nc_i nc_v popupable" title="a: A" data-content="<div><b>local var</b> <span>a:<a href="#base_simple3$A">base_simple3$A</a></span><br/></div>" data-toggle="popover">a</span><span class="popupable" title="call base_simple3$A$run" data-title="<a href="#base_simple3$A$run">call base_simple3$A$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$A$run">base_simple3$A$run</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$A$run" data-title="<a href="#base_simple3$A$run">call base_simple3$A$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$A$run">base_simple3$A$run</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>b:<a href="#base_simple3$B">base_simple3$B</a></span><br/></div>" data-toggle="popover">b</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$B$autoinit" data-title="<a href="#base_simple3$B$autoinit">call base_simple3$B$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$autoinit">base_simple3$B$autoinit</a><span>(v: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="B" data-content="<div><b>class</b> <span><a href="#base_simple3$B">base_simple3$B</a></span><br/></div>" data-toggle="popover">B</span><span>(</span><span class="nc_l popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>b:<a href="#base_simple3$B">base_simple3$B</a></span><br/></div>" data-toggle="popover">b</span><span class="popupable" title="call base_simple3$B$run" data-title="<a href="#base_simple3$B$run">call base_simple3$B$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$run">base_simple3$B$run</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$run" data-title="<a href="#base_simple3$B$run">call base_simple3$B$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$B$run">base_simple3$B$run</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>c:<a href="#base_simple3$C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$C$autoinit" data-title="<a href="#base_simple3$C$autoinit">call base_simple3$C$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$C$autoinit">base_simple3$C$autoinit</a><span>(val1: <a href="#base_simple3$Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="C" data-content="<div><b>class</b> <span><a href="#base_simple3$C">base_simple3$C</a></span><br/></div>" data-toggle="popover">C</span><span>(</span><span class="nc_l popupable" title="Int" data-content="<div><b>class</b> <span><a href="#base_simple3$Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>c:<a href="#base_simple3$C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val1" data-title="<a href="#base_simple3$C$val1">call base_simple3$C$val1</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$C$val1">base_simple3$C$val1</a><span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val1" data-title="<a href="#base_simple3$C$val1">call base_simple3$C$val1</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$C$val1">base_simple3$C$val1</a><span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L66"><span class="nc_i nc_v popupable" title="c: C" data-content="<div><b>local var</b> <span>c:<a href="#base_simple3$C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val2" data-title="<a href="#base_simple3$C$val2">call base_simple3$C$val2</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$C$val2">base_simple3$C$val2</a><span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val2" data-title="<a href="#base_simple3$C$val2">call base_simple3$C$val2</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$C$val2">base_simple3$C$val2</a><span>: <a href="#base_simple3$Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3$Int$output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3$Int$output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
</span></span></span>
\ No newline at end of file
|--\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)
|--\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)
|--\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)
-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)
|--\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)
--- /dev/null
+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)
--- /dev/null
+\e[1mexcluded\e[m (test_prog/game/excluded.nit)
+\e[1mmore\e[m (test_prog/game/excluded_dir/more.nit)
# MModules metrics
## package base_simple3
- `- group base_simple3
+ `- group base_simple3>
mnoa: number of ancestor modules
avg: 0.0
max: base_simple3 (0)
# MClasses metrics
## package base_simple3
- `- group base_simple3
+ `- group base_simple3>
cnoa: number of ancestor classes
avg: 0.0
max: Bool (1)
# Inheritance metrics
## package base_simple3
- `- group base_simple3
+ `- group base_simple3>
cnoac: number of class_kind ancestor
avg: 0.0
max: Object (0)
# Nullable metrics
## package base_simple3
- `- group base_simple3
+ `- group base_simple3>
cnba: number of accessible attributes (inherited + local)
avg: 0.0
max: C (2)
<=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
<=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/
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];
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];
-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.<module>" name="<module>"><system-err></system-err><system-out>assert true
-</system-out></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="<class>"><system-err></system-err><system-out>assert false
-</system-out><error message="Runtime error: Assert failed (.nitunit/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/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.<module>" name="<module>" time="0.0"><system-err></system-err><system-out>assert true
+</system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="<class>" time="0.0"><error message="Runtime error in nitunit.out/test_nitunit-2.nit">Runtime error: Assert failed (nitunit.out/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/test_nitunit-3.nit">nitunit.out/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 '!'."></failure><system-out>assert !@#$%^&*()
+</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/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
-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
-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
-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="<group>"><failure message="test_nitunit3/README.md:1,0--13,0: Invalid block of code. At 1,2--4: Syntax Error: unexpected malformed character '\].."></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>"><testcase classname="nitunit.test_nitunit3.test_nitunit3.<group>" name="<group>" time="0.0"><error message="Runtime error in nitunit.out/test_nitunit3-0.nit with argument 1">Runtime error: Assert failed (nitunit.out/test_nitunit3-0.nit:7)
+</error><system-out>assert false
assert true
-</system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
-"></error></testcase></testsuite><testsuite package="test_nitunit3"><testcase classname="nitunit.test_nitunit3.<module>" name="<module>"><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.<group>" name="<group>#2" time="0.0"><failure message="Syntax Error: unexpected malformed character '\]."></failure><system-out>;'\][]
+</system-out></testcase></testsuite><testsuite package="test_nitunit3::test_nitunit3"><testcase classname="nitunit.test_nitunit3::test_nitunit3.<module>" name="<module>" time="0.0"><system-err></system-err><system-out>assert true
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
-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.<file>" 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.<file>" name="test_nitunit_md.md" time="0.0"><error message="Runtime error in nitunit.out/file-0.nit with argument 1">Runtime error: Assert failed (nitunit.out/file-0.nit:8)
+</error><system-out>var a = 1
assert 1 == 1
assert false
-</system-out><error message="Runtime error: Assert failed (.nitunit/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
-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 'garbage'.."></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 'garbage'.."></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 'garbage'.."></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 'garbage'."></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 'garbage'."></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 'garbage'."></failure><system-out>*garbage*
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
-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>"></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/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/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/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/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/gen_test_nitunit4.nit">Before Test
Tested method
After Test
Runtime error: Assert failed (test_nitunit4/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/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1">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
+</error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_sav_conflict" time="0.0"><error message="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">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
--- /dev/null
+Usage: repeating_key_xor_solve <cipher_file>
--- /dev/null
+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
+
--- /dev/null
+40
+20
+20
+a
+either Rop
s isa Bytes
StringAB
537472696E674142
-s2 isa FlatString
+s2 isa UnicodeFlatString
String𐏓
s3 isa Bytes
StringA�
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
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
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%)
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
i=5 s=Hello
i:5; s:Hello
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
-<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
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
i=5 s=Hello
i:5; s:Hello
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
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
i=5 s=Hello
i:5; s:Hello
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
-<A i: <Int> s: <FlatString>>
+<A i: <Int> s: <ASCIIFlatString>>
i=5 s=Hello
i:5; s:Hello
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
<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="<a href="base_simple3.html#base_simple3#Int#output">base_simple3#Int#output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#A#run">base_simple3#A#run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">5</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__A___run">base_simple3$A$run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">6</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___val">base_simple3$B$val</a>" data-content="<div><b>fun</b> <span>val<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___val">base_simple3$B$val</a>" data-content="<div><b>fun</b> <span>val<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___val">base_simple3$B$val</a>" data-content="<div><b>fun</b> <span>val<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>1 message(s)</b> <span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li>Warning: init with signature in base_simple3$B</li></ul></div></div>" data-toggle="popover">init</span><span>(</span><span class="nc_i nc_v popupable" title="v: Int" data-content="<div><b>local var</b> <span>v:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">v</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">7</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L39"> <span class="nc_k popupable" title="B" data-title="<a href="#base_simple3__B">B</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">self</span><span class="popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3___base_simple3__B___val_61d">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</a><span>(val: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3___base_simple3__B___val_61d">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</a><span>(val: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">val</span> <span class="popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3___base_simple3__B___val_61d">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</a><span>(val: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">=</span> <span class="nc_i nc_v popupable" title="v: Int" data-content="<div><b>local var</b> <span>v:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#B#run">base_simple3#B#run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#foo">base_simple3#Sys#foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#bar">base_simple3#Sys#bar</a>" data-content="<div><b>fun</b> <span>bar<span>(i: <a href="base_simple3.html#base_simple3#Int">Int</a>)</span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#baz">base_simple3#Sys#baz</a>" data-content="<div><b>fun</b> <span>baz<span>: <a href="base_simple3.html#base_simple3#Int">Int</a></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___run">base_simple3$B$run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___val">call base_simple3$B$val</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val">base_simple3$B$val</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a>" data-content="<div><b>fun</b> <span>val1<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a>" data-content="<div><b>fun</b> <span>val1<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a>" data-content="<div><b>fun</b> <span>val1<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a>" data-content="<div><b>fun</b> <span>val2<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a>" data-content="<div><b>fun</b> <span>val2<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a>" data-content="<div><b>fun</b> <span>val2<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___foo">base_simple3$Sys$foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">2</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___bar">base_simple3$Sys$bar</a>" data-content="<div><b>fun</b> <span>bar<span>(i: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_i nc_v popupable" title="i: Int" data-content="<div><b>local var</b> <span>i:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">i</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>i:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">i</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___baz">base_simple3$Sys$baz</a>" data-content="<div><b>fun</b> <span>baz<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">1</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L54"><span class="nc_i popupable" title="call base_simple3$Sys$foo" data-title="<a href="#base_simple3___base_simple3__Sys___foo">call base_simple3$Sys$foo</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Sys___foo">base_simple3$Sys$foo</a><span></span></span><br/></div>" data-toggle="popover">foo</span>
+</span><span class="line" id="L55"><span class="nc_i popupable" title="call base_simple3$Sys$bar" data-title="<a href="#base_simple3___base_simple3__Sys___bar">call base_simple3$Sys$bar</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Sys___bar">base_simple3$Sys$bar</a><span>(i: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">bar</span><span>(</span><span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___baz">call base_simple3$Sys$baz</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Sys___baz">base_simple3$Sys$baz</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">baz</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>a:<a href="#base_simple3___base_simple3__A">base_simple3$A</a></span><br/></div>" data-toggle="popover">a</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$A$autoinit" data-title="<a href="#base_simple3___base_simple3__A___autoinit">call base_simple3$A$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__A___autoinit">base_simple3$A$autoinit</a><span></span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="A" data-title="<a href="#base_simple3__A">A</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__A">base_simple3$A</a></span><br/></div>" data-toggle="popover">A</span>
+</span><span class="line" id="L59"><span class="nc_i nc_v popupable" title="a: A" data-content="<div><b>local var</b> <span>a:<a href="#base_simple3___base_simple3__A">base_simple3$A</a></span><br/></div>" data-toggle="popover">a</span><span class="popupable" title="call base_simple3$A$run" data-title="<a href="#base_simple3___base_simple3__A___run">call base_simple3$A$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__A___run">base_simple3$A$run</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$A$run" data-title="<a href="#base_simple3___base_simple3__A___run">call base_simple3$A$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__A___run">base_simple3$A$run</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>b:<a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">b</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$B$autoinit" data-title="<a href="#base_simple3___base_simple3__B___autoinit">call base_simple3$B$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___autoinit">base_simple3$B$autoinit</a><span>(v: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="B" data-title="<a href="#base_simple3__B">B</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">B</span><span>(</span><span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>b:<a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">b</span><span class="popupable" title="call base_simple3$B$run" data-title="<a href="#base_simple3___base_simple3__B___run">call base_simple3$B$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___run">base_simple3$B$run</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$run" data-title="<a href="#base_simple3___base_simple3__B___run">call base_simple3$B$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___run">base_simple3$B$run</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>c:<a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$C$autoinit" data-title="<a href="#base_simple3___base_simple3__C___autoinit">call base_simple3$C$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___autoinit">base_simple3$C$autoinit</a><span>(val1: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="C" data-title="<a href="#base_simple3__C">C</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">C</span><span>(</span><span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>c:<a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val1" data-title="<a href="#base_simple3___base_simple3__C___val1">call base_simple3$C$val1</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val1" data-title="<a href="#base_simple3___base_simple3__C___val1">call base_simple3$C$val1</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L66"><span class="nc_i nc_v popupable" title="c: C" data-content="<div><b>local var</b> <span>c:<a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val2" data-title="<a href="#base_simple3___base_simple3__C___val2">call base_simple3$C$val2</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val2" data-title="<a href="#base_simple3___base_simple3__C___val2">call base_simple3$C$val2</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Object">class Object</a>" data-content="<div><b>class</b> <span>Object</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> sub-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Bool">base_simple3$Bool</a></li><li><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></li><li><a href="#base_simple3___base_simple3__A">base_simple3$A</a></li><li><a href="#base_simple3___base_simple3__B">base_simple3$B</a></li><li><a href="#base_simple3___base_simple3__C">base_simple3$C</a></li><li><a href="#base_simple3___base_simple3__Sys">base_simple3$Sys</a></li></ul></div></div>" 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="<a href="#base_simple3___base_simple3__Bool">class Bool</a>" data-content="<div><b>class</b> <span>Bool</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="base_simple3.html#base_simple3#Int#output">base_simple3#Int#output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Int">class Int</a>" data-content="<div><b>class</b> <span>Int</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#A#run">base_simple3#A#run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__A">class A</a>" data-content="<div><b>class</b> <span>A</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">5</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__A___run">base_simple3$A$run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">6</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B">class B</a>" data-content="<div><b>class</b> <span>B</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3___base_simple3__B___val">base_simple3$B$val</a>" data-content="<div><b>fun</b> <span>val<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>1 message(s)</b> <span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li>Warning: init with signature in base_simple3$B</li></ul></div></div>" data-toggle="popover">init</span><span>(</span><span class="nc_i nc_v popupable" title="v: Int" data-content="<div><b>local var</b> <span>v:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">v</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">7</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L39"> <span class="nc_k popupable" title="B" data-title="<a href="#base_simple3__B">B</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">self</span><span class="popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3___base_simple3__B___val_61d">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</a><span>(val: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3___base_simple3__B___val_61d">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</a><span>(val: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">val</span> <span class="popupable" title="call base_simple3$B$val=" data-title="<a href="#base_simple3___base_simple3__B___val_61d">call base_simple3$B$val=</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val_61d">base_simple3$B$val=</a><span>(val: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">=</span> <span class="nc_i nc_v popupable" title="v: Int" data-content="<div><b>local var</b> <span>v:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#B#run">base_simple3#B#run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___run">base_simple3$B$run</a>" data-content="<div><b>fun</b> <span>run<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__B___val">call base_simple3$B$val</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___val">base_simple3$B$val</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C">class C</a>" data-content="<div><b>class</b> <span>C</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> super-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Object">base_simple3$Object</a></li></ul></div></div>" 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="<a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a>" data-content="<div><b>fun</b> <span>val1<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a>" data-content="<div><b>fun</b> <span>val2<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">Int</span> <span>=</span> <span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#foo">base_simple3#Sys#foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#bar">base_simple3#Sys#bar</a>" data-content="<div><b>fun</b> <span>bar<span>(i: <a href="base_simple3.html#base_simple3#Int">Int</a>)</span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#baz">base_simple3#Sys#baz</a>" data-content="<div><b>fun</b> <span>baz<span>: <a href="base_simple3.html#base_simple3#Int">Int</a></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___foo">base_simple3$Sys$foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">2</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___bar">base_simple3$Sys$bar</a>" data-content="<div><b>fun</b> <span>bar<span>(i: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover"><span class="nc_i">bar</span></span><span>(</span><span class="nc_i nc_v popupable" title="i: Int" data-content="<div><b>local var</b> <span>i:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">i</span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>i:<a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">i</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___baz">base_simple3$Sys$baz</a>" data-content="<div><b>fun</b> <span>baz<span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover"><span class="nc_i">baz</span></span><span>:</span> <span class="nc_t popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">1</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L54"><span class="nc_i popupable" title="call base_simple3$Sys$foo" data-title="<a href="#base_simple3___base_simple3__Sys___foo">call base_simple3$Sys$foo</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Sys___foo">base_simple3$Sys$foo</a><span></span></span><br/></div>" data-toggle="popover">foo</span>
+</span><span class="line" id="L55"><span class="nc_i popupable" title="call base_simple3$Sys$bar" data-title="<a href="#base_simple3___base_simple3__Sys___bar">call base_simple3$Sys$bar</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Sys___bar">base_simple3$Sys$bar</a><span>(i: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">bar</span><span>(</span><span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Sys___baz">call base_simple3$Sys$baz</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Sys___baz">base_simple3$Sys$baz</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">baz</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>a:<a href="#base_simple3___base_simple3__A">base_simple3$A</a></span><br/></div>" data-toggle="popover">a</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$A$autoinit" data-title="<a href="#base_simple3___base_simple3__A___autoinit">call base_simple3$A$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__A___autoinit">base_simple3$A$autoinit</a><span></span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="A" data-title="<a href="#base_simple3__A">A</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__A">base_simple3$A</a></span><br/></div>" data-toggle="popover">A</span>
+</span><span class="line" id="L59"><span class="nc_i nc_v popupable" title="a: A" data-content="<div><b>local var</b> <span>a:<a href="#base_simple3___base_simple3__A">base_simple3$A</a></span><br/></div>" data-toggle="popover">a</span><span class="popupable" title="call base_simple3$A$run" data-title="<a href="#base_simple3___base_simple3__A___run">call base_simple3$A$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__A___run">base_simple3$A$run</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$A$run" data-title="<a href="#base_simple3___base_simple3__A___run">call base_simple3$A$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__A___run">base_simple3$A$run</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>b:<a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">b</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$B$autoinit" data-title="<a href="#base_simple3___base_simple3__B___autoinit">call base_simple3$B$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___autoinit">base_simple3$B$autoinit</a><span>(v: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="B" data-title="<a href="#base_simple3__B">B</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">B</span><span>(</span><span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>b:<a href="#base_simple3___base_simple3__B">base_simple3$B</a></span><br/></div>" data-toggle="popover">b</span><span class="popupable" title="call base_simple3$B$run" data-title="<a href="#base_simple3___base_simple3__B___run">call base_simple3$B$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___run">base_simple3$B$run</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$B$run" data-title="<a href="#base_simple3___base_simple3__B___run">call base_simple3$B$run</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__B___run">base_simple3$B$run</a><span></span></span><br/></div>" 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="<div><b>local var</b> <span>c:<a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span> <span>=</span> <span class="nc_k popupable" title="call base_simple3$C$autoinit" data-title="<a href="#base_simple3___base_simple3__C___autoinit">call base_simple3$C$autoinit</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___autoinit">base_simple3$C$autoinit</a><span>(val1: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a>)</span></span><br/></div>" data-toggle="popover">new</span> <span class="nc_t popupable" title="C" data-title="<a href="#base_simple3__C">C</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">C</span><span>(</span><span class="nc_l popupable" title="Int" data-title="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" 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="<div><b>local var</b> <span>c:<a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val1" data-title="<a href="#base_simple3___base_simple3__C___val1">call base_simple3$C$val1</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val1" data-title="<a href="#base_simple3___base_simple3__C___val1">call base_simple3$C$val1</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val1">base_simple3$C$val1</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val1</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">output</span>
+</span><span class="line" id="L66"><span class="nc_i nc_v popupable" title="c: C" data-content="<div><b>local var</b> <span>c:<a href="#base_simple3___base_simple3__C">base_simple3$C</a></span><br/></div>" data-toggle="popover">c</span><span class="popupable" title="call base_simple3$C$val2" data-title="<a href="#base_simple3___base_simple3__C___val2">call base_simple3$C$val2</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$C$val2" data-title="<a href="#base_simple3___base_simple3__C___val2">call base_simple3$C$val2</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__C___val2">base_simple3$C$val2</a><span>: <a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span></span><br/></div>" data-toggle="popover">val2</span><span class="popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" data-toggle="popover">.</span><span class="nc_i popupable" title="call base_simple3$Int$output" data-title="<a href="#base_simple3___base_simple3__Int___output">call base_simple3$Int$output</a>" data-content="<div><b>call</b> <span><a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a><span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Object">class Object</a>" data-content="<div><b>class</b> <span>Object</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> sub-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Bool">base_simple3$Bool</a></li><li><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></li><li><a href="#base_simple3___base_simple3__A">base_simple3$A</a></li><li><a href="#base_simple3___base_simple3__B">base_simple3$B</a></li><li><a href="#base_simple3___base_simple3__C">base_simple3$C</a></li><li><a href="#base_simple3___base_simple3__Sys">base_simple3$Sys</a></li></ul></div></div>" 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="<a href="#base_simple3___base_simple3__Object">class Object</a>" data-content="<div><b>class</b> <span>Object</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> sub-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Bool">base_simple3$Bool</a></li><li><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></li><li><a href="#base_simple3___base_simple3__A">base_simple3$A</a></li><li><a href="#base_simple3___base_simple3__B">base_simple3$B</a></li><li><a href="#base_simple3___base_simple3__C">base_simple3$C</a></li><li><a href="#base_simple3___base_simple3__Sys">base_simple3$Sys</a></li></ul></div></div>" 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="<a href="#base_simple3___base_simple3__Object">class Object</a>" data-content="<div><b>class</b> <span>Object</span><br/><div class="dropdown"> <a data-toggle="dropdown" href="#"><b>hier</b> sub-classes<span class="caret"></span></a><ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"><li><a href="#base_simple3___base_simple3__Bool">base_simple3$Bool</a></li><li><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></li><li><a href="#base_simple3___base_simple3__A">base_simple3$A</a></li><li><a href="#base_simple3___base_simple3__B">base_simple3$B</a></li><li><a href="#base_simple3___base_simple3__C">base_simple3$C</a></li><li><a href="#base_simple3___base_simple3__Sys">base_simple3$Sys</a></li></ul></div></div>" 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="<a href="base_simple3.html#base_simple3#Int#output">base_simple3#Int#output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Int#output">base_simple3#Int#output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Int#output">base_simple3#Int#output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="#base_simple3___base_simple3__Int___output">base_simple3$Int$output</a>" data-content="<div><b>fun</b> <span>output<span></span></span><br/></div>" 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="<a href="base_simple3.html#base_simple3#Sys#foo">base_simple3#Sys#foo</a>" data-content="<div><b>fun</b> <span>foo<span></span></span><br/></div>" 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="<a href="#base_simple3__Int">Int</a>" data-content="<div><b>class</b> <span><a href="#base_simple3___base_simple3__Int">base_simple3$Int</a></span><br/></div>" data-toggle="popover">5</span><span class="popupable***TRUNCATED***
<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>
<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>
<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>>
<- 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 ->
<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>
<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>
<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>>
<- 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 ->
<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>
<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>
<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>
<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>
<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>
<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"}}
--- /dev/null
+# 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: >
+
--- /dev/null
+# 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"
+ }
+}
+
# 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
--- /dev/null
+Usage: [OPTION]... [ARG]...
+Use --help for help
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
-FlatString
-FlatString
-Class[FlatString]
-Class[Class[FlatString]]
+ASCIIFlatString
+ASCIIFlatString
+Class[ASCIIFlatString]
+Class[Class[ASCIIFlatString]]
XObject
XObject
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
--- /dev/null
+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
--- /dev/null
+S&éstrS&éstr
# 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
-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
--- /dev/null
+
+[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
--- /dev/null
+aname class sex
+Whale mammal 1
+Snake reptile 0
--- /dev/null
+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
+++ /dev/null
-sleeping 1s
-true
-true
-sleeping 5000ns
-true
-true
-true
--- /dev/null
+à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
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
<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}}
<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}
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
<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}}
|--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
test.has_suffix("bt") => false
test.has_suffix("bat") => false
test.has_suffix("foot") => false
+........
+
+........
-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`?
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%
--- /dev/null
+import end
--- /dev/null
+import end
--- /dev/null
+import end
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"
l -= 1
end
-l = fb.bytelen - 1
+l = fb.byte_length - 1
for i in fb.bytes.reverse_iterator do
print "Byte {l} = {i}"
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+# 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)
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
#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
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
--- /dev/null
+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
base_simple3.nit
+names/n3.nit names1.nit -I .
--- /dev/null
+# 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)
--- /dev/null
+# 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
# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+Bad result file
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
--- /dev/null
+Before Test
+Tested method
+After Test
redef fun after_test do
print "After Test"
- assert false
+ assert before
end
end
--- /dev/null
+# 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
--- /dev/null
+# 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
* `nitmetrics`
* `nitx`
* or others `modelbuilder`.
+
+An image:
+
+![Tinks3D](../../contrib/tinks/doc/tinks3d.png)
--- /dev/null
+This is not a valid Nit program
--- /dev/null
+This is not a valid Nit program
--- /dev/null
+[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
--- /dev/null
+line 1
+line 2
+line 2bis
+line 3 \bine\b\b\b3\b
--- /dev/null
+# 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
--- /dev/null
+# 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(" ")
--- /dev/null
+# 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
--- /dev/null
+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\\'
--- /dev/null
+# 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
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
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"
print("test.has_suffix(\"bat\") => {test.has_suffix("bat")}")
print("test.has_suffix(\"foot\") => {test.has_suffix("foot")}")
+print "........"
+print "/".substring(7, 1)
+print "........"
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