Merge: FFI: use more c_name and protect some macros for global compilation
authorJean Privat <jean@pryen.org>
Sat, 5 Mar 2016 23:14:34 +0000 (18:14 -0500)
committerJean Privat <jean@pryen.org>
Sat, 5 Mar 2016 23:14:34 +0000 (18:14 -0500)
Pull-Request: #1967
Reviewed-by: Jean Privat <jean@pryen.org>

18 files changed:
benchmarks/markdown/engines/nitmd/nitmd.nit
lib/core/math.nit
lib/core/text/flat.nit
lib/markdown/markdown.nit
lib/markdown/test_markdown.nit
src/compiler/abstract_compiler.nit
src/frontend/check_annotation.nit
src/interpreter/naive_interpreter.nit
src/modelize/modelize_property.nit
src/semantize/typing.nit
tests/base_attr_optional.nit [new file with mode: 0644]
tests/error_arg.nit [new file with mode: 0644]
tests/sav/base_attr_optional.res [new file with mode: 0644]
tests/sav/base_meth_call_alt1.res
tests/sav/error_arg.res [new file with mode: 0644]
tests/sav/error_arg_alt1.res [new file with mode: 0644]
tests/sav/nitcg/test_text_stat.res
tests/sav/test_text_stat.res

index 2db91b2..09c7d07 100644 (file)
@@ -19,6 +19,7 @@ var n = args[1].to_i
 
 var str = file.to_path.read_all
 var parser = new MarkdownProcessor
+parser.no_location = true
 for i in [1..n] do
        print parser.process(str)
 end
index 36cf3fe..0f8325f 100644 (file)
@@ -128,7 +128,7 @@ redef class Int
        #
        # assert 3.is_prime
        # assert not 1.is_prime
-       # assert not 12.is_prime
+       # assert not 15.is_prime
        fun is_prime: Bool
        do
                if self == 2 then
@@ -136,7 +136,7 @@ redef class Int
                else if self <= 1 or self.is_even then
                        return false
                end
