├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs.config ├── docs.sh ├── erlang_ls.config ├── priv └── .keep ├── rebar.config ├── rebar.lock ├── shell.nix ├── src ├── hyper.app.src ├── hyper.erl ├── hyper_binary.erl ├── hyper_const.erl ├── hyper_register.erl └── hyper_utils.erl └── test ├── hyper_SUITE.erl ├── hyper_SUITE_data └── filter.txt ├── hyper_binary_SUITE.erl ├── prop_hyper.erl └── prop_test_not_suite_in_progress.erl /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | name: Erlang/OTP ${{matrix.otp}} / rebar3 ${{matrix.rebar3}} 7 | strategy: 8 | matrix: 9 | otp: ["24.3", "25.0"] 10 | rebar3: ["3.18", "3.17.0"] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: erlef/setup-beam@v1 14 | with: 15 | otp-version: ${{matrix.otp}} 16 | rebar3-version: ${{matrix.rebar3}} 17 | - run: rebar3 ct 18 | - run: rebar3 proper 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | hyper.png 2 | estimates.csv 3 | *.so 4 | *.d 5 | 6 | .rebar3 7 | _* 8 | .eunit 9 | *.o 10 | *.beam 11 | *.plt 12 | *.swp 13 | *.swo 14 | .erlang.cookie 15 | ebin 16 | log 17 | erl_crash.dump 18 | .rebar 19 | logs 20 | _build 21 | .idea 22 | *.iml 23 | rebar3.crashdump 24 | *~ 25 | doc -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog pre v1 2 | 3 | ## 1.0.1 [2022-12-22] 4 | 5 | * fix callback specs of `hyper_registers:precision/1` 6 | 7 | ## 1.0.0 [2022-12-22] 8 | 9 | * BREAKING CHANGE. Refactored precision into the backend, to allow each backend to have access to the precision information for their internal work. This means there is now a `precision/1` callback that every backend need to implement. 10 | 11 | ## 0.6.0 [2022-09-27] 12 | 13 | ### Enhancements 14 | 15 | * Added documentation to the main module 16 | 17 | ## 0.5.0 (2022-09-02) 18 | 19 | ### Enhancements 20 | 21 | * The behaviour have been cleaned up and the relationship between the behaviour and the API have been cleaned up. In particular, the run_of_zeroes is now done in the backend. 22 | * The array backend have been dropped. If you want it back, PR welcome. 23 | 24 | ## 0.4.0 (2022-06-25) 25 | 26 | ### Enhancements 27 | 28 | * The behaviour have been cleaned up and the relationship between the behaviour and the API have been cleaned up. 29 | * There is now a version atom in each HLL generated. This is not too important right now, but will be used in the future to block merging two HLL with incompatible versions (as an example if the hashing strategy changed) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Game Analytics ApS and 2021 Thomas Depierre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperLogLog for Erlang 2 | 3 | ![Hex.pm](https://img.shields.io/hexpm/v/hyper?style=flat-square) 4 | ![Hex.pm](https://img.shields.io/hexpm/l/hyper?style=flat-square) 5 | 6 | This is an implementation of the HyperLogLog algorithm in 7 | Erlang. Using HyperLogLog you can estimate the cardinality of very 8 | large data sets using constant memory. The relative error is `1.04 * 9 | sqrt(2^P)`. When creating a new HyperLogLog filter, you provide the 10 | precision P, allowing you to trade memory for accuracy. The union of 11 | two filters is lossless. 12 | 13 | In practice this allows you to build efficient analytics systems. For 14 | example, you can create a new filter in each mapper and feed it a 15 | portion of your dataset while the reducers simply union together all 16 | filters they receive. The filter you end up with is exactly the same 17 | filter as if you would sequentially insert all data into a single 18 | filter. 19 | 20 | In addition to the base algorithm, we have implemented the new estimator as 21 | based on Mean Limit as described this great [paper by Otmar Ertl][]. 22 | This new estimator greatly improves the estimates for lower cardinalities while 23 | using a single estimator for the whole range of cardinalities. 24 | 25 | ## TODO 26 | 27 | - [x] Use rebar3 28 | - [x] Work on OTP 23 29 | - [x] Fix the estimator 30 | - [x] Fix `reduce_precision` 31 | - [x] add `reduce_precision` for array, allowing unions 32 | - [ ] Better document the main module 33 | - [x] Move documentation to ExDoc 34 | - [x] Delete dead code 35 | - [x] Rework test suite to be nice to modify 36 | - [ ] Rework Intersection using this [paper by Otmar Ertl][] 37 | - [ ] Redo benchmarks 38 | 39 | ## Usage 40 | 41 | ```erlang 42 | 1> hyper:insert(<<"foobar">>, hyper:insert(<<"quux">>, hyper:new(4))). 43 | {hyper,4, 44 | {hyper_binary,{dense,<<0,0,0,0,0,0,0,0,64,0,0,0>>, 45 | [{8,1}], 46 | 1,16}}} 47 | 48 | 2> hyper:card(v(-1)). 49 | 2.136502281992361 50 | ``` 51 | 52 | The errors introduced by estimations can be seen in this example: 53 | 54 | ```erlang 55 | 3> rand:seed(exsss, {1, 2, 3}). 56 | {#{bits => 58,jump => #Fun, 57 | next => #Fun,type => exsss, 58 | uniform => #Fun, 59 | uniform_n => #Fun}, 60 | [117085240290607817|199386643319833935]} 61 | 4> Run = fun (P, Card) -> hyper:card(lists:foldl(fun (_, H) -> Int = rand:uniform(10000000000000), hyper:insert(<>, H) end, hyper:new(P), lists:seq(1, Card))) end. 62 | #Fun 63 | 5> Run(12, 10_000). 64 | 10038.192365345985 65 | 6> Run(14, 10_000). 66 | 9967.916262642864 67 | 7> Run(16, 10_000). 68 | 9972.832893293473 69 | ``` 70 | 71 | A filter can be persisted and read later. The serialized struct is formatted for usage with jiffy: 72 | 73 | ```erlang 74 | 8> Filter = hyper:insert(<<"foo">>, hyper:new(4)). 75 | {hyper,4, 76 | {hyper_binary,{dense,<<4,0,0,0,0,0,0,0,0,0,0,0>>,[],0,16}}} 77 | 9> Filter =:= hyper:from_json(hyper:to_json(Filter)). 78 | true 79 | ``` 80 | 81 | **As of today, we only support the binary backend. More to come** 82 | You can select a different backend. See below for a description of why 83 | you might want to do so. They serialize in exactly the same way, but 84 | can't be mixed in memory. 85 | 86 | ## Is it any good? 87 | 88 | No idea ! I do not know anyone that uses it extensively, but it is relatively 89 | well tested. As far as i can tell, it is the only FOSS implementation that does 90 | precision reduction properly ! 91 | 92 | ## Hacking 93 | 94 | ### Documentation 95 | 96 | We use ex_doc for documentation. In order to generate the docs, you need to install it 97 | 98 | ```bash 99 | mix escript.install hex ex_doc 100 | ex_doc --version 101 | ``` 102 | 103 | Then generate the docs, after targetting the correct version in docs.sh 104 | 105 | ```bash 106 | docs.sh 107 | ``` 108 | 109 | ## Backends 110 | 111 | Effort has been spent on implementing different backends in the 112 | pursuit of finding the right performance trade-off. Fill rate refers to how many 113 | registers has a value other than 0. 114 | 115 | - `hyper_binary`: Fixed memory usage (6 bits * 2^P), fastest on insert, 116 | union, cardinality and serialization. Best default choice. 117 | 118 | You can also implement your own backend. In `test` theres a 119 | bunch of tests run for all backends, including some PropEr tests. The 120 | test suite will ensure your backend gives correct estimates and 121 | correctly encodes/decodes the serialized filters. 122 | 123 | 124 | ## Fork 125 | 126 | This is a fork of the original Hyper library by GameAnalytics. It was not 127 | maintained anymore. 128 | 129 | The main difference are a move to the `rand` module for tests and to `rebar3` 130 | as a build tool, in order to support OTP 23+. 131 | 132 | The `carray` backend was dropped, as it was never moved outside of experimental 133 | status and could not be serialised for a distributed use. Some backends using 134 | NIF may come back in the future. 135 | 136 | The bisect implementation was dropped too. Its use case was limited and it 137 | forced a dependency on a library that was not maintained either. 138 | 139 | The gb backend was dropped for the time being too. 140 | 141 | The Array backend was dropped for the time being too. 142 | 143 | The estimator was rebuilt following this [paper by Otmar Ertl][], as it was 144 | broken for any precision not 14. This should also provide better estimation 145 | across the board for cardinality. 146 | 147 | The `reduce_precision` function has been rebuilt properly, as it was quite 148 | simply wrong. This fixed a lot of bugs for unions. 149 | 150 | [paper by Otmar Ertl]: https://arxiv.org/abs/1706.07290 151 | -------------------------------------------------------------------------------- /docs.config: -------------------------------------------------------------------------------- 1 | {main, <<"hyper">>}. 2 | {proglang, erlang}. 3 | {source_url, <<"https://github.com/LivewareProblems/hyper">>}. 4 | -------------------------------------------------------------------------------- /docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Setup: 5 | # 6 | # mix escript.install github elixir-lang/ex_doc 7 | # asdf install erlang 24.0.2 8 | # asdf local erlang 24.0.2 9 | 10 | rebar3 compile 11 | rebar3 as docs edoc 12 | version=0.3.1 13 | ex_doc "hyper" $version "_build/default/lib/hyper/ebin" \ 14 | --source-ref v${version} \ 15 | --config docs.config "$@" -------------------------------------------------------------------------------- /erlang_ls.config: -------------------------------------------------------------------------------- 1 | include_dirs: 2 | - "_build/*/lib/*/include" 3 | - "_build/*/lib/" 4 | - "include" 5 | deps_dirs: 6 | - "_build/default/lib/*" 7 | - "_build/test/lib/*" -------------------------------------------------------------------------------- /priv/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LivewareProblems/hyper/440a98dfc45d215eb1842ca40fc2ec3d28f4fb2e/priv/.keep -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {cover_enabled, true}. 2 | {deps, []}. 3 | {minimum_otp_vsn, "24.0"}. 4 | 5 | {xref_checks, [ 6 | %% enable most checks, but avoid 'unused calls' which is often 7 | %% very verbose 8 | undefined_function_calls, 9 | undefined_functions, 10 | locals_not_used, 11 | deprecated_function_calls, 12 | deprecated_functions 13 | ]}. 14 | 15 | {dialyzer, [ 16 | {warnings, [ 17 | %% Warn about undefined types and unknown functions 18 | unknown 19 | ]} 20 | ]}. 21 | 22 | {profiles, [ 23 | {prod, [ 24 | {erl_opts, [no_debug_info, warnings_as_errors]} 25 | ]}, 26 | {test, [ 27 | {deps, [{proper, "1.3.0"}]}, 28 | {erl_opts, [debug_info, nowarn_export_all]} 29 | ]} 30 | ]}. 31 | 32 | {plugins, [rebar3_proper]}. 33 | 34 | {project_plugins, [rebar3_ex_doc, rebar3_hex, erlfmt]}. 35 | 36 | {ex_doc, [ 37 | {extras, ["README.md", "LICENSE", "CHANGELOG.md"]}, 38 | {main, <<"hyper">>}, 39 | {source_url, "https://github.com/LivewareProblems/hyper"} 40 | ]}. 41 | 42 | {hex, [ 43 | {doc, #{provider => ex_doc}} 44 | ]}. 45 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { } }: 2 | 3 | with pkgs; 4 | mkShell { 5 | buildInputs = [ erlangR25 rebar3 ]; 6 | 7 | # Fix GLIBC Locale 8 | LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive"; 9 | LANG = "en_US.UTF-8"; 10 | } 11 | -------------------------------------------------------------------------------- /src/hyper.app.src: -------------------------------------------------------------------------------- 1 | {application, hyper, [ 2 | {description, "Implementation of the HyperLogLog algorithm in Erlang"}, 3 | {vsn, "1.0.1"}, 4 | {registered, []}, 5 | {modules, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {licenses, ["MIT"]}, 11 | {links, [{"Github", "https://github.com/LivewareProblems/hyper"}]}, 12 | {doc, "doc"} 13 | ]}. 14 | -------------------------------------------------------------------------------- /src/hyper.erl: -------------------------------------------------------------------------------- 1 | %% @doc `hyper' is the reference implementation of hyperloglog for erlang 2 | %% 3 | %% Hyperloglog is an algorithm based on a probabilistic datastructure to solve 4 | %% the count-distinct problem. It allows an approximation of the distinct 5 | %% element of a large collection, with a limited memory usage and the ability 6 | %% to be distributed and merged losslessly. 7 | %% 8 | %% This module is the interface you use to interact with the hyperloglog. `hyper' 9 | %% accept multiple backend for the implementation of hyperloglog, each with a 10 | %% different set of tradeoff and performance. 11 | %% 12 | %% For more details on how to use it, look at {@link new/3}, {@link insert/2} 13 | %% and {@link card/1} 14 | %% 15 | %% == How does it work == 16 | %% 17 | %% Without entering in all the details, the hyperloglog datastructure is based 18 | %% on a limited set of elements. Details of how these are implemented will vary 19 | %% between different backend and implementations, but the high level idea stay 20 | %% the same. 21 | %% - A hash function that output a 64bit hash 22 | %% - A set of numbered "bins", controlled by the precision 23 | %% - An estimator function 24 | %% 25 | %% For every element to insert, the element is hashed into a binary N, then 26 | %% the leftmost P bits are used to define which bin the element belong. In this 27 | %% bin, we store the number of zeroes before the first 1 in the leftmost bit of 28 | %% the leftover, after truncating the leftmost N bit. If this number is bigger 29 | %% than the one currently in the bin, we update it. Otherwise we do nothing. Our 30 | %% input is inserted. 31 | %% 32 | %% Later, when we want to get an estimation of the cardinality, we apply the 33 | %% estimator function to the set of bins. The details of the estimator are too 34 | %% complex for this documentation, but suffice to say that they are proven to 35 | %% reduce the estimation error to stay under a tight boundary. 36 | %% 37 | %% == References == 38 | %% For more details, here is a list of reference papers that have been used to 39 | %% implement this library. 40 | %% 41 | %% 45 | %% In particular huge thanks to Omar Ertl for his clear work on this domain. 46 | -module(hyper). 47 | 48 | -export([new/1, new/2, new/3, insert/2, insert_many/2]). 49 | -export([union/1, union/2]). 50 | -export([card/1, intersect_card/2]). 51 | -export([to_json/1, from_json/1, from_json/2, precision/1, bytes/1, is_hyper/1]). 52 | -export([compact/1, reduce_precision/2]). 53 | -export([generate_unique/1]). 54 | 55 | -type precision() :: 4..16. 56 | -type registers() :: any(). 57 | -type version() :: atom(). 58 | 59 | -record(hyper, {v :: version(), registers :: {module(), registers()}}). 60 | 61 | -type value() :: binary(). 62 | -type filter() :: #hyper{}. 63 | 64 | -export_type([filter/0, precision/0, registers/0]). 65 | 66 | -define(DEFAULT_BACKEND, hyper_binary). 67 | -define(DEFAULT_VERSION, 'sha-v1'). 68 | 69 | % constant for 0.5/ln(2) 70 | -define(HLL_ALPHA_INF, 0.721347520444481703680). 71 | 72 | %% 73 | %% API 74 | %% 75 | 76 | %% @doc Create a new Hyperloglog structure 77 | %% Equivalent to `hyper:new(P, hyper_binary, 'sha-v1')' 78 | %% 79 | %% see {@link hyper:new/3} 80 | -spec new(precision()) -> filter(). 81 | new(P) -> 82 | new(P, ?DEFAULT_BACKEND, ?DEFAULT_VERSION). 83 | 84 | %% @doc Create a new Hyperloglog structure with the specified backend 85 | %% Equivalent to `hyper:new(P, Mod, 'sha-v1')' 86 | %% 87 | %% see {@link hyper:new/3} 88 | -spec new(precision(), module()) -> filter(). 89 | new(P, Mod) -> 90 | new(P, Mod, ?DEFAULT_VERSION). 91 | 92 | %% @doc Create a new Hyperloglog structure with the specified backend and the 93 | %% specified version. 94 | %% 95 | %% `hyper' ship with one backend: 96 | %% - {@link hyper_binary} 97 | %% 98 | %% For implementing your own backend, see {@link hyper_register} 99 | %% 100 | %% %% hyper structure with different version will work transparently for now, 101 | %% but will generate deprecation warning in the future if we change some of the 102 | %% implementation details. 103 | %% 104 | %% You should use {@link new/2} most of the time, so that the versionning is 105 | %% handled by `hyper'. You should only use `new/3' if you need to do your own 106 | %% versionning for custom backends or for custom versionning problems. 107 | -spec new(precision(), module(), version()) -> filter(). 108 | new(P, Mod, Version) when 4 =< P andalso P =< 16 andalso is_atom(Mod) -> 109 | #hyper{v = Version, registers = {Mod, hyper_register:new(Mod, P)}}. 110 | 111 | %% @doc return `true' if the structure passed is a `hyper' structure. `false' 112 | %% otherwise 113 | -spec is_hyper(filter()) -> boolean(). 114 | is_hyper(#hyper{}) -> 115 | true; 116 | is_hyper(_) -> 117 | false. 118 | 119 | %% @doc Insert `Value' inside the Hyper structure passed. 120 | -spec insert(value(), filter()) -> filter(). 121 | insert(Value, #hyper{registers = {Mod, Registers}} = Hyper) when 122 | is_binary(Value) 123 | -> 124 | P = hyper_register:precision(Mod, Registers), 125 | Hash = crypto:hash(sha, Value), 126 | <> = Hash, 127 | 128 | %% Registers are only allowed to increase, implement by backend 129 | Hyper#hyper{registers = {Mod, hyper_register:set(Mod, Index, RegisterValue, Registers)}}; 130 | insert(_Value, _Hyper) -> 131 | error(badarg). 132 | 133 | %% @doc insert a list of values in the Hyper structure. 134 | -spec insert_many([value()], filter()) -> filter(). 135 | insert_many(L, Hyper) -> 136 | lists:foldl(fun insert/2, Hyper, L). 137 | 138 | %% Merge a list of hyperloglog together 139 | %% see {@link union/2} for details and implications. 140 | -spec union([filter()]) -> filter(). 141 | union(Filters) when is_list(Filters) -> 142 | case 143 | lists:usort( 144 | lists:map( 145 | fun(#hyper{registers = {Mod, Registers}}) -> 146 | {hyper_register:precision(Mod, Registers), Mod} 147 | end, 148 | Filters 149 | ) 150 | ) 151 | of 152 | %% same P and backend 153 | [{_P, Mod}] -> 154 | Registers = lists:map( 155 | fun(#hyper{registers = {_, R}}) -> 156 | R 157 | end, 158 | Filters 159 | ), 160 | [First | _] = Filters, 161 | First#hyper{registers = {Mod, hyper_register:max_merge(Mod, Registers)}}; 162 | %% mixed P, but still must have same backend 163 | [{MinP, Mod} | _] -> 164 | FoldedFilters = lists:map( 165 | fun(#hyper{registers = {M, _}} = F) when M =:= Mod -> 166 | hyper:reduce_precision(MinP, F) 167 | end, 168 | Filters 169 | ), 170 | union(FoldedFilters) 171 | end. 172 | 173 | %% Merge two `hyper' structure together. 174 | %% 175 | %% The two structure need to use the same backend. 176 | %% 177 | %% The precision does not have to be the same but the resulting structure will 178 | %% have the lowest precision of the two. 179 | %% 180 | %% See {@link reduce_precision/2} for details of the implications 181 | union(Small, Big) -> 182 | union([Small, Big]). 183 | 184 | %% @doc Provide the cardinality of the intersection of two `hyper' structure 185 | %% using the inclusion/exclusion principle. Use with caution as the precision 186 | %% of this methods is fairly limited. 187 | -spec intersect_card(filter(), filter()) -> float(). 188 | intersect_card( 189 | Left = #hyper{registers = {ModLeft, RegLeft}}, Right = #hyper{registers = {ModRight, RegRight}} 190 | ) -> 191 | PLeft = hyper_register:precision(ModLeft, RegLeft), 192 | PRight = hyper_register:precision(ModRight, RegRight), 193 | case PLeft =:= PRight of 194 | true -> max(0.0, card(Left) + card(Right) - card(union(Left, Right))); 195 | false -> error(not_equal_precision) 196 | end. 197 | 198 | %% @doc Estimate the cardinality of a `hyper' structure 199 | -spec card(filter()) -> float(). 200 | card(#hyper{registers = {Mod, Registers0}}) -> 201 | P = hyper_register:precision(Mod, Registers0), 202 | M = trunc(pow(2, P)), 203 | Qp1 = 65 - P, 204 | Registers = hyper_register:compact(Mod, Registers0), 205 | RegisterHisto = hyper_register:register_histogram(Mod, Registers), 206 | 207 | Z = M * tau(M - maps:get(Qp1, RegisterHisto, 0) / M), 208 | %TODO: drop after Q = 64 - P in histo before folding 209 | Z1 = lists:foldr( 210 | fun({_K, V}, Acc) -> (Acc + V) * 0.5 end, 211 | Z, 212 | lists:keysort(1, maps:to_list(maps:without([0, Qp1], RegisterHisto))) 213 | ), 214 | Zf = Z1 + M * sigma(maps:get(0, RegisterHisto, 0) / M), 215 | ?HLL_ALPHA_INF * M * M / Zf. 216 | 217 | %% @doc return the precision of the `hyper' structure passed. 218 | precision(#hyper{registers = {Mod, Registers}}) -> 219 | hyper_register:precision(Mod, Registers). 220 | 221 | %% @doc return the size in bytes of the `hyper' structure in memory 222 | bytes(#hyper{registers = {Mod, Registers}}) -> 223 | hyper_register:bytes(Mod, Registers). 224 | 225 | %% @doc compact the underlying `hyper' structure. 226 | %% Do not use, considered an implementation detail of the backend. 227 | %% Will be removed in the future. 228 | compact(#hyper{registers = {Mod, Registers}} = Hyper) -> 229 | Hyper#hyper{registers = {Mod, hyper_register:compact(Mod, Registers)}}. 230 | 231 | %% @doc Reduce the precision of the `hyper' structure to P. 232 | %% This is lossless in the sense that the old structure hold all the data needed 233 | %% to fill the reduced precision one. 234 | %% That said, reducing precision does grow the error in the estimator, and 235 | %% there is no way to get it back. Do this knowing you are losing precision. 236 | reduce_precision(P, #hyper{registers = {Mod, Registers}} = Hyper) -> 237 | OldP = hyper_register:precision(Mod, Registers), 238 | case {P, OldP} of 239 | {P1, P2} when P1 =:= P2 -> 240 | Hyper; 241 | {P1, P2} when P1 < P2 -> 242 | Hyper#hyper{registers = {Mod, hyper_register:reduce_precision(Mod, P, Registers)}}; 243 | _ -> 244 | error(new_precision_superior_to_old) 245 | end. 246 | 247 | %% 248 | %% SERIALIZATION 249 | %% 250 | 251 | %% @deprecated 0.6.0 252 | %% @doc Serialize the `hyper' structure to a json friendly format that can be 253 | %% decoded with {@link from_json/2} 254 | %% 255 | %% Do not use, this is going to be replaced with better solution in the future 256 | -spec to_json(filter()) -> any(). 257 | to_json(#hyper{v = V, registers = {Mod, Registers}}) -> 258 | P = hyper_register:precision(Mod, Registers), 259 | Compact = hyper_register:compact(Mod, Registers), 260 | {[ 261 | {<<"p">>, P}, 262 | {<<"v">>, atom_to_binary(V)}, 263 | {<<"registers">>, base64:encode(zlib:gzip(hyper_register:encode_registers(Mod, Compact)))} 264 | ]}. 265 | 266 | %% @deprecated 0.6.0 267 | %% @doc Deserialize the json friendly format from {@link to_json/1} to a `hyper' 268 | %% structure 269 | %% 270 | %% Equivalent to `from_json(Struct, hyper_binary)' 271 | %% 272 | %% Do not use, this is going to be replaced with better solution in the future 273 | -spec from_json(any()) -> filter(). 274 | from_json(Struct) -> 275 | from_json(Struct, ?DEFAULT_BACKEND). 276 | 277 | %% @deprecated 0.6.0 278 | %% @doc Deserialize the json friendly format from {@link to_json/1} to a `hyper' 279 | %% structure, with `Mod' as backend 280 | %% 281 | %% Do not use, this is going to be replaced with better solution in the future 282 | -spec from_json(any(), module()) -> filter(). 283 | from_json({Struct}, Mod) -> 284 | P = proplists:get_value(<<"p">>, Struct), 285 | V = binary_to_atom(proplists:get_value(<<"v">>, Struct)), 286 | Bytes = zlib:gunzip(base64:decode(proplists:get_value(<<"registers">>, Struct))), 287 | Registers = hyper_register:decode_registers(Mod, Bytes, P), 288 | 289 | #hyper{v = V, registers = {Mod, Registers}}. 290 | 291 | %% 292 | %% HELPERS 293 | %% 294 | 295 | generate_unique(N) -> 296 | generate_unique(lists:usort(random_bytes(N)), N). 297 | 298 | generate_unique(L, N) -> 299 | case length(L) of 300 | N -> 301 | L; 302 | Less -> 303 | generate_unique(lists:usort(random_bytes(N - Less) ++ L), N) 304 | end. 305 | 306 | random_bytes(N) -> 307 | random_bytes([], N). 308 | 309 | random_bytes(Acc, 0) -> 310 | Acc; 311 | random_bytes(Acc, N) -> 312 | Int = rand:uniform(100000000000000), 313 | random_bytes([<> | Acc], N - 1). 314 | 315 | sigma(1.0) -> 316 | infinity; 317 | sigma(X) -> 318 | sigma_sum(X, first, X, 1.0). 319 | 320 | sigma_sum(Z, Z, _X, _Y) -> 321 | Z; 322 | sigma_sum(Z, _Zp, X, Y) -> 323 | X1 = X * X, 324 | Z1 = (X1 * Y) + Z, 325 | sigma_sum(Z1, Z, X1, Y + Y). 326 | 327 | tau(0.0) -> 328 | 0.0; 329 | tau(1.0) -> 330 | 0.0; 331 | tau(X) -> 332 | tau_sum((1 - X), first, X, 1.0) / 3. 333 | 334 | tau_sum(Z, Z, _X, _Y) -> 335 | Z; 336 | tau_sum(Z, _Zp, X, Y) -> 337 | X1 = math:sqrt(X), 338 | Y1 = Y * 0.5, 339 | Z1 = Z - (math:pow(1 - X1, 2) * Y1), 340 | tau_sum(Z1, Z, X1, Y1). 341 | 342 | pow(X, Y) -> 343 | math:pow(X, Y). 344 | -------------------------------------------------------------------------------- /src/hyper_binary.erl: -------------------------------------------------------------------------------- 1 | %% @doc: Registers stored in one large binary 2 | %% 3 | %% This backend uses one plain Erlang binary to store registers. The 4 | %% cost of rebuilding the binary is amortized by keeping a buffer of 5 | %% inserts to perform in the future. 6 | 7 | -module(hyper_binary). 8 | 9 | -behaviour(hyper_register). 10 | 11 | %%-compile(native). 12 | 13 | -export([ 14 | new/1, 15 | set/3, 16 | compact/1, 17 | max_merge/1, max_merge/2, 18 | reduce_precision/2, 19 | bytes/1, 20 | register_sum/1, 21 | register_histogram/1, 22 | zero_count/1, 23 | encode_registers/1, 24 | decode_registers/2, 25 | empty_binary/1, 26 | max_registers/1, 27 | precision/1 28 | ]). 29 | 30 | -define(VALUE_SIZE, 6). 31 | -define(MERGE_THRESHOLD, 0.05). 32 | 33 | -type precision() :: 4..16. 34 | 35 | -record(buffer, {buf, buf_size, p :: precision(), convert_threshold}). 36 | -record(dense, {b, buf, buf_size, p :: precision(), merge_threshold}). 37 | 38 | new(P) -> 39 | new_buffer(P). 40 | 41 | new_buffer(P) -> 42 | M = hyper_utils:m(P), 43 | % 5 words for each entry 44 | ConvertThreshold = M div (5 * 8), 45 | #buffer{ 46 | buf = [], 47 | buf_size = 0, 48 | p = P, 49 | convert_threshold = ConvertThreshold 50 | }. 51 | 52 | new_dense(P) -> 53 | M = hyper_utils:m(P), 54 | T = max(trunc(M * ?MERGE_THRESHOLD), 16), 55 | #dense{ 56 | b = empty_binary(M), 57 | buf = [], 58 | buf_size = 0, 59 | p = P, 60 | merge_threshold = T 61 | }. 62 | 63 | set(Index, Value, #buffer{buf = [{Index, OldValue} | Rest]} = Buffer) -> 64 | NewVal = hyper_utils:run_of_zeroes(Value), 65 | Buffer#buffer{buf = [{Index, max(NewVal, OldValue)} | Rest]}; 66 | set(Index, Value, #buffer{buf = Buf, buf_size = BufSize} = Buffer) -> 67 | NewVal = hyper_utils:run_of_zeroes(Value), 68 | NewBuffer = Buffer#buffer{buf = [{Index, NewVal} | Buf], buf_size = BufSize + 1}, 69 | case NewBuffer#buffer.buf_size < NewBuffer#buffer.convert_threshold of 70 | true -> 71 | NewBuffer; 72 | false -> 73 | buffer2dense(NewBuffer) 74 | end; 75 | set(Index, Value, #dense{buf = Buf, buf_size = BufSize} = Dense) -> 76 | LeftOffset = Index * ?VALUE_SIZE, 77 | <<_:LeftOffset/bitstring, R:?VALUE_SIZE/integer, _/bitstring>> = Dense#dense.b, 78 | 79 | NewVal = hyper_utils:run_of_zeroes(Value), 80 | 81 | if 82 | R < NewVal -> 83 | New = Dense#dense{buf = [{Index, NewVal} | Buf], buf_size = BufSize + 1}, 84 | case New#dense.buf_size < Dense#dense.merge_threshold of 85 | true -> 86 | New; 87 | false -> 88 | compact(New) 89 | end; 90 | true -> 91 | Dense 92 | end. 93 | 94 | compact(#buffer{buf_size = BufSize, convert_threshold = ConvertThreshold} = Buffer) -> 95 | case BufSize < ConvertThreshold of 96 | true -> 97 | Buffer; 98 | false -> 99 | buffer2dense(Buffer) 100 | end; 101 | compact(#dense{b = B, buf = Buf} = Dense) -> 102 | NewB = merge_buf(B, max_registers(Buf)), 103 | Dense#dense{ 104 | b = NewB, 105 | buf = [], 106 | buf_size = 0 107 | }. 108 | 109 | max_merge([First | Rest]) -> 110 | lists:foldl(fun max_merge/2, First, Rest). 111 | 112 | max_merge( 113 | #dense{ 114 | b = SmallB, 115 | buf = SmallBuf, 116 | buf_size = SmallBufSize 117 | }, 118 | #dense{ 119 | b = BigB, 120 | buf = BigBuf, 121 | buf_size = BigBufSize 122 | } = 123 | Big 124 | ) -> 125 | case SmallBufSize + BigBufSize < Big#dense.merge_threshold of 126 | true -> 127 | Merged = do_merge(SmallB, BigB, <<>>), 128 | Big#dense{ 129 | b = Merged, 130 | buf = SmallBuf ++ BigBuf, 131 | buf_size = SmallBufSize + BigBufSize 132 | }; 133 | false -> 134 | BigWithBuf = merge_buf(BigB, max_registers(SmallBuf ++ BigBuf)), 135 | Merged = do_merge(SmallB, BigWithBuf, <<>>), 136 | Big#dense{ 137 | b = Merged, 138 | buf = [], 139 | buf_size = 0 140 | } 141 | end; 142 | max_merge( 143 | #buffer{buf = Buf, buf_size = BufferSize}, 144 | #dense{buf = DenseBuf, buf_size = DenseSize} = Dense 145 | ) -> 146 | case BufferSize + DenseSize < Dense#dense.merge_threshold of 147 | true -> 148 | Dense#dense{buf = Buf ++ DenseBuf, buf_size = BufferSize + DenseSize}; 149 | false -> 150 | Merged = max_registers(DenseBuf ++ Buf), 151 | Dense#dense{buf = Merged, buf_size = length(Merged)} 152 | end; 153 | max_merge(#dense{} = Dense, #buffer{} = Buffer) -> 154 | max_merge(Buffer, Dense); 155 | max_merge( 156 | #buffer{buf = LeftBuf, buf_size = LeftBufSize}, 157 | #buffer{buf = RightBuf, buf_size = RightBufSize} = Right 158 | ) -> 159 | case LeftBufSize + RightBufSize < Right#buffer.convert_threshold of 160 | true -> 161 | Right#buffer{buf = LeftBuf ++ RightBuf, buf_size = LeftBufSize + RightBufSize}; 162 | false -> 163 | Merged = max_registers(LeftBuf ++ RightBuf), 164 | NewRight = Right#buffer{buf = Merged, buf_size = length(Merged)}, 165 | case NewRight#buffer.buf_size < NewRight#buffer.convert_threshold of 166 | true -> 167 | NewRight; 168 | false -> 169 | buffer2dense(NewRight) 170 | end 171 | end. 172 | 173 | reduce_precision(NewP, #dense{p = OldP} = Dense) -> 174 | ChangeP = OldP - NewP, 175 | Buf = register_fold(ChangeP, Dense), 176 | Empty = new_dense(NewP), 177 | compact(Empty#dense{buf = Buf}); 178 | reduce_precision(NewP, #buffer{p = OldP} = Buffer) -> 179 | ChangeP = OldP - NewP, 180 | Buf = register_fold(ChangeP, Buffer), 181 | Empty = new_dense(NewP), 182 | compact(Empty#dense{buf = Buf}). 183 | 184 | %% fold an HLL to a lower number of registers `NewM` by projecting the values 185 | %% onto their new index, see 186 | %% http://research.neustar.biz/2012/09/12/set-operations-on-hlls-of-different-sizes/ 187 | %% NOTE: this function does not perform the max_registers step 188 | register_fold(ChangeP, B) -> 189 | ChangeM = hyper_utils:m(ChangeP), 190 | element( 191 | 3, 192 | fold( 193 | fun 194 | (I, V, {_Index, CurrentList, Acc}) when CurrentList == [] -> 195 | {I, [V], Acc}; 196 | (I, V, {Index, CurrentList, Acc}) when I - Index < ChangeM -> 197 | {Index, [V | CurrentList], Acc}; 198 | (I, V, {_Index, CurrentList, Acc}) -> 199 | {I, [V], [ 200 | {I bsr ChangeP, hyper_utils:changeV(lists:reverse(CurrentList), ChangeP)} 201 | | Acc 202 | ]} 203 | end, 204 | {0, [], []}, 205 | B 206 | ) 207 | ). 208 | 209 | register_sum(B) -> 210 | fold( 211 | fun 212 | (_, 0, Acc) -> 213 | Acc + 1.0; 214 | (_, 1, Acc) -> 215 | Acc + 0.5; 216 | (_, 2, Acc) -> 217 | Acc + 0.25; 218 | (_, 3, Acc) -> 219 | Acc + 0.125; 220 | (_, 4, Acc) -> 221 | Acc + 0.0625; 222 | (_, 5, Acc) -> 223 | Acc + 0.03125; 224 | (_, 6, Acc) -> 225 | Acc + 0.015625; 226 | (_, 7, Acc) -> 227 | Acc + 0.0078125; 228 | (_, 8, Acc) -> 229 | Acc + 0.00390625; 230 | (_, 9, Acc) -> 231 | Acc + 0.001953125; 232 | (_, V, Acc) -> 233 | Acc + math:pow(2, -V) 234 | end, 235 | 0, 236 | B 237 | ). 238 | 239 | register_histogram(#buffer{p = P} = B) -> 240 | register_histogram(B, P); 241 | register_histogram(#dense{p = P} = B) -> 242 | register_histogram(B, P). 243 | 244 | register_histogram(B, P) -> 245 | % todo use from_keys once we are at otp 26 246 | fold( 247 | fun(_, Value, Acc) -> maps:update_with(Value, fun(V) -> V + 1 end, 0, Acc) end, 248 | maps:from_list( 249 | lists:map(fun(I) -> {I, 0} end, lists:seq(0, 65 - P)) 250 | ), 251 | B 252 | ). 253 | 254 | zero_count(B) -> 255 | fold( 256 | fun 257 | (_, 0, Acc) -> 258 | Acc + 1; 259 | (_, _, Acc) -> 260 | Acc 261 | end, 262 | 0, 263 | B 264 | ). 265 | 266 | encode_registers(#buffer{} = Buffer) -> 267 | encode_registers(buffer2dense(Buffer)); 268 | encode_registers(#dense{b = B}) -> 269 | <<<> || <> <= B>>. 270 | 271 | decode_registers(AllBytes, P) -> 272 | M = hyper_utils:m(P), 273 | Bytes = 274 | case AllBytes of 275 | <> -> 276 | B; 277 | <> -> 278 | B 279 | end, 280 | Dense = new_dense(P), 281 | Dense#dense{b = <<<> || <> <= Bytes>>}. 282 | 283 | bytes(#dense{b = B}) -> 284 | erlang:byte_size(B); 285 | bytes(#buffer{} = Buffer) -> 286 | erts_debug:flat_size(Buffer) * 8. 287 | 288 | precision(#dense{p = P}) -> 289 | P; 290 | precision(#buffer{p = P}) -> 291 | P. 292 | 293 | %% 294 | %% INTERNALS 295 | %% 296 | 297 | empty_binary(M) -> 298 | list_to_bitstring([<<0:?VALUE_SIZE/integer>> || _ <- lists:seq(0, M - 1)]). 299 | 300 | max_registers(Buf) -> 301 | lists:keysort( 302 | 1, 303 | maps:to_list( 304 | lists:foldl( 305 | fun({I, V}, Acc) -> 306 | case maps:find(I, Acc) of 307 | {ok, R} when R >= V -> Acc; 308 | _ -> maps:put(I, V, Acc) 309 | end 310 | end, 311 | #{}, 312 | Buf 313 | ) 314 | ) 315 | ). 316 | 317 | buffer2dense(#buffer{buf = Buf, p = P}) -> 318 | Dense = new_dense(P), 319 | Merged = merge_buf(Dense#dense.b, max_registers(Buf)), 320 | Dense#dense{b = Merged}. 321 | 322 | do_merge(<<>>, <<>>, Acc) -> 323 | Acc; 324 | do_merge( 325 | <>, 326 | <>, 327 | Acc 328 | ) -> 329 | do_merge(SmallRest, BigRest, <>). 330 | 331 | fold(F, Acc, #buffer{} = Buffer) -> 332 | % TODO fix this to not need to move to a dense, it is not technically neeeded 333 | % We can just fold on the list i think 334 | fold(F, Acc, buffer2dense(Buffer)); 335 | fold(F, Acc, #dense{b = B, buf = Buf}) -> 336 | do_fold(F, Acc, merge_buf(B, max_registers(Buf)), 0). 337 | 338 | do_fold(_, Acc, <<>>, _) -> 339 | Acc; 340 | do_fold(F, Acc, <>, Index) -> 341 | do_fold(F, F(Index, Value, Acc), Rest, Index + 1). 342 | 343 | merge_buf(B, L) -> 344 | merge_buf(B, L, -1, <<>>). 345 | 346 | merge_buf(B, [], _PrevIndex, Acc) -> 347 | <>; 348 | merge_buf(B, [{Index, Value} | Rest], PrevIndex, Acc) -> 349 | I = (Index - PrevIndex - 1) * ?VALUE_SIZE, 350 | case B of 351 | <> -> 352 | case OldValue < Value of 353 | true -> 354 | NewAcc = <>, 355 | merge_buf(Right, Rest, Index, NewAcc); 356 | false -> 357 | NewAcc = <>, 358 | merge_buf(Right, Rest, Index, NewAcc) 359 | end; 360 | <> -> 361 | <> 362 | end. 363 | -------------------------------------------------------------------------------- /src/hyper_const.erl: -------------------------------------------------------------------------------- 1 | -module(hyper_const). 2 | -export([threshold/1, estimate_data/1, bias_data/1]). 3 | 4 | threshold(4) -> 10; 5 | threshold(5) -> 20; 6 | threshold(6) -> 40; 7 | threshold(7) -> 80; 8 | threshold(8) -> 220; 9 | threshold(9) -> 400; 10 | threshold(10) -> 900; 11 | threshold(11) -> 1800; 12 | threshold(12) -> 3100; 13 | threshold(13) -> 6500; 14 | threshold(14) -> 11500; 15 | threshold(15) -> 20000; 16 | threshold(16) -> 50000. 17 | 18 | 19 | estimate_data(4) -> 20 | {11, 11.717, 12.207, 12.7896, 13.2882, 13.8204, 14.3772, 14.9342, 21 | 15.5202, 16.161, 16.7722, 17.4636, 18.0396, 18.6766, 19.3566, 22 | 20.0454, 20.7936, 21.4856, 22.2666, 22.9946, 23.766, 24.4692, 23 | 25.3638, 26.0764, 26.7864, 27.7602, 28.4814, 29.433, 30.2926, 24 | 31.0664, 31.9996, 32.7956, 33.5366, 34.5894, 35.5738, 36.2698, 25 | 37.3682, 38.0544, 39.2342, 40.0108, 40.7966, 41.9298, 42.8704, 26 | 43.6358, 44.5194, 45.773, 46.6772, 47.6174, 48.4888, 49.3304, 27 | 50.2506, 51.4996, 52.3824, 53.3078, 54.3984, 55.5838, 56.6618, 28 | 57.2174, 58.3514, 59.0802, 60.1482, 61.0376, 62.3598, 62.8078, 29 | 63.9744, 64.914, 65.781, 67.1806, 68.0594, 68.8446, 69.7928, 30 | 70.8248, 71.8324, 72.8598, 73.6246, 74.7014, 75.393, 76.6708, 31 | 77.2394}; 32 | 33 | 34 | estimate_data(5) -> 35 | {23, 23.1194, 23.8208, 24.2318, 24.77, 25.2436, 25.7774, 26.2848, 36 | 26.8224, 27.3742, 27.9336, 28.503, 29.0494, 29.6292, 30.2124, 37 | 30.798, 31.367, 31.9728, 32.5944, 33.217, 33.8438, 34.3696, 38 | 35.0956, 35.7044, 36.324, 37.0668, 37.6698, 38.3644, 39.049, 39 | 39.6918, 40.4146, 41.082, 41.687, 42.5398, 43.2462, 43.857, 40 | 44.6606, 45.4168, 46.1248, 46.9222, 47.6804, 48.447, 49.3454, 41 | 49.9594, 50.7636, 51.5776, 52.331, 53.19, 53.9676, 54.7564, 42 | 55.5314, 56.4442, 57.3708, 57.9774, 58.9624, 59.8796, 60.755, 43 | 61.472, 62.2076, 63.1024, 63.8908, 64.7338, 65.7728, 66.629, 44 | 67.413, 68.3266, 69.1524, 70.2642, 71.1806, 72.0566, 72.9192, 45 | 73.7598, 74.3516, 75.5802, 76.4386, 77.4916, 78.1524, 79.1892, 46 | 79.8414, 80.8798, 81.8376, 82.4698, 83.7656, 84.331, 85.5914, 47 | 86.6012, 87.7016, 88.5582, 89.3394, 90.3544, 91.4912, 92.308, 48 | 93.3552, 93.9746, 95.2052, 95.727, 97.1322, 98.3944, 98.7588, 49 | 100.242, 101.1914, 102.2538, 102.8776, 103.6292, 105.1932, 50 | 105.9152, 107.0868, 107.6728, 108.7144, 110.3114, 110.8716, 51 | 111.245, 112.7908, 113.7064, 114.636, 115.7464, 116.1788, 52 | 117.7464, 118.4896, 119.6166, 120.5082, 121.7798, 122.9028, 53 | 123.4426, 124.8854, 125.705, 126.4652, 128.3464, 128.3462, 54 | 130.0398, 131.0342, 131.0042, 132.4766, 133.511, 134.7252, 55 | 135.425, 136.5172, 138.0572, 138.6694, 139.3712, 140.8598, 56 | 141.4594, 142.554, 143.4006, 144.7374, 146.1634, 146.8994, 57 | 147.605, 147.9304, 149.1636, 150.2468, 151.5876, 152.2096, 58 | 153.7032, 154.7146, 155.807, 156.9228, 157.0372, 158.5852}; 59 | 60 | 61 | estimate_data(6) -> 62 | {46, 46.1902, 47.271, 47.8358, 48.8142, 49.2854, 50.317, 51.354, 63 | 51.8924, 52.9436, 53.4596, 54.5262, 55.6248, 56.1574, 57.2822, 64 | 57.837, 58.9636, 60.074, 60.7042, 61.7976, 62.4772, 63.6564, 65 | 64.7942, 65.5004, 66.686, 67.291, 68.5672, 69.8556, 70.4982, 66 | 71.8204, 72.4252, 73.7744, 75.0786, 75.8344, 77.0294, 77.8098, 67 | 79.0794, 80.5732, 81.1878, 82.5648, 83.2902, 84.6784, 85.3352, 68 | 86.8946, 88.3712, 89.0852, 90.499, 91.2686, 92.6844, 94.2234, 69 | 94.9732, 96.3356, 97.2286, 98.7262, 100.3284, 101.1048, 102.5962, 70 | 103.3562, 105.1272, 106.4184, 107.4974, 109.0822, 109.856, 111.48, 71 | 113.2834, 114.0208, 115.637, 116.5174, 118.0576, 119.7476, 72 | 120.427, 122.1326, 123.2372, 125.2788, 126.6776, 127.7926, 73 | 129.1952, 129.9564, 131.6454, 133.87, 134.5428, 136.2, 137.0294, 74 | 138.6278, 139.6782, 141.792, 143.3516, 144.2832, 146.0394, 75 | 147.0748, 148.4912, 150.849, 151.696, 153.5404, 154.073, 156.3714, 76 | 157.7216, 158.7328, 160.4208, 161.4184, 163.9424, 165.2772, 77 | 166.411, 168.1308, 168.769, 170.9258, 172.6828, 173.7502, 175.706, 78 | 176.3886, 179.0186, 180.4518, 181.927, 183.4172, 184.4114, 79 | 186.033, 188.5124, 189.5564, 191.6008, 192.4172, 193.8044, 80 | 194.997, 197.4548, 198.8948, 200.2346, 202.3086, 203.1548, 81 | 204.8842, 206.6508, 206.6772, 209.7254, 210.4752, 212.7228, 82 | 214.6614, 215.1676, 217.793, 218.0006, 219.9052, 221.66, 223.5588, 83 | 225.1636, 225.6882, 227.7126, 229.4502, 231.1978, 232.9756, 84 | 233.1654, 236.727, 238.1974, 237.7474, 241.1346, 242.3048, 85 | 244.1948, 245.3134, 246.879, 249.1204, 249.853, 252.6792, 253.857, 86 | 254.4486, 257.2362, 257.9534, 260.0286, 260.5632, 262.663, 87 | 264.723, 265.7566, 267.2566, 267.1624, 270.62, 272.8216, 273.2166, 88 | 275.2056, 276.2202, 278.3726, 280.3344, 281.9284, 283.9728, 89 | 284.1924, 286.4872, 287.587, 289.807, 291.1206, 292.769, 294.8708, 90 | 296.665, 297.1182, 299.4012, 300.6352, 302.1354, 304.1756, 91 | 306.1606, 307.3462, 308.5214, 309.4134, 310.8352, 313.9684, 92 | 315.837, 316.7796, 318.9858}; 93 | 94 | estimate_data(7) -> 95 | 96 | {92, 93.4934, 94.9758, 96.4574, 97.9718, 99.4954, 101.5302, 97 | 103.0756, 104.6374, 106.1782, 107.7888, 109.9522, 111.592, 98 | 113.2532, 114.9086, 116.5938, 118.9474, 120.6796, 122.4394, 99 | 124.2176, 125.9768, 128.4214, 130.2528, 132.0102, 133.8658, 100 | 135.7278, 138.3044, 140.1316, 142.093, 144.0032, 145.9092, 101 | 148.6306, 150.5294, 152.5756, 154.6508, 156.662, 159.552, 102 | 161.3724, 163.617, 165.5754, 167.7872, 169.8444, 172.7988, 103 | 174.8606, 177.2118, 179.3566, 181.4476, 184.5882, 186.6816, 104 | 189.0824, 191.0258, 193.6048, 196.4436, 198.7274, 200.957, 105 | 203.147, 205.4364, 208.7592, 211.3386, 213.781, 215.8028, 218.656, 106 | 221.6544, 223.996, 226.4718, 229.1544, 231.6098, 234.5956, 107 | 237.0616, 239.5758, 242.4878, 244.5244, 248.2146, 250.724, 108 | 252.8722, 255.5198, 258.0414, 261.941, 264.9048, 266.87, 269.4304, 109 | 272.028, 274.4708, 278.37, 281.0624, 283.4668, 286.5532, 289.4352, 110 | 293.2564, 295.2744, 298.2118, 300.7472, 304.1456, 307.2928, 111 | 309.7504, 312.5528, 315.979, 318.2102, 322.1834, 324.3494, 112 | 327.325, 330.6614, 332.903, 337.2544, 339.9042, 343.215, 345.2864, 113 | 348.0814, 352.6764, 355.301, 357.139, 360.658, 363.1732, 366.5902, 114 | 369.9538, 373.0828, 375.922, 378.9902, 382.7328, 386.4538, 115 | 388.1136, 391.2234, 394.0878, 396.708, 401.1556, 404.1852, 116 | 406.6372, 409.6822, 412.7796, 416.6078, 418.4916, 422.131, 117 | 424.5376, 428.1988, 432.211, 434.4502, 438.5282, 440.912, 118 | 444.0448, 447.7432, 450.8524, 453.7988, 456.7858, 458.8868, 119 | 463.9886, 466.5064, 468.9124, 472.6616, 475.4682, 478.582, 120 | 481.304, 485.2738, 488.6894, 490.329, 496.106, 497.6908, 501.1374, 121 | 504.5322, 506.8848, 510.3324, 513.4512, 516.179, 520.4412, 122 | 522.6066, 526.167, 528.7794, 533.379, 536.067, 538.46, 542.9116, 123 | 545.692, 547.9546, 552.493, 555.2722, 557.335, 562.449, 564.2014, 124 | 569.0738, 571.0974, 574.8564, 578.2996, 581.409, 583.9704, 125 | 585.8098, 589.6528, 594.5998, 595.958, 600.068, 603.3278, 126 | 608.2016, 609.9632, 612.864, 615.43, 620.7794, 621.272, 625.8644, 127 | 629.206, 633.219, 634.5154, 638.6102}; 128 | 129 | estimate_data(8) -> 130 | 131 | {184.2152, 187.2454, 190.2096, 193.6652, 196.6312, 199.6822, 132 | 203.249, 206.3296, 210.0038, 213.2074, 216.4612, 220.27, 223.5178, 133 | 227.4412, 230.8032, 234.1634, 238.1688, 241.6074, 245.6946, 134 | 249.2664, 252.8228, 257.0432, 260.6824, 264.9464, 268.6268, 135 | 272.2626, 276.8376, 280.4034, 284.8956, 288.8522, 292.7638, 136 | 297.3552, 301.3556, 305.7526, 309.9292, 313.8954, 318.8198, 137 | 322.7668, 327.298, 331.6688, 335.9466, 340.9746, 345.1672, 138 | 349.3474, 354.3028, 358.8912, 364.114, 368.4646, 372.9744, 139 | 378.4092, 382.6022, 387.843, 392.5684, 397.1652, 402.5426, 140 | 407.4152, 412.5388, 417.3592, 422.1366, 427.486, 432.3918, 141 | 437.5076, 442.509, 447.3834, 453.3498, 458.0668, 463.7346, 142 | 469.1228, 473.4528, 479.7, 484.644, 491.0518, 495.5774, 500.9068, 143 | 506.432, 512.1666, 517.434, 522.6644, 527.4894, 533.6312, 144 | 538.3804, 544.292, 550.5496, 556.0234, 562.8206, 566.6146, 145 | 572.4188, 579.117, 583.6762, 590.6576, 595.7864, 601.509, 146 | 607.5334, 612.9204, 619.772, 624.2924, 630.8654, 636.1836, 147 | 642.745, 649.1316, 655.0386, 660.0136, 666.6342, 671.6196, 148 | 678.1866, 684.4282, 689.3324, 695.4794, 702.5038, 708.129, 149 | 713.528, 720.3204, 726.463, 732.7928, 739.123, 744.7418, 751.2192, 150 | 756.5102, 762.6066, 769.0184, 775.2224, 781.4014, 787.7618, 151 | 794.1436, 798.6506, 805.6378, 811.766, 819.7514, 824.5776, 152 | 828.7322, 837.8048, 843.6302, 849.9336, 854.4798, 861.3388, 153 | 867.9894, 873.8196, 880.3136, 886.2308, 892.4588, 899.0816, 154 | 905.4076, 912.0064, 917.3878, 923.619, 929.998, 937.3482, 155 | 943.9506, 947.991, 955.1144, 962.203, 968.8222, 975.7324, 156 | 981.7826, 988.7666, 994.2648, 1000.3128, 1007.4082, 1013.7536, 157 | 1020.3376, 1026.7156, 1031.7478, 1037.4292, 1045.393, 1051.2278, 158 | 1058.3434, 1062.8726, 1071.884, 1076.806, 1082.9176, 1089.1678, 159 | 1095.5032, 1102.525, 1107.2264, 1115.315, 1120.93, 1127.252, 160 | 1134.1496, 1139.0408, 1147.5448, 1153.3296, 1158.1974, 1166.5262, 161 | 1174.3328, 1175.657, 1184.4222, 1190.9172, 1197.1292, 1204.4606, 162 | 1210.4578, 1218.8728, 1225.3336, 1226.6592, 1236.5768, 1241.363, 163 | 1249.4074, 1254.6566, 1260.8014, 1266.5454, 1274.5192}; 164 | 165 | estimate_data(9) -> 166 | 167 | {369, 374.8294, 381.2452, 387.6698, 394.1464, 400.2024, 406.8782, 168 | 413.6598, 420.462, 427.2826, 433.7102, 440.7416, 447.9366, 169 | 455.1046, 462.285, 469.0668, 476.306, 483.8448, 491.301, 498.9886, 170 | 506.2422, 513.8138, 521.7074, 529.7428, 537.8402, 545.1664, 171 | 553.3534, 561.594, 569.6886, 577.7876, 585.65, 594.228, 602.8036, 172 | 611.1666, 620.0818, 628.0824, 637.2574, 646.302, 655.1644, 173 | 664.0056, 672.3802, 681.7192, 690.5234, 700.2084, 708.831, 174 | 718.485, 728.1112, 737.4764, 746.76, 756.3368, 766.5538, 775.5058, 175 | 785.2646, 795.5902, 804.3818, 814.8998, 824.9532, 835.2062, 176 | 845.2798, 854.4728, 864.9582, 875.3292, 886.171, 896.781, 177 | 906.5716, 916.7048, 927.5322, 937.875, 949.3972, 958.3464, 178 | 969.7274, 980.2834, 992.1444, 1003.4264, 1013.0166, 1024.018, 179 | 1035.0438, 1046.34, 1057.6856, 1068.9836, 1079.0312, 1091.677, 180 | 1102.3188, 1113.4846, 1124.4424, 1135.739, 1147.1488, 1158.9202, 181 | 1169.406, 1181.5342, 1193.2834, 1203.8954, 1216.3286, 1226.2146, 182 | 1239.6684, 1251.9946, 1262.123, 1275.4338, 1285.7378, 1296.076, 183 | 1308.9692, 1320.4964, 1333.0998, 1343.9864, 1357.7754, 1368.3208, 184 | 1380.4838, 1392.7388, 1406.0758, 1416.9098, 1428.9728, 1440.9228, 185 | 1453.9292, 1462.617, 1476.05, 1490.2996, 1500.6128, 1513.7392, 186 | 1524.5174, 1536.6322, 1548.2584, 1562.3766, 1572.423, 1587.1232, 187 | 1596.5164, 1610.5938, 1622.5972, 1633.1222, 1647.7674, 1658.5044, 188 | 1671.57, 1683.7044, 1695.4142, 1708.7102, 1720.6094, 1732.6522, 189 | 1747.841, 1756.4072, 1769.9786, 1782.3276, 1797.5216, 1808.3186, 190 | 1819.0694, 1834.354, 1844.575, 1856.2808, 1871.1288, 1880.7852, 191 | 1893.9622, 1906.3418, 1920.6548, 1932.9302, 1945.8584, 1955.473, 192 | 1968.8248, 1980.6446, 1995.9598, 2008.349, 2019.8556, 2033.0334, 193 | 2044.0206, 2059.3956, 2069.9174, 2082.6084, 2093.7036, 2106.6108, 194 | 2118.9124, 2132.301, 2144.7628, 2159.8422, 2171.0212, 2183.101, 195 | 2193.5112, 2208.052, 2221.3194, 2233.3282, 2247.295, 2257.7222, 196 | 2273.342, 2286.5638, 2299.6786, 2310.8114, 2322.3312, 2335.516, 197 | 2349.874, 2363.5968, 2373.865, 2387.1918, 2401.8328, 2414.8496, 198 | 2424.544, 2436.7592, 2447.1682, 2464.1958, 2474.3438, 2489.0006, 199 | 2497.4526, 2513.6586, 2527.19, 2540.7028, 2553.768}; 200 | 201 | estimate_data(10) -> 202 | 203 | {738.1256, 750.4234, 763.1064, 775.4732, 788.4636, 801.0644, 204 | 814.488, 827.9654, 841.0832, 854.7864, 868.1992, 882.2176, 205 | 896.5228, 910.1716, 924.7752, 938.899, 953.6126, 968.6492, 206 | 982.9474, 998.5214, 1013.1064, 1028.6364, 1044.2468, 1059.4588, 207 | 1075.3832, 1091.0584, 1106.8606, 1123.3868, 1139.5062, 1156.1862, 208 | 1172.463, 1189.339, 1206.1936, 1223.1292, 1240.1854, 1257.2908, 209 | 1275.3324, 1292.8518, 1310.5204, 1328.4854, 1345.9318, 1364.552, 210 | 1381.4658, 1400.4256, 1419.849, 1438.152, 1456.8956, 1474.8792, 211 | 1494.118, 1513.62, 1532.5132, 1551.9322, 1570.7726, 1590.6086, 212 | 1610.5332, 1630.5918, 1650.4294, 1669.7662, 1690.4106, 1710.7338, 213 | 1730.9012, 1750.4486, 1770.1556, 1791.6338, 1812.7312, 1833.6264, 214 | 1853.9526, 1874.8742, 1896.8326, 1918.1966, 1939.5594, 1961.07, 215 | 1983.037, 2003.1804, 2026.071, 2047.4884, 2070.0848, 2091.2944, 216 | 2114.333, 2135.9626, 2158.2902, 2181.0814, 2202.0334, 2224.4832, 217 | 2246.39, 2269.7202, 2292.1714, 2314.2358, 2338.9346, 2360.891, 218 | 2384.0264, 2408.3834, 2430.1544, 2454.8684, 2476.9896, 2501.4368, 219 | 2522.8702, 2548.0408, 2570.6738, 2593.5208, 2617.0158, 2640.2302, 220 | 2664.0962, 2687.4986, 2714.2588, 2735.3914, 2759.6244, 2781.8378, 221 | 2808.0072, 2830.6516, 2856.2454, 2877.2136, 2903.4546, 2926.785, 222 | 2951.2294, 2976.468, 3000.867, 3023.6508, 3049.91, 3073.5984, 223 | 3098.162, 3121.5564, 3146.2328, 3170.9484, 3195.5902, 3221.3346, 224 | 3242.7032, 3271.6112, 3296.5546, 3317.7376, 3345.072, 3369.9518, 225 | 3394.326, 3418.1818, 3444.6926, 3469.086, 3494.2754, 3517.8698, 226 | 3544.248, 3565.3768, 3588.7234, 3616.979, 3643.7504, 3668.6812, 227 | 3695.72, 3719.7392, 3742.6224, 3770.4456, 3795.6602, 3819.9058, 228 | 3844.002, 3869.517, 3895.6824, 3920.8622, 3947.1364, 3973.985, 229 | 3995.4772, 4021.62, 4046.628, 4074.65, 4096.2256, 4121.831, 230 | 4146.6406, 4173.276, 4195.0744, 4223.9696, 4251.3708, 4272.9966, 231 | 4300.8046, 4326.302, 4353.1248, 4374.312, 4403.0322, 4426.819, 232 | 4450.0598, 4478.5206, 4504.8116, 4528.8928, 4553.9584, 4578.8712, 233 | 4603.8384, 4632.3872, 4655.5128, 4675.821, 4704.6222, 4731.9862, 234 | 4755.4174, 4781.2628, 4804.332, 4832.3048, 4862.8752, 4883.4148, 235 | 4906.9544, 4935.3516, 4954.3532, 4984.0248, 5011.217, 5035.3258, 236 | 5057.3672, 5084.1828}; 237 | 238 | 239 | estimate_data(11) -> 240 | 241 | {1477, 1501.6014, 1526.5802, 1551.7942, 1577.3042, 1603.2062, 242 | 1629.8402, 1656.2292, 1682.9462, 1709.9926, 1737.3026, 1765.4252, 243 | 1793.0578, 1821.6092, 1849.626, 1878.5568, 1908.527, 1937.5154, 244 | 1967.1874, 1997.3878, 2027.37, 2058.1972, 2089.5728, 2120.1012, 245 | 2151.9668, 2183.292, 2216.0772, 2247.8578, 2280.6562, 2313.041, 246 | 2345.714, 2380.3112, 2414.1806, 2447.9854, 2481.656, 2516.346, 247 | 2551.5154, 2586.8378, 2621.7448, 2656.6722, 2693.5722, 2729.1462, 248 | 2765.4124, 2802.8728, 2838.898, 2876.408, 2913.4926, 2951.4938, 249 | 2989.6776, 3026.282, 3065.7704, 3104.1012, 3143.7388, 3181.6876, 250 | 3221.1872, 3261.5048, 3300.0214, 3339.806, 3381.409, 3421.4144, 251 | 3461.4294, 3502.2286, 3544.651, 3586.6156, 3627.337, 3670.083, 252 | 3711.1538, 3753.5094, 3797.01, 3838.6686, 3882.1678, 3922.8116, 253 | 3967.9978, 4009.9204, 4054.3286, 4097.5706, 4140.6014, 4185.544, 254 | 4229.5976, 4274.583, 4316.9438, 4361.672, 4406.2786, 4451.8628, 255 | 4496.1834, 4543.505, 4589.1816, 4632.5188, 4678.2294, 4724.8908, 256 | 4769.0194, 4817.052, 4861.4588, 4910.1596, 4956.4344, 5002.5238, 257 | 5048.13, 5093.6374, 5142.8162, 5187.7894, 5237.3984, 5285.6078, 258 | 5331.0858, 5379.1036, 5428.6258, 5474.6018, 5522.7618, 5571.5822, 259 | 5618.59, 5667.9992, 5714.88, 5763.454, 5808.6982, 5860.3644, 260 | 5910.2914, 5953.571, 6005.9232, 6055.1914, 6104.5882, 6154.5702, 261 | 6199.7036, 6251.1764, 6298.7596, 6350.0302, 6398.061, 6448.4694, 262 | 6495.933, 6548.0474, 6597.7166, 6646.9416, 6695.9208, 6742.6328, 263 | 6793.5276, 6842.1934, 6894.2372, 6945.3864, 6996.9228, 7044.2372, 264 | 7094.1374, 7142.2272, 7192.2942, 7238.8338, 7288.9006, 7344.0908, 265 | 7394.8544, 7443.5176, 7490.4148, 7542.9314, 7595.6738, 7641.9878, 266 | 7694.3688, 7743.0448, 7797.522, 7845.53, 7899.594, 7950.3132, 267 | 7996.455, 8050.9442, 8092.9114, 8153.1374, 8197.4472, 8252.8278, 268 | 8301.8728, 8348.6776, 8401.4698, 8453.551, 8504.6598, 8553.8944, 269 | 8604.1276, 8657.6514, 8710.3062, 8758.908, 8807.8706, 8862.1702, 270 | 8910.4668, 8960.77, 9007.2766, 9063.164, 9121.0534, 9164.1354, 271 | 9218.1594, 9267.767, 9319.0594, 9372.155, 9419.7126, 9474.3722, 272 | 9520.1338, 9572.368, 9622.7702, 9675.8448, 9726.5396, 9778.7378, 273 | 9827.6554, 9878.1922, 9928.7782, 9978.3984, 10026.578, 10076.5626, 274 | 10137.1618, 10177.5244, 10229.9176}; 275 | 276 | estimate_data(12) -> 277 | 278 | {2954, 3003.4782, 3053.3568, 3104.3666, 3155.324, 3206.9598, 279 | 3259.648, 3312.539, 3366.1474, 3420.2576, 3474.8376, 3530.6076, 280 | 3586.451, 3643.38, 3700.4104, 3757.5638, 3815.9676, 3875.193, 281 | 3934.838, 3994.8548, 4055.018, 4117.1742, 4178.4482, 4241.1294, 282 | 4304.4776, 4367.4044, 4431.8724, 4496.3732, 4561.4304, 4627.5326, 283 | 4693.949, 4761.5532, 4828.7256, 4897.6182, 4965.5186, 5034.4528, 284 | 5104.865, 5174.7164, 5244.6828, 5316.6708, 5387.8312, 5459.9036, 285 | 5532.476, 5604.8652, 5679.6718, 5753.757, 5830.2072, 5905.2828, 286 | 5980.0434, 6056.6264, 6134.3192, 6211.5746, 6290.0816, 6367.1176, 287 | 6447.9796, 6526.5576, 6606.1858, 6686.9144, 6766.1142, 6847.0818, 288 | 6927.9664, 7010.9096, 7091.0816, 7175.3962, 7260.3454, 7344.018, 289 | 7426.4214, 7511.3106, 7596.0686, 7679.8094, 7765.818, 7852.4248, 290 | 7936.834, 8022.363, 8109.5066, 8200.4554, 8288.5832, 8373.366, 291 | 8463.4808, 8549.7682, 8642.0522, 8728.3288, 8820.9528, 8907.727, 292 | 9001.0794, 9091.2522, 9179.988, 9269.852, 9362.6394, 9453.642, 293 | 9546.9024, 9640.6616, 9732.6622, 9824.3254, 9917.7484, 10007.9392, 294 | 10106.7508, 10196.2152, 10289.8114, 10383.5494, 10482.3064, 295 | 10576.8734, 10668.7872, 10764.7156, 10862.0196, 10952.793, 296 | 11049.9748, 11146.0702, 11241.4492, 11339.2772, 11434.2336, 297 | 11530.741, 11627.6136, 11726.311, 11821.5964, 11918.837, 298 | 12015.3724, 12113.0162, 12213.0424, 12306.9804, 12408.4518, 299 | 12504.8968, 12604.586, 12700.9332, 12798.705, 12898.5142, 300 | 12997.0488, 13094.788, 13198.475, 13292.7764, 13392.9698, 301 | 13486.8574, 13590.1616, 13686.5838, 13783.6264, 13887.2638, 302 | 13992.0978, 14081.0844, 14189.9956, 14280.0912, 14382.4956, 303 | 14486.4384, 14588.1082, 14686.2392, 14782.276, 14888.0284, 304 | 14985.1864, 15088.8596, 15187.0998, 15285.027, 15383.6694, 305 | 15495.8266, 15591.3736, 15694.2008, 15790.3246, 15898.4116, 306 | 15997.4522, 16095.5014, 16198.8514, 16291.7492, 16402.6424, 307 | 16499.1266, 16606.2436, 16697.7186, 16796.3946, 16902.3376, 308 | 17005.7672, 17100.814, 17206.8282, 17305.8262, 17416.0744, 309 | 17508.4092, 17617.0178, 17715.4554, 17816.758, 17920.1748, 310 | 18012.9236, 18119.7984, 18223.2248, 18324.2482, 18426.6276, 311 | 18525.0932, 18629.8976, 18733.2588, 18831.0466, 18940.1366, 312 | 19032.2696, 19131.729, 19243.4864, 19349.6932, 19442.866, 313 | 19547.9448, 19653.2798, 19754.4034, 19854.0692, 19965.1224, 314 | 20065.1774, 20158.2212, 20253.353, 20366.3264, 20463.22}; 315 | 316 | estimate_data(13) -> 317 | 318 | {5908.5052, 6007.2672, 6107.347, 6208.5794, 6311.2622, 6414.5514, 319 | 6519.3376, 6625.6952, 6732.5988, 6841.3552, 6950.5972, 7061.3082, 320 | 7173.5646, 7287.109, 7401.8216, 7516.4344, 7633.3802, 7751.2962, 321 | 7870.3784, 7990.292, 8110.79, 8233.4574, 8356.6036, 8482.2712, 322 | 8607.7708, 8735.099, 8863.1858, 8993.4746, 9123.8496, 9255.6794, 323 | 9388.5448, 9522.7516, 9657.3106, 9792.6094, 9930.5642, 10068.794, 324 | 10206.7256, 10347.81, 10490.3196, 10632.0778, 10775.9916, 325 | 10920.4662, 11066.124, 11213.073, 11358.0362, 11508.1006, 326 | 11659.1716, 11808.7514, 11959.4884, 12112.1314, 12265.037, 327 | 12420.3756, 12578.933, 12734.311, 12890.0006, 13047.2144, 328 | 13207.3096, 13368.5144, 13528.024, 13689.847, 13852.7528, 329 | 14018.3168, 14180.5372, 14346.9668, 14513.5074, 14677.867, 330 | 14846.2186, 15017.4186, 15184.9716, 15356.339, 15529.2972, 331 | 15697.3578, 15871.8686, 16042.187, 16216.4094, 16389.4188, 332 | 16565.9126, 16742.3272, 16919.0042, 17094.7592, 17273.965, 333 | 17451.8342, 17634.4254, 17810.5984, 17988.9242, 18171.051, 334 | 18354.7938, 18539.466, 18721.0408, 18904.9972, 19081.867, 335 | 19271.9118, 19451.8694, 19637.9816, 19821.2922, 20013.1292, 336 | 20199.3858, 20387.8726, 20572.9514, 20770.7764, 20955.1714, 337 | 21144.751, 21329.9952, 21520.709, 21712.7016, 21906.3868, 338 | 22096.2626, 22286.0524, 22475.051, 22665.5098, 22862.8492, 339 | 23055.5294, 23249.6138, 23437.848, 23636.273, 23826.093, 340 | 24020.3296, 24213.3896, 24411.7392, 24602.9614, 24805.7952, 341 | 24998.1552, 25193.9588, 25389.0166, 25585.8392, 25780.6976, 342 | 25981.2728, 26175.977, 26376.5252, 26570.1964, 26773.387, 343 | 26962.9812, 27163.0586, 27368.164, 27565.0534, 27758.7428, 344 | 27961.1276, 28163.2324, 28362.3816, 28565.7668, 28758.644, 345 | 28956.9768, 29163.4722, 29354.7026, 29561.1186, 29767.9948, 346 | 29959.9986, 30164.0492, 30366.9818, 30562.5338, 30762.9928, 347 | 30976.1592, 31166.274, 31376.722, 31570.3734, 31770.809, 348 | 31974.8934, 32179.5286, 32387.5442, 32582.3504, 32794.076, 349 | 32989.9528, 33191.842, 33392.4684, 33595.659, 33801.8672, 350 | 34000.3414, 34200.0922, 34402.6792, 34610.0638, 34804.0084, 351 | 35011.13, 35218.669, 35418.6634, 35619.0792, 35830.6534, 352 | 36028.4966, 36229.7902, 36438.6422, 36630.7764, 36833.3102, 353 | 37048.6728, 37247.3916, 37453.5904, 37669.3614, 37854.5526, 354 | 38059.305, 38268.0936, 38470.2516, 38674.7064, 38876.167, 355 | 39068.3794, 39281.9144, 39492.8566, 39684.8628, 39898.4108, 356 | 40093.1836, 40297.6858, 40489.7086, 40717.2424}; 357 | 358 | estimate_data(14) -> 359 | 360 | {11817.475, 12015.0046, 12215.3792, 12417.7504, 12623.1814, 361 | 12830.0086, 13040.0072, 13252.503, 13466.178, 13683.2738, 362 | 13902.0344, 14123.9798, 14347.394, 14573.7784, 14802.6894, 363 | 15033.6824, 15266.9134, 15502.8624, 15741.4944, 15980.7956, 364 | 16223.8916, 16468.6316, 16715.733, 16965.5726, 17217.204, 365 | 17470.666, 17727.8516, 17986.7886, 18247.6902, 18510.9632, 366 | 18775.304, 19044.7486, 19314.4408, 19587.202, 19862.2576, 367 | 20135.924, 20417.0324, 20697.9788, 20979.6112, 21265.0274, 368 | 21550.723, 21841.6906, 22132.162, 22428.1406, 22722.127, 369 | 23020.5606, 23319.7394, 23620.4014, 23925.2728, 24226.9224, 370 | 24535.581, 24845.505, 25155.9618, 25470.3828, 25785.9702, 371 | 26103.7764, 26420.4132, 26742.0186, 27062.8852, 27388.415, 372 | 27714.6024, 28042.296, 28365.4494, 28701.1526, 29031.8008, 373 | 29364.2156, 29704.497, 30037.1458, 30380.111, 30723.8168, 374 | 31059.5114, 31404.9498, 31751.6752, 32095.2686, 32444.7792, 375 | 32794.767, 33145.204, 33498.4226, 33847.6502, 34209.006, 376 | 34560.849, 34919.4838, 35274.9778, 35635.1322, 35996.3266, 377 | 36359.1394, 36722.8266, 37082.8516, 37447.7354, 37815.9606, 378 | 38191.0692, 38559.4106, 38924.8112, 39294.6726, 39663.973, 379 | 40042.261, 40416.2036, 40779.2036, 41161.6436, 41540.9014, 380 | 41921.1998, 42294.7698, 42678.5264, 43061.3464, 43432.375, 381 | 43818.432, 44198.6598, 44583.0138, 44970.4794, 45353.924, 382 | 45729.858, 46118.2224, 46511.5724, 46900.7386, 47280.6964, 383 | 47668.1472, 48055.6796, 48446.9436, 48838.7146, 49217.7296, 384 | 49613.7796, 50010.7508, 50410.0208, 50793.7886, 51190.2456, 385 | 51583.1882, 51971.0796, 52376.5338, 52763.319, 53165.5534, 386 | 53556.5594, 53948.2702, 54346.352, 54748.7914, 55138.577, 387 | 55543.4824, 55941.1748, 56333.7746, 56745.1552, 57142.7944, 388 | 57545.2236, 57935.9956, 58348.5268, 58737.5474, 59158.5962, 389 | 59542.6896, 59958.8004, 60349.3788, 60755.0212, 61147.6144, 390 | 61548.194, 61946.0696, 62348.6042, 62763.603, 63162.781, 391 | 63560.635, 63974.3482, 64366.4908, 64771.5876, 65176.7346, 392 | 65597.3916, 65995.915, 66394.0384, 66822.9396, 67203.6336, 393 | 67612.2032, 68019.0078, 68420.0388, 68821.22, 69235.8388, 394 | 69640.0724, 70055.155, 70466.357, 70863.4266, 71276.2482, 395 | 71677.0306, 72080.2006, 72493.0214, 72893.5952, 73314.5856, 396 | 73714.9852, 74125.3022, 74521.2122, 74933.6814, 75341.5904, 397 | 75743.0244, 76166.0278, 76572.1322, 76973.1028, 77381.6284, 398 | 77800.6092, 78189.328, 78607.0962, 79012.2508, 79407.8358, 399 | 79825.725, 80238.701, 80646.891, 81035.6436, 81460.0448, 400 | 81876.3884}; 401 | 402 | estimate_data(15) -> 403 | 404 | {23635.0036, 24030.8034, 24431.4744, 24837.1524, 25246.7928, 405 | 25661.326, 26081.3532, 26505.2806, 26933.9892, 27367.7098, 406 | 27805.318, 28248.799, 28696.4382, 29148.8244, 29605.5138, 407 | 30066.8668, 30534.2344, 31006.32, 31480.778, 31962.2418, 408 | 32447.3324, 32938.0232, 33432.731, 33930.728, 34433.9896, 409 | 34944.1402, 35457.5588, 35974.5958, 36497.3296, 37021.9096, 410 | 37554.326, 38088.0826, 38628.8816, 39171.3192, 39723.2326, 411 | 40274.5554, 40832.3142, 41390.613, 41959.5908, 42532.5466, 412 | 43102.0344, 43683.5072, 44266.694, 44851.2822, 45440.7862, 413 | 46038.0586, 46640.3164, 47241.064, 47846.155, 48454.7396, 414 | 49076.9168, 49692.542, 50317.4778, 50939.65, 51572.5596, 415 | 52210.2906, 52843.7396, 53481.3996, 54127.236, 54770.406, 416 | 55422.6598, 56078.7958, 56736.7174, 57397.6784, 58064.5784, 417 | 58730.308, 59404.9784, 60077.0864, 60751.9158, 61444.1386, 418 | 62115.817, 62808.7742, 63501.4774, 64187.5454, 64883.6622, 419 | 65582.7468, 66274.5318, 66976.9276, 67688.7764, 68402.138, 420 | 69109.6274, 69822.9706, 70543.6108, 71265.5202, 71983.3848, 421 | 72708.4656, 73433.384, 74158.4664, 74896.4868, 75620.9564, 422 | 76362.1434, 77098.3204, 77835.7662, 78582.6114, 79323.9902, 423 | 80067.8658, 80814.9246, 81567.0136, 82310.8536, 83061.9952, 424 | 83821.4096, 84580.8608, 85335.547, 86092.5802, 86851.6506, 425 | 87612.311, 88381.2016, 89146.3296, 89907.8974, 90676.846, 426 | 91451.4152, 92224.5518, 92995.8686, 93763.5066, 94551.2796, 427 | 95315.1944, 96096.1806, 96881.0918, 97665.679, 98442.68, 428 | 99229.3002, 100011.0994, 100790.6386, 101580.1564, 102377.7484, 429 | 103152.1392, 103944.2712, 104730.216, 105528.6336, 106324.9398, 430 | 107117.6706, 107890.3988, 108695.2266, 109485.238, 110294.7876, 431 | 111075.0958, 111878.0496, 112695.2864, 113464.5486, 114270.0474, 432 | 115068.608, 115884.3626, 116673.2588, 117483.3716, 118275.097, 433 | 119085.4092, 119879.2808, 120687.5868, 121499.9944, 122284.916, 434 | 123095.9254, 123912.5038, 124709.0454, 125503.7182, 126323.259, 435 | 127138.9412, 127943.8294, 128755.646, 129556.5354, 130375.3298, 436 | 131161.4734, 131971.1962, 132787.5458, 133588.1056, 134431.351, 437 | 135220.2906, 136023.398, 136846.6558, 137667.0004, 138463.663, 438 | 139283.7154, 140074.6146, 140901.3072, 141721.8548, 142543.2322, 439 | 143356.1096, 144173.7412, 144973.0948, 145794.3162, 146609.5714, 440 | 147420.003, 148237.9784, 149050.5696, 149854.761, 150663.1966, 441 | 151494.0754, 152313.1416, 153112.6902, 153935.7206, 154746.9262, 442 | 155559.547, 156401.9746, 157228.7036, 158008.7254, 158820.75, 443 | 159646.9184, 160470.4458, 161279.5348, 162093.3114, 162918.542, 444 | 163729.2842}; 445 | 446 | 447 | estimate_data(16) -> 448 | 449 | {47271, 48062.3584, 48862.7074, 49673.152, 50492.8416, 51322.9514, 450 | 52161.03, 53009.407, 53867.6348, 54734.206, 55610.5144, 451 | 56496.2096, 57390.795, 58297.268, 59210.6448, 60134.665, 452 | 61068.0248, 62010.4472, 62962.5204, 63923.5742, 64895.0194, 453 | 65876.4182, 66862.6136, 67862.6968, 68868.8908, 69882.8544, 454 | 70911.271, 71944.0924, 72990.0326, 74040.692, 75100.6336, 455 | 76174.7826, 77252.5998, 78340.2974, 79438.2572, 80545.4976, 456 | 81657.2796, 82784.6336, 83915.515, 85059.7362, 86205.9368, 457 | 87364.4424, 88530.3358, 89707.3744, 90885.9638, 92080.197, 458 | 93275.5738, 94479.391, 95695.918, 96919.2236, 98148.4602, 459 | 99382.3474, 100625.6974, 101878.0284, 103141.6278, 104409.4588, 460 | 105686.2882, 106967.5402, 108261.6032, 109548.1578, 110852.0728, 461 | 112162.231, 113479.0072, 114806.2626, 116137.9072, 117469.5048, 462 | 118813.5186, 120165.4876, 121516.2556, 122875.766, 124250.5444, 463 | 125621.2222, 127003.2352, 128387.848, 129775.2644, 131181.7776, 464 | 132577.3086, 133979.9458, 135394.1132, 136800.9078, 138233.217, 465 | 139668.5308, 141085.212, 142535.2122, 143969.0684, 145420.2872, 466 | 146878.1542, 148332.7572, 149800.3202, 151269.66, 152743.6104, 467 | 154213.0948, 155690.288, 157169.4246, 158672.1756, 160160.059, 468 | 161650.6854, 163145.7772, 164645.6726, 166159.1952, 167682.1578, 469 | 169177.3328, 170700.0118, 172228.8964, 173732.6664, 175265.5556, 470 | 176787.799, 178317.111, 179856.6914, 181400.865, 182943.4612, 471 | 184486.742, 186033.4698, 187583.7886, 189148.1868, 190688.4526, 472 | 192250.1926, 193810.9042, 195354.2972, 196938.7682, 198493.5898, 473 | 200079.2824, 201618.912, 203205.5492, 204765.5798, 206356.1124, 474 | 207929.3064, 209498.7196, 211086.229, 212675.1324, 214256.7892, 475 | 215826.2392, 217412.8474, 218995.6724, 220618.6038, 222207.1166, 476 | 223781.0364, 225387.4332, 227005.7928, 228590.4336, 230217.8738, 477 | 231805.1054, 233408.9, 234995.3432, 236601.4956, 238190.7904, 478 | 239817.2548, 241411.2832, 243002.4066, 244640.1884, 246255.3128, 479 | 247849.3508, 249479.9734, 251106.8822, 252705.027, 254332.9242, 480 | 255935.129, 257526.9014, 259154.772, 260777.625, 262390.253, 481 | 264004.4906, 265643.59, 267255.4076, 268873.426, 270470.7252, 482 | 272106.4804, 273722.4456, 275337.794, 276945.7038, 278592.9154, 483 | 280204.3726, 281841.1606, 283489.171, 285130.1716, 286735.3362, 484 | 288364.7164, 289961.1814, 291595.5524, 293285.683, 294899.6668, 485 | 296499.3434, 298128.0462, 299761.8946, 301394.2424, 302997.6748, 486 | 304615.1478, 306269.7724, 307886.114, 309543.1028, 311153.2862, 487 | 312782.8546, 314421.2008, 316033.2438, 317692.9636, 319305.2648, 488 | 320948.7406, 322566.3364, 324228.4224, 325847.1542}. 489 | 490 | 491 | bias_data(4) -> 492 | 493 | {10, 9.717, 9.207, 8.7896, 8.2882, 7.8204, 7.3772, 6.9342, 6.5202, 494 | 6.161, 5.7722, 5.4636, 5.0396, 4.6766, 4.3566, 4.0454, 3.7936, 495 | 3.4856, 3.2666, 2.9946, 2.766, 2.4692, 2.3638, 2.0764, 1.7864, 496 | 1.7602, 1.4814, 1.433, 1.2926, 1.0664, 0.999600000000001, 0.7956, 497 | 0.5366, 0.589399999999998, 0.573799999999999, 0.269799999999996, 498 | 0.368200000000002, 0.0544000000000011, 0.234200000000001, 499 | 0.0108000000000033, -0.203400000000002, -0.0701999999999998, 500 | -0.129600000000003, -0.364199999999997, -0.480600000000003, 501 | -0.226999999999997, -0.322800000000001, -0.382599999999996, 502 | -0.511200000000002, -0.669600000000003, -0.749400000000001, 503 | -0.500399999999999, -0.617600000000003, -0.6922, 504 | -0.601599999999998, -0.416200000000003, -0.338200000000001, 505 | -0.782600000000002, -0.648600000000002, -0.919800000000002, 506 | -0.851799999999997, -0.962400000000002, -0.6402, -1.1922, -1.0256, 507 | -1.086, -1.21899999999999, -0.819400000000002, -0.940600000000003, 508 | -1.1554, -1.2072, -1.1752, -1.16759999999999, -1.14019999999999, 509 | -1.3754, -1.29859999999999, -1.607, -1.3292, -1.7606}; 510 | 511 | 512 | bias_data(5) -> 513 | 514 | {22, 21.1194, 20.8208, 20.2318, 19.77, 19.2436, 18.7774, 18.2848, 515 | 17.8224, 17.3742, 16.9336, 16.503, 16.0494, 15.6292, 15.2124, 516 | 14.798, 14.367, 13.9728, 13.5944, 13.217, 12.8438, 12.3696, 517 | 12.0956, 11.7044, 11.324, 11.0668, 10.6698, 10.3644, 10.049, 518 | 9.6918, 9.4146, 9.082, 8.687, 8.5398, 8.2462, 7.857, 7.6606, 519 | 7.4168, 7.1248, 6.9222, 6.6804, 6.447, 6.3454, 5.9594, 5.7636, 520 | 5.5776, 5.331, 5.19, 4.9676, 4.7564, 4.5314, 4.4442, 4.3708, 521 | 3.9774, 3.9624, 3.8796, 3.755, 3.472, 3.2076, 3.1024, 2.8908, 522 | 2.7338, 2.7728, 2.629, 2.413, 2.3266, 2.1524, 2.2642, 2.1806, 523 | 2.0566, 1.9192, 1.7598, 1.3516, 1.5802, 1.43859999999999, 524 | 1.49160000000001, 1.1524, 1.1892, 0.841399999999993, 525 | 0.879800000000003, 0.837599999999995, 0.469800000000006, 526 | 0.765600000000006, 0.331000000000003, 0.591399999999993, 527 | 0.601200000000006, 0.701599999999999, 0.558199999999999, 528 | 0.339399999999998, 0.354399999999998, 0.491200000000006, 529 | 0.308000000000007, 0.355199999999996, -0.0254000000000048, 530 | 0.205200000000005, -0.272999999999996, 0.132199999999997, 531 | 0.394400000000005, -0.241200000000006, 0.242000000000004, 532 | 0.191400000000002, 0.253799999999998, -0.122399999999999, 533 | -0.370800000000003, 0.193200000000004, -0.0848000000000013, 534 | 0.0867999999999967, -0.327200000000005, -0.285600000000002, 535 | 0.311400000000006, -0.128399999999999, -0.754999999999995, 536 | -0.209199999999996, -0.293599999999998, -0.364000000000004, 537 | -0.253600000000006, -0.821200000000005, -0.253600000000006, 538 | -0.510400000000004, -0.383399999999995, -0.491799999999998, 539 | -0.220200000000006, -0.0972000000000008, -0.557400000000001, 540 | -0.114599999999996, -0.295000000000002, -0.534800000000004, 541 | 0.346399999999988, -0.65379999999999, 0.0398000000000138, 542 | 0.0341999999999985, -0.995800000000003, -0.523400000000009, 543 | -0.489000000000004, -0.274799999999999, -0.574999999999989, 544 | -0.482799999999997, 0.0571999999999946, -0.330600000000004, 545 | -0.628800000000012, -0.140199999999993, -0.540600000000012, 546 | -0.445999999999998, -0.599400000000003, -0.262599999999992, 547 | 0.163399999999996, -0.100599999999986, -0.39500000000001, 548 | -1.06960000000001, -0.836399999999998, -0.753199999999993, 549 | -0.412399999999991, -0.790400000000005, -0.29679999999999, 550 | -0.28540000000001, -0.193000000000012, -0.0772000000000048, 551 | -0.962799999999987, -0.414800000000014}; 552 | 553 | bias_data(6) -> 554 | 555 | {45, 44.1902, 43.271, 42.8358, 41.8142, 41.2854, 40.317, 39.354, 556 | 38.8924, 37.9436, 37.4596, 36.5262, 35.6248, 35.1574, 34.2822, 557 | 33.837, 32.9636, 32.074, 31.7042, 30.7976, 30.4772, 29.6564, 558 | 28.7942, 28.5004, 27.686, 27.291, 26.5672, 25.8556, 25.4982, 559 | 24.8204, 24.4252, 23.7744, 23.0786, 22.8344, 22.0294, 21.8098, 560 | 21.0794, 20.5732, 20.1878, 19.5648, 19.2902, 18.6784, 18.3352, 561 | 17.8946, 17.3712, 17.0852, 16.499, 16.2686, 15.6844, 15.2234, 562 | 14.9732, 14.3356, 14.2286, 13.7262, 13.3284, 13.1048, 12.5962, 563 | 12.3562, 12.1272, 11.4184, 11.4974, 11.0822, 10.856, 10.48, 564 | 10.2834, 10.0208, 9.637, 9.51739999999999, 9.05759999999999, 565 | 8.74760000000001, 8.42700000000001, 8.1326, 8.2372, 8.2788, 566 | 7.6776, 7.79259999999999, 7.1952, 6.9564, 6.6454, 6.87, 6.5428, 567 | 6.19999999999999, 6.02940000000001, 5.62780000000001, 5.6782, 568 | 5.792, 5.35159999999999, 5.28319999999999, 5.0394, 569 | 5.07480000000001, 4.49119999999999, 4.84899999999999, 4.696, 570 | 4.54040000000001, 4.07300000000001, 4.37139999999999, 3.7216, 571 | 3.7328, 3.42080000000001, 3.41839999999999, 3.94239999999999, 572 | 3.27719999999999, 3.411, 3.13079999999999, 2.76900000000001, 573 | 2.92580000000001, 2.68279999999999, 2.75020000000001, 574 | 2.70599999999999, 2.3886, 3.01859999999999, 2.45179999999999, 575 | 2.92699999999999, 2.41720000000001, 2.41139999999999, 576 | 2.03299999999999, 2.51240000000001, 2.5564, 2.60079999999999, 577 | 2.41720000000001, 1.80439999999999, 1.99700000000001, 578 | 2.45480000000001, 1.8948, 2.2346, 2.30860000000001, 579 | 2.15479999999999, 1.88419999999999, 1.6508, 0.677199999999999, 580 | 1.72540000000001, 1.4752, 1.72280000000001, 1.66139999999999, 581 | 1.16759999999999, 1.79300000000001, 1.00059999999999, 582 | 0.905200000000008, 0.659999999999997, 1.55879999999999, 1.1636, 583 | 0.688199999999995, 0.712600000000009, 0.450199999999995, 1.1978, 584 | 0.975599999999986, 0.165400000000005, 1.727, 1.19739999999999, 585 | -0.252600000000001, 1.13460000000001, 1.3048, 1.19479999999999, 586 | 0.313400000000001, 0.878999999999991, 1.12039999999999, 587 | 0.853000000000009, 1.67920000000001, 0.856999999999999, 588 | 0.448599999999999, 1.2362, 0.953399999999988, 1.02859999999998, 589 | 0.563199999999995, 0.663000000000011, 0.723000000000013, 590 | 0.756599999999992, 0.256599999999992, -0.837600000000009, 591 | 0.620000000000005, 0.821599999999989, 0.216600000000028, 592 | 0.205600000000004, 0.220199999999977, 0.372599999999977, 593 | 0.334400000000016, 0.928400000000011, 0.972800000000007, 594 | 0.192400000000021, 0.487199999999973, -0.413000000000011, 595 | 0.807000000000016, 0.120600000000024, 0.769000000000005, 596 | 0.870799999999974, 0.66500000000002, 0.118200000000002, 597 | 0.401200000000017, 0.635199999999998, 0.135400000000004, 598 | 0.175599999999974, 1.16059999999999, 0.34620000000001, 599 | 0.521400000000028, -0.586599999999976, -1.16480000000001, 600 | 0.968399999999974, 0.836999999999989, 0.779600000000016, 601 | 0.985799999999983}; 602 | 603 | bias_data(7) -> 604 | 605 | {91, 89.4934, 87.9758, 86.4574, 84.9718, 83.4954, 81.5302, 606 | 80.0756, 78.6374, 77.1782, 75.7888, 73.9522, 72.592, 71.2532, 607 | 69.9086, 68.5938, 66.9474, 65.6796, 64.4394, 63.2176, 61.9768, 608 | 60.4214, 59.2528, 58.0102, 56.8658, 55.7278, 54.3044, 53.1316, 609 | 52.093, 51.0032, 49.9092, 48.6306, 47.5294, 46.5756, 45.6508, 610 | 44.662, 43.552, 42.3724, 41.617, 40.5754, 39.7872, 38.8444, 611 | 37.7988, 36.8606, 36.2118, 35.3566, 34.4476, 33.5882, 32.6816, 612 | 32.0824, 31.0258, 30.6048, 29.4436, 28.7274, 27.957, 27.147, 613 | 26.4364, 25.7592, 25.3386, 24.781, 23.8028, 23.656, 22.6544, 614 | 21.996, 21.4718, 21.1544, 20.6098, 19.5956, 19.0616, 18.5758, 615 | 18.4878, 17.5244, 17.2146, 16.724, 15.8722, 15.5198, 15.0414, 616 | 14.941, 14.9048, 13.87, 13.4304, 13.028, 12.4708, 12.37, 12.0624, 617 | 11.4668, 11.5532, 11.4352, 11.2564, 10.2744, 10.2118, 618 | 9.74720000000002, 10.1456, 9.2928, 8.75040000000001, 619 | 8.55279999999999, 8.97899999999998, 8.21019999999999, 620 | 8.18340000000001, 7.3494, 7.32499999999999, 7.66140000000001, 621 | 6.90300000000002, 7.25439999999998, 6.9042, 7.21499999999997, 622 | 6.28640000000001, 6.08139999999997, 6.6764, 6.30099999999999, 623 | 5.13900000000001, 5.65800000000002, 5.17320000000001, 624 | 4.59019999999998, 4.9538, 5.08280000000002, 4.92200000000003, 625 | 4.99020000000002, 4.7328, 5.4538, 4.11360000000002, 626 | 4.22340000000003, 4.08780000000002, 3.70800000000003, 627 | 4.15559999999999, 4.18520000000001, 3.63720000000001, 628 | 3.68220000000002, 3.77960000000002, 3.6078, 2.49160000000001, 629 | 3.13099999999997, 2.5376, 3.19880000000001, 3.21100000000001, 630 | 2.4502, 3.52820000000003, 2.91199999999998, 3.04480000000001, 631 | 2.7432, 2.85239999999999, 2.79880000000003, 2.78579999999999, 632 | 1.88679999999999, 2.98860000000002, 2.50639999999999, 633 | 1.91239999999999, 2.66160000000002, 2.46820000000002, 634 | 1.58199999999999, 1.30399999999997, 2.27379999999999, 635 | 2.68939999999998, 1.32900000000001, 3.10599999999999, 636 | 1.69080000000002, 2.13740000000001, 2.53219999999999, 637 | 1.88479999999998, 1.33240000000001, 1.45119999999997, 638 | 1.17899999999997, 2.44119999999998, 1.60659999999996, 639 | 2.16700000000003, 0.77940000000001, 2.37900000000002, 640 | 2.06700000000001, 1.46000000000004, 2.91160000000002, 641 | 1.69200000000001, 0.954600000000028, 2.49300000000005, 2.2722, 642 | 1.33500000000004, 2.44899999999996, 1.20140000000004, 643 | 3.07380000000001, 2.09739999999999, 2.85640000000001, 644 | 2.29960000000005, 2.40899999999999, 1.97040000000004, 645 | 0.809799999999996, 1.65279999999996, 2.59979999999996, 646 | 0.95799999999997, 2.06799999999998, 2.32780000000002, 647 | 4.20159999999998, 1.96320000000003, 1.86400000000003, 648 | 1.42999999999995, 3.77940000000001, 1.27200000000005, 649 | 1.86440000000005, 2.20600000000002, 3.21900000000005, 1.5154, 650 | 2.61019999999996}; 651 | 652 | bias_data(8) -> 653 | 654 | {183.2152, 180.2454, 177.2096, 173.6652, 170.6312, 167.6822, 655 | 164.249, 161.3296, 158.0038, 155.2074, 152.4612, 149.27, 146.5178, 656 | 143.4412, 140.8032, 138.1634, 135.1688, 132.6074, 129.6946, 657 | 127.2664, 124.8228, 122.0432, 119.6824, 116.9464, 114.6268, 658 | 112.2626, 109.8376, 107.4034, 104.8956, 102.8522, 100.7638, 659 | 98.3552, 96.3556, 93.7526, 91.9292, 89.8954, 87.8198, 85.7668, 660 | 83.298, 81.6688, 79.9466, 77.9746, 76.1672, 74.3474, 72.3028, 661 | 70.8912, 69.114, 67.4646, 65.9744, 64.4092, 62.6022, 60.843, 662 | 59.5684, 58.1652, 56.5426, 55.4152, 53.5388, 52.3592, 51.1366, 663 | 49.486, 48.3918, 46.5076, 45.509, 44.3834, 43.3498, 42.0668, 664 | 40.7346, 40.1228, 38.4528, 37.7, 36.644, 36.0518, 34.5774, 665 | 33.9068, 32.432, 32.1666, 30.434, 29.6644, 28.4894, 27.6312, 666 | 26.3804, 26.292, 25.5496000000001, 25.0234, 24.8206, 22.6146, 667 | 22.4188, 22.117, 20.6762, 20.6576, 19.7864, 19.509, 18.5334, 668 | 17.9204, 17.772, 16.2924, 16.8654, 15.1836, 15.745, 15.1316, 669 | 15.0386, 14.0136, 13.6342, 12.6196, 12.1866, 12.4281999999999, 670 | 11.3324, 10.4794000000001, 11.5038, 10.129, 9.52800000000002, 671 | 10.3203999999999, 9.46299999999997, 9.79280000000006, 672 | 9.12300000000005, 8.74180000000001, 9.2192, 7.51020000000005, 673 | 7.60659999999996, 7.01840000000004, 7.22239999999999, 674 | 7.40139999999997, 6.76179999999999, 7.14359999999999, 675 | 5.65060000000005, 5.63779999999997, 5.76599999999996, 676 | 6.75139999999999, 5.57759999999996, 3.73220000000003, 5.8048, 677 | 5.63019999999995, 4.93359999999996, 3.47979999999995, 678 | 4.33879999999999, 3.98940000000005, 3.81960000000004, 679 | 3.31359999999995, 3.23080000000004, 3.4588, 3.08159999999998, 680 | 3.4076, 3.00639999999999, 2.38779999999997, 2.61900000000003, 681 | 1.99800000000005, 3.34820000000002, 2.95060000000001, 682 | 0.990999999999985, 2.11440000000005, 2.20299999999997, 683 | 2.82219999999995, 2.73239999999998, 2.7826, 3.76660000000004, 684 | 2.26480000000004, 2.31280000000004, 2.40819999999997, 685 | 2.75360000000001, 3.33759999999995, 2.71559999999999, 686 | 1.7478000000001, 1.42920000000004, 2.39300000000003, 687 | 2.22779999999989, 2.34339999999997, 0.87259999999992, 688 | 3.88400000000001, 1.80600000000004, 1.91759999999999, 689 | 1.16779999999994, 1.50320000000011, 2.52500000000009, 690 | 0.226400000000012, 2.31500000000005, 0.930000000000064, 691 | 1.25199999999995, 2.14959999999996, 0.0407999999999902, 692 | 2.5447999999999, 1.32960000000003, 0.197400000000016, 693 | 2.52620000000002, 3.33279999999991, -1.34300000000007, 694 | 0.422199999999975, 0.917200000000093, 1.12920000000008, 695 | 1.46060000000011, 1.45779999999991, 2.8728000000001, 696 | 3.33359999999993, -1.34079999999994, 1.57680000000005, 697 | 0.363000000000056, 1.40740000000005, 0.656600000000026, 698 | 0.801400000000058, -0.454600000000028, 1.51919999999996}; 699 | 700 | bias_data(9) -> 701 | 702 | {368, 361.8294, 355.2452, 348.6698, 342.1464, 336.2024, 329.8782, 703 | 323.6598, 317.462, 311.2826, 305.7102, 299.7416, 293.9366, 704 | 288.1046, 282.285, 277.0668, 271.306, 265.8448, 260.301, 254.9886, 705 | 250.2422, 244.8138, 239.7074, 234.7428, 229.8402, 225.1664, 706 | 220.3534, 215.594, 210.6886, 205.7876, 201.65, 197.228, 192.8036, 707 | 188.1666, 184.0818, 180.0824, 176.2574, 172.302, 168.1644, 708 | 164.0056, 160.3802, 156.7192, 152.5234, 149.2084, 145.831, 709 | 142.485, 139.1112, 135.4764, 131.76, 129.3368, 126.5538, 122.5058, 710 | 119.2646, 116.5902, 113.3818, 110.8998, 107.9532, 105.2062, 711 | 102.2798, 99.4728, 96.9582, 94.3292, 92.171, 89.7809999999999, 712 | 87.5716, 84.7048, 82.5322, 79.875, 78.3972, 75.3464, 73.7274, 713 | 71.2834, 70.1444, 68.4263999999999, 66.0166, 64.018, 714 | 62.0437999999999, 60.3399999999999, 58.6856, 57.9836, 715 | 55.0311999999999, 54.6769999999999, 52.3188, 51.4846, 716 | 49.4423999999999, 47.739, 46.1487999999999, 44.9202, 717 | 43.4059999999999, 42.5342000000001, 41.2834, 38.8954000000001, 718 | 38.3286000000001, 36.2146, 36.6684, 35.9946, 33.123, 33.4338, 719 | 31.7378000000001, 29.076, 28.9692, 27.4964, 27.0998, 25.9864, 720 | 26.7754, 24.3208, 23.4838, 22.7388000000001, 24.0758000000001, 721 | 21.9097999999999, 20.9728, 19.9228000000001, 19.9292, 16.617, 722 | 17.05, 18.2996000000001, 15.6128000000001, 15.7392, 14.5174, 723 | 13.6322, 12.2583999999999, 13.3766000000001, 11.423, 13.1232, 724 | 9.51639999999998, 10.5938000000001, 9.59719999999993, 725 | 8.12220000000002, 9.76739999999995, 7.50440000000003, 726 | 7.56999999999994, 6.70440000000008, 6.41419999999994, 727 | 6.71019999999999, 5.60940000000005, 4.65219999999999, 728 | 6.84099999999989, 3.4072000000001, 3.97859999999991, 729 | 3.32760000000007, 5.52160000000003, 3.31860000000006, 730 | 2.06940000000009, 4.35400000000004, 1.57500000000005, 731 | 0.280799999999999, 2.12879999999996, -0.214799999999968, 732 | -0.0378000000000611, -0.658200000000079, 0.654800000000023, 733 | -0.0697999999999865, 0.858400000000074, -2.52700000000004, 734 | -2.1751999999999, -3.35539999999992, -1.04019999999991, 735 | -0.651000000000067, -2.14439999999991, -1.96659999999997, 736 | -3.97939999999994, -0.604400000000169, -3.08260000000018, 737 | -3.39159999999993, -5.29640000000018, -5.38920000000007, 738 | -5.08759999999984, -4.69900000000007, -5.23720000000003, 739 | -3.15779999999995, -4.97879999999986, -4.89899999999989, 740 | -7.48880000000008, -5.94799999999987, -5.68060000000014, 741 | -6.67180000000008, -4.70499999999993, -7.27779999999984, 742 | -4.6579999999999, -4.4362000000001, -4.32139999999981, 743 | -5.18859999999995, -6.66879999999992, -6.48399999999992, 744 | -5.1260000000002, -4.4032000000002, -6.13500000000022, 745 | -5.80819999999994, -4.16719999999987, -4.15039999999999, 746 | -7.45600000000013, -7.24080000000004, -9.83179999999993, 747 | -5.80420000000004, -8.6561999999999, -6.99940000000015, 748 | -10.5473999999999, -7.34139999999979, -6.80999999999995, 749 | -6.29719999999998, -6.23199999999997}; 750 | 751 | bias_data(10) -> 752 | 753 | {737.1256, 724.4234, 711.1064, 698.4732, 685.4636, 673.0644, 754 | 660.488, 647.9654, 636.0832, 623.7864, 612.1992, 600.2176, 755 | 588.5228, 577.1716, 565.7752, 554.899, 543.6126, 532.6492, 756 | 521.9474, 511.5214, 501.1064, 490.6364, 480.2468, 470.4588, 757 | 460.3832, 451.0584, 440.8606, 431.3868, 422.5062, 413.1862, 758 | 404.463, 395.339, 386.1936, 378.1292, 369.1854, 361.2908, 759 | 353.3324, 344.8518, 337.5204, 329.4854, 321.9318, 314.552, 760 | 306.4658, 299.4256, 292.849, 286.152, 278.8956, 271.8792, 265.118, 761 | 258.62, 252.5132, 245.9322, 239.7726, 233.6086, 227.5332, 762 | 222.5918, 216.4294, 210.7662, 205.4106, 199.7338, 194.9012, 763 | 188.4486, 183.1556, 178.6338, 173.7312, 169.6264, 163.9526, 764 | 159.8742, 155.8326, 151.1966, 147.5594, 143.07, 140.037, 134.1804, 765 | 131.071, 127.4884, 124.0848, 120.2944, 117.333, 112.9626, 766 | 110.2902, 107.0814, 103.0334, 99.4832000000001, 96.3899999999999, 767 | 93.7202000000002, 90.1714000000002, 87.2357999999999, 85.9346, 768 | 82.8910000000001, 80.0264000000002, 78.3834000000002, 769 | 75.1543999999999, 73.8683999999998, 70.9895999999999, 770 | 69.4367999999999, 64.8701999999998, 65.0408000000002, 61.6738, 771 | 59.5207999999998, 57.0158000000001, 54.2302, 53.0962, 772 | 50.4985999999999, 52.2588000000001, 47.3914, 45.6244000000002, 773 | 42.8377999999998, 43.0072, 40.6516000000001, 40.2453999999998, 774 | 35.2136, 36.4546, 33.7849999999999, 33.2294000000002, 775 | 32.4679999999998, 30.8670000000002, 28.6507999999999, 776 | 28.9099999999999, 27.5983999999999, 26.1619999999998, 777 | 24.5563999999999, 23.2328000000002, 21.9484000000002, 778 | 21.5902000000001, 21.3346000000001, 17.7031999999999, 779 | 20.6111999999998, 19.5545999999999, 15.7375999999999, 780 | 17.0720000000001, 16.9517999999998, 15.326, 13.1817999999998, 781 | 14.6925999999999, 13.0859999999998, 13.2754, 10.8697999999999, 782 | 11.248, 7.3768, 4.72339999999986, 7.97899999999981, 783 | 8.7503999999999, 7.68119999999999, 9.7199999999998, 784 | 7.73919999999998, 5.6224000000002, 7.44560000000001, 785 | 6.6601999999998, 5.9058, 4.00199999999995, 4.51699999999983, 786 | 4.68240000000014, 3.86220000000003, 5.13639999999987, 787 | 5.98500000000013, 2.47719999999981, 2.61999999999989, 788 | 1.62800000000016, 4.65000000000009, 0.225599999999758, 789 | 0.831000000000131, -0.359400000000278, 1.27599999999984, 790 | -2.92559999999958, -0.0303999999996449, 2.37079999999969, 791 | -2.0033999999996, 0.804600000000391, 0.30199999999968, 792 | 1.1247999999996, -2.6880000000001, 0.0321999999996478, 793 | -1.18099999999959, -3.9402, -1.47940000000017, -0.188400000000001, 794 | -2.10720000000038, -2.04159999999956, -3.12880000000041, 795 | -4.16160000000036, -0.612799999999879, -3.48719999999958, 796 | -8.17900000000009, -5.37780000000021, -4.01379999999972, 797 | -5.58259999999973, -5.73719999999958, -7.66799999999967, 798 | -5.69520000000011, -1.1247999999996, -5.58520000000044, 799 | -8.04560000000038, -4.64840000000004, -11.6468000000004, 800 | -7.97519999999986, -5.78300000000036, -7.67420000000038, 801 | -10.6328000000003, -9.81720000000041}; 802 | 803 | bias_data(11) -> 804 | 805 | {1476, 1449.6014, 1423.5802, 1397.7942, 1372.3042, 1347.2062, 806 | 1321.8402, 1297.2292, 1272.9462, 1248.9926, 1225.3026, 1201.4252, 807 | 1178.0578, 1155.6092, 1132.626, 1110.5568, 1088.527, 1066.5154, 808 | 1045.1874, 1024.3878, 1003.37, 982.1972, 962.5728, 942.1012, 809 | 922.9668, 903.292, 884.0772, 864.8578, 846.6562, 828.041, 809.714, 810 | 792.3112, 775.1806, 757.9854, 740.656, 724.346, 707.5154, 811 | 691.8378, 675.7448, 659.6722, 645.5722, 630.1462, 614.4124, 812 | 600.8728, 585.898, 572.408, 558.4926, 544.4938, 531.6776, 517.282, 813 | 505.7704, 493.1012, 480.7388, 467.6876, 456.1872, 445.5048, 814 | 433.0214, 420.806, 411.409, 400.4144, 389.4294, 379.2286, 369.651, 815 | 360.6156, 350.337, 342.083, 332.1538, 322.5094, 315.01, 305.6686, 816 | 298.1678, 287.8116, 280.9978, 271.9204, 265.3286, 257.5706, 817 | 249.6014, 242.544, 235.5976, 229.583, 220.9438, 214.672, 208.2786, 818 | 201.8628, 195.1834, 191.505, 186.1816, 178.5188, 172.2294, 819 | 167.8908, 161.0194, 158.052, 151.4588, 148.1596, 143.4344, 820 | 138.5238, 133.13, 127.6374, 124.8162, 118.7894, 117.3984, 821 | 114.6078, 109.0858, 105.1036, 103.6258, 98.6018000000004, 822 | 95.7618000000002, 93.5821999999998, 88.5900000000001, 823 | 86.9992000000002, 82.8800000000001, 80.4539999999997, 824 | 74.6981999999998, 74.3644000000004, 73.2914000000001, 825 | 65.5709999999999, 66.9232000000002, 65.1913999999997, 826 | 62.5882000000001, 61.5702000000001, 55.7035999999998, 827 | 56.1764000000003, 52.7596000000003, 53.0302000000001, 828 | 49.0609999999997, 48.4694, 44.933, 46.0474000000004, 829 | 44.7165999999997, 41.9416000000001, 39.9207999999999, 830 | 35.6328000000003, 35.5276000000003, 33.1934000000001, 831 | 33.2371999999996, 33.3864000000003, 33.9228000000003, 832 | 30.2371999999996, 29.1373999999996, 25.2272000000003, 833 | 24.2942000000003, 19.8338000000003, 18.9005999999999, 834 | 23.0907999999999, 21.8544000000002, 19.5176000000001, 835 | 15.4147999999996, 16.9314000000004, 18.6737999999996, 836 | 12.9877999999999, 14.3688000000002, 12.0447999999997, 837 | 15.5219999999999, 12.5299999999997, 14.5940000000001, 838 | 14.3131999999996, 9.45499999999993, 12.9441999999999, 839 | 3.91139999999996, 13.1373999999996, 5.44720000000052, 840 | 9.82779999999912, 7.87279999999919, 3.67760000000089, 841 | 5.46980000000076, 5.55099999999948, 5.65979999999945, 842 | 3.89439999999922, 3.1275999999998, 5.65140000000065, 843 | 6.3062000000009, 3.90799999999945, 1.87060000000019, 844 | 5.17020000000048, 2.46680000000015, 0.770000000000437, 845 | -3.72340000000077, 1.16400000000067, 8.05340000000069, 846 | 0.135399999999208, 2.15940000000046, 0.766999999999825, 847 | 1.0594000000001, 3.15500000000065, -0.287399999999252, 848 | 2.37219999999979, -2.86620000000039, -1.63199999999961, 849 | -2.22979999999916, -0.15519999999924, -1.46039999999994, 850 | -0.262199999999211, -2.34460000000036, -2.8078000000005, 851 | -3.22179999999935, -5.60159999999996, -8.42200000000048, 852 | -9.43740000000071, 0.161799999999857, -10.4755999999998, 853 | -10.0823999999993}; 854 | 855 | bias_data(12) -> 856 | 857 | {2953, 2900.4782, 2848.3568, 2796.3666, 2745.324, 2694.9598, 858 | 2644.648, 2595.539, 2546.1474, 2498.2576, 2450.8376, 2403.6076, 859 | 2357.451, 2311.38, 2266.4104, 2221.5638, 2176.9676, 2134.193, 860 | 2090.838, 2048.8548, 2007.018, 1966.1742, 1925.4482, 1885.1294, 861 | 1846.4776, 1807.4044, 1768.8724, 1731.3732, 1693.4304, 1657.5326, 862 | 1621.949, 1586.5532, 1551.7256, 1517.6182, 1483.5186, 1450.4528, 863 | 1417.865, 1385.7164, 1352.6828, 1322.6708, 1291.8312, 1260.9036, 864 | 1231.476, 1201.8652, 1173.6718, 1145.757, 1119.2072, 1092.2828, 865 | 1065.0434, 1038.6264, 1014.3192, 988.5746, 965.0816, 940.1176, 866 | 917.9796, 894.5576, 871.1858, 849.9144, 827.1142, 805.0818, 867 | 783.9664, 763.9096, 742.0816, 724.3962, 706.3454, 688.018, 868 | 667.4214, 650.3106, 633.0686, 613.8094, 597.818, 581.4248, 869 | 563.834, 547.363, 531.5066, 520.455400000001, 505.583199999999, 870 | 488.366, 476.480799999999, 459.7682, 450.0522, 434.328799999999, 871 | 423.952799999999, 408.727000000001, 399.079400000001, 872 | 387.252200000001, 373.987999999999, 360.852000000001, 351.6394, 873 | 339.642, 330.902400000001, 322.661599999999, 311.662200000001, 874 | 301.3254, 291.7484, 279.939200000001, 276.7508, 263.215200000001, 875 | 254.811400000001, 245.5494, 242.306399999999, 234.8734, 876 | 223.787200000001, 217.7156, 212.0196, 200.793, 195.9748, 189.0702, 877 | 182.449199999999, 177.2772, 170.2336, 164.741, 158.613600000001, 878 | 155.311, 147.5964, 142.837, 137.3724, 132.0162, 130.0424, 879 | 121.9804, 120.451800000001, 114.8968, 111.585999999999, 880 | 105.933199999999, 101.705, 98.5141999999996, 95.0488000000005, 881 | 89.7880000000005, 91.4750000000004, 83.7764000000006, 882 | 80.9698000000008, 72.8574000000008, 73.1615999999995, 883 | 67.5838000000003, 62.6263999999992, 63.2638000000006, 884 | 66.0977999999996, 52.0843999999997, 58.9956000000002, 885 | 47.0912000000008, 46.4956000000002, 48.4383999999991, 886 | 47.1082000000006, 43.2392, 37.2759999999998, 40.0283999999992, 887 | 35.1864000000005, 35.8595999999998, 32.0998, 28.027, 888 | 23.6694000000007, 33.8266000000003, 26.3736000000008, 889 | 27.2008000000005, 21.3245999999999, 26.4115999999995, 890 | 23.4521999999997, 19.5013999999992, 19.8513999999996, 891 | 10.7492000000002, 18.6424000000006, 13.1265999999996, 892 | 18.2436000000016, 6.71860000000015, 3.39459999999963, 893 | 6.33759999999893, 7.76719999999841, 0.813999999998487, 894 | 3.82819999999992, 0.826199999999517, 8.07440000000133, 895 | -1.59080000000176, 5.01780000000144, 0.455399999998917, 896 | -0.24199999999837, 0.174800000000687, -9.07640000000174, 897 | -4.20160000000033, -3.77520000000004, -4.75179999999818, 898 | -5.3724000000002, -8.90680000000066, -6.10239999999976, 899 | -5.74120000000039, -9.95339999999851, -3.86339999999836, 900 | -13.7304000000004, -16.2710000000006, -7.51359999999841, 901 | -3.30679999999847, -13.1339999999982, -10.0551999999989, 902 | -6.72019999999975, -8.59660000000076, -10.9307999999983, 903 | -1.8775999999998, -4.82259999999951, -13.7788, -21.6470000000008, 904 | -10.6735999999983, -15.7799999999988}; 905 | 906 | bias_data(13) -> 907 | 908 | {5907.5052, 5802.2672, 5697.347, 5593.5794, 5491.2622, 5390.5514, 909 | 5290.3376, 5191.6952, 5093.5988, 4997.3552, 4902.5972, 4808.3082, 910 | 4715.5646, 4624.109, 4533.8216, 4444.4344, 4356.3802, 4269.2962, 911 | 4183.3784, 4098.292, 4014.79, 3932.4574, 3850.6036, 3771.2712, 912 | 3691.7708, 3615.099, 3538.1858, 3463.4746, 3388.8496, 3315.6794, 913 | 3244.5448, 3173.7516, 3103.3106, 3033.6094, 2966.5642, 2900.794, 914 | 2833.7256, 2769.81, 2707.3196, 2644.0778, 2583.9916, 2523.4662, 915 | 2464.124, 2406.073, 2347.0362, 2292.1006, 2238.1716, 2182.7514, 916 | 2128.4884, 2077.1314, 2025.037, 1975.3756, 1928.933, 1879.311, 917 | 1831.0006, 1783.2144, 1738.3096, 1694.5144, 1649.024, 1606.847, 918 | 1564.7528, 1525.3168, 1482.5372, 1443.9668, 1406.5074, 1365.867, 919 | 1329.2186, 1295.4186, 1257.9716, 1225.339, 1193.2972, 1156.3578, 920 | 1125.8686, 1091.187, 1061.4094, 1029.4188, 1000.9126, 972.3272, 921 | 944.004199999999, 915.7592, 889.965, 862.834200000001, 840.4254, 922 | 812.598399999999, 785.924200000001, 763.050999999999, 923 | 741.793799999999, 721.466, 699.040799999999, 677.997200000002, 924 | 649.866999999998, 634.911800000002, 609.8694, 591.981599999999, 925 | 570.2922, 557.129199999999, 538.3858, 521.872599999999, 926 | 502.951400000002, 495.776399999999, 475.171399999999, 459.751, 927 | 439.995200000001, 426.708999999999, 413.7016, 402.3868, 928 | 387.262599999998, 372.0524, 357.050999999999, 342.5098, 929 | 334.849200000001, 322.529399999999, 311.613799999999, 930 | 295.848000000002, 289.273000000001, 274.093000000001, 931 | 263.329600000001, 251.389599999999, 245.7392, 231.9614, 229.7952, 932 | 217.155200000001, 208.9588, 199.016599999999, 190.839199999999, 933 | 180.6976, 176.272799999999, 166.976999999999, 162.5252, 934 | 151.196400000001, 149.386999999999, 133.981199999998, 130.0586, 935 | 130.164000000001, 122.053400000001, 110.7428, 108.1276, 936 | 106.232400000001, 100.381600000001, 98.7668000000012, 937 | 86.6440000000002, 79.9768000000004, 82.4722000000002, 938 | 68.7026000000005, 70.1186000000016, 71.9948000000004, 939 | 58.998599999999, 59.0492000000013, 56.9818000000014, 940 | 47.5338000000011, 42.9928, 51.1591999999982, 37.2740000000013, 941 | 42.7220000000016, 31.3734000000004, 26.8090000000011, 942 | 25.8934000000008, 26.5286000000015, 29.5442000000003, 943 | 19.3503999999994, 26.0760000000009, 17.9527999999991, 944 | 14.8419999999969, 10.4683999999979, 8.65899999999965, 945 | 9.86720000000059, 4.34139999999752, -0.907800000000861, 946 | -3.32080000000133, -0.936199999996461, -11.9916000000012, 947 | -8.87000000000262, -6.33099999999831, -11.3366000000024, 948 | -15.9207999999999, -9.34659999999712, -15.5034000000014, 949 | -19.2097999999969, -15.357799999998, -28.2235999999975, 950 | -30.6898000000001, -19.3271999999997, -25.6083999999973, 951 | -24.409599999999, -13.6385999999984, -33.4473999999973, 952 | -32.6949999999997, -28.9063999999998, -31.7483999999968, 953 | -32.2935999999972, -35.8329999999987, -47.620600000002, 954 | -39.0855999999985, -33.1434000000008, -46.1371999999974, 955 | -37.5892000000022, -46.8164000000033, -47.3142000000007, 956 | -60.2914000000019, -37.7575999999972}; 957 | 958 | bias_data(14) -> 959 | 960 | {11816.475, 11605.0046, 11395.3792, 11188.7504, 10984.1814, 961 | 10782.0086, 10582.0072, 10384.503, 10189.178, 9996.2738, 962 | 9806.0344, 9617.9798, 9431.394, 9248.7784, 9067.6894, 8889.6824, 963 | 8712.9134, 8538.8624, 8368.4944, 8197.7956, 8031.8916, 7866.6316, 964 | 7703.733, 7544.5726, 7386.204, 7230.666, 7077.8516, 6926.7886, 965 | 6778.6902, 6631.9632, 6487.304, 6346.7486, 6206.4408, 6070.202, 966 | 5935.2576, 5799.924, 5671.0324, 5541.9788, 5414.6112, 5290.0274, 967 | 5166.723, 5047.6906, 4929.162, 4815.1406, 4699.127, 4588.5606, 968 | 4477.7394, 4369.4014, 4264.2728, 4155.9224, 4055.581, 3955.505, 969 | 3856.9618, 3761.3828, 3666.9702, 3575.7764, 3482.4132, 3395.0186, 970 | 3305.8852, 3221.415, 3138.6024, 3056.296, 2970.4494, 2896.1526, 971 | 2816.8008, 2740.2156, 2670.497, 2594.1458, 2527.111, 2460.8168, 972 | 2387.5114, 2322.9498, 2260.6752, 2194.2686, 2133.7792, 2074.767, 973 | 2015.204, 1959.4226, 1898.6502, 1850.006, 1792.849, 1741.4838, 974 | 1687.9778, 1638.1322, 1589.3266, 1543.1394, 1496.8266, 1447.8516, 975 | 1402.7354, 1361.9606, 1327.0692, 1285.4106, 1241.8112, 1201.6726, 976 | 1161.973, 1130.261, 1094.2036, 1048.2036, 1020.6436, 977 | 990.901400000002, 961.199800000002, 924.769800000002, 978 | 899.526400000002, 872.346400000002, 834.375, 810.432000000001, 979 | 780.659800000001, 756.013800000001, 733.479399999997, 980 | 707.923999999999, 673.858, 652.222399999999, 636.572399999997, 981 | 615.738599999997, 586.696400000001, 564.147199999999, 982 | 541.679600000003, 523.943599999999, 505.714599999999, 983 | 475.729599999999, 461.779600000002, 449.750800000002, 984 | 439.020799999998, 412.7886, 400.245600000002, 383.188199999997, 985 | 362.079599999997, 357.533799999997, 334.319000000003, 986 | 327.553399999997, 308.559399999998, 291.270199999999, 987 | 279.351999999999, 271.791400000002, 252.576999999997, 988 | 247.482400000001, 236.174800000001, 218.774599999997, 989 | 220.155200000001, 208.794399999999, 201.223599999998, 990 | 182.995600000002, 185.5268, 164.547400000003, 176.5962, 991 | 150.689599999998, 157.8004, 138.378799999999, 134.021200000003, 992 | 117.614399999999, 108.194000000003, 97.0696000000025, 993 | 89.6042000000016, 95.6030000000028, 84.7810000000027, 994 | 72.635000000002, 77.3482000000004, 59.4907999999996, 995 | 55.5875999999989, 50.7346000000034, 61.3916000000027, 996 | 50.9149999999936, 39.0384000000049, 58.9395999999979, 997 | 29.633600000001, 28.2032000000036, 26.0078000000067, 998 | 17.0387999999948, 9.22000000000116, 13.8387999999977, 999 | 8.07240000000456, 14.1549999999988, 15.3570000000036, 1000 | 3.42660000000615, 6.24820000000182, -2.96940000000177, 1001 | -8.79940000000352, -5.97860000000219, -14.4048000000039, 1002 | -3.4143999999942, -13.0148000000045, -11.6977999999945, 1003 | -25.7878000000055, -22.3185999999987, -24.409599999999, 1004 | -31.9756000000052, -18.9722000000038, -22.8678000000073, 1005 | -30.8972000000067, -32.3715999999986, -22.3907999999938, 1006 | -43.6720000000059, -35.9038, -39.7492000000057, -54.1641999999993, 1007 | -45.2749999999942, -42.2989999999991, -44.1089999999967, 1008 | -64.3564000000042, -49.9551999999967, -42.6116000000038}; 1009 | 1010 | bias_data(15) -> 1011 | 1012 | {23634.0036, 23210.8034, 22792.4744, 22379.1524, 21969.7928, 1013 | 21565.326, 21165.3532, 20770.2806, 20379.9892, 19994.7098, 1014 | 19613.318, 19236.799, 18865.4382, 18498.8244, 18136.5138, 1015 | 17778.8668, 17426.2344, 17079.32, 16734.778, 16397.2418, 1016 | 16063.3324, 15734.0232, 15409.731, 15088.728, 14772.9896, 1017 | 14464.1402, 14157.5588, 13855.5958, 13559.3296, 13264.9096, 1018 | 12978.326, 12692.0826, 12413.8816, 12137.3192, 11870.2326, 1019 | 11602.5554, 11340.3142, 11079.613, 10829.5908, 10583.5466, 1020 | 10334.0344, 10095.5072, 9859.694, 9625.2822, 9395.7862, 9174.0586, 1021 | 8957.3164, 8738.064, 8524.155, 8313.7396, 8116.9168, 7913.542, 1022 | 7718.4778, 7521.65, 7335.5596, 7154.2906, 6968.7396, 6786.3996, 1023 | 6613.236, 6437.406, 6270.6598, 6107.7958, 5945.7174, 5787.6784, 1024 | 5635.5784, 5482.308, 5337.9784, 5190.0864, 5045.9158, 4919.1386, 1025 | 4771.817, 4645.7742, 4518.4774, 4385.5454, 4262.6622, 1026 | 4142.74679999999, 4015.5318, 3897.9276, 3790.7764, 1027 | 3685.13800000001, 3573.6274, 3467.9706, 3368.61079999999, 1028 | 3271.5202, 3170.3848, 3076.4656, 2982.38400000001, 2888.4664, 1029 | 2806.4868, 2711.9564, 2634.1434, 2551.3204, 2469.7662, 1030 | 2396.61139999999, 2318.9902, 2243.8658, 2171.9246, 1031 | 2105.01360000001, 2028.8536, 1960.9952, 1901.4096, 1032 | 1841.86079999999, 1777.54700000001, 1714.5802, 1654.65059999999, 1033 | 1596.311, 1546.2016, 1492.3296, 1433.8974, 1383.84600000001, 1034 | 1339.4152, 1293.5518, 1245.8686, 1193.50659999999, 1035 | 1162.27959999999, 1107.19439999999, 1069.18060000001, 1036 | 1035.09179999999, 999.679000000004, 957.679999999993, 1037 | 925.300199999998, 888.099400000006, 848.638600000006, 1038 | 818.156400000007, 796.748399999997, 752.139200000005, 1039 | 725.271200000003, 692.216, 671.633600000001, 647.939799999993, 1040 | 621.670599999998, 575.398799999995, 561.226599999995, 1041 | 532.237999999998, 521.787599999996, 483.095799999996, 1042 | 467.049599999998, 465.286399999997, 415.548599999995, 1043 | 401.047399999996, 380.607999999993, 377.362599999993, 1044 | 347.258799999996, 338.371599999999, 310.096999999994, 1045 | 301.409199999995, 276.280799999993, 265.586800000005, 1046 | 258.994399999996, 223.915999999997, 215.925399999993, 1047 | 213.503800000006, 191.045400000003, 166.718200000003, 1048 | 166.259000000005, 162.941200000001, 148.829400000002, 1049 | 141.645999999993, 123.535399999993, 122.329800000007, 1050 | 89.473399999988, 80.1962000000058, 77.5457999999926, 1051 | 59.1056000000099, 83.3509999999951, 52.2906000000075, 1052 | 36.3979999999865, 40.6558000000077, 42.0003999999899, 1053 | 19.6630000000005, 19.7153999999864, -8.38539999999921, 1054 | -0.692799999989802, 0.854800000000978, 3.23219999999856, 1055 | -3.89040000000386, -5.25880000001052, -24.9052000000083, 1056 | -22.6837999999989, -26.4286000000138, -34.997000000003, 1057 | -37.0216000000073, -43.430400000012, -58.2390000000014, 1058 | -68.8034000000043, -56.9245999999985, -57.8583999999973, 1059 | -77.3097999999882, -73.2793999999994, -81.0738000000129, 1060 | -87.4530000000086, -65.0254000000132, -57.296399999992, 1061 | -96.2746000000043, -103.25, -96.081600000005, -91.5542000000132, 1062 | -102.465200000006, -107.688599999994, -101.458000000013, 1063 | -109.715800000005}; 1064 | 1065 | bias_data(16) -> 1066 | 1067 | {47270, 46423.3584, 45585.7074, 44757.152, 1068 | 43938.8416, 43130.9514, 42330.03, 41540.407, 40759.6348, 1069 | 39988.206, 39226.5144, 38473.2096, 37729.795, 36997.268, 1070 | 36272.6448, 35558.665, 34853.0248, 34157.4472, 33470.5204, 1071 | 32793.5742, 32127.0194, 31469.4182, 30817.6136, 30178.6968, 1072 | 29546.8908, 28922.8544, 28312.271, 27707.0924, 27114.0326, 1073 | 26526.692, 25948.6336, 25383.7826, 24823.5998, 24272.2974, 1074 | 23732.2572, 23201.4976, 22674.2796, 22163.6336, 21656.515, 1075 | 21161.7362, 20669.9368, 20189.4424, 19717.3358, 19256.3744, 1076 | 18795.9638, 18352.197, 17908.5738, 17474.391, 17052.918, 1077 | 16637.2236, 16228.4602, 15823.3474, 15428.6974, 15043.0284, 1078 | 14667.6278, 14297.4588, 13935.2882, 13578.5402, 13234.6032, 1079 | 12882.1578, 12548.0728, 12219.231, 11898.0072, 11587.2626, 1080 | 11279.9072, 10973.5048, 10678.5186, 10392.4876, 10105.2556, 1081 | 9825.766, 9562.5444, 9294.2222, 9038.2352, 8784.848, 8533.2644, 1082 | 8301.7776, 8058.30859999999, 7822.94579999999, 7599.11319999999, 1083 | 7366.90779999999, 7161.217, 6957.53080000001, 6736.212, 1084 | 6548.21220000001, 6343.06839999999, 6156.28719999999, 1085 | 5975.15419999999, 5791.75719999999, 5621.32019999999, 5451.66, 1086 | 5287.61040000001, 5118.09479999999, 4957.288, 4798.4246, 1087 | 4662.17559999999, 4512.05900000001, 4364.68539999999, 1088 | 4220.77720000001, 4082.67259999999, 3957.19519999999, 1089 | 3842.15779999999, 3699.3328, 3583.01180000001, 3473.8964, 1090 | 3338.66639999999, 3233.55559999999, 3117.799, 3008.111, 1091 | 2909.69140000001, 2814.86499999999, 2719.46119999999, 2624.742, 1092 | 2532.46979999999, 2444.7886, 2370.1868, 2272.45259999999, 1093 | 2196.19260000001, 2117.90419999999, 2023.2972, 1969.76819999999, 1094 | 1885.58979999999, 1833.2824, 1733.91200000001, 1682.54920000001, 1095 | 1604.57980000001, 1556.11240000001, 1491.3064, 1421.71960000001, 1096 | 1371.22899999999, 1322.1324, 1264.7892, 1196.23920000001, 1097 | 1143.8474, 1088.67240000001, 1073.60380000001, 1023.11660000001, 1098 | 959.036400000012, 927.433199999999, 906.792799999996, 1099 | 853.433599999989, 841.873800000001, 791.1054, 756.899999999994, 1100 | 704.343200000003, 672.495599999995, 622.790399999998, 1101 | 611.254799999995, 567.283200000005, 519.406599999988, 1102 | 519.188400000014, 495.312800000014, 451.350799999986, 1103 | 443.973399999988, 431.882199999993, 392.027000000002, 1104 | 380.924200000009, 345.128999999986, 298.901400000002, 1105 | 287.771999999997, 272.625, 247.253000000026, 222.490600000019, 1106 | 223.590000000026, 196.407599999977, 176.425999999978, 1107 | 134.725199999986, 132.4804, 110.445599999977, 86.7939999999944, 1108 | 56.7038000000175, 64.915399999998, 38.3726000000024, 1109 | 37.1606000000029, 46.170999999973, 49.1716000000015, 1110 | 15.3362000000197, 6.71639999997569, -34.8185999999987, 1111 | -39.4476000000141, 12.6830000000191, -12.3331999999937, 1112 | -50.6565999999875, -59.9538000000175, -65.1054000000004, 1113 | -70.7576000000117, -106.325200000021, -126.852200000023, 1114 | -110.227599999984, -132.885999999999, -113.897200000007, 1115 | -142.713800000027, -151.145399999979, -150.799200000009, 1116 | -177.756200000003, -156.036399999983, -182.735199999996, 1117 | -177.259399999981, -198.663600000029, -174.577600000019, 1118 | -193.84580000001}. 1119 | -------------------------------------------------------------------------------- /src/hyper_register.erl: -------------------------------------------------------------------------------- 1 | %% @doc: If you wish to implement your own backend for storing 2 | %% registers, your module needs to implement these interfaces. The 3 | %% backend modules have quite a lot of responsibility (detailed below) 4 | %% to allow for backend-specific optimizations. 5 | -module(hyper_register). 6 | 7 | -type t() :: module(). 8 | 9 | -export_type([t/0]). 10 | 11 | -export([ 12 | new/2, 13 | set/4, 14 | compact/2, 15 | max_merge/2, max_merge/3, 16 | reduce_precision/3, 17 | register_sum/2, 18 | register_histogram/2, 19 | zero_count/2, 20 | encode_registers/2, 21 | decode_registers/3, 22 | bytes/2, 23 | precision/2 24 | ]). 25 | 26 | -callback set( 27 | Index :: integer(), 28 | Value :: integer(), 29 | hyper:registers() 30 | ) -> 31 | hyper:registers(). 32 | 33 | -callback new(P :: hyper:precision()) -> 34 | hyper:registers(). 35 | 36 | -callback compact(hyper:registers()) -> 37 | hyper:registers(). 38 | 39 | -callback max_merge([hyper:registers()]) -> 40 | hyper:registers(). 41 | 42 | -callback max_merge( 43 | hyper:registers(), 44 | hyper:registers() 45 | ) -> 46 | hyper:registers(). 47 | 48 | -callback reduce_precision( 49 | hyper:precision(), 50 | hyper:registers() 51 | ) -> 52 | hyper:registers(). 53 | 54 | -callback register_sum(hyper:registers()) -> 55 | float(). 56 | 57 | -callback register_histogram(hyper:registers()) -> map(). 58 | 59 | -callback zero_count(hyper:registers()) -> 60 | integer(). 61 | 62 | -callback encode_registers(hyper:registers()) -> 63 | binary(). 64 | 65 | -callback decode_registers(binary(), hyper:precision()) -> 66 | hyper:registers(). 67 | 68 | -callback bytes(hyper:registers()) -> 69 | integer(). 70 | 71 | -callback precision(hyper:registers()) -> integer(). 72 | 73 | %% @doc: Creates a new instance of the backend. The return value of 74 | %% this function will be passed to all functions in this module. 75 | -spec new(t(), P :: hyper:precision()) -> hyper:registers(). 76 | new(RegisterImpl, P) -> 77 | RegisterImpl:new(P). 78 | 79 | %% @doc: Set the register to the given value, *only* if the value 80 | %% already stored is lower than the new value. The backend needs to 81 | %% ensure the register value is only allowed to increase. 82 | -spec set(t(), Index :: integer(), Value :: integer(), hyper:registers()) -> 83 | hyper:registers(). 84 | set(RegisterImpl, Index, Value, Registers) -> 85 | RegisterImpl:set(Index, Value, Registers). 86 | 87 | %% @doc: Compact is always called before any attempt at reading (sum, 88 | %% zero count, etc) or merging. It is intended to give backends that 89 | %% buffer the writes a chance to flush the buffer before the registers 90 | %% are needed. 91 | -spec compact(t(), hyper:registers()) -> hyper:registers(). 92 | compact(RegisterImpl, Registers) -> 93 | RegisterImpl:compact(Registers). 94 | 95 | %% @doc: Merge any number of registers, used to calculate the 96 | %% union. For two register values at the same index, the max value 97 | %% must be in the resulting register. 98 | -spec max_merge(t(), [hyper:registers()]) -> hyper:registers(). 99 | max_merge(RegisterImpl, RegisterLists) -> 100 | RegisterImpl:max_merge(RegisterLists). 101 | 102 | %% @doc: Same as max_merge/1 but used when we know only two filters 103 | %% are merged. 104 | -spec max_merge(t(), hyper:registers(), hyper:registers()) -> hyper:registers(). 105 | max_merge(RegisterImpl, Registers1, Registers2) -> 106 | RegisterImpl:max_merge(Registers1, Registers2). 107 | 108 | %% @doc: Reduce the precision of the registers. Used for mixed-precision 109 | %% union by first reducing the precision to the lowest of all filters. 110 | -spec reduce_precision(t(), hyper:precision(), hyper:registers()) -> hyper:registers(). 111 | reduce_precision(RegisterImpl, Precision, Registers) -> 112 | RegisterImpl:reduce_precision(Precision, Registers). 113 | 114 | %% @doc: Sum of 2^-R where R is the value in each register. 115 | -spec register_sum(t(), hyper:registers()) -> float(). 116 | register_sum(RegisterImpl, Registers) -> 117 | RegisterImpl:register_sum(Registers). 118 | 119 | %% @doc: A map with key the values possible for the register and values the 120 | %% number of register with that value. 121 | -spec register_histogram(t(), hyper:registers()) -> map(). 122 | register_histogram(RegisterImpl, Registers) -> 123 | RegisterImpl:register_histogram(Registers). 124 | 125 | %% @doc: Count of registers set to 0. 126 | -spec zero_count(t(), hyper:registers()) -> integer(). 127 | zero_count(RegisterImpl, Registers) -> 128 | RegisterImpl:zero_count(Registers). 129 | 130 | %% @doc: Encode and decode are called to convert the in-memory 131 | %% representation of the backend to the serialized format. Must return 132 | %% one binary where each register is encoded as an 8-bit integer. 133 | -spec encode_registers(t(), hyper:registers()) -> binary(). 134 | encode_registers(RegisterImpl, Registers) -> 135 | RegisterImpl:encode_registers(Registers). 136 | 137 | -spec decode_registers(t(), binary(), hyper:precision()) -> hyper:registers(). 138 | decode_registers(RegisterImpl, Encoded, Precision) -> 139 | RegisterImpl:decode_registers(Encoded, Precision). 140 | 141 | %% @doc: Size in bytes used to represent the registers in memory. 142 | -spec bytes(t(), hyper:registers()) -> integer(). 143 | bytes(RegisterImpl, Registers) -> 144 | RegisterImpl:bytes(Registers). 145 | 146 | -spec precision(t(), hyper:registers()) -> integer(). 147 | precision(RegisterImpl, Registers) -> 148 | RegisterImpl:precision(Registers). 149 | -------------------------------------------------------------------------------- /src/hyper_utils.erl: -------------------------------------------------------------------------------- 1 | -module(hyper_utils). 2 | 3 | -export([run_of_zeroes/1, run_of_zeroes/2, changeV/2, m/1]). 4 | 5 | run_of_zeroes(B) -> 6 | run_of_zeroes(1, B). 7 | 8 | run_of_zeroes(I, B) -> 9 | case B of 10 | <<0:I, _/bitstring>> -> 11 | run_of_zeroes(I + 1, B); 12 | _ -> 13 | I 14 | end. 15 | 16 | changeV(List, ChangeP) -> 17 | case lists:search(fun({_I, V}) -> V =/= 0 end, enumerate(List)) of 18 | {value, {I, V}} when I =:= 1 -> V + ChangeP; 19 | {value, {I, _V}} -> hyper_utils:run_of_zeroes(<<(I - 1):ChangeP/integer>>); 20 | false -> 0 21 | end. 22 | 23 | enumerate(List) -> 24 | {List1, _} = lists:mapfoldl(fun(T, Acc) -> {{Acc, T}, Acc + 1} end, 1, List), 25 | List1. 26 | 27 | m(P) -> 28 | trunc(math:pow(2, P)). 29 | -------------------------------------------------------------------------------- /test/hyper_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(hyper_SUITE). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("common_test/include/ct.hrl"). 6 | -include_lib("stdlib/include/assert.hrl"). 7 | 8 | % copy of #hyper in hyper.erl 9 | -record(hyper, {v, registers}). 10 | 11 | all() -> 12 | [ 13 | is_hyper_t, 14 | basic_t, 15 | serialization_t, 16 | backend_t, 17 | encoding_t, 18 | register_sum_t, 19 | error_range_t, 20 | reduce_precision_t, 21 | many_union_t, 22 | union_t, 23 | union_mixed_precision_t, 24 | intersect_card_t, 25 | bad_serialization_t 26 | ]. 27 | 28 | is_hyper_t(_Config) -> 29 | ?assert(hyper:is_hyper(hyper:new(4, hyper_binary))), 30 | ?assertNot(hyper:is_hyper(foo)). 31 | 32 | basic_t(_Config) -> 33 | [ 34 | ?assertEqual(1, trunc(hyper:card(hyper:insert(<<"1">>, hyper:new(4, Mod))))) 35 | || Mod <- backends() 36 | ]. 37 | 38 | serialization_t(_Config) -> 39 | Mod = hyper_binary, 40 | Hyper = hyper:compact(hyper:insert_many(generate_unique(10), hyper:new(5, Mod))), 41 | HyperJson = (hyper:from_json(hyper:to_json(Hyper), Mod)), 42 | 43 | ?assertEqual( 44 | trunc(hyper:card(Hyper)), 45 | trunc(hyper:card(HyperJson)) 46 | ), 47 | {Mod, Regs} = Hyper#hyper.registers, 48 | P = hyper_register:precision(Mod, Regs), 49 | {Mod, RegsJson} = HyperJson#hyper.registers, 50 | PJson = hyper_register:precision(Mod, RegsJson), 51 | ?assertEqual(P, PJson). 52 | 53 | reduce_precision_t(_Config) -> 54 | rand:seed(exsss, {1, 2, 3}), 55 | Card = 10_000, 56 | Values = generate_unique(Card), 57 | [ 58 | begin 59 | HighRes = hyper:insert_many(Values, hyper:new(16, Mod)), 60 | lists:foreach( 61 | fun(P) -> 62 | Estimate = hyper:card(hyper:reduce_precision(P, HighRes)), 63 | M = trunc(math:pow(2, P)), 64 | Error = 1.04 / math:sqrt(M), 65 | ?assert(abs(Estimate - Card) < Card * Error) 66 | end, 67 | lists:seq(10, 15) 68 | ) 69 | end 70 | || Mod <- backends() 71 | ]. 72 | 73 | backend_t(_Config) -> 74 | Values = generate_unique(10000), 75 | P = 9, 76 | M = trunc(math:pow(2, P)), 77 | 78 | Binary = hyper:compact(hyper:insert_many(Values, hyper:new(P, hyper_binary))), 79 | 80 | {hyper_binary, BinaryRegisters} = Binary#hyper.registers, 81 | 82 | ExpectedRegisters = lists:foldl( 83 | fun(Value, Registers) -> 84 | Hash = crypto:hash(sha, Value), 85 | <> = 86 | Hash, 87 | ZeroCount = hyper_utils:run_of_zeroes(RegisterValue), 88 | case dict:find(Index, Registers) of 89 | {ok, R} when R > ZeroCount -> 90 | Registers; 91 | _ -> 92 | dict:store(Index, ZeroCount, Registers) 93 | end 94 | end, 95 | dict:new(), 96 | Values 97 | ), 98 | ExpectedBytes = iolist_to_binary([ 99 | begin 100 | case dict:find(I, ExpectedRegisters) of 101 | {ok, V} -> 102 | <>; 103 | error -> 104 | <<0>> 105 | end 106 | end 107 | || I <- lists:seq(0, M - 1) 108 | ]), 109 | ?assertEqual(ExpectedBytes, hyper_binary:encode_registers(BinaryRegisters)), 110 | 111 | ?assertEqual(Binary, hyper:from_json(hyper:to_json(Binary), hyper_binary)). 112 | 113 | encoding_t(_Config) -> 114 | [ 115 | begin 116 | P = 15, 117 | M = trunc(math:pow(2, P)), 118 | Hyper = hyper:insert_many(generate_unique(1000), hyper:new(P, Mod)), 119 | ?assertEqual( 120 | trunc(hyper:card(Hyper)), 121 | trunc(hyper:card(hyper:from_json(hyper:to_json(Hyper), Mod))) 122 | ), 123 | {Struct} = hyper:to_json(Hyper), 124 | Serialized = zlib:gunzip(base64:decode(proplists:get_value(<<"registers">>, Struct))), 125 | WithPadding = <>, 126 | 127 | B = Mod:encode_registers(Mod:decode_registers(Serialized, P)), 128 | BWithPadding = Mod:encode_registers(Mod:decode_registers(WithPadding, P)), 129 | ?assertEqual(M, byte_size(B)), 130 | ?assertEqual(M, byte_size(BWithPadding)), 131 | ?assertEqual(B, BWithPadding) 132 | end 133 | || Mod <- backends() 134 | ]. 135 | 136 | register_sum_t(_Config) -> 137 | Mods = backends(), 138 | P = 4, 139 | M = trunc(math:pow(2, P)), 140 | 141 | SetRegisters = [1, 5, 10], 142 | RegisterValue = 3, 143 | RegisterInsertValue = <<0:(RegisterValue - 1), 1:1, 0:(61 - P)>>, 144 | 145 | ExpectedSum = 146 | math:pow(2, -0) * M - math:pow(2, -0) * length(SetRegisters) + 147 | math:pow(2, -RegisterValue) * length(SetRegisters), 148 | [ 149 | begin 150 | Registers = lists:foldl( 151 | fun(I, Acc) -> 152 | Mod:set(I, RegisterInsertValue, Acc) 153 | end, 154 | Mod:new(P), 155 | SetRegisters 156 | ), 157 | Compact = Mod:compact(Registers), 158 | ?assertEqual({Mod, ExpectedSum}, {Mod, Mod:register_sum(Compact)}) 159 | end 160 | || Mod <- Mods 161 | ]. 162 | 163 | error_range_t(_Config) -> 164 | Mods = backends(), 165 | Run = fun(Cardinality, P, Mod) -> 166 | lists:foldl( 167 | fun(V, H) -> 168 | hyper:insert(V, H) 169 | end, 170 | hyper:new(P, Mod), 171 | generate_unique(Cardinality) 172 | ) 173 | end, 174 | ExpectedError = 0.02, 175 | P = 14, 176 | rand:seed(exsss, {1, 2, 3}), 177 | 178 | [ 179 | begin 180 | Estimate = trunc(hyper:card(Run(Card, P, Mod))), 181 | ?assert(abs(Estimate - Card) < Card * ExpectedError) 182 | end 183 | || Card <- lists:seq(1_000, 50_000, 5_000), Mod <- Mods 184 | ]. 185 | 186 | many_union_t(_Config) -> 187 | rand:seed(exsss, {1, 2, 3}), 188 | Card = 100, 189 | NumSets = 3, 190 | 191 | Input = generate_unique(Card), 192 | [ 193 | begin 194 | M = trunc(math:pow(2, P)), 195 | Error = 1.04 / math:sqrt(M), 196 | 197 | Sets = [ 198 | sets:from_list(Input, [ 199 | {version, 2} 200 | ]) 201 | || _ <- lists:seq(1, NumSets) 202 | ], 203 | Filters = [hyper:insert_many(sets:to_list(S), hyper:new(P, Mod)) || S <- Sets], 204 | ExpectedFilter = hyper:compact( 205 | hyper:insert_many( 206 | lists:flatten([ 207 | sets:to_list(S) 208 | || S <- Sets 209 | ]), 210 | hyper:new(P, Mod) 211 | ) 212 | ), 213 | H = hyper:union(Filters), 214 | 215 | {Mod, ExpectedRegisters} = ExpectedFilter#hyper.registers, 216 | {Mod, ActualRegisters} = H#hyper.registers, 217 | 218 | ?assertEqual( 219 | Mod:encode_registers(ExpectedRegisters), 220 | Mod:encode_registers(ActualRegisters) 221 | ), 222 | Delta = abs(sets:size(sets:union(Sets)) - hyper:card(hyper:union(Filters))), 223 | case Delta < Card * NumSets * Error of 224 | true -> 225 | ok; 226 | false -> 227 | error_logger:info_msg( 228 | "too high error, expected ~.2f%, actual ~.2f%~n~p, p = ~p, card " 229 | "= ~p", 230 | [Error, Delta / (Card * NumSets), Mod, P, Card] 231 | ), 232 | ?assert(false) 233 | end 234 | end 235 | || Mod <- backends(), P <- [15] 236 | ]. 237 | 238 | union_t(_Config) -> 239 | rand:seed(exsss, {1, 2, 3}), 240 | Mod = hyper_binary, 241 | 242 | LeftDistinct = sets:from_list(generate_unique(100)), 243 | 244 | RightDistinct = sets:from_list( 245 | generate_unique(50) ++ 246 | lists:sublist(sets:to_list(LeftDistinct), 50) 247 | ), 248 | LeftHyper = hyper:insert_many(sets:to_list(LeftDistinct), hyper:new(13, Mod)), 249 | RightHyper = hyper:insert_many(sets:to_list(RightDistinct), hyper:new(13, Mod)), 250 | UnionHyper = hyper:union([LeftHyper, RightHyper]), 251 | Intersection = hyper:card(LeftHyper) + hyper:card(RightHyper) - hyper:card(UnionHyper), 252 | ?assert( 253 | abs(hyper:card(UnionHyper) - sets:size(sets:union(LeftDistinct, RightDistinct))) < 254 | 200 255 | ), 256 | ?assert( 257 | abs(Intersection - sets:size(sets:intersection(LeftDistinct, RightDistinct))) < 258 | 200 259 | ). 260 | 261 | union_mixed_precision_t(_Config) -> 262 | [ 263 | ?assertEqual( 264 | 4, 265 | trunc( 266 | hyper:card( 267 | hyper:union([ 268 | hyper:insert(<<"1">>, hyper:new(4, Mod)), 269 | hyper:insert(<<"2">>, hyper:new(6, Mod)), 270 | hyper:insert(<<"3">>, hyper:new(8, Mod)), 271 | hyper:insert(<<"4">>, hyper:new(16, Mod)) 272 | ]) 273 | ) 274 | ) 275 | ) 276 | || Mod <- backends() 277 | ]. 278 | 279 | intersect_card_t(_Config) -> 280 | rand:seed(exsss, {1, 2, 3}), 281 | 282 | LeftDistinct = sets:from_list(generate_unique(10000)), 283 | 284 | RightDistinct = sets:from_list( 285 | generate_unique(5000) ++ 286 | lists:sublist(sets:to_list(LeftDistinct), 5000) 287 | ), 288 | LeftHyper = hyper:insert_many(sets:to_list(LeftDistinct), hyper:new(13)), 289 | RightHyper = hyper:insert_many(sets:to_list(RightDistinct), hyper:new(13)), 290 | 291 | IntersectCard = hyper:intersect_card(LeftHyper, RightHyper), 292 | 293 | ?assert(IntersectCard =< hyper:card(hyper:union(LeftHyper, RightHyper))), 294 | 295 | %% NOTE: we can't really say much about the error here, 296 | %% so just pick something and see if the intersection makes sense 297 | Error = 0.05, 298 | ?assert(abs(5000 - IntersectCard) / 5000 =< Error). 299 | 300 | bad_serialization_t(Config) -> 301 | [ 302 | begin 303 | P = 15, 304 | M = trunc(math:pow(2, P)), 305 | Data_dir = ?config(data_dir, Config), 306 | {ok, WithNewlines} = file:read_file(Data_dir ++ "/filter.txt"), 307 | Raw = 308 | case zlib:gunzip(base64:decode(binary:replace(WithNewlines, <<"\n">>, <<>>))) of 309 | <> -> 310 | RawBytes; 311 | <> -> 312 | %% TODO: test padding 313 | RawBytes 314 | end, 315 | H = hyper:from_json( 316 | {[ 317 | {<<"p">>, 15}, 318 | {<<"v">>, <<"sha-v1">>}, 319 | {<<"registers">>, base64:encode(zlib:gzip(Raw))} 320 | ]}, 321 | Mod 322 | ), 323 | {Mod, Registers} = H#hyper.registers, 324 | Encoded = Mod:encode_registers(Registers), 325 | 326 | ?assertEqual(size(Raw), size(Encoded)), 327 | lists:foreach( 328 | fun(I) -> 329 | ?assertEqual(binary:at(Raw, I), binary:at(Encoded, I)) 330 | end, 331 | lists:seq(0, size(Encoded) - 1) 332 | ), 333 | ?assertEqual(Raw, Mod:encode_registers(Registers)), 334 | 335 | ?assertEqual( 336 | {[ 337 | {<<"p">>, 15}, 338 | {<<"v">>, <<"sha-v1">>}, 339 | {<<"registers">>, base64:encode(zlib:gzip(Raw))} 340 | ]}, 341 | hyper:to_json(H) 342 | ) 343 | end 344 | || Mod <- backends() 345 | ]. 346 | 347 | %% 348 | %% PROPERTIES 349 | %% 350 | 351 | backends() -> 352 | [hyper_binary]. 353 | 354 | %% 355 | %% HELPERS 356 | %% 357 | 358 | generate_unique(N) -> 359 | generate_unique(lists:usort(random_bytes(N)), N). 360 | 361 | generate_unique(L, N) -> 362 | case length(L) of 363 | N -> 364 | L; 365 | Less -> 366 | generate_unique(lists:usort(random_bytes(N - Less) ++ L), N) 367 | end. 368 | 369 | random_bytes(N) -> 370 | random_bytes([], N). 371 | 372 | random_bytes(Acc, 0) -> 373 | Acc; 374 | random_bytes(Acc, N) -> 375 | Int = rand:uniform(1 bsl 64), 376 | random_bytes([<> | Acc], N - 1). 377 | 378 | %% Lifted from stdlib2, https://github.com/cannedprimates/stdlib2 379 | partition(N, Xs) when is_integer(N), N > 0, is_list(Xs) -> 380 | Len = length(Xs), 381 | case {Len > 0, Len > N} of 382 | {true, true} -> 383 | [take(N, Xs) | partition(N, drop(N, Xs))]; 384 | {true, false} -> 385 | [Xs]; 386 | {false, false} -> 387 | [] 388 | end. 389 | 390 | take(N, _) when N =< 0 -> 391 | []; 392 | take(_, []) -> 393 | []; 394 | take(N, [X | Xs]) -> 395 | [X | take(N - 1, Xs)]. 396 | 397 | drop(N, Xs) when N =< 0 -> 398 | Xs; 399 | drop(_, []) -> 400 | []; 401 | drop(N, [_ | Xs]) -> 402 | drop(N - 1, Xs). 403 | 404 | run_of_zeroes(B) -> 405 | run_of_zeroes(1, B). 406 | 407 | run_of_zeroes(I, B) -> 408 | case B of 409 | <<0:I, _/bitstring>> -> 410 | run_of_zeroes(I + 1, B); 411 | _ -> 412 | I 413 | end. 414 | -------------------------------------------------------------------------------- /test/hyper_SUITE_data/filter.txt: -------------------------------------------------------------------------------- 1 | H4sIAAAAAAAAA02djbrbuI5s79cE/yUn7/+0g1oF5dyZ7pz03rYskSBQKBTgGH320deM0UfsHrPP1XfP/87/jxU9f39nzHXi9BazjTl36Df5z8o33ehrzXzj7Kv3tVeLke/sXW/Oa48981Wzt/z/fH3+m3/soc/J6/R98o+RPxz51znH3F0fOdY6Q9df98yxxxnrN/K3fefVx5jvzHvKD1l563ml/Ij8j5F/ydvKP2brveVdDD2e7jI/Mm8o3+gX6gc9nyLy7vKz5spbz+c/e+68+al/cg10W2fmY2w+a+QL8wPzftvQfeY6zbZ0P3lbcz59r9P74aFz/VY+Q35aviH/s3VuJq+Tv516Wr0sf5Y3r3+1akv3Op9x8znu8DLljeY+TN6Rr9Qq59+PrqE3r82DnaP3aSPzHy0WN5D7mduVj5/7kNt2uAXtay5sZ0/iz7P1qY8eP6/EVuVtNe3BamPvXI58dS781Ep2/s0XBwuolc1r51rM0fbYobvIy9y8fL45/Np849XLThpRHD28rCwfqz/6kDmPrrV4ZUxfWHeaW6EP0K1iknnnT978nykjymVmNYfMauqxcvmankn7t/WEUzeba6adzF3O3dd65CbNJmOXIeey5T3rXbwwLXKOm3c58wND1txZtiUzG7nTuej6Wd4QH8AnaGduw6p2w0oGdnNyl7Z3XWsXO0+K/je3eM63rwfbyGdpbWLZ+QDaXi1urlTnSvmpeasjz50s8azcj3z32Txw7r3MMu9ZZr21KdpANkdmgqGHjIeDlxYbbeat5suvHvr4mXWCVh7n78n00tAdTB+T/MHS1vFkMoHQYR77HVqB/DfSb7BTQ4czL4s3eFveXGCPceRA8gH30KflSdGKpsFr0/PRTp5ZHSsdRxl9mrZO0U5LlB+SDepZ9XD5aYsbGbohPZIeXveqxcr3a7m0mtMHG7vWSb6YzSNnscbRpQ43rWfX+c8t127pbnW4ZsszrnPEzvORcir6mGD18w7zE04/etz8pLZyVW8+aRpWmpG2XUch3dEJL+LU+dC+yRbyQbZ84xiPnw9r0GoOFu1sn9nR5N/ka7WjXd5Ga344JC04NezKo0fkecJLkU5qsYNLfkzu8uJRNi4Pt5ObHzK8/Htugv1HfpiuErhp3fbQfebJ02Pm0uHc+pVxLe113c/AMS3dahqBjrvcU/70lW+U0eZhvzoYsWzSuvLActPTd1wMvmJyKM7wLstN6D7yKlrLfKmOhjZXm7JlxflBucacMgWc06a2fSoO5JZqJXqusS6In5iyt47JTLxLTFv9vHIR5dhkCHqN/tExV1jC7+hA9lo93aPcUrys3dFJ0p8hJ9s5D/FyCO0mdVaDdVVUkH3rdHQeiMiVy6IH1ipt7RznOx/1r9bL9qPFWJx+HLmCgxyKzM5mpBjam5yXXsRWa4/ywy+OAT/Bh8jbLTnBXBQ5mPxrb6dujP2V/8c/6JX5Hjm4wZEUQJCHmfrj4azKWhXz5WC0ybr1JbM4WtW0A7963ykb0n4E3mriI+R1lv1yWteDFeZF2+I0biCBHr5pVxR/H9lIehTBh9z1S5TXib867/Zf+AVhjXUwujO/2K/gTyTSU89/G4s3PPLWwId8/4tteOXPObgRnYrlHdBiXxYjt6oFp3TcpfvKOL5Y1kVUaAOMxT2CDtKI0x8LaUQ+r24yxpETwlvr4fsmOuHSdsIjmUXg3/LZt7Zt3WG7FAQK/Ifcc8G3jO2NN+N9B6GRQ4CbEd4rLx+81yipy5uzeXZ6BLHlAJm/2nLgA0PCIbCZD1frbZb70YFtwQW0mvmZl3iUSy2vT0TNFTiCQ1rk3KK9cIxCQMJZOJW/C2DA4eTRBrd/wAYBwsGxXu3Y0QEGLg1Hrrx2IkpApD48LTk/g+iXS6zYvHW0m86mXNZal+XgnLLfil8v0SP/tqfjn/HABhnqgYNtjV/I4Sm6BGcp8aBMRIeS9VJ80WFNw7g6ij3RlWPTEDqa06uNtQOkurZacVju+XD73kKdU4VkHLQtLq+H8XPbiaq1So19tc9iWbg+gVR3lJah256NmKqbzgtuvRevIgydVkmgy+c52IrWIQjdPkHr4k11auXabBpyqIrSAxcywAiyeZ1WZQoDGAy8BdPYQcUxhgkFwzPBTdioDEJHVXuBfeujiagc2SuPK3ghs0pfIdws+CR/nGssuDmxpVXe3vhQP2H9FxbuUHP1MWnVdxMUtf9yTBtvqLXKKw5yBn6AJ8ZWtVxgXcGLraxA7phlT3/cL6dJKDRXR6aqg3t2UzAN+9ERf/NWLvEPwCQXDRRoXh7HaN3UJR4vgYMmt9y3H0qAQhtxFLY6fi0t3w44/0sQK88Y0KV78bcB91RuAoxIVAjcHcAQhUCdeXkHHV8O3xZ+JdHSZulJNxcTmGZVmv1IZlJyX8oUpz4p8DAKu4DRVci+skxnOUvwlGyBEN9lkfp8H6M3Go6L/IVdaxham6et5piYkf0AXoD0OkNpK0oyZZ+hVO31uhLUxjKs2iS5aXNCQCSvezl9yhe2zXHGG1fo0Cfwf391xNLVy1wIqVozZS26yQB6bvbuJFzLzWoyoEWOYhOcH9YIziCfrY95uhOCNPRzExkoduVj5OIaHC2FnqWAspvg65kcOjDPz2GQZIIjpQCmTdWNDSfNecFHp0crrGRT11+ZUfffJqFQ+qZ/5DOiPcBogZOBVXCLJ29yD6MZ24VS86XAn4+SV9XZ4bdyhTPe6Rxu2gLOxiYW2Xw4USABmRiakqQ3lEZ0LIhUHH+sXy485QC6dZv8JZs9hDrhJFKuXu4ys1IHN3xdWTJxoOPrnnBGvnQluQC/WEkXzEZwEHn/FlzSLUCXgAqfAI4TpDgi+fIn8ajS3ybLwUM+uOyMO5lsT5I4PSMnceMY4Rza/RiQTCcEdQVC5bteQ5Mhmyc2AyR1ONOttjOcbeixj+9D9zr0oqak/pHTz+wEKIOpHhKC7sRwEheJ3GP8fY2YFd2i27mHUfgjTMo5VgRlHXHbcAmZW2vp046ftcnefOKFwJXQgV3kn/PjVuRdAVn0m0PY7QW/+EgwiIKTGJXOAVtsWAZa4PctOyBr6Hi8tY1wtnHQ0rFbMC6slT7i+lixbqRJHZ/7mcmAG9AjAhvkbRTiyRpEMKy81UYIwh45CMrNO/nOjsqrGnFDyEsmnvcFWrQrhY4ax7BUWaNsshVq0iK02sbALSwy2IWpy8+kb2zdp5GVzWXOT2jTMJJr62AvcKxuTNg2IOBgtLxkw7zMWWRZj9zZ1uPmH+9UCqvsM6CfOsA5lPGNSgq6iEDMKGCiOIoCmovs2Fdns0A8xJvpqJB/fc/FJzQec9u6MLL5Z4mSgD0A0j/4/gkzohRKt75JEfTgAj5XaFUL3IqRGb6NfPE2laLlFSuF4e/5n6xRW69YN8ifWCBuBlvaTqoESvP2G47UHoiEz9ZiuiUUHnSW8t8GHSHbuo4TAjIbQw6RNkPwCIYVCggkZ/CeIegP/70CD51u/cqPKLcGSQ4DCRDg3GYuBZ2EAMl0SfxfR0o70BUg46YfCHIDZUgNF16wDfvAdEZ5LsfLLnQWt2MqubK6l00uKPeXpwl8aEply+FNY/4fnngC6YfpQ93U7XjjkGtpZHMEijnJbAumw9qIN7MfXIYmyl6nIoQ2Q8zvIrrjf7V6ed8/AQ9ByvQlGSQXoTzYyb7EaXacAEcFxkD5Vq5gC0BpfNkkizDNsChDG0qrgHWmaaHQbI/Nhz5hQg+OUvxEdcKI5uv0vI+5ivYaWgF/vV938aRTaeUW5t42OwHyRxTChe51ECEZhraqG4fClsWJETtn86O8gDw1nMvSqiwlqBOnBPx7CMsY29G2TTLREMoiMP82507hTc8uqPPCd4vXX8N8t9DtbTLCJlRLaF4CsApgMoxQTJrm3HT6Am9l7nyAxLpo2wXL0fSLpuX66f1bYEGsyzK1LLhmExaohd/Q/cFGHDkTXtFNCRQrrJCsx5SJPfnQd9j5wJcrBimy4Uk6uA/HOnx7Mmrzm7mUhPSnF+cPJQapvIFSAt0DngrOR9BpYZ1kZU1UJqcAqKwMTonUNf0IXuwgYGH1ME0n6j4ENUUUaP12Rp991iNzD4VF/OUkdOh6YhpyPZ4GJ9hJNVif/ND3KoOG1S8UhfWv3yIfEsY7AuzD6+psm8SaGC3fotURpynvnxnYUNLp2sG8hBVdMFd3Y1y7PzJjY0yzKscOYZF0cqg6drr5gcK+jNiM7ax0GXKpVz0CCA8rSIiFFGs/hZGloESxQQFzQDdtdnQp8TPJrg90ugTqeh5BgCFyOvLBsLGdATc4XqG0ZwqziBZSjsY979BTHaH7A6kpJA0h8+EtvXoKxAniLzgG/D55uLyS8ZWZqckR3Ty+6RX+voD0UKTiS3X2BE0G2FzHihJW/sjJnUxo88rHSTD2I0e1coUOxuKMGAdiHh/uxYCfJd/5wlXVCXKbUOY6TQ5sbOZM86ywTw1mA8AHzXo72Fi5d6XHuax/AaOxMcJgJbeP61TxKLHEzkzDpxtzg7af1NsMo2BGpplelQREYayCZHKgzawpblV1GeUDdX63DFyBEXp1w1lTp1D4EnjXz4rvymc9DsNyil3lQaFZAlunAuV0YDktzYiIfyOmE6S0pmtdSPVu1sl1p+WKJkbX+EXm6P0NnQYTV1qZR2xc4zgrNpHbTxcbtIB/nfXk7R2XSBNDD9cRJ0VADiA3UjzALqpaqG9S9gn4Xv1KEbP14s1lws3gk0A1WCOerq//MFqhbxU3HLjln2UcezoTZKeoUeCZFY8OJIARAu4kd7s57WhC5ccYdEPycjvxTJhibtKZG9cnSQsz8NtUQ7cDt7Oc4n/lAGy5IKRO5cBVLRxMoyJZheZDoMkr/2ROzWkvt4ZXwWXo1QLo4083b+oshkVU4bgrU0/XvFcVFc7YsCpVHCHvx8UZPsikt2sVuvYD7p6CBsuUHHFDWbG5NoijIGeBYMukI1gU3dg5pIX/4QlmUCRxOULrv8ZPENkBrMsNnTtcfcOPKsvsioSgj2OaasPq4nlkvJl9y9eCjTJCBNTNwqa4hcvmZ8gFzCvU2akTEHGHxsA87arAmqu/7oFfXeYEw0kiiR2FOX1cOC3nDAm4A+xhLh0kXQVwhRgcEvaLmVOpKm0C1uznjr/aB93qcIZkt+Qq/zDrnjeTL/gtFyvyh7/BPRk+6PRQPVmubi8VNlwNqpS/yCbVnWKMD2Bjuvr3yc3WwVGuSeY1qqZA/Zl6KJa5/w7SbShZjpzAwbB24R7hRXEmooAE++HbMrfobV+X5S4glOzB2CxNTHh1wT2YRV+CvvJC+FMds1d8pavpyyslEtOBWc764EoD9Yd+QwJAiXQC7Ei+p2hAJTnp0w5l2uFC9cKvio3KfbbLXorUSmJV4whIS2GwvCBhBUoEQ2yUVN/SB1BZmBXrnDaz6/o3njDO7NAWJl9BT9i4vPloHKT+nfEgqXXZdZG98zz/yxLTzWpHySyKWdAJfsSY4lnS2DbFEmg02FtlwNQCByFyUNUafjVsc/7xE4AVxl/DtV7ZcwPQLacd+fcmUQV444Ga2w4IlR/p0XLj5EJh7fLnbaJFuSb4Zf/b1Bae3+wDhKZSQZmJJTJioGTiuYniwnGDJneVrZ24nCRqQWu6zpYu76Vo2KnApgmAChTSOPDQYTwiBUQX40mzwPoQgMSxib9q8KODMh3hyDTAJtECQcTFXbv2S3KMS28FBx2eiiet5HvmIaH+qvxwVS0PL7cIhERBlzxV6KVaCSbZioN2E0r4HiKQfCPpjFJm8lNhiYG5cLQ24PKQMgny20bnLOQEs7G2RQGursH7amG2yks7iJYiQlx+cM0TcKT1xgUIIRCcha8MgwK4RKa/AiPniY/c0pLwwCcAyChjexyWwpZxlKdc2EDxuJMSfLqhkEiGM6u7IMN4GvSvbS8SX0frFYahLFQSIkYsYMWldNmdbu9LcdAsH9Fxki1xFvNXd8tjUPzGzcN66kV4S+DNKc7I9VqZJloa3cuxH+MZkXmEiv1yukrojIUesYnkAmRopvb1YFwPdmS93fnSA/Jd8CwmlDhH+ktbVedroMtmOQOVWm2mueVoVsh0a2ti+pG6UyAB7UwnUOPsYVBCuX2UNk2VizWd84icJSXS3sUC+yyOi0/RUCSYTc6/P7DkEuB1Z03Ke4/LiuyZQpzK7LlBiWn3ddAnUWrwqAudleQioMf5yFF1Z7NWZ4TcX61JWKTSwW+rOZKsWVRxcKIHKEYuoFzgMs+3IYjGaRXjxe3ofGz5Ikrcy2qOMMshTmO6mPydcRyRZRpWmlHtHrWyw9mVaUaViIi4eDdXaaHywA0AM8QIhHKUPNblDMMZVWamVmwZPEFi6U1a/gm4VQ0W5aCOkhdAUWNXBfCH5HEVxxreUTIbqgTHYYxsZ7mCXQKAC/IRNa08cvEZ0w6a+8FR+TBzqFD7ybCgPMBpGa3YAy2LzBbYRGFc5fbFzndVL/VDsYOiKHWvN6ijvBK/YTjaVCPs5UrCa9LLcit50/63tTOsRtnLnNar+2lQabFF58M7Tou28n0ZwY4Rj1zuHqbhCkZiOwZU4TdmVJQEUusUVYfLJd9IAnoR3vJCUbIS1eDzcX5QpHnLmQMLZQTRsYvTjO7SLsUKisgYXhGPgBE0Fyrn20HC4CMMC4u95HYWxAtpQ+7bywOjBaykpLvgOqBQwjXhbixOHiUdCbWAvMtrlknoIKivLVZZ6oWmDyWhbcivckEIG1y9OdfgXvPxZPTNApwSi1AzB8pQ5tThsQSAiCOv85S2bxfQiOIHsEor/YzRt1xp2smfIIIg15KPFVw6ZDRtWONxVDkLHpTwpSuQKEvRJYRr4BaOm7B5J02lHn3yx58lbWG+1AmldGx56ypuAwSuPviQHT8iVi3tsbIYP72w3F35qRgsecL0ewBdFQqWqEsF58eKLUJqKTz0LG1U5JaCAcSb7kvrhb62yIhjeyl78Fq6Ok2m5JrN3NaKJVQs9Zx4tirGDlP34vP3ehbqGFi9AmwT8YkIvvPIXQ7u3NkTKNthkJA/vi0Lu7UOk+vgv9Fin30tYzRKscZ41OmUb1eh3NIohScdhVc02Zq9snO7n+IIc8WbdZeq4M0LzUWlgdLJgaucYY1G2L+k733JuQ55LNGNqumyCDsaRBUnYAP25T3QmsrLWMfxLhfiwpUQFx/6T/UjXcVqA2dEaAJNlpG2w8qsQcWugVBQVZMlotuKUsYtX2Gaa6883Lhy4aVOHIALMTqzqO2CpEkQJYlNtcdpBSxQiLR/ktO7ZpZ2eT/5V14P+tXMFmVdaHjdxRkWA8BUlO/v5rMTSe1lwuVxrTHPGoor2AAd2Yx/F+4xzJUcEOVeXyVsYN4bLcoxCSHJBpoWC9xQpM7PsdlBAg0n5QUpgTApXVTBg+vhlj/dl95wqGnLEQpkb1edwK4xzPeyQpRDzcnsAtMyFu63ITmjQILcR0fJ8FiL0Yyym8u5sIDspTKtGa5ENhujnDu4fLv6qrN6xYVKrD/AFC5lQFD/mhD7Jg6nS1XVmvwEy+1GOJzjifQF+X64dLXKhjfOo6RGpKXSwuO9XSYBi5YcYVk/Iw10OUA5cCo+UeJ3VsPReCtvo5eABK/N1v6e5cKeFBDTcgEyOi3amzf0O/OP65LNbRnLIEY4mPTTRJ5JqSNgZJ9YUp2HvSKXPTqLLxXLpRpqyU/NVFIXNn8t6jpvRfz31kutRoDwaSrUNJpBFsrASd1L1uD6CLJIC5iWRezLCjHckHe7lEbdouwohpZsEmPtNtBRxDKvQPRDvq59uaZr4co61eN0T2J3yTb2RZgIWS8YAxsEpujGhrgtOZstXxXINSyTHiyGVgIMgUsblbWa653lriDuXcFEK5ZoCuGeAHJzIbkynN3vV0MKymMbQO/QPczCptFmPg/OMpn+095euC2MSUeAUCDFD1q8hbQBuZLr3+mPpZxYBf9RbhaBbLuk7UXswN/ijJVq9OcOGmEoRgQFCANA62anuakKu8Ps2iXoyaf8NSLs1XIk3yXEeac5csscVMFAaR4CRXy4qHYZq4Apwj4hedhKCwU5cIlT7nzo8JhFpZj/MHdu+EkonaiBqR+4O8iGRMTa9pMNXxxEeRZDBZpm9ftd/cNHgQTa/LRcw7KurVMPpObifhe3E5mpWU5WXGPZArKSohFNinGhPl0yFkgs0mYIRzMYQywTdz5KityHWRSlSG2YhOrIYZXhr/uJBBRK2saadhEaEz3jgzANGa+ExNA5w+4oINuRTmPNTrO+IoNZh03HCPnFKsIg01N0ixc0VyU/2hu6I8Q2KwlGGRJ0L4r8hEi0MXhEuXSzlqAwXaaZJZLnOPDm69M2hxSlx8a4TvlTxBOlrk88+mdbFsbRFHYCCB8nlmhpJLCxzjfCdeG5HNVw5i4Ur5INZPrkY7Cv/tIMtOGREuo2GCmaF/LA6HDDym7BT/cxiY1wtTHz5mpg2tMSGq4C45hHT+Vlrr7tfEAHrPYofUeonnEoXVFUFtcl6CTE+kxXq6DBXE0wK3HU0vdTAY8toERn5rD6s5w8UB1a0zwc/TasYMERiurPXLVcJZ2Bzum7XUAFqK1fi2jZ4vVCj3fJKbTbkAaUjV28Rdu5nlmlKHmEeHbVPkg8dFB3UL080u5Ldsbe6qMaNBuwrSqW0650P502O2mibgPFdHtOsWQwA2daDv27TqqUT0NbL9hhXAeWCOcjIYab5GA0tsuSOmxyGy42ymR3Q3xKYeQi9SEkv+R1CjQHwzJrYM4h3Cikdh5rzSkS5+OyeepDGs7vYLkOAsFR4me0VHIr4hgE+5SdFmjm2JyDi/oLq/QRchtWxLrdV5h7mZucFDPJUm6n5wn9WSjtp+0JZaT1JWKgLcp83JqGWWckEJe+aC84m6KPZPpEIcopxHpifB9O+6SOosuk+dVwYuuqOiSqu5+nuwruileVgWV/zZVrih8vdcXaJFd0m7HxNPNAzobArjuuFBMASl1RiHBFHSKczQaAwvKX3J5QQULTqdHLBk61Z0CGofOCXKIhZlyh1Yf8ZxjCrH+qvuG+LiruFrvC2feAIbRMkL6IiaBrTbKDHQSRSR0FcSKWfuy/jzXsZucpxYbbkoD7ShSIIsOqnp/S8YzChGCQuOlQpbzXjDPZy5SW3onMFMQNcgS4dHzuX36pquYvqkvXFyS/XZRMM1+nqj+t0Q/nnr1wko+0ykx3tgoCC66NpJZyWnMoDB+r4jrcTeF2RkEy6kBypSiXDTKBqCTGKvtoY2n3CvVfkj31ijoAPOEkZ7LsAUR16bsBE+IBc+mGLQE7xm5BU9QYeLN6MeSApgCAi+GCdGHyBR8ahXvFFx4vtZqX0BjTQjn+aTlmaRbQZM1uRh64R7Vbvb1Ll7iqUbb6OAs+kUDCfFqXUyCVaOui/IADVHEoYGDN/tLWuHKNENJBd1zVHBvIcI1/YBXs0jjMbrKqWnKzgCgMd9F+u1TrUl5e8dW+Lsp/m8oWcFD2izBJNGuEP3HA3Agn6bj9cWXxmtlOfLxcK3GC75apz0ssKtMJ6sKiH3pVlSxyN0qChUbz6PC8BrpwMypdTLfClJ6XdFphq1HX7tSW7v2AGtZy2K/+qaeQ/W022zTEpuCKjTSV5Ne/bnc7nnu71UklfYKBsdpU10gDE/1w+PWEiFrj6Spv/uf7U3w7ozpj3NPvWjhMTDUtk2QrTB0yW53Wp9Qc0AWXF7svubtFqVaGWqBKdr9xuA1FmRIzCBP+3IQzKl2m6wRKWliJ9iPSSzWLY0Y+Z9paHNiqqtOO7/Tam4hLEmR+tlWoYZkYMLhZFqBqVo0D6DLmZcA/Aatoe2aRRBwai5wV3jND7KCEADGfS31vOPu1ijVKoVW7qRc8bv5w18G2vJRFz4h18bB6fKWjaqXXx/5HB+t2hYw2xas8n3pEd38KZkBPGtXZrwQ2Xcw1K0g3vAjw/nanMITm5iJE3uiv25rMBORC/uCCqZo3Ze2NVJAk3d0z0yXhoEJASrEOet3Se7idN4wYVX9tZt+5PE3MIvetVnUe++Cr9LCzrC9XRB48nQcPRoRVVpBbpSp6BV5dnad9UNEvs/BmleQMoTHU0O/WNDITWWOblhK4dnRVKtNvj3WKgxKQk/f0Jn8Cc3KFUWerHUvnEBGYGSXWHjfvzmkd+EHWJ3e5i/KiseuTubi6nfhK/cvmQPINs5IplSLnv+S5g0mEiY98+mLRt7mHTqeIZyIgfFAQX8hE5Lhbd0FwecTAxgac51MSW5ayAyEHHpR7oxPjsMfqen36R4HlqaMO8SCCQiixSdVD0nW3FxQnaOIWUoRJFBST0OCGdnetEshAFCwKpGY7zZTMqoAZaeXBayVqpMNEox/Ua8Juu3Paz1EyVxWCNweT1N6HJd/7Q9br6EOYFnGxfJcOkRv4tFCibFc+OePLeb9Bn705EcaGIsfydLsrFf/1ibe7Hug+RO4JsY8BYjOnQMtLt0JpuHmkgCY2TDW1xx8zhlL4DEffRetW7yWPH6hZZ7WbdyHwWYNdaD2b6OjhtlwcDOsYpoVVZGfKkTK44t85AT5mo/q95Un7rYI+jXnbueBfkqEf6XI4RdwmfVR3+ddgXq2jVja7ZIWOadHr46kNeOC0xeWsjy21fmSaGTetp8C5Kr/yDA8XajjirpDSGBrWvyFw2zw6LTodgSMKKVIS9+GJkyQiKDEibV/+VDf6QhIooLKJJNMwaSjkRONBQww6ZwThDSAC4iGO/Y07w6pw0aW5Qu8rV6fRCcJDj7wawo/uno696DqgzhxGuwiXZgWarZ6C9WxLA9fX+66+wy2Vvda6PhVGAq7F9UCyUtVqGcag5f9LYj9Kfy+iUjXJNLX/5gyHPTIwF3xKVhhuu/XSW9p8S7I5Z3V40qxRHNwj7kPCUUTLWvTl0o2qK9Ntn926D9qVKRtdFgxATVr4NbwPz7ooOp4Wj+PiVGBbx2FRLE64RkSSDOs3dStypGrNDcb9+EgdNEz7X/sR2GKNQkdqvh2WiShfMH9JC9rr85lBczwbWwdAw5ygP0H3TsOARetKuaCuhgnsuGTm4Vp1puhnudEXx+oeJc5gWHSOphQBVHcrmTPO/rXkGj5v+s9hPq5nAmkf9xMeErFdB9ienGBNp5H/NWsrAjLNvP7rBlVyuN71yo89tGxDIc4iCnd1+DlChjV4LijCfrubq8/HTTkqxn70pkI0FAchU63RA+n/QtbmcjLOHEJ2lBvdjunzPVGhQ/3TipSW9c1igoS+lsux1q+NmjeyPRPqMRDUMj6WpU7XiClWLovWaDYHIookHeMfaliGOhckqp6fdDsHLQENMBSltgffGPz01x3bcpqPff6E5bk+DgNyqjdxV8rf1ILE2TqeaIOCNbdV5+huykDdgg4rUN1ZD00NmeLpShZUKhuDBnMzRdgnbPpYepS0F7e/XXJ3wKQxfqCSNDnFSj6qwzvd1w2C8nEOB0vNfFZ9ZNrivAXFii2ME0C5/AE1f4v+HVshA6wc02kMc3G0Mm8rBhtl8mkJDpxZuH+3OmRAM4Sc1ZoTpl45oHUgQjyk/s/VEC0xVghz+2+6wHSUI1Qf05pm9mFDPddnoxgH16w3LKV1FeE7qW17LoRx8bCCBa3+UeME0rWpiH/oRUEST/OcsgwZ/y5Z8SoxK61O7uSf/7k9YLrqpgEM7o9ocHLCixaQl8bS4zM+3DNceLU+zZvb3B4/7YolEDWSk7e2NhqlI2VCJY0xPa+lf2AFEpB2vKPpEHrkZobIqFIzI6iKaMFwrqrbWBHNaiuQ5dJKd0alziJz9/KR05uTbmY0XtnC8Ri1AwXt3gzqYBDRm5bS8H9wXlf9EoeuTX4dR5UGxKP4KvEWq1CazXDliJW02KwfO1NXlBXUJbCbDJPyqAYiOX0UV6YsNKPiFaMdqrcc9hD8OJ4DBULj/OV2mSMw8YyCGRt3G3TMCVvZKTgFpCv5n3SbkzA5CaRH9+2WAMWp1jA8+KDLEvKURk36umAOxi7p4hkW05batfads630ZzcoX+PvXLqDBmoU3f5AOqF9eWdprV7mmdBdaM0Uo7UcQsm7wuQUqBZO3P0hHFxdH5UGVKVii9WbRwfeSiC5cStv3EqAvyyhoHgbJThsqHuJhT0VCHSJw91UWVVg3wW5YRHpcF7Qh6WexxKv9Vie6kYeDb7yvD/93X34h1EqQiNG4p6KMK1mk2MkQTP475Zxec6XUNpEvUwQrTyIg2QpI+lrmDCZqODNC9PEvUy5g06j6LQE9jusSkPltDxrAh0h9rAfYwlLusOhAD35NCt3kISmt6ZjteYxXCbVDGdG1qex2SopLjPAYRQvtmHcZzMiw8SiiaU53kQh/6kX4TXRr6IA/shOwaUEN/iZbzainaBYjfaq5ihXkhLPv9x11V8evbSqYJOhEo5yTxkjMwszbPdntFOtRBAJWy2xnYIDBy2Y16QkV/lrQJGT2DHaTZMMmX3n8Wlkf5tS4mwuh1EwYoKGbdMjgdCynFkga1oPL1ZaGlL4sCJQVGFp5BcL4nPMr3dGQ0CmGU8iFbXmPeyiTKjkoy0XL7WTlDUtwJGznggXIJLtnr6BHAh9pORktgdJneaBWFUgR37fw6cKD8xtLYOrTLKt68LlONYzNFXuYniFqc9XIkPBlrEAxLXh+m637oLKd//zvwFA4mz2PwNmDRVJsfqtiZxTsxWlMvDAtPFHnrlaOCfYq5UgiCRxQZ7RXIIAgjbfj+qoInRFoKokFZLbx+ShS5R5oR+Im2Lo/tcjRQpAYOpGbhuOhZavVXXETlPS8RmuoqMDMsrDjCFVTSrRFjRZt45aQss1jdNpxxb0/LnPbFwPfkJLBHOMgjZe11rc0IKMukheklbF5TZLm8phoRNGCOYPKif3f5v58zEWzs7LNzRqVnUFHStanYNdCTD8qFPf8TXmKy0lFbN4nxy1ambyga27wuJ+8Gndxio4zJQl9PPq9qEh+5Rgdg8fczctkAv34VLyolgzra2+b4mFRnP1nvtl+FYzjd1d8Yee4gpDrKlDdbknHVNGPDUDJ9g7uuyRk7oOYBreMzoG3QTbeonVXFhE+Ip8DckQzccOj4zNDDS5ZDl71Y7B7FXx2HoQqvk1dk3aqyoTeA7edHasdmWM+lx3TrtK10ENVrqbSdGwh1aSNLzicm3ZH4TSnt+hcHc6tsMYUJsGEXPeQHyp57vDpQbYajRObsSWI8XshntSy8al6YUUwthQ4WtizdNd04yHgST4yFbNIxlitos6Je1yL8KBg4xC7vgCTwHQ9AYGFqhJAdwd5nX2dqmwV0usjjCFhmmjq+mGrNneHh7qR1Sl6WWOGJi2JTR7Vvva3JH96bCrsQ8+53FG4yaeUawrzb2MVuFAb5m3SQdPYLRzKscumVev9EeNLN3FacxiffO1Ngp8DzmereoJAmMariy4qoGrcbER3VO4DwqvukhK9jtBfdgkAux96CbQvdKIeN09wo1FdV+NakgnG1Mb4UJLDCQJ6jGs7QQkiQtTYjDpogGYdtJhzwx8OI0MGh3fWYCBjK9I/wmurJpys8r07BJCMbpEgx04/o74genOXV3qMK54XjfS1uZ73KurcbnYl7lqEZ9aB5uFfFhU0VA+7sV4tVsTV6w7AM2qX5R50oSTTi3yhdCE2QpmEqplWpJAXSt9ufu8yOxgiNwpRodK89Cj7ccHqkpisb5zWo3j7bF03OHI4y0cU4ysON9mqeHgwcdKqSh3TYfZXhO8jrPybVGQnuHpJlC/YXqL4odSHu1/u6c+qU1mNLJ4UIHzM+KuDHPCtzk5E0o5PJIbcwh7X+qCdZt1X6XMVWPLqFELqt0yImpanz/hJSz5ZOm3BzNiRii7nTgGwz4qGy6pZ+KdNr5OHAs+yBCYZdkpLzM44XGqQXHu2VaCSclhGAbVKEWaCIvGeHJGCc75ujMZptyqNgFhjdCehu7V5amGF3dXd/O705i2/WbRyR6qTI2aWdfuFhv9lYpM5+ZhvKGL7R6W4DEo3doHlbV05tiPaRGMFfKDRijx5JNODdPthyRAI5CYQsti9m//pEJQQrRQTx104+ZNLRIryj/cyQ0oFud6DQLkHLb1pgzoZLcooJmVJ8KSUU13dCJSOdM6YcUcyfR5xKeSK1ESjP/yxmLGxaDTQNjdAym0cqO6+5n53f647cKR6UMW/eP13ZNkrAilplC8fx729EhOgKhxdyukNt281nyNyulcMHKHDJmKWAIxQp9wHvaWkW2KXN4dD/Fxs+3yI30FOg3vKGx8QBuQeF42ymfN46sU5MVLVHWnRxHKHnvSTe56TsFAXaUGHpMbu7JR5GqUXECnx6LkqFPi1Lu7Nlq1eevt4+tWDWsFhFrpf7IKYlSrF90UpWfc90LfmY8niZCIhYi90Dt0tyVgciJyvFOUN2d9I8FBTqQFbL5pFW+ei/JK7Z3uOtYNc1A8mi9mRV8r6+TvWz/tVLcPBCxRd9gfKEF88FLypBbE6Fl8+Lo7VcULMgyrRmhW7NxzfKzpqGn8ls3RXWhmFL/nmTiem3/cbWHWX+Vxp/+3MQGUStePQQKSwBZtMDxfczW3FHqU72UmjD5mk6hQsOW4h8EI5Jon8YRTbk1J60ywkVa3P+5kp5VDn30NqpVNo1PZwpwbZ7Y0e+L8neXXJ5MLfkVbUGLAqjiLVQeq5hbuZXie+dP/psv4RwFUEXharl1SyO4CM4VjIAM8+qIB2oIDCrFimAXZ6EwyVEO0GA+WvK0sz588hylU/ctd3C6rK96i9WoUheWw6xv9ALZAZhZmNc0DUSrgYMOKWHlCr1b39JitxjBkboGJMGN90j1BPhVPFIeEtEPTvMzXUfuHhZQIRB0cnyGHE/HjieFqSNPhdv11Wi0wf1q4PwqeGgu+GF92KzPH1bIiHtaAxEdJ7F8yGQoeKLbN43er/HqYf+KhPWmHqKGVbC6De1yLE0iLTkhI3c89hq+w4vO7y6NKYEu2e9B2eKDycPG0wv6/r0vADvK1CE42s0os6ZzOephq0a12ZZ5Ahvp2mOfj4Veunw4Eigv3KQTxdsmVmNuPGyGnudPycFoMGs/yhlGZmUUYmeVuZlVdfkgVPJACSUMxNjUpY345BM3F7j0gzIFMqgtDj/vevPU/Hl8cHjcOn30VK4t1G1Kqe8zyhA0uddMhg18Cr+Iv9Xo3VVs+yTh1nWGre762pXagCyW8oyODTMY1XaYZQ+0xXc9je6wsaCgiQC6zpAG7hpFPF+yd7TKslFHMk+HQ8tinMZZE1bRZohpQRE0/YKSHhZSuGujU+LtoBv23qttae4qOj0EVVhu6VJPxgLLyti/g6FH4NXNBqrwx7Co7kAQ+o+Xi59FwGz+NEGq+sibGrMsN0nNpFpdFRlFE7W1ky62NisUoaJonA5n/grtMQEGptXWrvsyTFqccFTQ0+IAinWHL9gD6HdXtjgipM66khGRVO3nyri4U/jDI72pqQ0mPtubgazTHH3fhNrgiV61jMVTarpWb0uUDWbTljnUGJBkxeYrLmq+wHvpTcyioxejT6UwWkhpx/SEkaTOL8Caa8uJp3sr4XL0S1jP/f0KMZS30cEGmu72hr7t8HKk2nPyIzpBCoCv5iidSGGmHm4S/Qc9GYcOol6m34gUVIVzUxqeqZr0MfBgGhRy/MzoOQSl+xjAD6G8Vo/VpVKmtCLWEABlCeCgThW5oC1RxfqgtNTY0iBEYSVjVzfOND0M2HFmrDIgKxWUWUZPTGnbFpR8ZtKdK/a8B/zTE+NRLH3/TwHRnLx0oesCjCSelQrB6W+6iaRCdqTUaG5EeWEBrAYZe/mvOSZC79/q2G5sShA3RrhUscLrspRgmzDwigA+3wZLWjhqET2YbrkDkbxmYTcBbn2SV3KBZVSe/38CBVaLx91Mx6H+x3GQ/tf0mwhi4ViNLZymXUMCskmkT2I/Qkk0YXYN7uA3SH1oaei+5DFWk3FNJiZ9OlSsBLoXMHwLYa3DoJPZ106784H8WJgKrJfheaMS6MWgBT1Qonl+hN0pk81db6XF2y+V+yreW+JaIoCu3G93jAGqPrZh+EOjrUNmVDwrS3XP7M/J2yYApwu7wjvX25XXdM1YZL+Wx5xYn0D3hUcNoetxY65xbUEnjT5YefZwS6+CBOGfRPIhNcehXvMF0/yidGs7mKGSeFk5v3MntsaFuBO+aYd6Ln2XVBspZ1ODL3fSUB4QpmPTGVjJ3tlcG1CH5H3cLvgzJ6lah+6twCqt4ASJI8AFs1knKfo5b1FcNQY1nW9WBZsojC2V+8f2Q3N5vliZqtqYdwPMvyCYjTTiVtelC3vwEkxkX6XoFGPIxNwLt8MQZWgoxolMt4OOfNkXNACCK1txktH2G6Y909seY1V6XQFG7PiUMnsytrvwPfTXV3uwZLCI+RzgAuv3P0hbH6a8HznMd4HRwXddJaXcFclVXgtXCNMZQIpiewzjs9aFjHwST+4ActnxEjcxhvJTJQLojIP8pAqrDyBBx2iHR1ercYP6vw1bTS4SzhJybJTD0zcdzNcbXfZbhec7qnWTgg6TZ2/PKuhW4/iYBqQVJptfvEU6A4GcRi2h29uFRRh5+J/dySzai5j9+bxDnoRPIrESDqVgMnRrFj44KL27P0apdsmqAHl+R5clo04WghVzXS+B+bJsyixd27v7aAGh1um5dK2JUxfQg6XCewzn/DcsBZo1WV8n88YRc12ZPfL1cYFe7a4thSaUM21CIQznrJKOYbrB2wQxdeYDHfoIS/aEaS6PH8kFnZrcOhmaczFUK5FF6C771rRrb2/QsSaNOp0dqs7DwLjxy51TBLYNii1FfSlZDszxynt4VTxkYzBi3nNLRL6/5lzKBaeFXR/qiMiXvYaURHyArubRIuDtGfAVzAFZ3nY/jfszvA9byPY/DJLP+nN+TLjW3yHZnVR5CTzuU2mmoZFLQDLf9YzqgIkbtaSRfd3cOkmf3RlOzVYUbmXt4HDowozPIndDpUBtukgWO+csWR1SbcPX3NSagBMrO9B5q3+eb6ZwCHUTqJXmzwfKdI8VuEYNXUUF9FJvlEa7bcwssGunuR6UzyDPJyWRVJcRJm3oLFRKAhq40VwF2eL4Xijui9iWCEMPdyb6si56FaWXK1xRid0v7sUyFYwvPjozgxr8b7wcSPg2iOZTh30HNMzyYlG5S9g5uVPw9lQQLdNxKIc9zaEAY0M01lOc/avouynTXCQXnKOQx/lbryKTl+v6/VcOnPN5CHsRjXsNM6L3go+2xv8GXcq1vKIELtlUaft2/WV/cVm3FxUSEGz2iGNPuoitpereNoPN//O065tMnvZqs/NOsNe7uEkIKjLa6tIo6HSo+MceswiCzD6a/s2Vw2KnIP+TdKPQs2qCP1RPjFwUf2e7xGBRsjGmcShHOrC8Fcvg0eYzw0tbOffG1AXoqOyaNe1PhnzkCHv9TaXvJ+TAsVlfzRCRxWCdjnZauMUOCsFa5wqsSnLuW3L9pEPa3M+bP0+gSYbzVOmNXvfHLDCbq0A98NQklAxhgoN9xOa2+OxHcd7gtnYb6lo5VIEgjdpcJ8CjiQH1ew8iPyYTczmcj1NUwRr6PB/WlCzC9mpRBQVHoozoAZEifWqCSZF8FCfJyo0Al25m4G+6lazuCPei5KTtVK5wqJXNYhYDqyImXKnfh+ozpC4Zy40SkPHVjpb0N5IWfhHrkGfQbujnRQyPJrfHtLvP4G2ZgrLe+QqlXB3lvrXUzBRYGeBy3Ae8nsBql86Ds93hQlScKWBIXUQUMy8y2B7+zCoqzx9oEZsHV3NFGiRjSZBWN4bVZ/nY5Iz9sFILVgyEWyG/G/wZt23QmX9mldX4tmavvLOBLMdUQT8NBOI/Iu34soAEKJLpq8GGe++i6Q2/zUwDOT8MCZWvWkLqLgpSVScXWbn95YQ3ngMn5z6UNMgx/FRFNH8xEtDCxmE1RfJ4003qvVkPkvzK1QwuCtExaIDrhEPlUAduqU4YRubWK2Ou97P4CjOKgn+7Z1JsvMvZ8LddN6UGYaNoIQAtYNNrWVIywzz7dPEF9OXSMr9fUM2D9S0jBdx31SvMdM5zkuPENpvawtCjZEU2lvRfH68YqT0VYpljwUm8wKmUy2K+5EG88ZWPhf9NM3gWj0Kuw0//z8Y7mtIf6lGLiOQjE3YZNAtA8jJ3UzG2D05lA1FgDatZG913FXInEHtLuIyqK5WQsrJVd71zFRUnzKRR1u4EtNPlxNdtsz6iJFWwU0/66lL+TgQjSDANMRn0djTb05TwvmlTU7TOdGvKldSh7mWnyWrlkTKFSWLjYpoSN1mYLkAhg/moza1rdv0f1Um8QCcQsPS3Y1fdBbGZsueoDzfU1+IPsab6m/UPLxBw1tUdwE1rOhtI3b/EeJPrMHegmd43kxcXShXe+3MMCDmXjP6AEX46JMIzcdFod6dHfiy8h0Ndx14wlrSdl5ypDFpbs1SasimywnOPhix/lIfQV1ZA8dHQ95OlRrXj2NGYOujXW89A4fwzn/MVVYPI+/gZdD3z9IHOAK+uyc2Mkg5ilhdZ6VC02r/1OF3eB01Zx6NJKE9SG22nDVHMGyqjpbgDyfocTaP78n3cxd5JBQZMv1plu5K4v/GQFDxCO4UnGUCwS87C26yRCHi44M7am8x3ffX6qXn3NGTOERfuDWEY5HU8BNafI92oNviwz1+au8DdI1vdI4o3WcAUAPuIMfSM24B13rsmPd3xiE4ZXARc9hIPXcH0Y4uFmyMW3wiyrJHctu/XoRTj7G/pW9Z+40OQ2gUDkJUDb4GHI8tegtMmXJY7uQkI/x+EUQnNPZFo/5/aTSVYuk28nSS7gRKsSDd+WOa0tGPTtDnkAa7ia/dz862n4j48mXuqM4gn7ZiK5YF3ksVLFoQWCOxC+BmORbKM7h9IgAWHVKEgqhv+//wM46eVOAYAAAA== 2 | -------------------------------------------------------------------------------- /test/hyper_binary_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(hyper_binary_SUITE). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("common_test/include/ct.hrl"). 6 | -include_lib("stdlib/include/assert.hrl"). 7 | 8 | all() -> 9 | [merge_test, serialize_test, max_registers_test]. 10 | 11 | -define(VALUE_SIZE, 6). 12 | 13 | %% 14 | %% TESTS 15 | %% 16 | 17 | merge_test(_Config) -> 18 | P = 4, 19 | M = hyper_utils:m(P), 20 | Tmp1 = [{1, 1}, {3, 3}, {9, 3}, {15, 15}], 21 | Tmp2 = [{3, 5}, {9, 2}, {10, 5}], 22 | 23 | {buffer, [], 0, T, _} = hyper_binary:new(P), 24 | 25 | {dense, Compact, [], 0, _P, _} = hyper_binary:compact( 26 | {dense, hyper_binary:empty_binary(M), Tmp1, length(Tmp1), P, T} 27 | ), 28 | 29 | {dense, Compact2, [], 0, _P1, _} = hyper_binary:compact( 30 | {dense, Compact, Tmp2, length(Tmp2), P, T} 31 | ), 32 | 33 | Expected = [0, 1, 0, 5, 0, 0, 0, 0, 0, 3, 5, 0, 0, 0, 0, 15], 34 | Ints = [I || <> <= Compact2], 35 | 36 | ?assertEqual(Expected, Ints). 37 | 38 | serialize_test(_Config) -> 39 | H = hyper_binary:compact( 40 | lists:foldl( 41 | fun(I, Acc) -> hyper_binary:set(I, <<0:I/integer, 1:1/integer>>, Acc) end, 42 | hyper_binary:new(4), 43 | lists:seq(0, 15) 44 | ) 45 | ), 46 | 47 | ?assertEqual( 48 | <<1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16>>, 49 | hyper_binary:encode_registers(H) 50 | ), 51 | ?assertEqual(H, hyper_binary:decode_registers(hyper_binary:encode_registers(H), 4)). 52 | 53 | max_registers_test(_Config) -> 54 | ?assertEqual([{3, 3}], hyper_binary:max_registers([{3, 1}, {3, 2}, {3, 3}])). 55 | -------------------------------------------------------------------------------- /test/prop_hyper.erl: -------------------------------------------------------------------------------- 1 | -module(prop_hyper). 2 | 3 | -export([prop_set/0]). 4 | 5 | -include_lib("proper/include/proper.hrl"). 6 | % -include_lib("common_test/include/ct_property_test.hrl"). 7 | 8 | %% 9 | %% PROPERTIES 10 | %% 11 | 12 | backends() -> 13 | [hyper_binary]. 14 | 15 | gen_values() -> 16 | ?SIZED(Size, gen_values(Size)). 17 | 18 | gen_values(0) -> 19 | [<<(rand:uniform(100000000000000)):64/integer>>]; 20 | gen_values(Size) -> 21 | [<<(rand:uniform(100000000000000)):64/integer>> | gen_values(Size - 1)]. 22 | 23 | %%gen_values(0) -> 24 | %% [non_empty(binary())]; 25 | %%gen_values(Size) -> 26 | %% [non_empty(binary()) | gen_values(Size-1)]. 27 | 28 | gen_getset(P) -> 29 | ?SIZED(Size, gen_getset(Size, P)). 30 | 31 | gen_getset(0, _P) -> 32 | []; 33 | gen_getset(Size, P) -> 34 | M = trunc(math:pow(2, P)), 35 | ?LET( 36 | {I, V}, 37 | {choose(0, M - 1), choose(1, 6)}, 38 | ?LET(X, trunc(math:pow(2, 64 - V - P + 1)) - 1, [ 39 | {I, <<0:V, X:(64 - V - P)>>} | gen_getset(Size - 1, P) 40 | ]) 41 | ). 42 | 43 | prop_set() -> 44 | ?FORALL( 45 | {Mod, P}, 46 | {oneof(backends()), choose(4, 16)}, 47 | ?FORALL( 48 | Values, 49 | gen_getset(P), 50 | begin 51 | R = lists:foldl( 52 | fun({Index, BinaryValue}, Register) -> 53 | Mod:set(Index, BinaryValue, Register) 54 | end, 55 | Mod:new(P), 56 | Values 57 | ), 58 | Max = lists:foldl( 59 | fun({I, V}, Acc) -> 60 | RoZV = hyper_utils:run_of_zeroes(V), 61 | case dict:find(I, Acc) of 62 | {ok, RoZOtherV} when RoZOtherV >= RoZV -> 63 | Acc; 64 | _ -> 65 | dict:store(I, RoZV, Acc) 66 | end 67 | end, 68 | dict:new(), 69 | Values 70 | ), 71 | Expected = lists:map( 72 | fun(I) -> 73 | case dict:find(I, Max) of 74 | {ok, V} -> 75 | <>; 76 | error -> 77 | <<0>> 78 | end 79 | end, 80 | lists:seq(0, trunc(math:pow(2, P)) - 1) 81 | ), 82 | case Mod:encode_registers(Mod:compact(R)) =:= iolist_to_binary(Expected) of 83 | true -> 84 | true; 85 | false -> 86 | %% error_logger:info_msg("values~n~p~n" 87 | %% "encoded~n~p~n" 88 | %% "expected~n~p~n", 89 | %% [Values, 90 | %% Mod:encode_registers(R), 91 | %% iolist_to_binary(Expected)]), 92 | false 93 | end 94 | end 95 | ) 96 | ). 97 | 98 | prop_serialize() -> 99 | ?FORALL( 100 | {LeftMod, RightMod, P, Values}, 101 | {oneof(backends()), oneof(backends()), choose(4, 16), gen_values()}, 102 | begin 103 | Left = hyper:compact(hyper:insert_many(Values, hyper:new(P, LeftMod))), 104 | Right = hyper:compact(hyper:insert_many(Values, hyper:new(P, RightMod))), 105 | hyper:to_json(Left) =:= hyper:to_json(Right) 106 | end 107 | ). 108 | 109 | prop_union_binary() -> 110 | ?FORALL( 111 | {P, NumFilters, Values}, 112 | {choose(4, 16), choose(2, 5), gen_values()}, 113 | begin 114 | Filters = lists:map( 115 | fun(Vs) -> 116 | hyper:insert_many(Vs, hyper:new(P, hyper_binary)) 117 | end, 118 | partition(NumFilters, Values) 119 | ), 120 | Filter = hyper:insert_many(Values, hyper:new(P, hyper_binary)), 121 | Union = hyper:union(Filters), 122 | hyper:card(Filter) =:= hyper:card(Union) 123 | end 124 | ). 125 | 126 | %% 127 | %% HELPERS 128 | %% 129 | 130 | %% Lifted from stdlib2, https://github.com/cannedprimates/stdlib2 131 | partition(N, Xs) when is_integer(N), N > 0, is_list(Xs) -> 132 | Len = length(Xs), 133 | case {Len > 0, Len > N} of 134 | {true, true} -> 135 | [take(N, Xs) | partition(N, drop(N, Xs))]; 136 | {true, false} -> 137 | [Xs]; 138 | {false, false} -> 139 | [] 140 | end. 141 | 142 | take(N, _) when N =< 0 -> 143 | []; 144 | take(_, []) -> 145 | []; 146 | take(N, [X | Xs]) -> 147 | [X | take(N - 1, Xs)]. 148 | 149 | drop(N, Xs) when N =< 0 -> 150 | Xs; 151 | drop(_, []) -> 152 | []; 153 | drop(N, [_ | Xs]) -> 154 | drop(N - 1, Xs). 155 | -------------------------------------------------------------------------------- /test/prop_test_not_suite_in_progress.erl: -------------------------------------------------------------------------------- 1 | -module(prop_test_not_suite_in_progress). 2 | -compile(export_all). 3 | 4 | -include_lib("common_test/include/ct.hrl"). 5 | 6 | all() -> [prop_set_case]. 7 | 8 | init_per_suite(Config) -> 9 | ct_property_test:init_per_suite(Config). 10 | 11 | end_per_suite(Config) -> 12 | Config. 13 | 14 | prop_set_case(Config) -> 15 | ct_property_test:quickcheck( 16 | prop_hyper:prop_set(), 17 | Config 18 | ). 19 | --------------------------------------------------------------------------------