-               for i in [3..self.sqrt[ do
+               for i in [3..self.sqrt] do
                        if self % i == 0 then return false
                end
                return true
index 47b8608..8081ecc 100644 (file)
@@ -52,20 +52,25 @@ redef class FlatText
        fun char_to_byte_index(index: Int): Int do
                var dpos = index - _position
                var b = _bytepos
+               var its = _items
 
-               if dpos == 0 then return b
                if dpos == 1 then
-                       b += _items.length_of_char_at(b)
+                       if its[b] & 0x80u8 == 0x00u8 then
+                               b += 1
+                       else
+                               b += its.length_of_char_at(b)
+                       end
                        _bytepos = b
                        _position = index
                        return b
                end
                if dpos == -1 then
-                       b = _items.find_beginning_of_char_at(b - 1)
+                       b = its.find_beginning_of_char_at(b - 1)
                        _bytepos = b
                        _position = index
                        return b
                end
+               if dpos == 0 then return b
 
                var ln = _length
                var pos = _position
@@ -74,7 +79,6 @@ redef class FlatText
                var delta_end = (ln - 1) - index
                var delta_cache = (pos - index).abs
                var min = delta_begin
-               var its = _items
 
                if delta_cache < min then min = delta_cache
                if delta_end < min then min = delta_end
@@ -292,7 +296,49 @@ redef class FlatText
        end
 
        redef fun [](index) do
-               assert index >= 0 and index < _length
+               var len = _length
+
+               # Statistically:
+               # * ~70% want the next char
+               # * ~23% want the previous
+               # * ~7% want the same char
+               #
+               # So it makes sense to shortcut early. And early is here.
+               var dpos = index - _position
+               var b = _bytepos
+               if dpos == 1 and index < len - 1 then
+                       var its = _items
+                       var c = its[b]
+                       if c & 0x80u8 == 0x00u8 then
+                               # We want the next, and current is easy.
+                               # So next is easy to find!
+                               b += 1
+                               _position = index
+                               _bytepos = b
+                               # The rest will be done by `dpos==0` bellow.
+                               dpos = 0
+                       end
+               else if dpos == -1 and index > 1 then
+                       var its = _items
+                       var c = its[b-1]
+                       if c & 0x80u8 == 0x00u8 then
+                               # We want the previous, and it is easy.
+                               b -= 1
+                               dpos = 0
+                               _position = index
+                               _bytepos = b
+                               return c.ascii
+                       end
+               end
+               if dpos == 0 then
+                       # We know what we want (+0 or +1) just get it now!
+                       var its = _items
+                       var c = its[b]
+                       if c & 0x80u8 == 0x00u8 then return c.ascii
+                       return items.char_at(b)
+               end
+
+               assert index >= 0 and index < len
                return fetch_char_at(index)
        end
 
@@ -1250,6 +1296,10 @@ redef class NativeString
        #
        # Very unsafe, make sure to have room for this char prior to calling this function.
        private fun set_char_at(pos: Int, c: Char) do
+               if c.code_point < 128 then
+                       self[pos] = c.code_point.to_b
+                       return
+               end
                var ln = c.u8char_len
                native_set_char(pos, c, ln)
        end
index 4c2428f..d63f3e4 100644 (file)
@@ -133,6 +133,14 @@ class MarkdownProcessor
        # ~~~
        var ext_mode = true
 
+       # Disable attaching MDLocation to Tokens
+       #
+       # Locations are useful for some tools but they may
+       # cause an important time and space overhead.
+       #
+       # Default = `false`
+       var no_location = false is writable
+
        init do self.emitter = new MarkdownEmitter(self)
 
        # Process the mardown `input` string and return the processed output.
@@ -397,11 +405,16 @@ class MarkdownProcessor
                        c2 = ' '
                end
 
-               var loc = new MDLocation(
-                       current_loc.line_start,
-                       current_loc.column_start + pos,
-                       current_loc.line_start,
-                       current_loc.column_start + pos)
+               var loc
+               if no_location then
+                       loc = null
+               else
+                       loc = new MDLocation(
+                               current_loc.line_start,
+                               current_loc.column_start + pos,
+                               current_loc.line_start,
+                               current_loc.column_start + pos)
+               end
 
                if c == '*' then
                        if c1 == '*' then
@@ -610,10 +623,12 @@ class MarkdownEmitter
        end
 
        # Append `c` to current buffer.
-       fun addc(c: Char) do add c.to_s
+       fun addc(c: Char) do
+               current_buffer.add c
+       end
 
        # Append a "\n" line break.
-       fun addn do add "\n"
+       fun addn do addc '\n'
 end
 
 # A Link Reference.
@@ -1938,7 +1953,7 @@ end
 abstract class Token
 
        # Location of `self` in the original input.
-       var location: MDLocation
+       var location: nullable MDLocation
 
        # Position of `self` in input independant from lines.
        var pos: Int
@@ -2330,18 +2345,11 @@ redef class Text
                        if c == '\\' and pos + 1 < length then
                                pos = escape(out, self[pos + 1], pos)
                        else
-                               var end_reached = false
-                               for n in nend do
-                                       if c == n then
-                                               end_reached = true
-                                               break
-                                       end
-                               end
-                               if end_reached then break
+                               for n in nend do if c == n then break label
                                out.add c
                        end
                        pos += 1
-               end
+               end label
                if pos == length then return -1
                return pos
        end
index 53c355f..8a2c7a0 100644 (file)
@@ -2830,7 +2830,7 @@ class TestTokenProcessor
        redef fun token_at(input, pos) do
                var token = super
                if token isa TokenNone then return token
-               var res = "{token.class_name} at {token.location}"
+               var res = "{token.class_name} at {token.location or else "?"}"
                var exp = test_stack.shift
                print ""
                print "EXP {exp}"
index 347aa11..1fad40d 100644 (file)
@@ -2046,6 +2046,8 @@ redef class MMethodDef
                else if node isa AClassdef then
                        # Automatic free init is always inlined since it is empty or contains only attribtes assigments
                        return true
+               else if node == null then
+                       return true
                else
                        abort
                end
@@ -3094,7 +3096,18 @@ redef class AAttrPropdef
                        v.assign(v.frame.returnvar.as(not null), res)
                else if mpropdef == mwritepropdef then
                        assert arguments.length == 2
-                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+                       var recv = arguments.first
+                       var arg = arguments[1]
+                       if is_optional then
+                               var value = v.new_var(self.mpropdef.static_mtype.as(not null))
+                               v.add("if ({arg} == NULL) \{")
+                               v.assign(value, evaluate_expr(v, recv))
+                               v.add("\} else \{")
+                               v.assign(value, arg)
+                               v.add("\}")
+                               arg = value
+                       end
+                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
                        if is_lazy then
                                var ret = self.mtype
                                var useiset = not ret.is_c_primitive and not ret isa MNullableType
index d640ac2..5adcabd 100644 (file)
@@ -84,6 +84,7 @@ lazy
 noinit
 readonly
 writable
+optional
 autoinit
 noautoinit
 lateinit
index bbe07d1..23d3ba1 100644 (file)
@@ -1497,7 +1497,12 @@ redef class AAttrPropdef
                        return evaluate_expr(v, recv, f)
                else if mpropdef == mwritepropdef then
                        assert args.length == 2
-                       v.write_attribute(attr, recv, args[1])
+                       var arg = args[1]
+                       if is_optional and arg.mtype isa MNullType then
+                               var f = v.new_frame(self, mpropdef, args)
+                               arg = evaluate_expr(v, recv, f)
+                       end
+                       v.write_attribute(attr, recv, arg)
                        return null
                else
                        abort
index 03d9951..6588268 100644 (file)
@@ -203,19 +203,21 @@ redef class ModelBuilder
                                        mreadpropdef.mproperty.is_autoinit = true
                                        continue
                                end
-                               if npropdef.has_value then continue
-                               var paramname = mreadpropdef.mproperty.name
-                               var ret_type = msignature.return_mtype
-                               if ret_type == null then return
-                               var mparameter = new MParameter(paramname, ret_type, false)
-                               mparameters.add(mparameter)
+                               if npropdef.has_value and not npropdef.is_optional then continue
                                var msetter = npropdef.mwritepropdef
                                if msetter == null then
                                        # No setter, it is a readonly attribute, so just add it
+                                       var paramname = mreadpropdef.mproperty.name
+                                       var ret_type = msignature.return_mtype
+                                       if ret_type == null then return
+                                       var mparameter = new MParameter(paramname, ret_type, false)
+                                       mparameters.add(mparameter)
+
                                        initializers.add(npropdef.mpropdef.mproperty)
                                        npropdef.mpropdef.mproperty.is_autoinit = true
                                else
                                        # Add the setter to the list
+                                       mparameters.add_all msetter.msignature.mparameters
                                        initializers.add(msetter.mproperty)
                                        msetter.mproperty.is_autoinit = true
                                end
@@ -1154,6 +1156,9 @@ redef class AAttrPropdef
        # Is the node tagged lazy?
        var is_lazy = false
 
+       # Is the node tagged optional?
+       var is_optional = false
+
        # Has the node a default value?
        # Could be through `n_expr` or `n_block`
        var has_value = false
@@ -1250,6 +1255,14 @@ redef class AAttrPropdef
                        self.mlazypropdef = mlazypropdef
                end
 
+               var atoptional = self.get_single_annotation("optional", modelbuilder)
+               if atoptional != null then
+                       if not has_value then
+                               modelbuilder.error(atoptional, "Error: `optional` attributes need a default value.")
+                       end
+                       is_optional = true
+               end
+
                var atreadonly = self.get_single_annotation("readonly", modelbuilder)
                if atreadonly != null then
                        if not has_value then
@@ -1421,9 +1434,13 @@ redef class AAttrPropdef
 
                var mwritepropdef = self.mwritepropdef
                if mwritepropdef != null then
+                       var mwritetype = mtype
+                       if is_optional then
+                               mwritetype = mwritetype.as_nullable
+                       end
                        var name: String
                        name = n_id2.text
-                       var mparameter = new MParameter(name, mtype, false)
+                       var mparameter = new MParameter(name, mwritetype, false)
                        var msignature = new MSignature([mparameter], null)
                        mwritepropdef.msignature = msignature
                end
index aeb3b7e..ddc87e2 100644 (file)
@@ -152,7 +152,11 @@ private class TypeVisitor
                        end
                        return null # forward error
                end
-               self.error(nexpr, "Error: expected an expression.")
+               var more_message = null
+               var p = nexpr.parent
+               if p != null then more_message = p.bad_expr_message(nexpr)
+               if more_message == null then more_message = "" else more_message = " " + more_message
+               self.error(nexpr, "Error: expected an expression{more_message}.")
                return null
        end
 
@@ -802,6 +806,12 @@ end
 
 redef class ANode
        private fun accept_post_typing(v: TypeVisitor) do end
+
+       # An additional information message to explain the role of a child expression.
+       #
+       # The point of the method is to allow some kind of double dispatch so the parent
+       # choose how to describe its children.
+       private fun bad_expr_message(child: AExpr): nullable String do return null
 end
 
 redef class AAttrPropdef
@@ -1710,6 +1720,14 @@ redef class ASendExpr
        # The property invoked by the send.
        var callsite: nullable CallSite
 
+       redef fun bad_expr_message(child)
+       do
+               if child == self.n_expr then
+                       return "to be the receiver of `{self.property_name}`"
+               end
+               return null
+       end
+
        redef fun accept_typing(v)
        do
                var nrecv = self.n_expr
diff --git a/tests/base_attr_optional.nit b/tests/base_attr_optional.nit
new file mode 100644 (file)
index 0000000..4417afe
--- /dev/null
@@ -0,0 +1,43 @@
+# 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 kernel
+
+class A
+       var i: Int = 99 is optional
+
+       var o: Object = 999 is optional
+end
+
+var a = new A
+a.i.output
+a.o.output
+
+a.i = 1
+a.o = 10
+a.i.output
+a.o.output
+
+a.i = null
+a.o = null
+a.i.output
+a.o.output
+
+a = new A(2)
+a.i.output
+a.o.output
+
+a = new A(3, true)
+a.i.output
+a.o.output
diff --git a/tests/error_arg.nit b/tests/error_arg.nit
new file mode 100644 (file)
index 0000000..c550d9f
--- /dev/null
@@ -0,0 +1,39 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core::kernel
+
+redef class Int
+       fun a: Int do return 10*self+self.abs
+end
+
+fun foo(a: Int): Int do
+       a.output
+       return a
+end
+fun bar(a: Int) do a.output
+
+foo 1.a
+foo(1.a)
+foo (1).a
+foo(1).a
+foo((1).a)
+
+'\n'.output
+
+bar 1.a
+bar(1.a)
+#alt1#bar (1).a
+#alt1#bar(1).a
+bar((1).a)
diff --git a/tests/sav/base_attr_optional.res b/tests/sav/base_attr_optional.res
new file mode 100644 (file)
index 0000000..c879e94
--- /dev/null
@@ -0,0 +1,10 @@
+99
+999
+1
+10
+99
+999
+2
+999
+3
+true
index 5b14b4e..b2c999e 100644 (file)
@@ -1 +1 @@
-alt/base_meth_call_alt1.nit:36,1--6: Error: expected an expression.
+alt/base_meth_call_alt1.nit:36,1--6: Error: expected an expression to be the receiver of `output`.
diff --git a/tests/sav/error_arg.res b/tests/sav/error_arg.res
new file mode 100644 (file)
index 0000000..1aa7d08
--- /dev/null
@@ -0,0 +1,9 @@
+11
+11
+1
+1
+11
+
+11
+11
+11
diff --git a/tests/sav/error_arg_alt1.res b/tests/sav/error_arg_alt1.res
new file mode 100644 (file)
index 0000000..efe82e4
--- /dev/null
@@ -0,0 +1,2 @@
+alt/error_arg_alt1.nit:37,1--7: Error: expected an expression to be the receiver of `a`.
+alt/error_arg_alt1.nit:38,1--6: Error: expected an expression to be the receiver of `a`.
index 8fd2394..e28137f 100644 (file)
@@ -9,37 +9,37 @@ Allocations, by type:
        -RopeBuffer = 0
 
 Calls to length, by type:
-       FlatString = 23 (cache misses 5, 21.73%)
+       FlatString = 18 (cache misses 5, 27.77%)
 Indexed accesses, by type:
-       FlatString = 13
+       FlatString = 8
 Calls to bytelen for each type:
        FlatString = 61
 Calls to position for each type:
-       FlatString = 27
+       FlatString = 17
 Calls to bytepos for each type:
-       FlatString = 14
-Calls to first_byte on FlatString 216
+       FlatString = 9
+Calls to first_byte on FlatString 191
 Calls to last_byte on FlatString 19
 FlatStrings allocated with length 78 (86.813%)
 Length of travel for index distribution:
-* null = 16 => occurences 80.0%, cumulative 80.0% 
-* 1 = 14 => occurences 35.0%, cumulative 75.0% 
+* null = 11 => occurences 73.333%, cumulative 73.333% 
+* 1 = 8 => occurences 27.586%, cumulative 65.517% 
 Byte length of the FlatStrings created:
 * null = 6 => occurences 4.444%, cumulative 4.444% 
-* 1 = 21 => occurences 14.094%, cumulative 18.121% 
-* 2 = 33 => occurences 20.245%, cumulative 36.81% 
+* 1 = 24 => occurences 16.107%, cumulative 20.134% 
+* 2 = 30 => occurences 18.405%, cumulative 36.81% 
 * 3 = 29 => occurences 16.292%, cumulative 50.0% 
-* 4 = 9 => occurences 4.663%, cumulative 50.777% 
-* 5 = 20 => occurences 9.615%, cumulative 56.731% 
-* 6 = 21 => occurences 9.417%, cumulative 62.332% 
+* 4 = 5 => occurences 2.591%, cumulative 48.705% 
+* 5 = 20 => occurences 9.615%, cumulative 54.808% 
+* 6 = 25 => occurences 11.211%, cumulative 62.332% 
 * 9 = 1 => occurences 0.42%, cumulative 58.824% 
 * 10 = 9 => occurences 3.557%, cumulative 58.893% 
 * 11 = 2 => occurences 0.746%, cumulative 56.343% 
 * 12 = 1 => occurences 0.355%, cumulative 53.901% 
 * 13 = 1 => occurences 0.339%, cumulative 51.864% 
 * 14 = 1 => occurences 0.325%, cumulative 50.0% 
-* 15 = 5 => occurences 1.558%, cumulative 49.533% 
-* 16 = 7 => occurences 2.083%, cumulative 49.405% 
+* 15 = 7 => occurences 2.181%, cumulative 50.156% 
+* 16 = 5 => occurences 1.488%, cumulative 49.405% 
 * 17 = 1 => occurences 0.285%, cumulative 47.578% 
 * 25 = 2 => occurences 0.549%, cumulative 46.429% 
 * 26 = 1 => occurences 0.265%, cumulative 44.974% 
@@ -53,5 +53,5 @@ Byte length of the FlatStrings created:
 * 40 = 1 => occurences 0.207%, cumulative 37.19% 
 * 43 = 1 => occurences 0.201%, cumulative 36.419% 
 * 46 = 1 => occurences 0.196%, cumulative 35.686% 
-* 48 = 1 => occurences 0.191%, cumulative 34.99% 
-* 51 = 21 => occurences 3.918%, cumulative 38.06% 
+* 51 = 20 => occurences 3.824%, cumulative 38.623% 
+* 55 = 1 => occurences 0.186%, cumulative 37.732% 
index e320033..702e2b2 100644 (file)
@@ -9,48 +9,49 @@ Allocations, by type:
        -RopeBuffer = 0
 
 Calls to length, by type:
-       FlatString = 23 (cache misses 5, 21.73%)
+       FlatString = 18 (cache misses 5, 27.77%)
 Indexed accesses, by type:
-       FlatString = 13
+       FlatString = 8
 Calls to bytelen for each type:
        FlatString = 61
 Calls to position for each type:
-       FlatString = 27
+       FlatString = 17
 Calls to bytepos for each type:
-       FlatString = 14
-Calls to first_byte on FlatString 216
+       FlatString = 9
+Calls to first_byte on FlatString 191
 Calls to last_byte on FlatString 19
 FlatStrings allocated with length 78 (86.813%)
 Length of travel for index distribution:
-* 0 = 16 => occurences 80.0%, cumulative 80.0% 
-* 1 = 14 => occurences 35.0%, cumulative 75.0% 
+* 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 = 21 => occurences 14.189%, cumulative 18.243% 
-* 2 = 33 => occurences 20.37%, cumulative 37.037% 
+* 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 = 7 => occurences 3.646%, cumulative 50.0% 
-* 5 = 20 => occurences 9.662%, cumulative 56.039% 
-* 6 = 21 => occurences 9.459%, cumulative 61.712% 
-* 9 = 1 => occurences 0.422%, cumulative 58.228% 
-* 10 = 9 => occurences 3.571%, cumulative 58.333% 
-* 11 = 2 => occurences 0.749%, cumulative 55.805% 
-* 12 = 1 => occurences 0.356%, cumulative 53.381% 
-* 13 = 1 => occurences 0.34%, cumulative 51.361% 
-* 14 = 1 => occurences 0.326%, cumulative 49.511% 
-* 15 = 5 => occurences 1.563%, cumulative 49.063% 
-* 16 = 7 => occurences 2.09%, cumulative 48.955% 
-* 17 = 1 => occurences 0.286%, cumulative 47.143% 
-* 25 = 2 => occurences 0.551%, cumulative 46.006% 
-* 26 = 1 => occurences 0.265%, cumulative 44.562% 
-* 31 = 2 => occurences 0.513%, cumulative 43.59% 
-* 32 = 1 => occurences 0.248%, cumulative 42.327% 
-* 33 = 1 => occurences 0.24%, cumulative 41.247% 
-* 34 = 2 => occurences 0.465%, cumulative 40.465% 
-* 35 = 1 => occurences 0.225%, cumulative 39.414% 
-* 37 = 1 => occurences 0.219%, cumulative 38.512% 
-* 39 = 1 => occurences 0.213%, cumulative 37.66% 
-* 40 = 1 => occurences 0.207%, cumulative 36.853% 
-* 43 = 1 => occurences 0.202%, cumulative 36.089% 
-* 46 = 1 => occurences 0.196%, cumulative 35.363% 
-* 48 = 3 => occurences 0.575%, cumulative 35.057% 
+* 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%