├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .rspec ├── CITATION.bib ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE.txt ├── README.md ├── Rakefile ├── bin └── nimbus ├── lib ├── nimbus.rb └── nimbus │ ├── application.rb │ ├── classification_tree.rb │ ├── configuration.rb │ ├── exceptions.rb │ ├── forest.rb │ ├── individual.rb │ ├── loss_functions.rb │ ├── regression_tree.rb │ ├── training_set.rb │ ├── tree.rb │ └── version.rb ├── nimbus.gemspec ├── paper ├── nimbus_outputs.png ├── paper.bib └── paper.md └── spec ├── classification_tree_spec.rb ├── configuration_spec.rb ├── fixtures ├── classification │ ├── config.yml │ ├── random_forest.yml │ ├── testing.data │ └── training.data └── regression │ ├── config.yml │ ├── random_forest.yml │ ├── testing.data │ └── training.data ├── forest_spec.rb ├── individual_spec.rb ├── loss_functions_spec.rb ├── nimbus_spec.rb ├── regression_tree_spec.rb ├── spec_helper.rb ├── training_set_spec.rb └── tree_spec.rb /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | continue-on-error: ${{ matrix.failure-allowed }} 8 | strategy: 9 | matrix: 10 | ruby-version: ['2.6.10', '2.7.8', '3.0.6', '3.1.4', '3.2.2'] 11 | failure-allowed: [false] 12 | include: 13 | - ruby-version: 'head' 14 | failure-allowed: true 15 | - ruby-version: '3.3.0' 16 | failure-allowed: true 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up Ruby 20 | uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: ${{ matrix.ruby-version }} 23 | bundler-cache: true 24 | - name: Run tests 25 | run: | 26 | bundle exec ruby -v 27 | bundle exec rspec spec 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | pkg 3 | rdoc 4 | .rvmrc 5 | .bundle 6 | testdata/* 7 | .DS_Store 8 | doc/* 9 | 10 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -f d 3 | -------------------------------------------------------------------------------- /CITATION.bib: -------------------------------------------------------------------------------- 1 | % This article is the definitive citation for Nimbus. 2 | 3 | @article{Bazan2017, 4 | doi = {10.21105/joss.00351}, 5 | url = {https://doi.org/10.21105/joss.00351}, 6 | year = {2017}, 7 | month = aug, 8 | publisher = {The Open Journal}, 9 | volume = {2}, 10 | number = {16}, 11 | pages = {351}, 12 | author = {Juanjo Baz{\'{a}}n and Oscar Gonzalez-Recio}, 13 | title = {Nimbus: a Ruby gem to implement Random Forest algorithms in a genomic selection context}, 14 | journal = {The Journal of Open Source Software} 15 | } 16 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.1.0 2 | message: If you use Nimbus, please cite it as below. 3 | authors: 4 | - family-names: Bazán 5 | given-names: Juanjo 6 | orcid: https://orcid.org/0000-0001-7699-3983 7 | - family-names: Gonzalez-Recio 8 | given-names: Oscar 9 | orcid: https://orcid.org/0000-0002-9106-4063 10 | title: "Nimbus: a Ruby gem to implement Random Forest algorithms in a genomic selection context" 11 | version: 2.3.0 12 | doi: 10.21105/joss.00351 13 | date-released: 2017-08-18 14 | repository-code: "https://github.com/xuanxu/nimbus" 15 | license: MIT 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | The Nimbus team is committed to fostering a welcoming community. 4 | 5 | we adopt an inclusive Code of Conduct adapted from the Contributor Covenant, version 1.4, you can read it here: [Contributor Covenant Code of Conduct](http://contributor-covenant.org/version/1/4/). 6 | 7 | 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute to this Project 2 | 3 | ## Report an issue 4 | 5 | The prefered way to report any bug is [opening an issue in the project's Github repo](https://github.com/xuanxu/nimbus/issues/new). 6 | 7 | For more informal communication, you can contact [@xuanxu via twitter](https://twitter.com/xuanxu) 8 | 9 | ## Resolve an issue 10 | 11 | Pull request are welcome. If you want to contribute code to solve an issue: 12 | 13 | * Add a comment to tell everyone you are working on the issue. 14 | * If an issue has someone assigned it means that person is already working on it. 15 | * Fork the project. 16 | * Create a topic branch based on master. 17 | * Commit there your code to solve the issue. 18 | * Make sure all test are passing (and add specs to test any new feature if needed). 19 | * Follow these [best practices](https://github.com/styleguide/ruby) 20 | * Open a *pull request* to the main repository describing what issue you are addressing. 21 | 22 | ## Cleaning up 23 | 24 | In the rush of time sometimes things get messy, you can help us cleaning things up: 25 | 26 | * implementing pending specs 27 | * increasing code coverage 28 | * improving code quality 29 | * updating dependencies 30 | * making code consistent 31 | 32 | ## Other ways of contributing without coding 33 | 34 | * If you think there's a feature missing, or find a bug, create an issue (make sure it has not already been reported). 35 | * You can also help promoting the project talking about it in your social networks. 36 | 37 | ## How to report an issue 38 | 39 | * Try to use a descriptive and to-the-point title 40 | * Is a good idea to include some of there sections: 41 | * Steps to reproduce the bug 42 | * Expected behaviour/response 43 | * Actual response 44 | * Sometimes it is also helpful if you mention your operating system or shell. 45 | 46 | Thanks! :heart: :heart: :heart: 47 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | gem 'rake', '>=13.0' 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | nimbus (2.4.1) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | diff-lcs (1.5.0) 10 | rake (13.0.6) 11 | rspec (3.12.0) 12 | rspec-core (~> 3.12.0) 13 | rspec-expectations (~> 3.12.0) 14 | rspec-mocks (~> 3.12.0) 15 | rspec-core (3.12.1) 16 | rspec-support (~> 3.12.0) 17 | rspec-expectations (3.12.2) 18 | diff-lcs (>= 1.2.0, < 2.0) 19 | rspec-support (~> 3.12.0) 20 | rspec-mocks (3.12.3) 21 | diff-lcs (>= 1.2.0, < 2.0) 22 | rspec-support (~> 3.12.0) 23 | rspec-support (3.12.0) 24 | 25 | PLATFORMS 26 | ruby 27 | 28 | DEPENDENCIES 29 | nimbus! 30 | rake (>= 13.0) 31 | rspec (~> 3.12) 32 | 33 | BUNDLED WITH 34 | 2.4.8 35 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2023 Juanjo Bazán & Oscar González Recio 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nimbus 2 | Random Forest algorithm for genomic selection. 3 | 4 | [![Build Status](https://github.com/xuanxu/nimbus/actions/workflows/tests.yml/badge.svg)](https://github.com/xuanxu/nimbus/actions/workflows/tests.yml) 5 | [![Gem Version](https://badge.fury.io/rb/nimbus.png)](http://badge.fury.io/rb/nimbus) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/xuanxu/nimbus/blob/master/MIT-LICENSE.txt) 7 | [![DOI](http://joss.theoj.org/papers/10.21105/joss.00351/status.svg)](https://doi.org/10.21105/joss.00351) 8 | 9 | ## Random Forest 10 | 11 | The [random forest algorithm](http://en.wikipedia.org/wiki/Random_forest) is a classifier consisting in many random decision trees. It is based on choosing random subsets of variables for each tree and using the most frequent, or the averaged tree output as the overall classification. In machine learning terms, it is an ensemble classifier, so it uses multiple models to obtain better predictive performance than could be obtained from any of the constituent models. 12 | 13 | The forest outputs the class that is the mean or the mode (in regression problems) or the majority class (in classification problems) of the node's output by individual trees. 14 | 15 | ## Genomic selection context 16 | 17 | Nimbus is a Ruby gem implementing Random Forest in a genomic selection context, meaning every input file is expected to contain genotype and/or fenotype data from a sample of individuals. 18 | 19 | Other than the ids of the individuals, Nimbus handle the data as genotype values for [single-nucleotide polymorphisms](http://en.wikipedia.org/wiki/SNPs) (SNPs), so the variables in the classifier must have values of 0, 1 or 2, corresponding with SNPs classes of AA, AB and BB. 20 | 21 | Nimbus can be used to: 22 | 23 | * Create a random forest using a training sample of individuals with fenotype data. 24 | * Use an existent random forest to get predictions for a testing sample. 25 | 26 | ## Learning algorithm 27 | 28 | **Training**: Each tree in the forest is constructed using the following algorithm: 29 | 30 | 1. Let the number of training cases be N, and the number of variables (SNPs) in the classifier be M. 31 | 1. We are told the number mtry of input variables to be used to determine the decision at a node of the tree; m should be much less than M 32 | 1. Choose a training set for this tree by choosing n times with replacement from all N available training cases (i.e. take a bootstrap sample). Use the rest of the cases (Out Of Bag sample) to estimate the error of the tree, by predicting their classes. 33 | 1. For each node of the tree, randomly choose m SNPs on which to base the decision at that node. Calculate the best split based on these m SNPs in the training set. 34 | 1. Each tree is fully grown and not pruned (as may be done in constructing a normal tree classifier). 35 | 1. When in a node there is not any SNP split that minimizes the general loss function of the node, or the number of individuals in the node is less than the minimum node size then label the node with the average fenotype value of the individuals in the node. 36 | 37 | **Testing**: For prediction a sample is pushed down the tree. It is assigned the label of the training sample in the terminal node it ends up in. This procedure is iterated over all trees in the ensemble, and the average vote of all trees is reported as random forest prediction. 38 | 39 | ## Regression and Classification 40 | 41 | Nimbus can be used both with regression and classification problems. 42 | 43 | **Regression**: is the default mode. 44 | 45 | * The split of nodes uses quadratic loss as loss function. 46 | * Labeling of nodes is made averaging the fenotype values of the individuals in the node. 47 | 48 | **Classification**: user-activated declaring `classes` in the configuration file. 49 | 50 | * The split of nodes uses the Gini index as loss function. 51 | * Labeling of nodes is made finding the majority fenotype class of the individuals in the node. 52 | 53 | ## Variable importances 54 | 55 | By default Nimbus will estimate SNP importances everytime a training file is run to create a forest. 56 | 57 | You can disable this behaviour (and speed up the training process) by setting the parameter `var_importances: No` in the configuration file. 58 | 59 | ## Installation 60 | 61 | You need to have [Ruby](https://www.ruby-lang.org) (2.6 or higher) with Rubygems installed in your computer. Then install Nimbus with: 62 | 63 | ````shell 64 | > gem install nimbus 65 | ```` 66 | 67 | There are not extra dependencies needed. 68 | 69 | ## Getting Started 70 | 71 | Once you have nimbus installed in your system, you can run the gem using the `nimbus` executable: 72 | 73 | ````shell 74 | > nimbus 75 | ```` 76 | 77 | It will look for these files in the directory where Nimbus is running: 78 | 79 | * `training.data`: If found it will be used to build a random forest. 80 | * `testing.data` : If found it will be pushed down the forest to obtain predictions for every individual in the file. 81 | * `random_forest.yml`: If found it will be the forest used for the testing instead of building one. 82 | * `config.yml`: A file detailing random forest parameters and datasets. If not found default values will be used. 83 | 84 | That way in order to train a forest a training file is needed. And to do the testing you need two files: the testing file and one of the other two: the training OR the random_forest file, because Nimbus needs a forest from which obtain the predictions. 85 | 86 | ## Configuration (config.yml) 87 | 88 | The names for the input data files and the forest parameters can be specified in the `config.yml` file that should be located in the directory where you are running `nimbus`. 89 | 90 | The `config.yml` has the following structure and parameters: 91 | 92 | #Input files 93 | input: 94 | training: training_regression.data 95 | testing: testing_regression.data 96 | forest: my_forest.yml 97 | classes: [0, 1] 98 | 99 | #Forest parameters 100 | forest: 101 | forest_size: 10 #how many trees 102 | SNP_sample_size_mtry: 60 #mtry 103 | SNP_total_count: 200 104 | node_min_size: 5 105 | 106 | ### Under the input chapter: 107 | 108 | * `training`: specify the path to the training data file (optional, if specified `nimbus` will create a random forest). 109 | * `testing`: specify the path to the testing data file (optional, if specified `nimbus` will traverse this data through a random forest). 110 | * `forest`: specify the path to a file containing a random forest structure (optional, if there is also testing file, this will be the forest used for the testing). 111 | * `classes`: **optional (needed only for classification problems)**. Specify the list of classes in the input files as a comma separated list between squared brackets, e.g.:`[A, B]`. 112 | 113 | ### Under the forest chapter: 114 | 115 | * `forest_size`: number of trees for the forest. 116 | * `SNP_sample_size_mtry`: size of the random sample of SNPs to be used in every tree node. 117 | * `SNP_total_count`: total count of SNPs in the training and/or testing files 118 | * `node_min_size`: minimum amount of individuals in a tree node to make a split. 119 | * `var_importances`: **optional**. If set to `No` Nimbus will not calculate SNP importances. 120 | 121 | ### Default values 122 | 123 | If there is no config.yml file present, Nimbus will use these default values: 124 | 125 | ````yaml 126 | forest_size: 300 127 | tree_SNP_sample_size: 60 128 | tree_SNP_total_count: 200 129 | tree_node_min_size: 5 130 | training_file: 'training.data' 131 | testing_file: 'testing.data' 132 | forest_file: 'forest.yml 133 | ```` 134 | 135 | ## Input files 136 | 137 | The three input files you can use with Nimbus should have proper format: 138 | 139 | **The training file** has any number of rows, each representing data for an individual, with this columns: 140 | 141 | 1. A column with the fenotype for the individual 142 | 1. A column with the ID of the individual 143 | 1. M columns (where M = SNP_total_count in `config.yml`) with values 0, 1 or 2, representing the genotype of the individual. 144 | 145 | **The testing file** has any number of rows, each representing data for an individual, similar to the training file but without the fenotype column: 146 | 147 | 1. A column with the ID of the individual 148 | 1. M columns (where M = SNP_total_count in `config.yml`) with values 0, 1 or 2, representing the genotype of the individual. 149 | 150 | **The forest file** contains the structure of a forest in YAML format. It is the output file of a nimbus training run. 151 | 152 | ## Output files 153 | 154 | Nimbus will generate the following output files: 155 | 156 | After training: 157 | 158 | * `random_forest.yml`: A file defining the structure of the computed Random Forest. It can be used as input forest file. 159 | * `generalization_errors.txt`: A file with the generalization error for every tree in the forest. 160 | * `training_file_predictions.txt`: A file with predictions for every individual from the training file. 161 | * `snp_importances.txt`: A file with the computed importance for every SNP. _(unless `var_importances` set to `No` in config file)_ 162 | 163 | After testing: 164 | 165 | * `testing_file_predictions.txt`: A file detailing the predicted results for the testing dataset. 166 | 167 | ## Example usage 168 | 169 | ### Sample files 170 | 171 | Sample files are located in the `/spec/fixtures` directory, both for regression and classification problems. They can be used as a starting point to tweak your own configurations. 172 | 173 | Depending on the kind of problem you want to test different files are needed: 174 | 175 | ### Regression 176 | 177 | **Test with a Random Forest created from a training data set** 178 | 179 | Download/copy the `config.yml`, `training.data` and `testing.data` files from the [regression folder](./tree/master/spec/fixtures/regression). 180 | 181 | Then run nimbus: 182 | 183 | ````shell 184 | > nimbus 185 | ```` 186 | 187 | It should output a `random_forest.yml` file with the nodes and structure of the resulting random forest, the `generalization_errors` and `snp_importances` files, and the predictions for both training and testing datasets (`training_file_predictions.txt` and `testing_file_predictions.txt` files). 188 | 189 | **Test with a Random Forest previously created** 190 | 191 | Download/copy the `config.yml`, `testing.data` and `random_forest.yml` files from the [regression folder](./tree/master/spec/fixtures/regression). 192 | 193 | Edit the `config.yml` file to comment/remove the training entry. 194 | 195 | Then use nimbus to run the testing: 196 | 197 | ````shell 198 | > nimbus 199 | ```` 200 | 201 | It should output a `testing_file_predictions.txt` file with the resulting predictions for the testing dataset using the given random forest. 202 | 203 | ### Classification 204 | 205 | **Test with a Random Forest created from a training data set** 206 | 207 | Download/copy the `config.yml`, `training.data` and `testing.data` files from the [classification folder](./tree/master/spec/fixtures/classification). 208 | 209 | Then run nimbus: 210 | 211 | ````shell 212 | > nimbus 213 | ```` 214 | 215 | It should output a `random_forest.yml` file with the nodes and structure of the resulting random forest, the `generalization_errors` file, and the predictions for both training and testing datasets (`training_file_predictions.txt` and `testing_file_predictions.txt` files). 216 | 217 | **Test with a Random Forest previously created** 218 | 219 | Download/copy the `config.yml`, `testing.data` and `random_forest.yml` files from the [classification folder](./tree/master/spec/fixtures/classification). 220 | 221 | Edit the `config.yml` file to comment/remove the training entry. 222 | 223 | Then use nimbus to run the testing: 224 | 225 | ````shell 226 | > nimbus 227 | ```` 228 | 229 | It should output a `testing_file_predictions.txt` file with the resulting predictions for the testing dataset using the given random forest. 230 | 231 | 232 | ## Test suite 233 | 234 | Nimbus includes a test suite located in the `spec` directory. The current state of the build is [publicly tracked by Travis CI](https://travis-ci.org/xuanxu/nimbus). You can run the specs locally if you clone the code to your local machine and run the default rake task: 235 | 236 | ````shell 237 | > git clone git://github.com/xuanxu/nimbus.git 238 | > cd nimbus 239 | > bundle install 240 | > rake 241 | ```` 242 | 243 | ## Resources 244 | 245 | * [Source code](http://github.com/xuanxu/nimbus) – Fork the code 246 | * [Issues](http://github.com/xuanxu/nimbus/issues) – Bugs and feature requests 247 | * [Online rdocs](http://rubydoc.info/gems/nimbus/frames) 248 | * [Nimbus at rubygems.org](https://rubygems.org/gems/nimbus) 249 | * [Random Forest at Wikipedia](http://en.wikipedia.org/wiki/Random_forest) 250 | * [RF Leo Breiman page](http://www.stat.berkeley.edu/~breiman/RandomForests/) 251 | 252 | 253 | ## Contributing 254 | 255 | Contributions are welcome. We encourage you to contribute to the Nimbus codebase. 256 | 257 | Please read the [CONTRIBUTING](CONTRIBUTING.md) file. 258 | 259 | 260 | ## Credits and DOI 261 | 262 | If you use Nimbus, please cite our [JOSS paper: http://dx.doi.org/10.21105/joss.00351](http://dx.doi.org/10.21105/joss.00351) 263 | 264 | You can find the citation info in [Bibtex format here](CITATION.bib). 265 | 266 | **Cite as:** 267 | *Bazán et al, (2017), Nimbus: a Ruby gem to implement Random Forest algorithms in a genomic selection context, Journal of Open Source Software, 2(16), 351, doi:10.21105/joss.0035* 268 | 269 | Nimbus was developed by [Juanjo Bazán](http://twitter.com/xuanxu) in collaboration with Oscar González-Recio. 270 | 271 | 272 | ## LICENSE 273 | 274 | Copyright © Juanjo Bazán, released under the [MIT license](MIT-LICENSE.txt) 275 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 'rspec/core/rake_task' 3 | 4 | Bundler::GemHelper.install_tasks 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | task default: :spec -------------------------------------------------------------------------------- /bin/nimbus: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | #-- 4 | # Copyright (c) 2011-2019 Juanjo Bazan 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to 8 | # deal in the Software without restriction, including without limitation the 9 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | # sell copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | # IN THE SOFTWARE. 23 | #++ 24 | 25 | require 'nimbus' 26 | Nimbus.application.run 27 | -------------------------------------------------------------------------------- /lib/nimbus.rb: -------------------------------------------------------------------------------- 1 | require 'psych' 2 | require 'nimbus/exceptions' 3 | require 'nimbus/training_set' 4 | require 'nimbus/configuration' 5 | require 'nimbus/loss_functions' 6 | require 'nimbus/individual' 7 | require 'nimbus/tree' 8 | require 'nimbus/regression_tree' 9 | require 'nimbus/classification_tree' 10 | require 'nimbus/forest' 11 | require 'nimbus/application' 12 | require 'nimbus/version' 13 | 14 | ##################################################################### 15 | # Nimbus module. 16 | # Used as a namespace containing all the Nimbus code. 17 | # The module defines a Nimbus::Application and interacts with the user output console. 18 | # 19 | module Nimbus 20 | 21 | STDERR = $stderr 22 | STDOUT = $stdout 23 | STDOUT.sync = true 24 | 25 | # Nimbus module singleton methods. 26 | # 27 | class << self 28 | # Current Nimbus Application 29 | def application 30 | @application ||= ::Nimbus::Application.new 31 | end 32 | 33 | # Set the current Nimbus application object. 34 | def application=(app) 35 | @application = app 36 | end 37 | 38 | # Stops the execution of the Nimbus application. 39 | def stop(msg = "Error: Nimbus finished.") 40 | self.error_message msg 41 | exit(false) 42 | end 43 | 44 | # Writes message to the standard output 45 | def message(msg) 46 | STDOUT.puts msg 47 | end 48 | 49 | # Writes message to the error output 50 | def error_message(msg) 51 | STDERR.puts msg 52 | end 53 | 54 | # Writes to the standard output 55 | def write(str) 56 | STDOUT.write str 57 | end 58 | 59 | # Clear current console line 60 | def clear_line! 61 | print "\r\e[2K" 62 | end 63 | 64 | end 65 | 66 | end -------------------------------------------------------------------------------- /lib/nimbus/application.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | 3 | ##################################################################### 4 | # Nimbus main application object. 5 | # 6 | # When invoking +nimbus+ from the command line, 7 | # a Nimbus::Application object is created and run. 8 | # 9 | class Application 10 | attr_accessor :config 11 | 12 | # Initialize a Nimbus::Application object. 13 | # Check and load the configuration options. 14 | def initialize(c = nil) 15 | @config = c unless c.nil? 16 | nimbus_exception_handling do 17 | config.load 18 | @forest = nil 19 | end 20 | end 21 | 22 | # Run the Nimbus application. The run method performs the following 23 | # three steps: 24 | # 25 | # * Create a Nimbus::Forest object. 26 | # * Decide action to take: training a random forest and/or use the forest to predict values for a testing set 27 | # * Write results to output files. 28 | def run 29 | nimbus_exception_handling do 30 | 31 | if @config.do_training && @config.load_training_data 32 | @forest = ::Nimbus::Forest.new @config 33 | @forest.grow 34 | output_random_forest_file(@forest) 35 | output_tree_errors_file(@forest) 36 | output_training_file_predictions(@forest) 37 | output_snp_importances_file(@forest) if @config.do_importances 38 | end 39 | 40 | if @config.do_testing 41 | @forest = @config.load_forest if @config.forest_file 42 | @forest.traverse 43 | output_testing_set_predictions(@forest) 44 | end 45 | 46 | end 47 | end 48 | 49 | # Creates an instance of Nimbus::Configuration if it does not exist. 50 | # This config object contains every option to be used for the random forest 51 | # including the user input set through the config.yml file. 52 | def config 53 | @config ||= ::Nimbus::Configuration.new 54 | end 55 | 56 | # Provides the default exception handling for the given block. 57 | def nimbus_exception_handling 58 | begin 59 | yield 60 | rescue SystemExit => ex 61 | raise 62 | rescue Nimbus::Error => ex 63 | display_error_message(ex) 64 | Nimbus.stop 65 | rescue Exception => ex 66 | display_error_message(ex) 67 | Nimbus.stop 68 | end 69 | end 70 | 71 | # Display an error message that caused a exception. 72 | def display_error_message(ex) 73 | Nimbus.error_message "* Nimbus encountered an error! The random forest was not generated *" 74 | Nimbus.error_message "#{ex.class}: #{ex.message}" 75 | # if config.trace 76 | # Nimbus.error_message ex.backtrace.join("\n") 77 | # else 78 | # Nimbus.error_message "(See full error trace by running Nimbus with --trace)" 79 | # end 80 | end 81 | 82 | protected 83 | def output_random_forest_file(forest) 84 | File.open(@config.output_forest_file , 'w') {|f| f.write(forest.to_yaml) } 85 | Nimbus.message "* Random forest structure saved to:" 86 | Nimbus.message "* Output forest file: #{@config.output_forest_file}" 87 | Nimbus.message "*" * 50 88 | end 89 | 90 | def output_tree_errors_file(forest) 91 | File.open(@config.output_tree_errors_file , 'w') {|f| 92 | 1.upto(forest.tree_errors.size) do |te| 93 | f.write("generalization error for tree #{te}: #{forest.tree_errors[te-1].round(5)}\n") 94 | end 95 | } 96 | Nimbus.message "* Generalization errors for every tree saved to:" 97 | Nimbus.message "* Output tree errors file: #{@config.output_tree_errors_file}" 98 | Nimbus.message "*" * 50 99 | end 100 | 101 | def output_training_file_predictions(forest) 102 | File.open(@config.output_training_file , 'w') {|f| 103 | forest.predictions.sort.each{|p| 104 | f.write("#{p[0]} #{p[1]}\n") 105 | } 106 | } 107 | Nimbus.message "* Predictions for the training sample saved to:" 108 | Nimbus.message "* Output from training file: #{@config.output_training_file}" 109 | Nimbus.message "*" * 50 110 | end 111 | 112 | def output_testing_set_predictions(forest) 113 | File.open(@config.output_testing_file , 'w') {|f| 114 | forest.predictions.sort.each{|p| 115 | f.write("#{p[0]} #{p[1]}\n") 116 | } 117 | } 118 | Nimbus.message "* Predictions for the testing set saved to:" 119 | Nimbus.message "* Output from testing file: #{@config.output_testing_file}" 120 | Nimbus.message "*" * 50 121 | end 122 | 123 | def output_snp_importances_file(forest) 124 | File.open(@config.output_snp_importances_file , 'w') {|f| 125 | forest.snp_importances.sort.each{|p| 126 | f.write("SNP #{p[0]}: #{p[1].round(5)}\n") 127 | } 128 | } 129 | Nimbus.message "* SNP importances for the forest saved to:" 130 | Nimbus.message "* Output snp importance file: #{@config.output_snp_importances_file}" 131 | Nimbus.message "*" * 50 132 | end 133 | 134 | end 135 | 136 | end -------------------------------------------------------------------------------- /lib/nimbus/classification_tree.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | 3 | ##################################################################### 4 | # Tree object representing a random classification tree. 5 | # 6 | # A tree is generated following this steps: 7 | # 8 | # * 1: Calculate loss function for the individuals in the node (first node contains all the individuals). 9 | # * 2: Take a random sample of the SNPs (size m << total count of SNPs) 10 | # * 3: Compute the loss function (default: gini index) for the split of the sample based on value of every SNP. 11 | # * 4: If the SNP with minimum loss function also minimizes the general loss of the node, split the individuals sample in two nodes, based on average value for that SNP [0,1][2], or [0][1,2] 12 | # * 5: Repeat from 1 for every node until: 13 | # - a) The individuals count in that node is < minimum size OR 14 | # - b) None of the SNP splits has a loss function smaller than the node loss function 15 | # * 6) When a node stops, label the node with the majority class in the node. 16 | # 17 | class ClassificationTree < Tree 18 | attr_accessor :classes 19 | 20 | # Initialize Tree object with the configuration (as in Nimbus::Configuration.tree) options received. 21 | def initialize(options) 22 | @classes = options[:classes] 23 | super 24 | end 25 | 26 | # Creates the structure of the tree, as a hash of SNP splits and values. 27 | # 28 | # It just initializes the needed variables and then defines the first node of the tree. 29 | # The rest of the structure of the tree is computed recursively building every node calling +build_node+. 30 | def seed(all_individuals, individuals_sample, ids_fenotypes) 31 | super 32 | @structure = build_node individuals_sample, Nimbus::LossFunctions.majority_class(individuals_sample, @id_to_fenotype, @classes) 33 | end 34 | 35 | # Creates a node by taking a random sample of the SNPs and computing the loss function for every split by SNP of that sample. 36 | # 37 | # * If SNP_min is the SNP with smaller loss function and it is < the loss function of the node, it splits the individuals sample in two: 38 | # (the average of the 0,1,2 values for the SNP_min in the individuals is computed, and they are splitted in [<=avg], [>avg]) then it builds these 2 new nodes. 39 | # * Otherwise every individual in the node gets labeled with the average of the fenotype values of all of them. 40 | def build_node(individuals_ids, y_hat) 41 | # General loss function value for the node 42 | individuals_count = individuals_ids.size 43 | return label_node(y_hat, individuals_ids) if individuals_count < @node_min_size 44 | node_loss_function = Nimbus::LossFunctions.gini_index individuals_ids, @id_to_fenotype, @classes 45 | 46 | # Finding the SNP that minimizes loss function 47 | snps = snps_random_sample 48 | min_loss, min_SNP, split, split_type, ginis = node_loss_function, nil, nil, nil, nil 49 | 50 | snps.each do |snp| 51 | individuals_split_by_snp_value, node_split_type = split_by_snp_avegare_value individuals_ids, snp 52 | y_hat_0 = Nimbus::LossFunctions.majority_class(individuals_split_by_snp_value[0], @id_to_fenotype, @classes) 53 | y_hat_1 = Nimbus::LossFunctions.majority_class(individuals_split_by_snp_value[1], @id_to_fenotype, @classes) 54 | 55 | gini_0 = Nimbus::LossFunctions.gini_index individuals_split_by_snp_value[0], @id_to_fenotype, @classes 56 | gini_1 = Nimbus::LossFunctions.gini_index individuals_split_by_snp_value[1], @id_to_fenotype, @classes 57 | loss_snp = (individuals_split_by_snp_value[0].size * gini_0 + 58 | individuals_split_by_snp_value[1].size * gini_1) / individuals_count 59 | 60 | min_loss, min_SNP, split, split_type, ginis = loss_snp, snp, individuals_split_by_snp_value, node_split_type, [y_hat_0, y_hat_1] if loss_snp < min_loss 61 | end 62 | return build_branch(min_SNP, split, split_type, ginis, y_hat) if min_loss < node_loss_function 63 | return label_node(y_hat, individuals_ids) 64 | end 65 | 66 | # Compute generalization error for the tree. 67 | # 68 | # Traversing the 'out of bag' (OOB) sample (those individuals of the training set not 69 | # used in the building of this tree) through the tree, and comparing 70 | # the prediction with the real fenotype class of the individual is possible 71 | # to calculate the error frequency, an unbiased generalization error for the tree. 72 | def generalization_error_from_oob(oob_ids) 73 | return nil if (@structure.nil? || @individuals.nil? || @id_to_fenotype.nil?) 74 | oob_errors = 0.0 75 | oob_ids.each do |oobi| 76 | oob_errors += 1 unless @id_to_fenotype[oobi] == Tree.traverse(@structure, individuals[oobi].snp_list) 77 | end 78 | @generalization_error = oob_errors / oob_ids.size 79 | end 80 | 81 | # Estimation of importance for every SNP. 82 | # 83 | # The importance of any SNP in the tree is calculated using the OOB sample. 84 | # For every SNP, every individual in the sample is pushed down the tree but with the 85 | # value of that SNP permuted with other individual in the sample. 86 | # 87 | # That way the difference between the generalization error and the error frequency with the SNP value modified can be estimated for any given SNP. 88 | # 89 | # This method computes importance estimations for every SNPs used in the tree (for any other SNP it would be 0). 90 | def estimate_importances(oob_ids) 91 | return nil if (@generalization_error.nil? && generalization_error_from_oob(oob_ids).nil?) 92 | oob_individuals_count = oob_ids.size 93 | @importances = {} 94 | @used_snps.uniq.each do |current_snp| 95 | shuffled_ids = oob_ids.shuffle 96 | permutated_snp_errors = 0.0 97 | oob_ids.each_with_index {|oobi, index| 98 | permutated_prediction = traverse_with_permutation @structure, individuals[oobi].snp_list, current_snp, individuals[shuffled_ids[index]].snp_list 99 | permutated_snp_errors += 1 unless @id_to_fenotype[oobi] == permutated_prediction 100 | } 101 | @importances[current_snp] = ((permutated_snp_errors / oob_individuals_count) - @generalization_error).round(5) 102 | end 103 | @importances 104 | end 105 | 106 | end 107 | 108 | end -------------------------------------------------------------------------------- /lib/nimbus/configuration.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | ##################################################################### 3 | # Nimbus configuration object. 4 | # 5 | # This class reads every user file. 6 | # Once the user's config.yml file is loaded, a set of default and 7 | # custom options is created and stored. 8 | # 9 | # Nimbus::Configuration also reads the testing files and the data 10 | # to create the training set to be passed to the Nimbus::Forest random 11 | # forest generator and the Nimbus::Tree classes in it. 12 | # 13 | class Configuration 14 | attr_accessor( 15 | :training_file, 16 | :testing_file, 17 | :forest_file, 18 | :classes, 19 | :config_file, 20 | :forest_size, 21 | :tree_SNP_sample_size, 22 | :tree_SNP_total_count, 23 | :tree_node_min_size, 24 | :loss_function_discrete, 25 | :loss_function_continuous, 26 | :do_training, 27 | :do_testing, 28 | :do_importances, 29 | :training_set, 30 | :output_forest_file, 31 | :output_training_file, 32 | :output_testing_file, 33 | :output_tree_errors_file, 34 | :output_snp_importances_file, 35 | :silent 36 | ) 37 | 38 | DEFAULTS = { 39 | forest_size: 300, 40 | tree_SNP_sample_size: 60, 41 | tree_SNP_total_count: 200, 42 | tree_node_min_size: 5, 43 | 44 | loss_function_discrete: 'majority_class', 45 | loss_function_continuous: 'average', 46 | 47 | training_file: 'training.data', 48 | testing_file: 'testing.data', 49 | forest_file: 'forest.yml', 50 | config_file: 'config.yml', 51 | 52 | output_forest_file: 'random_forest.yml', 53 | output_training_file: 'training_file_predictions.txt', 54 | output_testing_file: 'testing_file_predictions.txt', 55 | output_tree_errors_file: 'generalization_errors.txt', 56 | output_snp_importances_file: 'snp_importances.txt', 57 | 58 | silent: false 59 | } 60 | 61 | # Initialize a Nimbus::Configuration object. 62 | # 63 | # Set all options to their default values. 64 | def initialize 65 | @do_training = false 66 | @do_testing = false 67 | @do_importances = true 68 | 69 | @forest_size = DEFAULTS[:forest_size] 70 | @tree_SNP_sample_size = DEFAULTS[:tree_SNP_sample_size] 71 | @tree_SNP_total_count = DEFAULTS[:tree_SNP_total_count] 72 | @tree_node_min_size = DEFAULTS[:tree_node_min_size] 73 | @loss_function_discrete = DEFAULTS[:loss_function_discrete] 74 | @loss_function_continuous = DEFAULTS[:loss_function_continuous] 75 | 76 | @output_forest_file = File.expand_path(DEFAULTS[:output_forest_file], Dir.pwd) 77 | @output_training_file = File.expand_path(DEFAULTS[:output_training_file], Dir.pwd) 78 | @output_testing_file = File.expand_path(DEFAULTS[:output_testing_file], Dir.pwd) 79 | @output_tree_errors_file = File.expand_path(DEFAULTS[:output_tree_errors_file], Dir.pwd) 80 | @output_snp_importances_file = File.expand_path(DEFAULTS[:output_snp_importances_file], Dir.pwd) 81 | 82 | @silent = ENV['nimbus_test'] == 'running_nimbus_tests' ? true : DEFAULTS[:silent] 83 | end 84 | 85 | # Accessor method for the tree-related subset of options. 86 | def tree 87 | { 88 | snp_sample_size: @tree_SNP_sample_size, 89 | snp_total_count: @tree_SNP_total_count, 90 | tree_node_min_size: @tree_node_min_size, 91 | classes: @classes 92 | } 93 | end 94 | 95 | # This is the first method to be called on Configuration when a config.yml file 96 | # exists with user input options for the forest. 97 | # 98 | # * The method will read the config file and change the default value of the selected options. 99 | # * Then based on the options and the existence of training, testing and forest files, it will mark: 100 | # - if training is needed, 101 | # - if testing is needed, 102 | # - which forest will be used for the testing. 103 | # * Finally it will run basic checks on the input data trying to prevent future program errors. 104 | # 105 | def load(config_file = DEFAULTS[:config_file]) 106 | user_config_params = {} 107 | dirname = Dir.pwd 108 | if File.exist?(File.expand_path(config_file, Dir.pwd)) 109 | begin 110 | config_file_path = File.expand_path config_file, Dir.pwd 111 | user_config_params = Psych.load(File.open(config_file_path)) 112 | dirname = File.dirname config_file_path 113 | rescue ArgumentError => e 114 | raise Nimbus::WrongFormatFileError, "It was not posible to parse the config file (#{config_file}): \r\n#{e.message} " 115 | end 116 | end 117 | 118 | if user_config_params['input'] 119 | @training_file = File.expand_path(user_config_params['input']['training'], dirname) if user_config_params['input']['training'] 120 | @testing_file = File.expand_path(user_config_params['input']['testing' ], dirname) if user_config_params['input']['testing'] 121 | @forest_file = File.expand_path(user_config_params['input']['forest' ], dirname) if user_config_params['input']['forest'] 122 | @classes = user_config_params['input']['classes'] if user_config_params['input']['classes'] 123 | else 124 | @training_file = File.expand_path(DEFAULTS[:training_file], Dir.pwd) if File.exist? File.expand_path(DEFAULTS[:training_file], Dir.pwd) 125 | @testing_file = File.expand_path(DEFAULTS[:testing_file ], Dir.pwd) if File.exist? File.expand_path(DEFAULTS[:testing_file ], Dir.pwd) 126 | @forest_file = File.expand_path(DEFAULTS[:forest_file ], Dir.pwd) if File.exist? File.expand_path(DEFAULTS[:forest_file ], Dir.pwd) 127 | end 128 | 129 | @do_training = true unless @training_file.nil? 130 | @do_testing = true unless @testing_file.nil? 131 | @classes = @classes.map{|c| c.to_s.strip} if @classes 132 | 133 | if @do_testing && !@do_training && !@forest_file 134 | raise Nimbus::InputFileError, "There is not random forest data (training file not defined, and forest file not found)." 135 | end 136 | 137 | if user_config_params['forest'] 138 | @forest_size = user_config_params['forest']['forest_size'].to_i if user_config_params['forest']['forest_size'] 139 | @tree_SNP_total_count = user_config_params['forest']['SNP_total_count'].to_i if user_config_params['forest']['SNP_total_count'] 140 | @tree_SNP_sample_size = user_config_params['forest']['SNP_sample_size_mtry'].to_i if user_config_params['forest']['SNP_sample_size_mtry'] 141 | @tree_node_min_size = user_config_params['forest']['node_min_size'].to_i if user_config_params['forest']['node_min_size'] 142 | @do_importances = user_config_params['forest']['var_importances'].to_s.strip.downcase 143 | @do_importances = (@do_importances != 'no' && @do_importances != 'false') 144 | end 145 | 146 | check_configuration 147 | log_configuration 148 | end 149 | 150 | # The method reads the training file, and if the data is valid, creates a Nimbus::TrainingSet 151 | # containing every individual to be used as training sample for a random forest. 152 | def load_training_data 153 | File.open(@training_file) {|file| 154 | @training_set = Nimbus::TrainingSet.new({}, {}) 155 | file.each do |line| 156 | next if line.strip == '' 157 | data_feno, data_id, *snp_list = line.strip.split 158 | raise Nimbus::InputFileError, "Individual ##{data_id} from training set has no value for all #{@tree_SNP_total_count} SNPs" unless snp_list.size == @tree_SNP_total_count 159 | raise Nimbus::InputFileError, "There are individuals with no ID, please check data in training file." unless (!data_id.nil? && data_id.strip != '') 160 | raise Nimbus::InputFileError, "Individual ##{data_id} has no fenotype value, please check data in training file." unless (!data_feno.nil? && data_feno.strip != '') 161 | raise Nimbus::InputFileError, "Individual ##{data_id} has invalid class (not in [#{classes*', '}]), please check data in training file." unless (@classes.nil? || @classes.include?(data_feno)) 162 | 163 | data_feno = (@classes ? data_feno.to_s : data_feno.to_f) 164 | @training_set.individuals[data_id.to_i] = Nimbus::Individual.new(data_id.to_i, data_feno, snp_list.map{|snp| snp.to_i}) 165 | @training_set.ids_fenotypes[data_id.to_i] = data_feno 166 | end 167 | } 168 | end 169 | 170 | # Reads the testing file, and if the data is valid, yields one Nimbus::Individual at a time. 171 | def read_testing_data 172 | File.open(@testing_file) {|file| 173 | file.each do |line| 174 | next if line.strip == '' 175 | data_id, *snp_list = line.strip.split 176 | raise Nimbus::InputFileError, "There are individuals with no ID, please check data in Testing file." unless (!data_id.nil? && data_id.strip != '') 177 | raise Nimbus::InputFileError, "Individual ##{data_id} from testing set has no value for all #{@tree_SNP_total_count} SNPs." unless snp_list.size == @tree_SNP_total_count 178 | individual_test = Nimbus::Individual.new(data_id.to_i, nil, snp_list.map{|snp| snp.to_i}) 179 | yield individual_test 180 | end 181 | } 182 | end 183 | 184 | # Creates a Nimbus::Forest object from a user defined random forest data file. 185 | # 186 | # The format of the input file should be the same as the forest output data of a Nimbus Application. 187 | def load_forest 188 | trees = [] 189 | if File.exist?(@forest_file) 190 | begin 191 | trees = Psych.load(File.open @forest_file) 192 | rescue ArgumentError => e 193 | raise Nimbus::WrongFormatFileError, "It was not posible to parse the random forest file (#{@forest_file}): \r\n#{e.message} " 194 | end 195 | else 196 | raise Nimbus::InputFileError, "Forest file not found (#{@forest_file})" 197 | end 198 | forest = Nimbus::Forest.new self 199 | forest.trees = trees 200 | forest 201 | end 202 | 203 | # Include tests to be passed by the info contained in the config file. 204 | # 205 | # If some of the configuration data provided by the user is invalid, an error is raised and execution stops. 206 | def check_configuration 207 | raise Nimbus::ConfigurationError, "The mtry sample size must be smaller than the total SNPs count." if @tree_SNP_sample_size > @tree_SNP_total_count 208 | end 209 | 210 | # Prints the information stored in the Nimbus::Configuration object 211 | # 212 | # It could include errors on the configuration input data, training related info and/or testing related info. 213 | def log_configuration 214 | return if @silent 215 | if !@do_training && !@do_testing 216 | Nimbus.message "*" * 50 217 | Nimbus.message "* Nimbus could not find any input file: " 218 | Nimbus.message "* No training file (default: training.data)" 219 | Nimbus.message "* No testing file (default: testing.data)" 220 | Nimbus.message "* Not defined in config file (default: config.yml)" 221 | Nimbus.message "* Nothing to do." 222 | Nimbus.message "*" * 50 223 | Nimbus.stop "Error: No input data. Nimbus finished." 224 | end 225 | 226 | Nimbus.message "*" * 50 227 | Nimbus.message "* Nimbus version #{::Nimbus::VERSION}" 228 | Nimbus.message "* configured with the following parameters: " 229 | Nimbus.message "* Forest size: #{@forest_size} trees" 230 | Nimbus.message "* Total SNP count: #{@tree_SNP_total_count}" 231 | Nimbus.message "* SNPs sample size (mtry): #{@tree_SNP_sample_size}" 232 | Nimbus.message "* Minimun node size in tree: #{@tree_node_min_size}" 233 | 234 | if @classes 235 | Nimbus.message "* Mode: CLASSIFICATION" 236 | Nimbus.message "* Classes: [#{@classes*', '}]" 237 | else 238 | Nimbus.message "* Mode: REGRESSION" 239 | end 240 | Nimbus.message "*" * 50 241 | 242 | if @do_training 243 | Nimbus.message "* Training data:" 244 | Nimbus.message "* Training file: #{@training_file}" 245 | Nimbus.message "*" * 50 246 | end 247 | 248 | if @do_testing 249 | Nimbus.message "* Data to be tested:" 250 | Nimbus.message "* Testing file: #{@testing_file}" 251 | if @forest_file 252 | Nimbus.message "* using the structure of the random forest stored in:" 253 | Nimbus.message "* Random forest file: #{@forest_file}" 254 | end 255 | Nimbus.message "*" * 50 256 | end 257 | end 258 | 259 | end 260 | end -------------------------------------------------------------------------------- /lib/nimbus/exceptions.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | # Nimbus custom Error class. 3 | class Error < StandardError; end 4 | # Error when a non existent or invalid option is used. 5 | class InvalidOptionError < Error; end 6 | # Error in some of the input files. 7 | class InputFileError < Error; end 8 | # Error if data from some input file are incorrectly formatted. 9 | class WrongFormatFileError < Error; end 10 | # Error if configuration options are invalid. 11 | class ConfigurationError < Error; end 12 | # Error handling a random Forest. 13 | class ForestError < Error; end 14 | # Error handling a Tree object. 15 | class TreeError < Error; end 16 | # Error in the data of an Individual object. 17 | class IndividualError < Error; end 18 | end -------------------------------------------------------------------------------- /lib/nimbus/forest.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | 3 | ##################################################################### 4 | # Forest represents the Random forest being generated 5 | # (or used to test samples) by the application object. 6 | # 7 | class Forest 8 | attr_accessor :size, :trees, :bag, :predictions, :tree_errors, :snp_importances 9 | attr_accessor :options 10 | 11 | # Initialize Forest object with options included in the Nimbus::Configuration object received. 12 | def initialize(config) 13 | @trees = [] 14 | @tree_errors = [] 15 | @options = config 16 | @size = config.forest_size 17 | @predictions = {} 18 | @times_predicted = [] 19 | @snp_importances = {} 20 | @tree_snp_importances = [] 21 | raise Nimbus::ForestError, "Forest size parameter (#{@size}) is invalid. You need at least one tree." if @size < 1 22 | end 23 | 24 | # Creates a random forest based on the TrainingSet included in the configuration, creating N random trees (size N defined in the configuration). 25 | # 26 | # This is the method called when the application's configuration flags training on. 27 | # 28 | # It performs this tasks: 29 | # 30 | # * grow the forest (all the N random trees) 31 | # * store generalization errors for every tree 32 | # * obtain averaged importances for all the SNPs 33 | # * calculate averaged predictions for all individuals in the training sample 34 | # 35 | # Every tree of the forest is created with a different random sample of the individuals in the training set. 36 | def grow 37 | @size.times do |i| 38 | Nimbus.write("\rCreating trees: #{i+1}/#{@size} ") 39 | tree_individuals_bag = individuals_random_sample 40 | tree_out_of_bag = oob tree_individuals_bag 41 | tree_class = (classification? ? ClassificationTree : RegressionTree) 42 | tree = tree_class.new @options.tree 43 | @trees << tree.seed(@options.training_set.individuals, tree_individuals_bag, @options.training_set.ids_fenotypes) 44 | @tree_errors << tree.generalization_error_from_oob(tree_out_of_bag) 45 | @tree_snp_importances << tree.estimate_importances(tree_out_of_bag) if @options.do_importances 46 | acumulate_predictions tree.predictions 47 | Nimbus.clear_line! 48 | end 49 | average_snp_importances if @options.do_importances 50 | totalize_predictions 51 | end 52 | 53 | # Traverse a testing set through every tree of the forest. 54 | # 55 | # This is the method called when the application's configuration flags testing on. 56 | def traverse 57 | classification? ? traverse_classification_forest : traverse_regression_forest 58 | end 59 | 60 | # Traverse a testing set through every regression tree of the forest and get averaged predictions for every individual in the sample. 61 | def traverse_regression_forest 62 | @predictions = {} 63 | prediction_count = trees.size 64 | @options.read_testing_data{|individual| 65 | individual_prediction = 0.0 66 | trees.each do |t| 67 | individual_prediction = (individual_prediction + Nimbus::Tree.traverse(t, individual.snp_list)).round(5) 68 | end 69 | @predictions[individual.id] = (individual_prediction / prediction_count).round(5) 70 | } 71 | end 72 | 73 | # Traverse a testing set through every classification tree of the forest and get majority class predictions for every individual in the sample. 74 | def traverse_classification_forest 75 | @predictions = {} 76 | @options.read_testing_data{|individual| 77 | individual_prediction = [] 78 | trees.each do |t| 79 | individual_prediction << Nimbus::Tree.traverse(t, individual.snp_list) 80 | end 81 | class_sizes = Nimbus::LossFunctions.class_sizes_in_list(individual_prediction, @options.tree[:classes]).map{|p| (p/individual_prediction.size.to_f).round(3)} 82 | @predictions[individual.id] = Hash[@options.tree[:classes].zip class_sizes].map{|k,v| "'#{k}': #{v}"}.join(' , ') 83 | } 84 | end 85 | 86 | # The array containing every tree in the forest, to YAML format. 87 | def to_yaml 88 | @trees.to_yaml 89 | end 90 | 91 | def classification? 92 | @options.tree[:classes] 93 | end 94 | 95 | def regression? 96 | @options.tree[:classes].nil? 97 | end 98 | 99 | private 100 | 101 | def individuals_random_sample 102 | individuals_sample = bag.inject([]){|items, i| items << bag.sample }.sort 103 | end 104 | 105 | def oob(in_bag=[]) 106 | bag - in_bag.uniq 107 | end 108 | 109 | def bag 110 | @bag ||= @options.training_set.all_ids 111 | end 112 | 113 | def acumulate_predictions(preds) 114 | preds.each_pair.each{|id, value| 115 | if @predictions[id].nil? 116 | @predictions[id] = (classification? ? [value] : value) 117 | @times_predicted[id] = 1.0 118 | else 119 | classification? ? (@predictions[id] << value) : (@predictions[id] += value) 120 | @times_predicted[id] += 1 121 | end 122 | } 123 | end 124 | 125 | def totalize_predictions 126 | classification? ? majority_class_predicted : average_predictions 127 | end 128 | 129 | def average_predictions 130 | @predictions.each_pair{|id, value| 131 | @predictions[id] = (@predictions[id] / @times_predicted[id]).round(5) 132 | } 133 | end 134 | 135 | def majority_class_predicted 136 | @predictions.each_pair{|id, values| 137 | @predictions[id] = Nimbus::LossFunctions.majority_class_in_list(values, @options.tree[:classes]) 138 | } 139 | end 140 | 141 | def average_snp_importances 142 | 1.upto(@options.tree_SNP_total_count) {|snp| 143 | @snp_importances[snp] = 0.0 144 | @tree_snp_importances.each{|tree_snp_importance| 145 | @snp_importances[snp] += tree_snp_importance[snp] unless tree_snp_importance[snp].nil? 146 | } 147 | @snp_importances[snp] = @snp_importances[snp] / @size 148 | } 149 | end 150 | 151 | end 152 | 153 | end -------------------------------------------------------------------------------- /lib/nimbus/individual.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | ##################################################################### 3 | # Nimbus Individual object. 4 | # 5 | # It represents a single individual of a training or testing sample. 6 | # 7 | # This class stores information about a individual: 8 | # 9 | # * id, 10 | # * values for all the SNPs of the individual, 11 | # * fenotype if present, 12 | # * the prediction is it exists. 13 | # 14 | class Individual 15 | attr_accessor :id, :fenotype, :prediction, :snp_list 16 | 17 | # Initialize individual with passed data. 18 | def initialize(i, fen, snps=[]) 19 | self.id = i 20 | self.fenotype = fen 21 | self.snp_list = snps 22 | end 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /lib/nimbus/loss_functions.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | module Nimbus 3 | 4 | ##################################################################### 5 | # Math functions. 6 | # 7 | # The LossFunctions class provides handy mathematical functions as class methods 8 | # to be used by Tree and Forest when estimating predictions, errors and loss functions 9 | # for training and testing data. 10 | # 11 | module LossFunctions 12 | 13 | class << self 14 | ## REGRESSION 15 | 16 | # Simple average: sum(n) / n 17 | def average(ids, value_table) 18 | ids.inject(0.0){|sum, i| sum + value_table[i]} / ids.size 19 | end 20 | 21 | # Mean squared error: sum (x-y)^2 22 | def mean_squared_error(ids, value_table, mean = nil) 23 | mean ||= self.average ids, value_table 24 | ids.inject(0.0){|sum, i| sum + ((value_table[i] - mean)**2) } 25 | end 26 | 27 | # Quadratic loss: averaged mean squared error: sum (x-y)^2 / n 28 | # 29 | # Default loss function for regression forests. 30 | def quadratic_loss(ids, value_table, mean = nil) 31 | self.mean_squared_error(ids, value_table, mean) / ids.size 32 | end 33 | 34 | # Difference between two values, squared. (x-y)^2 35 | def squared_difference(x,y) 36 | 0.0 + (x-y)**2 37 | end 38 | 39 | # Simplified Huber function 40 | def pseudo_huber_error(ids, value_table, mean = nil) 41 | mean ||= self.average ids, value_table 42 | ids.inject(0.0){|sum, i| sum + (Math.log(Math.cosh(value_table[i] - mean))) } 43 | end 44 | 45 | # Simplified Huber loss function: PHE / n 46 | def pseudo_huber_loss(ids, value_table, mean = nil) 47 | self.pseudo_huber_error(ids, value_table, mean) / ids.size 48 | end 49 | 50 | ## CLASSSIFICATION 51 | 52 | # Gini index of a list of classified individuals. 53 | # 54 | # If a dataset T contains examples from n classes, then: 55 | # gini(T) = 1 - Sum (Pj)^2 56 | # where Pj is the relative frequency of class j in T 57 | def gini_index(ids, value_table, classes) 58 | total_size = ids.size.to_f 59 | gini = 1 - class_sizes(ids, value_table, classes).inject(0.0){|sum, size| 60 | sum + (size/total_size)**2} 61 | gini.round(5) 62 | end 63 | 64 | # Majority class of a list of classified individuals. 65 | # If more than one class has the same number of individuals, 66 | # one of the majority classes is selected randomly. 67 | def majority_class(ids, value_table, classes) 68 | sizes = class_sizes(ids, value_table, classes) 69 | Hash[classes.zip sizes].keep_if{|k,v| v == sizes.max}.keys.sample 70 | end 71 | 72 | # Majority class of a list of classes. 73 | # If more than one class has the same number of individuals, 74 | # one of the majority classes is selected randomly. 75 | def majority_class_in_list(list, classes) 76 | sizes = classes.map{|c| list.count{|i| i == c}} 77 | Hash[classes.zip sizes].keep_if{|k,v| v == sizes.max}.keys.sample 78 | end 79 | 80 | # Array with the list of sizes of each class in the given list of individuals. 81 | def class_sizes(ids, value_table, classes) 82 | classes.map{|c| ids.count{|i| value_table[i] == c}} 83 | end 84 | 85 | # Array with the list of sizes of each class in the given list of classes. 86 | def class_sizes_in_list(list, classes) 87 | classes.map{|c| list.count{|i| i == c}} 88 | end 89 | end 90 | 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/nimbus/regression_tree.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | 3 | ##################################################################### 4 | # Tree object representing a random regression tree. 5 | # 6 | # A tree is generated following this steps: 7 | # 8 | # * 1: Calculate loss function for the individuals in the node (first node contains all the individuals). 9 | # * 2: Take a random sample of the SNPs (size m << total count of SNPs) 10 | # * 3: Compute the loss function (quadratic loss) for the split of the sample based on value of every SNP. 11 | # * 4: If the SNP with minimum loss function also minimizes the general loss of the node, split the individuals sample in two nodes, based on average value for that SNP [0,1][2], or [0][1,2] 12 | # * 5: Repeat from 1 for every node until: 13 | # - a) The individuals count in that node is < minimum size OR 14 | # - b) None of the SNP splits has a loss function smaller than the node loss function 15 | # * 6) When a node stops, label the node with the average fenotype value of the individuals in the node. 16 | # 17 | class RegressionTree < Tree 18 | 19 | # Creates the structure of the tree, as a hash of SNP splits and values. 20 | # 21 | # It just initializes the needed variables and then defines the first node of the tree. 22 | # The rest of the structure of the tree is computed recursively building every node calling +build_node+. 23 | def seed(all_individuals, individuals_sample, ids_fenotypes) 24 | super 25 | @structure = build_node individuals_sample, Nimbus::LossFunctions.average(individuals_sample, @id_to_fenotype) 26 | end 27 | 28 | # Creates a node by taking a random sample of the SNPs and computing the loss function for every split by SNP of that sample. 29 | # 30 | # * If SNP_min is the SNP with smaller loss function and it is < the loss function of the node, it splits the individuals sample in two: 31 | # (the average of the 0,1,2 values for the SNP_min in the individuals is computed, and they are splitted in [<=avg], [>avg]) then it builds these 2 new nodes. 32 | # * Otherwise every individual in the node gets labeled with the average of the fenotype values of all of them. 33 | def build_node(individuals_ids, y_hat) 34 | # General loss function value for the node 35 | individuals_count = individuals_ids.size 36 | return label_node(y_hat, individuals_ids) if individuals_count < @node_min_size 37 | node_loss_function = Nimbus::LossFunctions.quadratic_loss individuals_ids, @id_to_fenotype, y_hat 38 | 39 | # Finding the SNP that minimizes loss function 40 | snps = snps_random_sample 41 | min_loss, min_SNP, split, split_type, means = node_loss_function, nil, nil, nil, nil 42 | 43 | snps.each do |snp| 44 | individuals_split_by_snp_value, node_split_type = split_by_snp_avegare_value individuals_ids, snp 45 | mean_0 = Nimbus::LossFunctions.average individuals_split_by_snp_value[0], @id_to_fenotype 46 | mean_1 = Nimbus::LossFunctions.average individuals_split_by_snp_value[1], @id_to_fenotype 47 | loss_0 = Nimbus::LossFunctions.mean_squared_error individuals_split_by_snp_value[0], @id_to_fenotype, mean_0 48 | loss_1 = Nimbus::LossFunctions.mean_squared_error individuals_split_by_snp_value[1], @id_to_fenotype, mean_1 49 | loss_snp = (loss_0 + loss_1) / individuals_count 50 | 51 | min_loss, min_SNP, split, split_type, means = loss_snp, snp, individuals_split_by_snp_value, node_split_type, [mean_0, mean_1] if loss_snp < min_loss 52 | end 53 | 54 | return build_branch(min_SNP, split, split_type, means, y_hat) if min_loss < node_loss_function 55 | return label_node(y_hat, individuals_ids) 56 | end 57 | 58 | # Compute generalization error for the tree. 59 | # 60 | # Traversing the 'out of bag' (OOB) sample (those individuals of the training set not 61 | # used in the building of this tree) through the tree, and comparing 62 | # the prediction with the real fenotype of the individual (and then averaging) is 63 | # possible to calculate the unbiased generalization error for the tree. 64 | def generalization_error_from_oob(oob_ids) 65 | return nil if (@structure.nil? || @individuals.nil? || @id_to_fenotype.nil?) 66 | oob_errors = {} 67 | oob_ids.each do |oobi| 68 | oob_prediction = Tree.traverse @structure, individuals[oobi].snp_list 69 | oob_errors[oobi] = Nimbus::LossFunctions.squared_difference oob_prediction, @id_to_fenotype[oobi] 70 | end 71 | @generalization_error = Nimbus::LossFunctions.average oob_ids, oob_errors 72 | end 73 | 74 | # Estimation of importance for every SNP. 75 | # 76 | # The importance of any SNP in the tree is calculated using the OOB sample. 77 | # For every SNP, every individual in the sample is pushed down the tree but with the 78 | # value of that SNP permuted with other individual in the sample. 79 | # 80 | # That way the difference between the regular prediction and the prediction with the SNP value modified can be estimated for any given SNP. 81 | # 82 | # This method computes importance estimations for every SNPs used in the tree (for any other SNP it would be 0). 83 | def estimate_importances(oob_ids) 84 | return nil if (@generalization_error.nil? && generalization_error_from_oob(oob_ids).nil?) 85 | oob_individuals_count = oob_ids.size 86 | @importances = {} 87 | @used_snps.uniq.each do |current_snp| 88 | shuffled_ids = oob_ids.shuffle 89 | permutated_snp_error = 0.0 90 | oob_ids.each_with_index {|oobi, index| 91 | permutated_prediction = traverse_with_permutation @structure, individuals[oobi].snp_list, current_snp, individuals[shuffled_ids[index]].snp_list 92 | permutated_snp_error += Nimbus::LossFunctions.squared_difference @id_to_fenotype[oobi], permutated_prediction 93 | } 94 | @importances[current_snp] = ((permutated_snp_error / oob_individuals_count) - @generalization_error).round(5) 95 | end 96 | @importances 97 | end 98 | 99 | end 100 | 101 | end -------------------------------------------------------------------------------- /lib/nimbus/training_set.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | ##################################################################### 3 | # Set of individuals to be used as training sample for a random forest. 4 | # 5 | # the TrainingSet class stores an array of individuals, and a hash with the fenotypes of every individual indexed by id. 6 | # 7 | class TrainingSet 8 | attr_accessor :individuals, :ids_fenotypes 9 | 10 | # Initialize a new training set with the individuals and fenotype info received. 11 | def initialize(individuals, ids_fenotypes) 12 | @individuals = individuals 13 | @ids_fenotypes = ids_fenotypes 14 | end 15 | 16 | # Array of all the ids of the individuals in this training sample. 17 | def all_ids 18 | @all_ids ||= @ids_fenotypes.keys 19 | @all_ids 20 | end 21 | end 22 | 23 | end -------------------------------------------------------------------------------- /lib/nimbus/tree.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | 3 | ##################################################################### 4 | # Tree object representing a random tree. 5 | # 6 | # A tree is generated following this steps: 7 | # 8 | # * 1: Calculate loss function for the individuals in the node (first node contains all the individuals). 9 | # * 2: Take a random sample of the SNPs (size m << total count of SNPs) 10 | # * 3: Compute the loss function for the split of the sample based on value of every SNP. 11 | # * 4: If the SNP with minimum loss function also minimizes the general loss of the node, split the individuals sample in three nodes, based on value for that SNP [0, 1, or 2] 12 | # * 5: Repeat from 1 for every node until: 13 | # - a) The individuals count in that node is < minimum size OR 14 | # - b) None of the SNP splits has a loss function smaller than the node loss function 15 | # * 6) When a node stops, label the node with the average fenotype value (for regression problems) or the majority class (for classification problems) of the individuals in the node. 16 | # 17 | class Tree 18 | attr_accessor :snp_sample_size, :snp_total_count, :node_min_size, :used_snps, :structure, :generalization_error, :predictions, :importances 19 | attr_accessor :individuals, :id_to_fenotype 20 | 21 | NODE_SPLIT_01_2 = "zero" 22 | NODE_SPLIT_0_12 = "two" 23 | 24 | # Initialize Tree object with the configuration (as in Nimbus::Configuration.tree) options received. 25 | def initialize(options) 26 | @snp_total_count = options[:snp_total_count] 27 | @snp_sample_size = options[:snp_sample_size] 28 | @node_min_size = options[:tree_node_min_size] 29 | end 30 | 31 | # Creates the structure of the tree, as a hash of SNP splits and values. 32 | # 33 | # It just initializes the needed variables and then defines the first node of the tree. 34 | # The rest of the structure of the tree is computed recursively building every node calling +build_node+. 35 | def seed(all_individuals, individuals_sample, ids_fenotypes) 36 | @individuals = all_individuals 37 | @id_to_fenotype = ids_fenotypes 38 | @predictions = {} 39 | @used_snps = [] 40 | end 41 | 42 | # Creates a node by taking a random sample of the SNPs and computing the loss function for every split by SNP of that sample. 43 | def build_node(individuals_ids, y_hat) 44 | end 45 | 46 | # Compute generalization error for the tree. 47 | def generalization_error_from_oob(oob_ids) 48 | end 49 | 50 | # Estimation of importance for every SNP. 51 | def estimate_importances(oob_ids) 52 | end 53 | 54 | # Class method to traverse a single individual through a tree structure. 55 | # 56 | # Returns the prediction for that individual (the label of the final node reached by the individual). 57 | def self.traverse(tree_structure, data) 58 | return tree_structure if tree_structure.is_a?(Numeric) || tree_structure.is_a?(String) 59 | 60 | raise Nimbus::TreeError, "Forest data has invalid structure. Please check your forest data (file)." if !(tree_structure.is_a?(Hash) && tree_structure.keys.size == 1) 61 | 62 | branch = tree_structure.values.first 63 | split_type = branch[1].to_s 64 | datum = data_traversing_value(data[tree_structure.keys.first - 1], split_type) 65 | 66 | return self.traverse(branch[datum], data) 67 | end 68 | 69 | protected 70 | 71 | def snps_random_sample 72 | (1..@snp_total_count).to_a.sample(@snp_sample_size).sort 73 | end 74 | 75 | def build_branch(snp, split, split_type, y_hats, parent_y_hat) 76 | node_a = split[0].size == 0 ? label_node(parent_y_hat, []) : build_node(split[0], y_hats[0]) 77 | node_b = split[1].size == 0 ? label_node(parent_y_hat, []) : build_node(split[1], y_hats[1]) 78 | 79 | split_by_snp(snp) 80 | return { snp => [node_a, split_type, node_b] } 81 | end 82 | 83 | def label_node(value, ids) 84 | label = value.is_a?(String) ? value : value.round(5) 85 | ids.uniq.each{|i| @predictions[i] = label} 86 | label 87 | end 88 | 89 | def split_by_snp_avegare_value(ids, snp) 90 | split_012 = [[], [], []] 91 | ids.each do |i| 92 | split_012[ @individuals[i].snp_list[snp-1] ] << @individuals[i].id 93 | end 94 | # we split by the average number of 0,1,2 values. 95 | # So if there are less or equal 0s than 2s the split is [0,1][2] 96 | # and if there are more 0s than 2s the average will be <1 so the split is [0][1,2] 97 | split_type = (split_012[0].size <= split_012[2].size ? NODE_SPLIT_01_2 : NODE_SPLIT_0_12) 98 | split_type == NODE_SPLIT_01_2 ? split_012[0] += split_012[1] : split_012[2] += split_012[1] 99 | split = [split_012[0], split_012[2]] 100 | [split, split_type] 101 | rescue => ex 102 | raise Nimbus::TreeError, "Values for SNPs columns must be in [0, 1, 2]" 103 | end 104 | 105 | def split_by_value(ids, snp, value) 106 | split = [[], []] 107 | ids.each do |i| 108 | @individuals[i].snp_list[snp-1] > value ? (split[1] << @individuals[i].id) : (split[0] << @individuals[i].id) 109 | end 110 | split 111 | rescue => ex 112 | raise Nimbus::TreeError, "Values for SNPs columns must be numeric" 113 | end 114 | 115 | def split_by_snp(x) 116 | @used_snps << x 117 | end 118 | 119 | def traverse_with_permutation(tree_structure, data, snp_to_permute, individual_to_permute) 120 | return tree_structure if tree_structure.is_a?(Numeric) || tree_structure.is_a?(String) 121 | 122 | key = tree_structure.keys.first 123 | branch = tree_structure.values.first 124 | individual_data = (key == snp_to_permute ? individual_to_permute : data) 125 | split_type = branch[1] 126 | datum = data_traversing_value(individual_data[key - 1].to_i, split_type) 127 | 128 | return traverse_with_permutation branch[datum], data, snp_to_permute, individual_to_permute 129 | end 130 | 131 | def data_traversing_value(datum, split_type) 132 | Nimbus::Tree.data_traversing_value(datum, split_type) 133 | end 134 | 135 | def self.data_traversing_value(datum, split_type) 136 | if datum == 1 137 | return 0 if split_type == NODE_SPLIT_01_2 138 | return 2 if split_type == NODE_SPLIT_0_12 139 | end 140 | datum 141 | end 142 | 143 | end 144 | 145 | end -------------------------------------------------------------------------------- /lib/nimbus/version.rb: -------------------------------------------------------------------------------- 1 | module Nimbus 2 | VERSION = "2.4.1" 3 | end 4 | -------------------------------------------------------------------------------- /nimbus.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require File.expand_path "#{File.dirname(__FILE__)}/lib/nimbus/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.name = 'nimbus' 6 | s.version = Nimbus::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.date = Time.now.strftime('%Y-%m-%d') 9 | 10 | s.summary = "Random Forest algorithm for Genomics" 11 | s.description = "Nimbus is a Ruby gem to implement Random Forest in a genomic selection context." 12 | 13 | s.authors = ['Juanjo Bazán', 'Oscar González Recio'] 14 | s.email = ["jjbazan@gmail.com"] 15 | s.homepage = 'http://nimbusgem.org' 16 | s.license = "MIT" 17 | 18 | s.rdoc_options = ['--main', 'README.rdoc', '--charset=UTF-8'] 19 | 20 | s.files = %w(MIT-LICENSE.txt README.md CONTRIBUTING.md CODE_OF_CONDUCT.md) + Dir.glob("{spec,lib/**/*}") & `git ls-files -z`.split("\0") 21 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 22 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 23 | s.require_paths = ["lib"] 24 | 25 | s.add_development_dependency("rspec", "~> 3.12") 26 | end 27 | -------------------------------------------------------------------------------- /paper/nimbus_outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuanxu/nimbus/70c69e3a55e5f59ed6746593db64a0d074cf9d94/paper/nimbus_outputs.png -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @article{Breiman2001, 2 | doi = {10.1023/a:1010933404324}, 3 | url = {https://doi.org/10.1023/a:1010933404324}, 4 | year = {2001}, 5 | publisher = {Springer Nature}, 6 | volume = {45}, 7 | number = {1}, 8 | pages = {5--32}, 9 | author = {Leo Breiman}, 10 | title = {Random forest}, 11 | journal = {Machine Learning} 12 | } 13 | 14 | @article{GonzlezRecio2011, 15 | doi = {10.1186/1297-9686-43-7}, 16 | url = {https://doi.org/10.1186/1297-9686-43-7}, 17 | year = {2011}, 18 | publisher = {Springer Nature}, 19 | volume = {43}, 20 | number = {1}, 21 | pages = {7}, 22 | author = {Oscar Gonz{\'{a}}lez-Recio and Selma Forni}, 23 | title = {Genome-wide prediction of discrete traits using bayesian regressions and machine learning}, 24 | journal = {Genetics Selection Evolution} 25 | } 26 | 27 | @article{Kamiski2017, 28 | doi = {10.1007/s10100-017-0479-6}, 29 | url = {https://doi.org/10.1007/s10100-017-0479-6}, 30 | year = {2017}, 31 | month = {may}, 32 | publisher = {Springer Nature}, 33 | author = {Bogumi{\l} Kami{\'{n}}ski and Micha{\l} Jakubczyk and Przemys{\l}aw Szufel}, 34 | title = {A framework for sensitivity analysis of decision trees}, 35 | journal = {Central European Journal of Operations Research} 36 | } 37 | 38 | @online{breimanweb, 39 | author = {Breiman, L., Cutler, A.}, 40 | title = {Leo Breiman RF website}, 41 | year = {2017}, 42 | url = {https://www.stat.berkeley.edu/~breiman/RandomForests/} 43 | } 44 | 45 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Nimbus: a Ruby gem to implement Random Forest algorithms in a genomic selection context' 3 | tags: 4 | - random forest 5 | - genomics 6 | - machine learning 7 | - ruby 8 | - open science 9 | authors: 10 | - name: Juanjo Bazán 11 | orcid: 0000-0001-7699-3983 12 | affiliation: 1 13 | - name: Oscar Gonzalez-Recio 14 | orcid: 0000-0002-9106-4063 15 | affiliation: 2 16 | affiliations: 17 | - name: Departamento de Física Teórica. Universidad Autónoma de Madrid. 18 | index: 1 19 | - name: Departamento de Mejora Genética Animal. Instituto Nacional de Investigación y Tecnología Agraria y Alimentaria. 20 | index: 2 21 | date: 12 July 2017 22 | bibliography: paper.bib 23 | --- 24 | 25 | # Summary 26 | 27 | Nimbus is a Ruby gem implementing Random Forest in a genomic selection context, meaning every input file is expected to contain genotype and/or phenotype data from a sample of individuals. Other than the ids of the individuals, Nimbus handle the data as genotype values for single-nucleotide polymorphisms (SNPs), so the variables in the classifier must have values of 0, 1 or 2, corresponding with SNPs classes of AA, AB and BB. 28 | 29 | Nimbus provides a novel dataframe of random forest under ruby, and implements a modified algorithm that can separate all genotypes for a single marker, which can accommodate both additivity and dominance. Further, it allows the user to specify a loss function and provide full information of the trees in a .yml file. 30 | 31 | Nimbus can be used to: 32 | 33 | - Create a random forest using a training sample of individuals with phenotype data. 34 | 35 | - Use an existent random forest to get predictions for a testing sample. 36 | 37 | 38 | ### Random Forest 39 | 40 | The random forest algorithm is a classifier consisting in many random decision trees, it was first proposed as a massively non-parametric machine-learning algorithm by Leo Breiman [@Breiman2001]. It is based on choosing random subsets of variables for each tree and using the most frequent, or the averaged tree output as the overall classification. Random forest makes use of bagging and randomization, constructing many decision trees [@Kamiski2017] on bootstrapped samples of a given data set. The prediction from the trees are averaged to make final predictions. 41 | 42 | In machine learning terms, it is an ensemble classifier, so it uses multiple models to obtain better predictive performance than could be obtained from any of the constituent models. 43 | 44 | The forest outputs the class that is the mean or the mode (in regression problems) or the majority class (in classification problems) of the node's output by individual trees. 45 | 46 | The algorithm is robust to over-fitting and able to capture complex interaction structures in the data, which may alleviate the problems of analyzing genome-wide data. 47 | 48 | 49 | ### Learning algorithm 50 | 51 | **Training**: Each tree in the forest is constructed using the following algorithm: 52 | 53 | - Let the number of training cases be N, and the number of variables (SNPs) in the classifier be M. 54 | 55 | - The mtry number of input variables is told to the algorithm to be used in determining the decision at a node of the tree; m should be much less than M (usually 1/3), and the optimal value should be tuned for methods like grid search. 56 | 57 | - Choose a training set for this tree by drawing n samples with replacement from all N available training cases (i.e. bootstrap sampling). The rest of the cases (Out Of Bag sample) will be used to estimate the error of the tree at predicting the classes of this out of Bag samples. 58 | 59 | - For each node of the tree, randomly choose m SNPs on which to base the decision at that node. Calculate the best split based on these m SNPs in the training set. 60 | 61 | - Each tree is fully grown and not pruned (as may be done in constructing a normal tree classifier). 62 | 63 | - When in a node there is not any SNP split that minimizes the general loss function of the node, or the number of individuals in the node is less than the minimum node size then label the node with the average phenotype value of the individuals in the node. 64 | 65 | 66 | **Testing**: An independent sample can be pushed down the tree to predict the most probable phenotype given the SNP genotypes. It is assigned the label of the training sample in the terminal node it ends up in. This procedure is iterated over all trees in the ensemble, and the average vote of all trees is reported as the random forest prediction. 67 | 68 | 69 | ### Nimbus 70 | 71 | Nimbus trains the algorithm based on an input file (learning sample) containing the phenotypes of the individuals and their respective list of genotype markers (i.e. SNPs). A random forest is created and stored in a .yml file for future use. 72 | 73 | Nimbus can also be run to make prediction in a validation set or in a set of data containing yet to be observed response variable. In this case, the predictions can be obtained using the random forest created with a learning sample or using a previously stored random forest. 74 | 75 | If a learning sample is provided, the gem will create a file with the variable importance of each feature (marker) in the data. The higher the importance is, the more relevant the marker is to correctly predict the response variable in new data. 76 | 77 | Nimbus can be used for both classification or regression problems, and the user may provide different parameter values in a configuration file to tune the performance of the algorithm. 78 | 79 | -![Output predictions](nimbus_outputs.png) 80 | 81 | # References 82 | -------------------------------------------------------------------------------- /spec/classification_tree_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/spec_helper' 2 | 3 | describe Nimbus::ClassificationTree do 4 | 5 | before(:each) do 6 | @config = Nimbus::Configuration.new 7 | @config.load fixture_file('classification/config.yml') 8 | 9 | @tree = Nimbus::ClassificationTree.new @config.tree 10 | end 11 | 12 | it "is initialized with tree config info" do 13 | expect(@tree.snp_total_count).to eq 100 14 | expect(@tree.snp_sample_size).to eq 33 15 | expect(@tree.node_min_size).to eq 5 16 | expect(@tree.classes.size).to eq 2 17 | expect(@tree.classes[0]).to eq '0' 18 | expect(@tree.classes[1]).to eq '1' 19 | end 20 | 21 | it "creates a tree structure when seeded with training data" do 22 | @config.load_training_data 23 | expect(@tree.structure).to be_nil 24 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 25 | expect(@tree.structure).to_not be_nil 26 | expect(@tree.structure).to be_kind_of Hash 27 | 28 | expect(@tree.structure.keys.first).to eq @tree.used_snps.last 29 | expect(@tree.used_snps).to_not be_empty 30 | end 31 | 32 | it "splits node when building a node and finds a suitable split" do 33 | @config.load_training_data 34 | allow_any_instance_of(Nimbus::ClassificationTree).to receive(:snps_random_sample).and_return((68..100).to_a) #97 is best split 35 | 36 | @tree.individuals = @config.training_set.individuals 37 | @tree.id_to_fenotype = @config.training_set.ids_fenotypes 38 | @tree.used_snps = [] 39 | @tree.predictions = {} 40 | 41 | branch = @tree.build_node @config.training_set.all_ids, Nimbus::LossFunctions.majority_class(@config.training_set.all_ids, @config.training_set.ids_fenotypes, @config.classes) 42 | expect(branch.keys.size).to eq 1 43 | expect(branch.keys.first).to eq 97 44 | expect(branch[97].size).to eq 3 45 | expect(branch[97][0]).to be_kind_of Hash 46 | expect([Nimbus::Tree::NODE_SPLIT_01_2, Nimbus::Tree::NODE_SPLIT_0_12]).to include(branch[97][1]) 47 | expect(branch[97][2]).to be_kind_of Hash 48 | end 49 | 50 | it "keeps track of all SNPs used for the tree" do 51 | @config.load_training_data 52 | snps = (33..65).to_a 53 | allow_any_instance_of(Nimbus::ClassificationTree).to receive(:snps_random_sample).and_return(snps) 54 | expect(@tree.used_snps).to be_nil 55 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 56 | expect(@tree.used_snps.size).to be > 4 57 | @tree.used_snps.each{|snp| 58 | expect(snps.include?(snp)).to be true 59 | } 60 | end 61 | 62 | it "labels node when building a node and there is not a suitable split" do 63 | @config.load_training_data 64 | allow_any_instance_of(Nimbus::ClassificationTree).to receive(:snps_random_sample).and_return([11]) 65 | 66 | @tree.individuals = @config.training_set.individuals 67 | @tree.id_to_fenotype = @config.training_set.ids_fenotypes 68 | @tree.used_snps = [] 69 | @tree.predictions = {} 70 | 71 | branch = @tree.build_node @config.training_set.all_ids, Nimbus::LossFunctions.majority_class(@config.training_set.all_ids, @config.training_set.ids_fenotypes, @config.classes) 72 | expect(branch[11][0]).to be_kind_of String 73 | expect(branch[11][1]).to be_kind_of String 74 | expect(branch[11][2]).to be_kind_of String 75 | end 76 | 77 | it "labels node when building a node with less individuals than the minimum node size" do 78 | @config.load_training_data 79 | 80 | @tree.individuals = @config.training_set.individuals 81 | @tree.id_to_fenotype = @config.training_set.ids_fenotypes 82 | @tree.used_snps = [] 83 | @tree.predictions = {} 84 | 85 | label = @tree.build_node [1, 10, 33], Nimbus::LossFunctions.majority_class(@config.training_set.all_ids, @config.training_set.ids_fenotypes, @config.classes) 86 | expect(label).to be_kind_of String 87 | 88 | label = @tree.build_node [2, 10], Nimbus::LossFunctions.majority_class(@config.training_set.all_ids, @config.training_set.ids_fenotypes, @config.classes) 89 | expect(label).to be_kind_of String 90 | 91 | label = @tree.build_node [1, 10, 33], Nimbus::LossFunctions.majority_class(@config.training_set.all_ids, @config.training_set.ids_fenotypes, @config.classes) 92 | expect(label).to be_kind_of String 93 | 94 | label = @tree.build_node [99, 22, 10, 33], Nimbus::LossFunctions.majority_class(@config.training_set.all_ids, @config.training_set.ids_fenotypes, @config.classes) 95 | expect(label).to be_kind_of String 96 | end 97 | 98 | it 'computes generalization error for the tree' do 99 | @config.load_training_data 100 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 101 | expect(@tree.generalization_error).to be_nil 102 | @tree.generalization_error_from_oob((3..300).to_a) 103 | expect(@tree.generalization_error).to be_kind_of Numeric 104 | expect(@tree.generalization_error).to be > 0.0 105 | expect(@tree.generalization_error).to be < 1.0 106 | end 107 | 108 | it 'estimates importance for all SNPs' do 109 | @config.load_training_data 110 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 111 | expect(@tree.importances).to be_nil 112 | @tree.estimate_importances((200..533).to_a) 113 | expect(@tree.importances).to be_kind_of Hash 114 | expect(@tree.importances.keys).to_not be_empty 115 | expect((@tree.importances.keys - (1..100).to_a)).to be_empty #all keys are snp indexes (100 snps in training file) 116 | end 117 | 118 | it 'get prediction for an individual pushing it down a tree structure' do 119 | tree_structure = Psych.load(File.open fixture_file('classification/random_forest.yml')).first 120 | individual_data = [0]*100 121 | prediction = Nimbus::Tree.traverse tree_structure, individual_data 122 | expect(prediction).to eq '0' 123 | 124 | individual_data[8-1] = 2 125 | individual_data[29-1] = 0 126 | individual_data[1-1] = 1 127 | individual_data[7-1] = 1 128 | prediction = Nimbus::Tree.traverse tree_structure, individual_data 129 | expect(prediction).to eq '1' 130 | end 131 | 132 | end -------------------------------------------------------------------------------- /spec/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Nimbus::Configuration do 5 | 6 | it "loads configuration options from config file" do 7 | config = Nimbus::Configuration.new 8 | config.load fixture_file('regression/config.yml') 9 | 10 | expect(config.training_file).to eq fixture_file('regression/training.data') 11 | expect(config.testing_file).to eq fixture_file('regression/testing.data') 12 | expect(config.forest_file).to eq fixture_file('regression/random_forest.yml') 13 | expect(config.classes).to be_nil 14 | expect(config.do_importances).to be 15 | 16 | expect(config.forest_size).to eq 3 17 | expect(config.tree_SNP_sample_size).to eq 60 18 | expect(config.tree_SNP_total_count).to eq 200 19 | expect(config.tree_node_min_size).to eq 5 20 | 21 | config = Nimbus::Configuration.new 22 | config.load fixture_file('classification/config.yml') 23 | 24 | expect(config.training_file).to eq fixture_file('classification/training.data') 25 | expect(config.testing_file).to eq fixture_file('classification/testing.data') 26 | expect(config.forest_file).to eq fixture_file('classification/random_forest.yml') 27 | expect(config.classes).to eq ['0','1'] 28 | expect(config.do_importances).to_not be 29 | 30 | expect(config.forest_size).to eq 3 31 | expect(config.tree_SNP_sample_size).to eq 33 32 | expect(config.tree_SNP_total_count).to eq 100 33 | expect(config.tree_node_min_size).to eq 5 34 | end 35 | 36 | it 'tree method return tree-related subset of options for regression trees' do 37 | config = Nimbus::Configuration.new 38 | config.load fixture_file('regression/config.yml') 39 | tree_options = config.tree 40 | 41 | expect(tree_options[:snp_sample_size]).to_not be_nil 42 | expect(tree_options[:snp_total_count]).to_not be_nil 43 | expect(tree_options[:tree_node_min_size]).to_not be_nil 44 | expect(tree_options[:classes]).to be_nil 45 | end 46 | 47 | it 'tree method return tree-related subset of options for classification trees' do 48 | config = Nimbus::Configuration.new 49 | config.load fixture_file('classification/config.yml') 50 | tree_options = config.tree 51 | 52 | expect(tree_options[:snp_sample_size]).to_not be_nil 53 | expect(tree_options[:snp_total_count]).to_not be_nil 54 | expect(tree_options[:tree_node_min_size]).to_not be_nil 55 | expect(tree_options[:classes]).to_not be_nil 56 | end 57 | 58 | it "creates a training set object from training data file" do 59 | config = Nimbus::Configuration.new 60 | config.load fixture_file('regression/config.yml') 61 | expect(config.training_set).to be_nil 62 | config.load_training_data 63 | expect(config.training_set).to be_kind_of Nimbus::TrainingSet 64 | expect(config.training_set.all_ids.sort).to eq (1..800).to_a 65 | 66 | File.open(fixture_file('regression/training.data')) {|file| 67 | feno1, id1, *snp_list_1 = file.readline.split 68 | feno2, id2, *snp_list_2 = file.readline.split 69 | feno3, id3, *snp_list_3 = file.readline.split 70 | 71 | i1 = Nimbus::Individual.new(id1.to_i, feno1.to_f, snp_list_1.map{|snp| snp.to_i}) 72 | i2 = Nimbus::Individual.new(id2.to_i, feno2.to_f, snp_list_2.map{|snp| snp.to_i}) 73 | i3 = Nimbus::Individual.new(id3.to_i, feno3.to_f, snp_list_3.map{|snp| snp.to_i}) 74 | 75 | expect(config.training_set.individuals[id1.to_i].id).to eq i1.id 76 | expect(config.training_set.individuals[id2.to_i].fenotype).to eq i2.fenotype 77 | expect(config.training_set.individuals[id3.to_i].snp_list).to eq i3.snp_list 78 | 79 | config.training_set.ids_fenotypes[id1.to_i] = feno1.to_f 80 | config.training_set.ids_fenotypes[id2.to_i] = feno2.to_f 81 | config.training_set.ids_fenotypes[id3.to_i] = feno3.to_f 82 | } 83 | end 84 | 85 | it "reads testing data and yields one individual at a time" do 86 | config = Nimbus::Configuration.new 87 | config.load fixture_file('regression/config.yml') 88 | 89 | test_individuals = [] 90 | File.open(fixture_file('regression/testing.data')) {|file| 91 | file.each do |line| 92 | data_id, *snp_list = line.strip.split 93 | test_individuals << Nimbus::Individual.new(data_id.to_i, nil, snp_list.map{|snp| snp.to_i}) 94 | end 95 | } 96 | expect(test_individuals.size).to eq 200 97 | config.read_testing_data{|individual| 98 | test_individual = test_individuals.shift 99 | expect(individual.id).to_not be_nil 100 | expect(individual.id).to eq test_individual.id 101 | expect(individual.snp_list).to_not be_empty 102 | expect(individual.snp_list).to eq test_individual.snp_list 103 | } 104 | end 105 | 106 | it "creates a forest object loading data from a yaml file" do 107 | config = Nimbus::Configuration.new 108 | config.load fixture_file('regression/config.yml') 109 | 110 | trees = Psych.load(File.open fixture_file('regression/random_forest.yml')) 111 | expect(trees.first.keys.first).to eq 176 112 | expect(trees.size).to eq 3 113 | 114 | forest = config.load_forest 115 | expect(forest).to be_kind_of Nimbus::Forest 116 | expect(forest.trees[0]).to eq trees.first 117 | expect(forest.trees[1]).to eq trees[1] 118 | expect(forest.trees.last).to eq trees[2] 119 | end 120 | 121 | end -------------------------------------------------------------------------------- /spec/fixtures/classification/config.yml: -------------------------------------------------------------------------------- 1 | #Input files 2 | input: 3 | training: training.data 4 | testing: testing.data 5 | forest: random_forest.yml 6 | classes: [0,1] 7 | 8 | #Forest parameters 9 | forest: 10 | forest_size: 3 #how many trees 11 | SNP_sample_size_mtry: 33 #mtry 12 | SNP_total_count: 100 13 | node_min_size: 5 14 | var_importances: NO -------------------------------------------------------------------------------- /spec/fixtures/classification/random_forest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 8: 3 | - 31: 4 | - 41: 5 | - 41: 6 | - 74: 7 | - 52: 8 | - '0' 9 | - two 10 | - 46: 11 | - '1' 12 | - zero 13 | - 62: 14 | - '0' 15 | - two 16 | - '1' 17 | - two 18 | - 16: 19 | - 35: 20 | - '1' 21 | - two 22 | - 25: 23 | - 22: 24 | - '1' 25 | - two 26 | - '0' 27 | - zero 28 | - '1' 29 | - two 30 | - 83: 31 | - 87: 32 | - '0' 33 | - two 34 | - '1' 35 | - zero 36 | - 91: 37 | - '0' 38 | - two 39 | - '0' 40 | - two 41 | - 97: 42 | - 17: 43 | - 13: 44 | - '0' 45 | - two 46 | - '0' 47 | - two 48 | - 51: 49 | - 71: 50 | - 8: 51 | - '1' 52 | - two 53 | - '0' 54 | - two 55 | - '0' 56 | - zero 57 | - 99: 58 | - '1' 59 | - zero 60 | - '0' 61 | - zero 62 | - 66: 63 | - '0' 64 | - zero 65 | - 1: 66 | - 2: 67 | - '0' 68 | - two 69 | - '1' 70 | - two 71 | - '0' 72 | - zero 73 | - 98: 74 | - 53: 75 | - 8: 76 | - '0' 77 | - two 78 | - 23: 79 | - '1' 80 | - zero 81 | - '1' 82 | - two 83 | - 25: 84 | - '0' 85 | - zero 86 | - 78: 87 | - '0' 88 | - zero 89 | - '1' 90 | - zero 91 | - '0' 92 | - two 93 | - 26: 94 | - 98: 95 | - 29: 96 | - 79: 97 | - 83: 98 | - 65: 99 | - 87: 100 | - '1' 101 | - zero 102 | - '0' 103 | - zero 104 | - 68: 105 | - '1' 106 | - zero 107 | - '0' 108 | - zero 109 | - '1' 110 | - zero 111 | - '1' 112 | - zero 113 | - 42: 114 | - 40: 115 | - 99: 116 | - '0' 117 | - zero 118 | - 5: 119 | - '1' 120 | - zero 121 | - '0' 122 | - zero 123 | - '1' 124 | - zero 125 | - 12: 126 | - 27: 127 | - '1' 128 | - zero 129 | - '0' 130 | - two 131 | - '1' 132 | - two 133 | - 54: 134 | - 45: 135 | - 71: 136 | - 22: 137 | - '1' 138 | - zero 139 | - '1' 140 | - zero 141 | - 99: 142 | - '0' 143 | - zero 144 | - '1' 145 | - two 146 | - 95: 147 | - '0' 148 | - zero 149 | - 10: 150 | - '0' 151 | - two 152 | - '1' 153 | - two 154 | - 41: 155 | - 19: 156 | - 30: 157 | - 48: 158 | - 28: 159 | - 52: 160 | - '0' 161 | - zero 162 | - '1' 163 | - zero 164 | - '1' 165 | - zero 166 | - '0' 167 | - zero 168 | - 73: 169 | - '1' 170 | - two 171 | - 13: 172 | - '0' 173 | - zero 174 | - '1' 175 | - two 176 | - 22: 177 | - '1' 178 | - two 179 | - 31: 180 | - 12: 181 | - 44: 182 | - '0' 183 | - two 184 | - '1' 185 | - zero 186 | - '1' 187 | - zero 188 | - '1' 189 | - zero 190 | - 73: 191 | - 3: 192 | - 66: 193 | - '1' 194 | - zero 195 | - '1' 196 | - zero 197 | - 82: 198 | - '1' 199 | - two 200 | - '0' 201 | - zero 202 | - 74: 203 | - '0' 204 | - two 205 | - '0' 206 | - zero 207 | - 98: 208 | - 14: 209 | - 50: 210 | - 72: 211 | - 68: 212 | - 59: 213 | - '1' 214 | - two 215 | - 15: 216 | - '0' 217 | - zero 218 | - '1' 219 | - zero 220 | - '0' 221 | - zero 222 | - '0' 223 | - zero 224 | - 39: 225 | - '0' 226 | - two 227 | - '1' 228 | - zero 229 | - 22: 230 | - 97: 231 | - 20: 232 | - '1' 233 | - zero 234 | - '0' 235 | - zero 236 | - '0' 237 | - two 238 | - '0' 239 | - zero 240 | - 54: 241 | - '0' 242 | - zero 243 | - 9: 244 | - '0' 245 | - two 246 | - 7: 247 | - '1' 248 | - two 249 | - 81: 250 | - '1' 251 | - two 252 | - '0' 253 | - zero 254 | - 29: 255 | - 1: 256 | - 44: 257 | - 97: 258 | - 18: 259 | - '1' 260 | - zero 261 | - '0' 262 | - zero 263 | - 61: 264 | - '0' 265 | - zero 266 | - '0' 267 | - two 268 | - 93: 269 | - 75: 270 | - 59: 271 | - 92: 272 | - '1' 273 | - zero 274 | - '0' 275 | - zero 276 | - 11: 277 | - '0' 278 | - two 279 | - '1' 280 | - zero 281 | - '1' 282 | - zero 283 | - 38: 284 | - 4: 285 | - '0' 286 | - zero 287 | - '1' 288 | - zero 289 | - '1' 290 | - two 291 | - 63: 292 | - 60: 293 | - 7: 294 | - '1' 295 | - two 296 | - 17: 297 | - '1' 298 | - zero 299 | - '0' 300 | - two 301 | - '1' 302 | - two 303 | - 58: 304 | - 61: 305 | - 31: 306 | - 70: 307 | - '0' 308 | - zero 309 | - 4: 310 | - '1' 311 | - two 312 | - '0' 313 | - two 314 | - '1' 315 | - two 316 | - 67: 317 | - 12: 318 | - 69: 319 | - 17: 320 | - '0' 321 | - two 322 | - '1' 323 | - zero 324 | - '0' 325 | - zero 326 | - '1' 327 | - zero 328 | - '1' 329 | - zero 330 | - 60: 331 | - '1' 332 | - two 333 | - 1: 334 | - 14: 335 | - '1' 336 | - two 337 | - 27: 338 | - '0' 339 | - zero 340 | - '1' 341 | - zero 342 | - '1' 343 | - zero 344 | - 66: 345 | - 13: 346 | - 1: 347 | - 71: 348 | - '0' 349 | - zero 350 | - 3: 351 | - '1' 352 | - zero 353 | - '0' 354 | - zero 355 | - 72: 356 | - 51: 357 | - 7: 358 | - '1' 359 | - zero 360 | - '0' 361 | - two 362 | - '0' 363 | - zero 364 | - '1' 365 | - zero 366 | - 19: 367 | - 88: 368 | - 45: 369 | - '1' 370 | - zero 371 | - 16: 372 | - '0' 373 | - zero 374 | - '1' 375 | - zero 376 | - 75: 377 | - '1' 378 | - zero 379 | - '0' 380 | - zero 381 | - 7: 382 | - '0' 383 | - zero 384 | - '1' 385 | - zero 386 | - 31: 387 | - 100: 388 | - '1' 389 | - two 390 | - 1: 391 | - '0' 392 | - two 393 | - '1' 394 | - two 395 | - 39: 396 | - 21: 397 | - '0' 398 | - two 399 | - '1' 400 | - two 401 | - '1' 402 | - 93: 403 | - 31: 404 | - 97: 405 | - 36: 406 | - 29: 407 | - 37: 408 | - 7: 409 | - '0' 410 | - zero 411 | - '1' 412 | - two 413 | - '1' 414 | - zero 415 | - 60: 416 | - '0' 417 | - two 418 | - 22: 419 | - '1' 420 | - zero 421 | - '0' 422 | - zero 423 | - 47: 424 | - 73: 425 | - '0' 426 | - two 427 | - 37: 428 | - '1' 429 | - two 430 | - '0' 431 | - two 432 | - 5: 433 | - 43: 434 | - '0' 435 | - zero 436 | - '1' 437 | - two 438 | - '0' 439 | - two 440 | - 54: 441 | - 16: 442 | - 7: 443 | - '0' 444 | - zero 445 | - 66: 446 | - '1' 447 | - two 448 | - '0' 449 | - two 450 | - '0' 451 | - two 452 | - 85: 453 | - 24: 454 | - '0' 455 | - zero 456 | - 2: 457 | - '0' 458 | - zero 459 | - '0' 460 | - two 461 | - 96: 462 | - 54: 463 | - 45: 464 | - 12: 465 | - '1' 466 | - zero 467 | - '0' 468 | - two 469 | - '0' 470 | - zero 471 | - 3: 472 | - 29: 473 | - '1' 474 | - zero 475 | - '0' 476 | - two 477 | - '0' 478 | - zero 479 | - '0' 480 | - two 481 | - 98: 482 | - 29: 483 | - 97: 484 | - 61: 485 | - 52: 486 | - '1' 487 | - zero 488 | - 37: 489 | - '1' 490 | - zero 491 | - '1' 492 | - zero 493 | - 17: 494 | - 93: 495 | - '0' 496 | - two 497 | - '1' 498 | - two 499 | - 4: 500 | - '1' 501 | - zero 502 | - '0' 503 | - zero 504 | - 69: 505 | - 14: 506 | - 56: 507 | - 41: 508 | - '1' 509 | - two 510 | - '0' 511 | - two 512 | - '0' 513 | - two 514 | - 21: 515 | - '1' 516 | - zero 517 | - '0' 518 | - zero 519 | - '1' 520 | - zero 521 | - 40: 522 | - 41: 523 | - '1' 524 | - two 525 | - 7: 526 | - '0' 527 | - zero 528 | - 4: 529 | - '0' 530 | - two 531 | - '1' 532 | - two 533 | - 95: 534 | - 15: 535 | - '0' 536 | - two 537 | - '1' 538 | - two 539 | - 28: 540 | - '0' 541 | - zero 542 | - 1: 543 | - '1' 544 | - two 545 | - '0' 546 | - two 547 | - 99: 548 | - 46: 549 | - 92: 550 | - 26: 551 | - 62: 552 | - 32: 553 | - '0' 554 | - zero 555 | - '1' 556 | - two 557 | - '1' 558 | - zero 559 | - 32: 560 | - '1' 561 | - zero 562 | - '0' 563 | - two 564 | - 4: 565 | - '0' 566 | - two 567 | - 76: 568 | - '0' 569 | - two 570 | - 78: 571 | - '1' 572 | - zero 573 | - '0' 574 | - zero 575 | - 88: 576 | - '0' 577 | - zero 578 | - 43: 579 | - '0' 580 | - zero 581 | - '0' 582 | - two 583 | - 34: 584 | - 27: 585 | - 74: 586 | - '0' 587 | - two 588 | - 65: 589 | - 4: 590 | - '0' 591 | - zero 592 | - '1' 593 | - two 594 | - 19: 595 | - '0' 596 | - two 597 | - '1' 598 | - two 599 | - 52: 600 | - 72: 601 | - '0' 602 | - two 603 | - 18: 604 | - '0' 605 | - two 606 | - '1' 607 | - zero 608 | - '0' 609 | - two 610 | - 12: 611 | - 80: 612 | - '0' 613 | - two 614 | - 64: 615 | - '1' 616 | - two 617 | - 11: 618 | - '1' 619 | - two 620 | - 56: 621 | - '0' 622 | - zero 623 | - '1' 624 | - two 625 | - 76: 626 | - 26: 627 | - 6: 628 | - 4: 629 | - '1' 630 | - zero 631 | - '0' 632 | - two 633 | - '1' 634 | - zero 635 | - 84: 636 | - '0' 637 | - two 638 | - '1' 639 | - two 640 | - 58: 641 | - 57: 642 | - '1' 643 | - zero 644 | - 10: 645 | - '1' 646 | - two 647 | - '0' 648 | - zero 649 | - 54: 650 | - 1: 651 | - '0' 652 | - zero 653 | - '0' 654 | - zero 655 | - '1' 656 | - zero 657 | - 83: 658 | - 71: 659 | - 31: 660 | - 84: 661 | - '1' 662 | - two 663 | - 77: 664 | - 6: 665 | - 67: 666 | - '1' 667 | - zero 668 | - '0' 669 | - zero 670 | - '0' 671 | - zero 672 | - 57: 673 | - '1' 674 | - zero 675 | - '1' 676 | - zero 677 | - '1' 678 | - two 679 | - 98: 680 | - 51: 681 | - 4: 682 | - 57: 683 | - '1' 684 | - zero 685 | - '1' 686 | - zero 687 | - 5: 688 | - 32: 689 | - '0' 690 | - zero 691 | - '1' 692 | - two 693 | - '0' 694 | - zero 695 | - '1' 696 | - two 697 | - 21: 698 | - 97: 699 | - '1' 700 | - two 701 | - 88: 702 | - 34: 703 | - '0' 704 | - zero 705 | - '0' 706 | - two 707 | - 60: 708 | - '1' 709 | - zero 710 | - '0' 711 | - two 712 | - 91: 713 | - 9: 714 | - 57: 715 | - 34: 716 | - '0' 717 | - zero 718 | - '1' 719 | - zero 720 | - 69: 721 | - '1' 722 | - zero 723 | - '0' 724 | - zero 725 | - '0' 726 | - zero 727 | - 56: 728 | - 4: 729 | - '0' 730 | - zero 731 | - 98: 732 | - '0' 733 | - zero 734 | - '1' 735 | - zero 736 | - '1' 737 | - zero 738 | - 98: 739 | - 31: 740 | - 40: 741 | - 27: 742 | - '1' 743 | - zero 744 | - '1' 745 | - two 746 | - 35: 747 | - 12: 748 | - 19: 749 | - '1' 750 | - two 751 | - '0' 752 | - two 753 | - '0' 754 | - zero 755 | - '0' 756 | - two 757 | - 50: 758 | - 41: 759 | - 88: 760 | - '1' 761 | - zero 762 | - '1' 763 | - zero 764 | - 24: 765 | - '0' 766 | - zero 767 | - '1' 768 | - zero 769 | - '1' 770 | - zero 771 | - 36: 772 | - 67: 773 | - 79: 774 | - 47: 775 | - '0' 776 | - zero 777 | - '1' 778 | - two 779 | - '0' 780 | - zero 781 | - 11: 782 | - '1' 783 | - zero 784 | - '0' 785 | - zero 786 | - 60: 787 | - '1' 788 | - zero 789 | - 8: 790 | - '0' 791 | - two 792 | - '1' 793 | - 97: 794 | - 41: 795 | - 72: 796 | - 1: 797 | - 47: 798 | - 49: 799 | - '1' 800 | - zero 801 | - 75: 802 | - '1' 803 | - zero 804 | - '0' 805 | - zero 806 | - 17: 807 | - '0' 808 | - zero 809 | - '0' 810 | - two 811 | - 77: 812 | - 6: 813 | - 67: 814 | - '0' 815 | - two 816 | - '1' 817 | - two 818 | - '1' 819 | - two 820 | - 43: 821 | - 40: 822 | - '0' 823 | - zero 824 | - '0' 825 | - two 826 | - 45: 827 | - 18: 828 | - '1' 829 | - zero 830 | - '1' 831 | - zero 832 | - 11: 833 | - '0' 834 | - zero 835 | - '1' 836 | - two 837 | - 84: 838 | - 13: 839 | - 26: 840 | - 69: 841 | - '1' 842 | - zero 843 | - 91: 844 | - '0' 845 | - two 846 | - '1' 847 | - zero 848 | - 68: 849 | - 46: 850 | - '1' 851 | - two 852 | - '0' 853 | - two 854 | - '1' 855 | - zero 856 | - 50: 857 | - 79: 858 | - '1' 859 | - two 860 | - '0' 861 | - zero 862 | - 46: 863 | - '0' 864 | - two 865 | - '1' 866 | - two 867 | - 17: 868 | - '1' 869 | - two 870 | - 10: 871 | - 48: 872 | - '0' 873 | - two 874 | - 75: 875 | - '1' 876 | - two 877 | - '1' 878 | - two 879 | - 34: 880 | - 23: 881 | - '1' 882 | - zero 883 | - '1' 884 | - two 885 | - '1' 886 | - two 887 | - 8: 888 | - 71: 889 | - 73: 890 | - 55: 891 | - 33: 892 | - '0' 893 | - zero 894 | - '0' 895 | - two 896 | - 84: 897 | - 20: 898 | - '1' 899 | - zero 900 | - '0' 901 | - two 902 | - 41: 903 | - 55: 904 | - '1' 905 | - zero 906 | - '1' 907 | - zero 908 | - '0' 909 | - zero 910 | - 38: 911 | - 21: 912 | - '1' 913 | - zero 914 | - '0' 915 | - two 916 | - 93: 917 | - 5: 918 | - '1' 919 | - zero 920 | - '1' 921 | - zero 922 | - '1' 923 | - two 924 | - 44: 925 | - 7: 926 | - 84: 927 | - '0' 928 | - zero 929 | - 76: 930 | - 53: 931 | - '0' 932 | - two 933 | - '1' 934 | - two 935 | - '1' 936 | - two 937 | - '0' 938 | - zero 939 | - 71: 940 | - 82: 941 | - '1' 942 | - zero 943 | - '0' 944 | - zero 945 | - 9: 946 | - '0' 947 | - zero 948 | - 54: 949 | - '0' 950 | - two 951 | - 30: 952 | - '1' 953 | - two 954 | - '0' 955 | - two 956 | - 29: 957 | - 31: 958 | - 81: 959 | - 87: 960 | - 36: 961 | - '1' 962 | - two 963 | - '0' 964 | - two 965 | - 44: 966 | - 57: 967 | - '0' 968 | - two 969 | - '0' 970 | - two 971 | - 28: 972 | - 39: 973 | - 72: 974 | - '1' 975 | - two 976 | - '0' 977 | - two 978 | - '1' 979 | - zero 980 | - '1' 981 | - zero 982 | - 22: 983 | - 28: 984 | - '1' 985 | - zero 986 | - '0' 987 | - zero 988 | - '0' 989 | - two 990 | - 98: 991 | - 14: 992 | - '1' 993 | - zero 994 | - 55: 995 | - '1' 996 | - two 997 | - '1' 998 | - two 999 | - 59: 1000 | - 20: 1001 | - 36: 1002 | - 12: 1003 | - '1' 1004 | - two 1005 | - '1' 1006 | - two 1007 | - '1' 1008 | - zero 1009 | - 76: 1010 | - 16: 1011 | - '0' 1012 | - zero 1013 | - '1' 1014 | - two 1015 | - '1' 1016 | - zero 1017 | - 20: 1018 | - '1' 1019 | - two 1020 | - 14: 1021 | - 22: 1022 | - '0' 1023 | - two 1024 | - '1' 1025 | - two 1026 | - 37: 1027 | - '0' 1028 | - zero 1029 | - '0' 1030 | - zero 1031 | - 100: 1032 | - 97: 1033 | - 28: 1034 | - 22: 1035 | - '0' 1036 | - zero 1037 | - '1' 1038 | - two 1039 | - 20: 1040 | - '0' 1041 | - zero 1042 | - '1' 1043 | - two 1044 | - 98: 1045 | - 27: 1046 | - 13: 1047 | - '1' 1048 | - two 1049 | - '0' 1050 | - zero 1051 | - '0' 1052 | - two 1053 | - '0' 1054 | - zero 1055 | - 35: 1056 | - 33: 1057 | - '0' 1058 | - zero 1059 | - 30: 1060 | - '1' 1061 | - zero 1062 | - '0' 1063 | - zero 1064 | - 65: 1065 | - '1' 1066 | - two 1067 | - '1' 1068 | - zero 1069 | - 71: 1070 | - 41: 1071 | - 51: 1072 | - 15: 1073 | - '1' 1074 | - zero 1075 | - 98: 1076 | - '0' 1077 | - zero 1078 | - '1' 1079 | - two 1080 | - 33: 1081 | - 45: 1082 | - 76: 1083 | - 94: 1084 | - '0' 1085 | - two 1086 | - '0' 1087 | - zero 1088 | - '1' 1089 | - zero 1090 | - '1' 1091 | - zero 1092 | - 85: 1093 | - 4: 1094 | - '0' 1095 | - zero 1096 | - '1' 1097 | - two 1098 | - '0' 1099 | - two 1100 | - 8: 1101 | - 10: 1102 | - 19: 1103 | - 99: 1104 | - '1' 1105 | - zero 1106 | - '0' 1107 | - two 1108 | - '0' 1109 | - two 1110 | - '0' 1111 | - two 1112 | - 67: 1113 | - 93: 1114 | - 93: 1115 | - '0' 1116 | - two 1117 | - 83: 1118 | - '1' 1119 | - two 1120 | - 95: 1121 | - '0' 1122 | - zero 1123 | - '1' 1124 | - zero 1125 | - 29: 1126 | - 67: 1127 | - 20: 1128 | - '0' 1129 | - zero 1130 | - '1' 1131 | - two 1132 | - '1' 1133 | - zero 1134 | - 22: 1135 | - '0' 1136 | - zero 1137 | - '1' 1138 | - zero 1139 | - 46: 1140 | - 49: 1141 | - '1' 1142 | - zero 1143 | - '0' 1144 | - two 1145 | - '0' 1146 | - zero 1147 | - 8: 1148 | - 87: 1149 | - '0' 1150 | - two 1151 | - 81: 1152 | - '0' 1153 | - zero 1154 | - 5: 1155 | - 4: 1156 | - '1' 1157 | - two 1158 | - 13: 1159 | - '1' 1160 | - two 1161 | - '0' 1162 | - zero 1163 | - '0' 1164 | - zero 1165 | - 59: 1166 | - 73: 1167 | - '1' 1168 | - two 1169 | - '0' 1170 | - zero 1171 | - 9: 1172 | - '0' 1173 | - two 1174 | - '0' 1175 | -------------------------------------------------------------------------------- /spec/fixtures/regression/config.yml: -------------------------------------------------------------------------------- 1 | #Input files 2 | input: 3 | training: training.data 4 | testing: testing.data 5 | forest: random_forest.yml 6 | 7 | #Forest parameters 8 | forest: 9 | forest_size: 3 #how many trees 10 | SNP_sample_size_mtry: 60 #mtry 11 | SNP_total_count: 200 12 | node_min_size: 5 13 | -------------------------------------------------------------------------------- /spec/fixtures/regression/testing.data: -------------------------------------------------------------------------------- 1 | 801 1 2 0 1 1 2 2 1 1 0 2 0 0 1 1 0 2 0 0 0 0 2 2 1 0 1 2 0 1 1 0 1 1 1 0 2 2 2 0 2 1 1 2 2 1 1 0 2 2 0 1 2 0 0 1 1 2 2 1 1 0 2 2 2 0 2 2 0 2 1 2 0 2 0 1 0 2 1 1 2 2 2 1 1 0 1 1 1 0 1 0 0 1 2 2 0 2 1 0 0 1 1 2 2 2 2 1 0 0 1 2 1 0 1 1 0 1 1 0 0 1 0 2 0 2 1 1 0 2 2 0 1 0 0 1 0 2 0 1 2 2 1 1 0 0 1 1 0 0 1 1 0 0 1 1 2 1 0 0 0 2 2 0 2 2 0 0 2 0 2 2 0 2 0 0 0 0 2 2 0 0 2 2 2 0 1 2 1 0 1 0 2 2 1 1 1 1 0 0 1 2 | 802 0 1 0 2 1 1 2 1 2 0 1 0 1 2 1 0 2 0 0 0 2 0 2 1 1 0 0 2 2 0 1 2 1 1 2 2 2 0 0 1 2 1 1 2 2 1 1 2 1 0 2 1 0 0 2 2 1 1 1 0 0 1 1 2 0 2 1 2 0 2 0 0 2 0 1 0 1 2 1 2 2 2 0 2 1 2 0 2 1 2 0 1 0 1 1 0 1 0 1 0 2 0 2 0 0 1 0 1 0 1 0 2 1 2 2 2 0 2 0 0 0 0 2 0 2 0 1 0 2 2 0 1 2 0 0 1 2 1 2 1 2 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 0 0 1 2 0 0 2 2 1 0 2 0 1 2 1 2 1 1 0 1 2 2 0 2 1 2 2 0 0 2 1 1 0 0 1 1 1 1 2 0 0 1 2 3 | 803 1 1 0 1 0 1 1 1 2 0 2 0 0 1 1 0 2 0 0 0 2 0 2 0 0 1 2 1 1 1 2 1 2 1 0 2 2 1 0 1 2 2 0 2 1 1 1 2 1 1 2 1 1 0 0 1 2 2 1 1 0 1 1 2 1 1 2 2 0 2 0 0 1 0 1 0 0 1 1 2 2 2 2 1 0 2 0 2 0 0 0 1 1 1 1 0 1 1 0 1 1 1 2 1 1 0 0 0 0 1 0 2 2 1 2 1 0 1 0 1 1 1 2 1 1 1 1 0 1 0 0 0 1 0 1 2 2 2 1 1 2 2 2 1 1 2 0 0 1 1 1 0 0 2 0 2 0 1 0 1 2 1 0 2 2 1 1 1 0 2 2 2 1 1 2 1 1 2 2 0 1 1 2 2 0 1 2 0 1 0 0 2 2 1 2 1 1 1 1 1 4 | 804 0 0 0 2 0 1 2 1 2 0 1 1 1 0 0 0 1 0 0 0 2 0 1 0 0 0 2 1 2 0 0 1 1 1 1 2 2 1 0 1 2 2 1 1 2 2 0 1 1 0 2 1 0 1 1 1 2 1 0 0 0 1 1 2 0 1 1 2 1 1 2 0 1 0 2 0 1 2 1 2 2 1 1 1 0 2 1 1 1 2 0 0 0 1 2 0 0 1 0 1 1 1 1 2 1 1 1 1 0 1 0 0 1 2 1 1 2 2 0 0 1 2 2 0 0 0 1 0 2 0 0 0 2 0 0 0 2 1 2 0 2 2 2 1 1 2 1 0 0 1 1 0 1 1 0 1 1 0 0 1 2 0 0 1 2 0 0 1 0 2 2 1 1 0 0 1 1 1 0 0 2 2 2 2 2 1 2 2 0 1 0 1 1 2 2 1 1 1 1 2 5 | 805 0 0 0 2 0 1 2 1 2 0 1 0 0 1 1 0 2 1 0 1 2 1 1 0 0 0 2 0 1 1 2 1 1 1 1 2 2 1 0 2 2 2 2 1 1 1 0 2 0 1 1 0 1 1 0 1 2 2 2 1 0 1 1 1 0 2 1 2 0 2 0 1 0 0 2 1 0 1 1 2 2 2 0 0 0 2 2 1 0 2 0 1 0 1 2 1 1 1 0 1 1 0 1 1 2 2 1 0 0 2 1 2 0 2 2 1 2 1 0 1 0 2 2 0 1 1 1 0 2 2 1 0 2 0 0 1 2 1 2 2 0 2 0 1 1 1 1 1 0 1 0 0 0 1 0 2 0 0 0 2 1 1 1 2 1 0 1 2 0 2 2 1 2 2 0 1 2 2 1 1 0 2 2 2 1 0 1 1 0 1 1 1 1 1 1 1 2 0 1 1 6 | 806 1 1 0 2 1 1 2 2 1 0 2 0 1 2 1 0 2 0 0 0 0 1 1 0 0 1 2 0 1 0 2 0 0 0 2 1 2 0 0 2 2 2 1 2 1 1 1 2 0 0 2 1 1 0 2 1 1 2 1 2 0 2 2 2 1 2 0 2 0 2 2 0 2 0 2 0 1 1 1 2 2 2 0 1 1 2 0 0 1 2 0 1 0 2 1 0 0 0 0 2 2 0 1 1 2 1 0 0 0 1 1 1 1 2 1 2 1 1 1 0 0 2 2 0 2 1 2 0 2 2 0 0 1 1 0 1 2 1 2 0 2 1 2 0 1 1 1 0 0 2 1 0 0 0 2 2 1 2 1 0 2 0 0 2 1 0 0 2 0 2 1 2 2 2 1 0 0 1 1 0 0 1 2 2 2 0 1 1 0 2 0 1 2 1 1 1 1 1 0 2 7 | 807 1 1 0 2 2 0 2 2 2 0 2 0 0 2 1 0 2 1 0 0 1 1 1 1 0 0 1 0 2 0 0 1 2 2 0 2 2 0 0 1 2 2 1 2 2 0 0 2 0 0 2 2 0 0 1 2 1 2 0 0 0 1 2 2 1 2 2 2 1 2 2 1 2 0 1 0 0 1 1 1 2 1 0 2 1 1 1 2 0 0 0 1 0 0 0 0 1 0 0 1 2 0 1 0 1 1 0 1 0 0 1 0 0 2 1 1 0 2 0 1 0 2 2 0 1 0 1 0 2 0 1 0 2 0 1 2 1 1 2 1 1 2 2 0 1 1 0 1 1 1 1 0 1 1 0 2 0 0 0 0 2 0 0 1 1 1 0 2 1 2 1 1 2 1 1 0 1 1 2 1 0 2 2 2 1 0 1 2 1 0 0 1 1 1 2 0 1 1 1 2 8 | 808 0 1 0 2 1 1 2 2 2 0 1 0 0 1 1 1 2 1 0 0 1 0 1 1 0 1 2 1 2 0 0 1 1 1 0 2 2 0 0 2 1 2 0 2 2 1 0 2 1 0 2 1 1 0 1 2 2 2 1 1 0 2 2 2 0 2 1 2 1 2 2 0 0 0 2 0 1 1 0 1 2 2 0 0 0 1 0 2 0 0 0 2 0 0 0 0 1 1 0 1 2 0 2 1 2 0 0 1 0 0 0 1 2 2 1 2 2 1 0 0 0 2 1 1 2 0 2 0 2 2 0 0 1 0 0 1 1 1 2 2 2 1 2 1 1 1 0 1 1 0 1 0 0 1 1 2 0 1 0 0 1 0 0 1 2 0 2 1 0 1 2 1 2 1 0 1 1 2 0 1 1 2 2 2 0 0 2 0 1 1 0 0 0 1 1 1 0 1 0 1 9 | 809 2 0 0 2 1 2 2 2 2 0 1 0 0 2 1 0 2 0 0 1 0 1 2 1 0 1 1 0 2 0 1 1 0 0 2 2 1 1 0 2 1 1 1 1 2 2 0 2 0 0 1 0 1 0 1 1 0 2 0 1 0 1 1 2 0 2 2 2 0 1 2 0 0 0 1 0 0 0 0 2 2 1 1 1 0 2 0 2 1 1 0 1 1 1 1 0 1 1 0 1 1 0 2 1 2 0 0 0 0 2 0 2 1 1 0 2 0 2 0 1 0 2 2 0 0 0 0 0 2 0 0 0 2 0 0 0 2 1 2 0 1 1 2 1 2 2 1 0 2 0 1 0 0 1 1 2 0 0 0 0 2 1 0 2 2 0 0 0 0 0 2 2 2 1 0 0 1 2 2 0 1 2 2 2 2 0 1 1 1 0 0 1 2 1 1 1 0 1 0 1 10 | 810 0 2 1 2 1 2 2 1 1 0 1 1 1 0 1 0 2 0 0 0 2 0 1 2 1 0 2 0 2 1 0 1 0 1 1 2 2 0 0 2 1 1 1 2 2 0 1 2 0 1 2 2 1 0 1 1 2 2 2 1 0 2 1 2 0 2 1 2 1 2 0 0 2 0 2 0 1 0 2 2 2 2 0 1 0 1 2 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 2 0 1 0 0 0 0 0 1 1 2 2 2 2 1 1 1 0 1 2 2 0 0 0 2 0 2 0 0 1 2 0 0 1 1 1 1 1 0 1 2 0 0 2 1 0 1 1 1 0 0 0 0 2 0 1 0 1 2 1 0 2 1 2 0 1 0 2 2 1 2 1 1 2 1 1 1 1 0 1 2 2 1 0 2 1 0 0 0 2 1 2 0 1 0 2 0 2 11 | 811 0 1 1 2 1 1 2 1 2 0 2 1 0 2 1 0 2 1 0 1 0 2 1 1 0 1 1 0 2 0 1 1 0 2 1 2 2 1 1 2 2 2 2 1 0 1 0 1 2 0 2 0 0 1 1 1 2 2 1 0 0 2 1 2 1 2 1 1 0 2 1 1 1 0 2 0 1 1 2 2 2 2 0 2 1 1 1 1 1 2 0 2 2 2 0 0 2 0 0 1 2 0 2 2 2 1 1 1 0 1 1 2 2 2 1 2 1 1 0 1 0 1 2 0 1 1 2 0 2 1 0 0 1 0 0 0 2 0 2 1 2 1 2 1 0 2 1 0 2 0 2 0 0 1 0 2 1 1 0 0 2 0 0 1 2 0 1 0 0 2 2 2 2 0 0 1 1 1 2 1 1 2 1 2 1 0 2 1 1 1 1 1 2 2 2 0 1 0 1 1 12 | 812 1 1 0 2 2 2 2 2 2 0 2 0 0 2 2 0 2 0 0 0 2 1 1 0 0 2 2 1 2 0 1 1 1 1 1 2 1 0 0 2 2 1 1 1 1 2 0 2 0 0 2 1 1 0 2 1 1 2 0 1 0 1 2 2 1 1 1 2 1 2 2 0 2 0 1 0 1 0 2 1 2 1 0 2 1 1 0 1 1 1 0 0 1 1 0 0 1 0 0 0 2 0 2 2 2 2 0 1 0 0 1 2 1 2 2 1 2 2 0 1 2 2 2 0 1 1 1 0 2 0 0 0 1 0 1 0 2 2 2 1 1 1 2 1 1 1 2 0 1 0 1 0 0 0 0 2 1 0 0 1 2 1 1 2 2 0 1 2 0 2 2 1 1 1 0 2 0 2 0 2 1 2 2 2 1 0 1 1 1 0 1 2 2 2 1 0 1 1 1 2 13 | 813 1 2 1 2 1 0 1 2 2 0 2 1 1 1 1 0 2 1 0 0 1 1 2 1 0 0 1 2 2 0 0 2 0 2 0 2 2 1 0 2 2 2 1 2 2 2 0 2 1 1 2 2 2 1 1 2 2 2 1 1 0 2 0 2 0 2 2 1 0 1 1 0 2 0 2 0 1 1 0 1 2 2 1 2 0 2 1 2 0 0 0 0 0 2 0 0 1 0 0 1 2 2 1 1 1 1 1 0 0 0 1 1 1 1 2 2 1 1 0 1 1 1 2 0 1 0 2 0 2 1 0 1 2 0 0 1 2 0 2 2 2 0 0 0 2 1 2 2 0 2 0 0 0 1 0 2 0 2 1 0 2 0 0 2 2 0 0 1 1 1 1 2 2 2 1 1 1 1 1 0 0 2 2 2 2 0 1 0 0 2 0 1 2 2 1 0 1 1 0 0 14 | 814 0 1 1 2 0 2 2 2 1 0 2 0 0 2 1 1 2 0 0 1 1 1 0 1 0 0 2 0 2 0 1 1 1 1 0 2 2 0 0 2 2 1 1 1 2 1 0 2 0 0 1 1 1 0 0 2 2 1 1 0 0 1 2 2 0 2 1 2 0 1 0 0 0 0 2 0 0 0 1 1 1 2 1 2 0 1 1 2 0 1 0 1 0 2 0 0 1 0 0 1 1 1 1 0 2 1 0 0 0 0 1 2 2 1 2 1 0 2 0 0 0 2 2 0 2 1 2 0 2 1 0 0 2 0 0 1 2 0 2 1 1 2 2 0 1 1 2 1 1 1 2 0 0 1 0 2 0 2 0 0 1 1 0 1 1 1 1 2 0 1 1 1 2 2 0 1 1 1 2 1 1 2 2 2 1 0 1 1 2 1 0 0 2 1 2 2 1 1 1 1 15 | 815 1 0 0 2 1 1 2 1 2 0 1 0 0 2 1 0 2 1 0 0 0 1 1 0 0 1 2 0 2 1 1 0 1 2 1 1 1 0 0 2 2 0 0 2 1 2 0 2 1 1 1 2 0 0 1 2 1 2 1 2 0 2 1 2 0 2 1 2 0 2 1 1 1 0 0 0 1 0 1 1 2 1 1 2 0 2 0 1 1 0 0 2 0 1 1 0 0 0 0 0 1 1 2 0 1 2 0 1 0 0 1 2 2 2 1 0 0 1 1 2 0 1 2 0 0 0 2 0 2 2 0 1 2 0 1 0 2 1 2 0 2 2 2 1 0 1 2 0 1 1 0 0 0 1 0 2 0 1 0 1 2 0 0 2 1 2 1 2 0 2 1 1 1 2 1 0 1 1 2 1 1 0 2 2 0 0 2 1 1 2 0 2 2 2 1 2 1 0 0 1 16 | 816 0 0 0 2 0 2 2 2 2 0 1 0 1 2 1 1 1 0 0 1 2 2 2 1 1 1 1 0 1 1 0 0 1 1 2 2 2 2 0 2 2 1 1 1 1 1 0 2 1 1 2 1 1 0 1 0 1 2 2 1 0 2 1 2 0 2 1 1 0 2 1 1 0 0 1 1 1 0 0 1 1 2 0 1 1 2 0 1 0 1 0 1 1 1 0 0 1 1 0 1 1 1 2 0 2 0 0 0 1 0 0 1 0 2 2 2 0 0 0 0 1 2 1 1 0 0 1 0 1 1 0 1 2 0 0 2 1 1 2 0 1 2 1 1 1 2 1 0 2 0 1 0 0 1 1 2 1 0 0 1 1 0 1 0 2 0 1 1 0 2 2 2 1 2 1 0 0 1 1 0 2 1 2 2 0 0 1 2 1 2 1 2 1 1 2 1 1 0 1 2 17 | 817 0 1 1 1 1 2 2 1 2 0 2 0 0 2 0 0 2 0 0 1 2 2 2 1 0 0 1 1 2 0 1 1 0 2 0 1 2 0 0 1 2 2 1 2 1 1 0 2 0 0 2 2 0 1 2 2 2 1 1 1 0 1 1 2 0 1 1 1 0 2 1 1 1 0 1 0 1 1 0 1 1 2 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 1 0 2 2 2 2 1 2 0 0 0 0 0 0 2 1 1 2 2 2 2 1 1 0 1 2 0 0 0 2 0 2 0 0 0 0 1 0 0 1 1 0 1 1 1 1 2 1 1 2 0 1 0 0 0 0 1 0 2 0 0 0 1 2 1 2 1 1 0 0 1 0 1 2 1 2 2 1 0 1 2 1 0 0 2 2 2 0 0 1 1 1 1 0 2 2 2 2 0 1 1 0 2 18 | 818 0 1 0 2 0 2 2 1 2 0 0 2 0 1 1 0 2 0 0 1 1 2 1 0 0 1 1 0 2 0 0 1 0 1 1 2 2 1 1 2 2 0 1 1 1 2 0 2 1 0 2 2 1 1 1 1 1 1 1 0 0 1 2 2 1 1 2 2 0 2 1 0 0 0 1 0 2 2 2 1 1 2 1 1 2 2 1 2 1 0 0 1 1 0 0 0 2 1 0 1 2 2 2 0 2 1 0 1 0 1 0 1 1 1 2 1 1 2 0 0 0 2 2 0 0 0 2 0 2 2 0 0 1 1 0 2 2 0 2 1 2 1 2 1 1 2 1 2 0 0 0 0 1 1 2 1 0 0 0 0 1 0 0 1 2 0 0 2 0 2 2 2 1 2 2 1 1 1 1 2 1 2 2 1 1 0 2 1 1 1 0 2 2 1 0 1 1 1 1 2 19 | 819 1 1 0 2 1 1 2 2 2 0 1 0 0 2 1 0 2 0 0 0 1 1 2 1 0 1 2 0 1 1 0 1 0 0 1 1 2 0 1 2 2 1 1 2 2 1 0 2 2 0 2 2 0 0 2 1 2 1 0 0 0 1 1 2 1 1 1 2 0 2 0 0 0 0 2 0 0 0 1 1 2 1 1 0 1 1 2 0 2 1 0 2 0 1 0 1 0 2 0 1 2 0 1 1 2 1 1 0 0 0 0 2 0 2 2 1 1 1 0 1 1 2 2 0 0 0 1 0 2 2 0 0 2 0 0 1 2 0 2 1 0 1 1 0 2 1 1 1 1 1 1 0 0 2 0 2 2 0 0 0 2 0 0 1 1 0 1 2 0 2 1 2 2 2 0 1 1 2 1 1 0 2 2 2 0 1 2 0 0 0 0 2 1 2 2 0 0 1 1 2 20 | 820 1 1 1 2 1 2 2 2 2 0 2 0 0 2 1 0 1 0 0 1 2 1 1 1 1 0 2 0 2 1 0 1 0 1 1 2 2 1 0 2 2 2 0 2 2 1 1 2 1 0 2 1 1 0 1 0 2 2 1 2 0 1 2 2 2 2 1 1 1 2 1 0 1 0 2 0 0 0 1 1 1 1 2 1 1 2 0 1 1 2 1 2 1 2 0 0 0 0 0 2 2 0 2 0 0 1 0 1 1 1 0 1 1 2 2 2 2 2 1 1 0 2 0 0 0 1 2 0 2 0 0 0 1 0 0 0 1 2 1 2 1 1 2 1 1 1 2 1 2 1 1 0 0 2 0 1 1 0 0 1 2 0 0 1 2 0 1 2 1 1 2 1 2 2 1 0 1 0 2 0 0 1 2 2 0 0 1 1 1 0 0 2 2 2 2 0 1 0 0 2 21 | 821 1 1 0 2 0 1 2 0 2 0 2 1 1 2 0 1 0 2 1 0 2 1 2 2 1 0 1 1 2 1 1 1 0 1 1 2 2 1 0 2 2 2 0 1 1 1 0 2 1 0 2 1 0 0 2 1 2 2 1 0 0 1 0 2 0 2 1 1 1 2 1 0 2 0 1 0 1 0 1 1 1 2 0 2 0 2 0 1 1 0 0 0 1 1 0 0 0 1 0 1 1 0 2 0 2 0 0 0 0 1 1 2 0 2 1 2 1 1 0 1 2 2 2 0 1 0 2 0 2 1 0 0 1 0 0 0 1 1 2 1 1 2 2 0 1 1 2 1 0 1 0 0 0 2 0 2 2 1 0 2 1 1 1 1 2 0 0 1 0 2 2 2 2 0 0 2 1 1 1 1 1 2 2 2 2 0 2 1 0 2 0 2 2 2 1 2 1 1 0 1 22 | 822 1 1 0 1 1 0 2 2 2 0 2 2 1 2 1 1 1 0 0 0 0 2 2 0 0 1 1 1 1 1 2 1 0 1 1 2 2 1 1 2 2 0 2 2 1 1 0 2 0 1 1 1 2 1 2 1 2 0 1 0 0 2 1 2 0 2 2 2 1 2 2 2 2 0 2 0 1 2 1 1 2 2 1 1 1 2 2 1 0 2 0 1 0 2 1 0 1 1 0 0 1 2 2 0 2 1 2 0 0 1 0 2 1 2 2 1 1 2 0 0 1 2 2 0 1 0 2 0 2 2 0 0 0 1 0 0 1 1 2 0 2 1 1 0 0 1 1 1 2 1 0 0 0 2 1 2 1 1 0 0 2 0 0 1 2 0 1 2 0 1 2 2 2 0 0 0 1 1 1 1 1 2 2 2 2 0 2 1 0 1 0 2 2 0 1 1 0 1 0 0 23 | 823 0 2 0 1 1 2 2 1 2 0 0 1 0 2 2 0 2 0 0 1 2 0 2 1 1 0 2 0 2 0 2 0 0 1 1 2 2 2 1 2 2 2 1 2 2 1 1 2 1 0 2 0 0 2 2 2 0 2 1 2 0 1 1 2 0 2 2 1 1 2 2 0 0 0 2 0 0 1 1 2 2 2 0 1 0 1 2 1 1 1 0 2 0 1 1 0 1 0 0 0 1 1 2 0 0 1 1 0 0 1 0 1 0 2 1 2 2 2 0 1 1 1 2 0 1 0 1 0 2 1 0 0 1 0 0 1 2 1 2 1 1 2 2 2 1 0 1 1 1 1 1 0 0 2 1 2 0 2 0 0 2 0 0 2 2 0 1 1 0 2 2 2 0 2 1 1 0 1 0 0 0 2 2 2 1 0 2 2 2 0 1 1 2 1 2 2 1 0 0 2 24 | 824 0 2 0 1 1 2 2 2 2 0 2 0 0 2 1 0 2 1 0 1 1 1 1 1 0 0 1 0 2 0 1 1 1 0 0 1 2 2 0 2 2 1 0 2 2 1 0 2 2 0 2 2 0 1 1 1 1 0 0 2 0 2 0 2 0 2 2 2 0 2 2 1 1 0 1 0 2 1 2 1 1 2 0 2 0 2 0 1 1 2 0 1 1 2 1 0 0 0 0 2 2 0 1 0 1 2 0 0 0 1 1 1 0 2 1 1 0 0 2 1 0 1 2 0 0 0 2 0 1 1 0 0 2 0 0 2 1 1 2 2 2 1 2 0 0 1 1 1 0 0 1 0 0 2 0 2 0 1 0 1 2 0 0 2 2 0 0 2 0 2 2 2 2 1 1 0 1 1 0 0 0 2 2 2 1 0 1 2 1 0 0 2 2 0 1 2 0 0 0 1 25 | 825 1 1 0 2 1 1 2 0 2 0 1 2 0 1 0 0 0 0 0 1 2 0 2 0 1 0 1 0 2 0 1 1 0 1 1 2 2 0 2 2 2 0 2 0 1 0 1 2 1 1 2 2 1 0 0 2 2 2 0 1 0 1 1 2 0 2 1 2 0 2 1 1 2 0 2 0 1 2 1 2 2 2 0 1 0 2 1 2 1 1 0 1 1 1 1 1 0 0 0 0 0 1 2 1 2 1 0 0 0 0 1 1 2 2 1 2 0 1 0 1 1 2 2 0 0 0 1 0 2 2 0 0 2 0 0 2 2 1 2 0 2 2 1 1 1 2 1 1 0 0 1 0 1 0 0 2 0 1 0 0 1 0 0 1 1 1 1 1 0 2 2 1 2 0 0 0 2 0 1 1 0 1 2 2 0 0 1 1 1 1 1 1 2 1 1 1 0 1 0 0 26 | 826 0 2 1 2 0 0 1 1 2 0 1 1 0 2 0 0 2 0 0 0 1 2 1 1 1 1 2 0 2 1 2 2 1 1 1 1 2 1 0 2 2 2 1 0 2 1 0 2 2 1 2 2 1 0 2 2 2 1 0 2 0 2 1 2 0 2 2 2 0 2 0 2 0 0 2 0 1 0 0 2 2 2 0 2 0 2 0 2 0 1 0 2 2 1 1 0 1 0 0 2 0 0 2 1 2 1 0 0 0 0 0 2 0 2 1 1 0 0 0 0 0 1 2 0 1 1 2 0 2 1 0 0 2 1 1 1 2 1 2 1 2 2 2 1 0 2 2 1 2 1 1 0 0 1 0 2 1 2 0 0 1 0 0 2 1 0 0 2 0 1 2 2 2 2 0 1 1 1 0 0 1 2 2 2 2 1 2 0 1 0 0 1 2 1 2 0 1 1 0 2 27 | 827 0 2 0 1 0 1 2 1 2 0 2 1 0 1 2 0 1 0 0 1 1 1 1 1 1 1 0 0 2 0 2 0 0 0 1 2 2 0 0 2 2 1 1 2 1 0 0 1 1 0 1 2 1 1 1 2 1 2 1 0 0 2 2 2 0 1 0 2 1 2 2 0 1 0 1 0 0 0 2 2 2 1 0 1 1 2 2 2 0 2 0 0 1 0 1 0 1 0 0 0 0 0 2 1 2 2 0 0 0 0 0 2 1 2 2 1 1 2 0 1 1 2 2 0 0 0 2 0 2 2 0 0 1 0 0 1 1 2 2 1 1 1 1 1 1 1 1 2 1 1 1 0 0 1 0 2 0 1 0 0 2 0 1 1 2 1 0 1 0 2 2 2 2 2 0 1 0 0 0 0 2 1 2 2 1 0 2 0 1 1 0 2 2 2 2 2 1 2 0 1 28 | 828 1 1 0 2 2 1 2 1 2 0 1 0 1 2 1 0 2 0 0 0 1 1 2 2 1 2 2 0 1 0 2 1 0 0 1 2 1 1 1 1 2 1 1 1 1 1 0 1 1 0 2 0 1 1 1 2 1 1 1 1 1 1 2 2 0 2 2 2 1 2 0 0 2 0 2 0 1 1 2 1 2 0 1 2 0 2 1 2 0 1 0 2 0 2 0 0 0 0 0 1 1 0 1 1 2 1 0 1 1 2 0 1 1 1 2 0 1 0 0 1 0 2 1 1 1 0 2 0 2 1 1 0 0 0 0 1 2 1 1 1 1 1 1 0 1 2 2 1 0 0 1 1 1 2 0 2 1 2 0 1 2 0 0 2 1 0 0 2 0 2 1 2 2 1 0 2 1 2 1 0 0 2 2 2 0 0 2 0 1 0 0 1 1 1 2 1 0 0 0 2 29 | 829 1 1 0 1 1 1 2 2 1 0 1 0 0 1 1 0 1 1 0 1 0 2 2 1 1 1 2 0 2 0 2 2 0 0 2 2 2 0 1 2 2 1 1 2 1 1 1 1 1 1 2 2 2 0 1 1 0 1 0 0 0 2 1 2 0 2 2 2 0 2 0 0 1 0 2 0 0 2 1 2 1 2 1 1 0 1 1 2 0 0 0 1 2 1 1 0 0 0 0 2 0 1 2 1 2 2 2 0 0 0 1 1 1 1 1 2 1 2 0 1 1 2 2 0 0 0 2 0 2 1 0 0 2 0 0 0 2 2 2 2 1 1 1 0 0 1 1 0 1 1 1 0 0 1 0 1 0 2 0 0 1 1 0 0 1 0 1 2 1 2 1 1 1 2 0 0 1 2 0 2 1 2 2 2 2 0 2 1 2 0 0 2 2 2 2 1 1 1 0 2 30 | 830 1 1 0 1 1 2 2 1 2 0 2 1 0 2 0 0 2 0 0 0 2 1 1 2 2 0 2 0 2 1 1 0 1 0 1 2 2 1 1 2 2 2 0 2 1 1 1 2 1 0 1 1 1 0 1 0 2 1 0 1 0 1 1 2 0 2 1 2 0 1 0 0 1 0 1 0 1 1 0 1 1 2 0 0 1 1 1 1 0 1 0 0 0 1 0 0 0 0 1 0 2 0 2 0 1 0 0 1 0 0 0 2 1 2 2 2 1 2 0 1 0 2 2 1 0 1 1 0 2 1 0 1 2 0 0 1 2 0 2 0 2 1 1 0 1 1 1 0 2 1 0 0 0 0 1 2 0 2 0 0 1 0 0 2 2 0 0 1 0 2 2 2 2 2 1 0 0 0 1 0 1 2 2 2 1 0 2 1 0 1 0 1 1 1 2 1 1 0 0 2 31 | 831 1 1 0 2 2 1 2 2 2 0 2 1 2 1 2 0 2 0 1 1 1 0 2 0 0 1 2 0 1 0 1 0 0 0 1 2 2 0 2 1 1 0 0 2 1 0 1 2 2 0 1 2 0 0 1 2 2 2 1 0 0 2 2 2 1 1 2 2 1 2 2 1 2 1 0 0 1 1 1 1 1 2 2 1 0 2 1 2 1 2 0 0 1 1 1 0 1 0 0 1 1 1 2 0 1 0 1 0 0 1 1 2 1 1 1 0 2 1 0 1 1 1 2 0 1 1 2 0 2 1 0 0 1 1 1 1 2 1 2 0 2 1 1 0 2 1 0 1 0 2 0 0 0 1 1 1 1 1 0 0 2 0 0 1 2 0 1 1 0 2 2 0 2 1 0 2 1 1 1 1 1 1 2 2 1 1 2 1 2 1 0 2 2 2 1 1 1 2 0 1 32 | 832 1 1 1 2 2 1 1 1 2 0 2 1 0 2 1 0 2 0 0 0 0 0 2 0 1 2 2 1 2 1 0 2 0 1 0 2 2 1 1 2 2 1 0 1 1 1 0 2 1 0 1 0 1 1 1 0 1 2 1 1 0 1 1 2 0 2 2 2 0 1 1 1 2 0 1 1 1 0 1 1 0 2 0 1 1 2 1 1 0 0 0 0 1 0 1 0 1 1 0 1 1 0 2 2 1 0 0 0 0 1 0 1 1 1 2 1 0 1 0 1 1 1 2 0 0 0 2 0 2 0 0 0 1 0 0 2 1 0 1 0 1 2 2 1 0 0 1 1 0 1 0 0 0 2 0 2 0 1 1 0 0 0 0 2 1 0 0 1 0 1 2 2 2 1 0 0 1 1 0 1 0 1 2 2 0 0 2 1 1 0 0 2 2 0 1 2 0 0 1 2 33 | 833 1 1 0 1 1 1 2 0 2 0 1 1 0 1 1 0 2 0 0 1 0 1 1 1 0 2 1 0 1 1 1 0 1 2 2 2 2 1 0 2 2 2 1 2 1 0 0 2 1 1 1 1 1 1 1 1 2 1 0 1 0 2 2 2 1 2 1 2 0 1 2 0 0 0 2 0 1 0 0 1 2 1 0 1 0 2 1 1 0 2 0 2 1 0 1 0 2 0 0 0 1 0 1 0 1 0 0 1 1 1 0 2 0 1 1 1 0 1 1 0 1 1 2 0 1 0 2 0 2 1 0 0 0 1 0 2 2 2 2 1 1 1 2 0 1 1 1 1 0 1 1 0 0 0 1 2 2 0 0 0 1 1 1 2 2 1 1 2 0 2 2 2 1 1 0 1 1 0 1 1 1 1 1 2 1 0 2 1 0 1 0 2 2 2 2 1 1 2 0 2 34 | 834 0 2 1 1 0 1 2 2 1 0 1 1 0 1 1 0 2 0 0 0 0 1 1 1 0 0 1 0 2 0 2 0 1 0 2 2 2 0 0 0 2 2 2 2 2 0 0 2 1 1 2 0 1 0 2 2 1 2 1 0 0 0 1 2 1 1 1 2 0 2 1 1 2 0 1 0 0 0 2 0 2 2 1 2 0 2 0 1 0 1 0 1 0 0 0 0 0 1 0 2 0 2 2 2 2 0 1 2 0 1 1 2 0 2 1 1 0 2 0 0 0 1 1 0 0 1 2 0 2 2 0 1 0 1 0 1 2 2 2 1 2 2 1 0 0 0 0 0 1 0 1 0 0 0 2 2 0 0 0 0 1 1 1 0 2 0 2 1 0 2 2 0 2 2 0 1 1 2 2 2 0 2 2 2 1 0 2 1 1 0 0 1 1 2 2 1 0 0 1 1 35 | 835 0 1 1 0 1 0 1 0 2 0 2 1 0 1 1 0 2 0 0 1 2 1 1 1 0 1 2 0 2 0 2 0 0 0 0 2 2 1 0 1 2 2 1 1 1 1 0 1 1 0 0 2 2 1 2 1 2 1 1 1 0 0 2 2 0 2 2 2 0 2 2 1 1 0 2 0 0 0 1 1 2 2 0 2 1 2 1 2 0 0 0 2 0 0 1 0 0 1 0 1 2 0 2 0 1 1 1 1 0 0 0 2 2 2 2 1 0 2 0 2 0 1 1 0 0 0 2 0 1 1 0 0 1 1 1 2 1 0 1 0 2 1 2 0 0 2 1 0 0 1 1 0 1 1 0 2 0 1 0 1 2 0 0 2 1 0 1 2 0 2 2 2 2 1 1 0 1 2 1 0 0 2 2 2 1 1 2 1 0 0 0 2 2 2 2 1 1 2 2 2 36 | 836 1 0 1 2 1 1 2 2 2 0 1 0 2 2 1 0 2 1 0 1 2 2 2 0 0 0 2 0 2 2 0 0 1 0 2 2 2 1 1 1 2 1 0 1 0 2 0 2 0 0 2 2 0 0 1 2 0 2 1 2 0 2 1 2 0 2 1 2 0 2 2 0 1 0 1 1 1 1 1 1 2 2 1 2 0 1 1 2 0 2 0 2 1 2 0 0 1 0 0 1 1 1 2 1 1 1 0 1 1 1 1 2 0 2 2 1 1 2 1 1 0 2 2 0 0 1 1 0 2 2 1 0 2 0 1 1 1 1 2 0 1 2 1 0 0 1 1 1 2 0 1 0 0 0 0 2 0 1 0 1 2 0 1 2 1 0 0 2 0 2 2 2 0 1 2 1 0 1 2 2 1 2 2 2 1 0 0 0 1 0 0 2 1 2 2 1 1 1 1 2 37 | 837 0 1 1 2 1 2 2 1 2 0 0 2 1 2 0 1 1 0 0 1 0 0 1 0 0 0 2 0 2 1 1 0 1 0 0 2 2 0 0 1 2 1 1 2 1 0 0 2 2 1 2 0 1 0 1 2 1 2 1 0 0 2 0 2 0 2 2 2 1 2 1 1 1 0 1 0 0 0 1 1 1 2 1 1 0 2 1 1 1 0 0 2 0 0 1 0 0 1 0 1 2 0 2 0 1 1 1 1 0 2 0 2 0 1 2 2 1 2 0 1 0 2 1 0 0 1 2 0 2 1 0 0 0 1 0 0 2 0 2 1 1 1 0 1 0 1 0 0 0 2 0 0 0 1 0 2 1 1 0 0 2 0 0 2 2 0 1 2 0 1 2 1 2 2 0 1 2 1 1 0 1 2 2 2 1 0 2 0 0 0 0 2 1 2 1 1 1 2 0 1 38 | 838 0 2 0 2 2 2 2 2 1 0 1 1 0 1 2 0 2 0 0 1 1 2 1 1 0 1 2 0 2 1 0 1 1 2 0 2 2 1 0 2 2 1 1 1 1 0 0 2 1 0 2 1 1 1 0 1 0 2 1 1 0 1 2 2 0 2 2 1 0 2 0 1 2 0 1 0 0 1 1 2 2 2 2 0 0 1 0 2 1 2 0 2 2 1 0 0 1 0 0 0 0 0 2 0 1 1 1 1 0 1 0 2 1 2 1 1 0 1 0 0 0 1 2 0 0 0 0 0 2 1 0 0 0 0 0 1 2 1 2 1 1 2 2 0 0 1 2 2 1 1 0 0 0 2 0 2 1 1 0 1 0 0 1 2 1 0 0 2 2 2 2 2 2 1 0 0 1 1 0 2 0 2 2 2 0 0 2 1 1 0 0 2 2 2 1 1 1 0 0 2 39 | 839 0 2 1 2 1 2 2 1 2 0 2 0 0 1 2 0 2 0 0 1 1 2 1 0 0 0 1 1 2 0 1 0 0 1 1 2 2 1 0 1 2 1 1 0 2 1 0 2 1 1 1 1 2 0 2 1 1 1 0 2 0 0 2 2 0 2 1 2 0 2 1 0 2 0 2 1 2 1 0 1 2 1 0 2 1 2 0 2 1 1 0 2 1 0 2 0 1 0 0 0 1 1 2 0 1 1 0 0 0 0 1 2 2 2 1 0 0 2 0 0 0 2 2 0 0 0 2 0 2 1 0 0 1 0 0 1 2 1 2 1 2 2 1 1 1 0 0 1 2 2 1 0 0 1 0 2 0 0 0 1 2 0 0 1 2 0 0 2 0 2 1 2 2 2 0 1 2 2 1 0 0 2 2 2 0 0 1 1 1 0 1 1 1 2 1 1 1 1 1 2 40 | 840 0 1 0 2 2 1 2 0 2 0 0 0 0 2 0 0 2 1 0 1 1 1 1 0 1 2 1 0 2 0 1 2 0 0 0 2 2 1 0 1 2 1 2 2 1 0 0 2 1 0 1 1 0 0 2 1 2 2 0 1 0 1 2 2 0 1 1 2 1 2 2 0 1 0 1 1 1 2 0 2 2 2 0 1 0 2 0 1 1 0 1 1 1 1 0 0 0 0 0 0 0 1 2 0 1 1 2 0 0 1 0 2 2 2 0 1 2 2 0 2 0 1 2 0 0 1 2 0 2 1 0 0 1 0 0 0 2 2 2 1 0 0 2 2 0 1 1 0 0 1 2 0 0 1 1 2 0 1 1 0 2 0 2 1 2 0 1 1 0 2 1 1 1 1 0 1 2 2 1 0 0 2 2 2 0 1 1 1 0 0 0 0 1 0 2 1 0 1 1 2 41 | 841 1 2 0 1 1 1 2 0 2 0 2 0 0 1 1 1 2 0 0 0 2 0 1 0 0 0 1 0 2 0 1 1 0 0 1 2 2 0 1 2 1 2 1 2 2 0 0 2 1 0 0 0 0 0 1 1 1 1 1 1 0 0 2 2 0 2 2 2 0 2 2 0 1 0 1 0 1 1 1 2 0 2 1 2 0 2 1 2 0 0 0 1 1 0 0 1 0 1 0 1 1 0 1 1 2 0 0 1 0 0 1 2 1 1 0 2 0 2 0 0 0 1 2 0 2 0 2 0 2 1 0 1 0 0 0 1 1 2 1 1 0 1 2 0 0 2 1 1 1 0 2 0 0 2 0 2 0 0 0 0 2 1 0 2 2 1 0 1 0 2 2 1 1 1 1 1 1 2 1 0 1 1 2 2 2 0 2 1 0 1 1 2 2 1 2 1 1 1 0 2 42 | 842 0 1 0 2 1 0 2 0 2 0 2 1 0 2 1 0 1 0 0 1 1 1 2 0 0 1 1 0 1 1 1 0 1 2 1 2 1 0 0 2 2 1 1 1 2 1 0 2 1 0 2 0 1 0 1 1 1 2 0 0 0 1 2 2 0 2 1 2 0 1 1 2 1 0 2 0 1 0 0 2 2 2 1 1 1 2 0 2 0 1 0 2 0 1 1 0 0 1 0 1 1 2 2 1 1 0 1 2 0 0 2 2 2 2 1 1 2 2 0 2 0 1 2 0 0 0 2 0 2 1 0 0 1 1 1 0 2 0 2 0 1 2 1 0 0 2 1 1 0 0 1 0 0 1 0 2 1 0 0 1 1 0 0 2 1 0 1 2 0 2 2 2 2 2 0 1 1 2 1 1 0 1 2 2 1 1 2 1 2 1 1 2 0 1 2 0 1 1 0 1 43 | 843 0 1 0 2 1 2 2 1 2 0 2 1 1 2 1 1 1 0 0 0 1 0 1 0 0 1 1 1 1 2 2 1 1 2 0 2 2 2 0 2 2 2 1 2 1 1 0 2 1 0 2 0 1 0 1 1 2 2 1 1 0 0 2 1 0 2 2 2 1 2 2 1 1 0 2 0 2 0 2 1 2 1 1 0 0 2 1 1 0 1 0 1 0 0 2 0 1 0 0 0 1 0 2 1 1 1 1 1 0 0 0 2 1 2 1 1 1 2 0 2 1 1 2 0 2 0 1 0 2 1 0 0 2 0 1 0 2 0 2 0 0 2 1 0 0 1 1 1 0 0 1 0 0 1 1 2 1 0 0 0 2 0 1 2 1 0 0 2 1 2 2 2 2 1 2 1 2 1 0 2 1 1 2 2 2 1 2 1 0 0 0 1 1 1 2 2 2 1 0 2 44 | 844 0 2 0 1 1 0 1 1 2 0 2 0 0 1 0 0 2 0 0 0 0 0 2 1 1 2 2 0 2 1 0 0 1 2 0 2 2 1 0 2 2 1 2 2 1 0 0 2 0 0 2 0 1 1 0 0 2 2 2 2 0 2 2 2 1 2 2 2 1 2 2 1 2 0 1 0 0 0 2 1 1 2 1 2 0 2 0 1 0 2 0 1 0 1 0 0 2 0 0 0 1 1 2 0 1 1 0 0 0 0 1 2 1 2 2 2 1 1 0 1 1 2 2 0 0 0 1 0 2 1 0 1 2 0 0 2 2 1 1 0 1 1 2 0 0 0 1 1 1 2 1 0 0 2 0 2 0 2 0 0 1 1 0 0 2 0 1 2 0 1 2 1 2 1 1 0 1 0 2 1 0 2 2 2 1 0 2 1 2 1 0 2 2 2 2 2 0 0 0 2 45 | 845 0 2 0 2 0 0 2 0 2 0 1 0 0 2 1 0 2 0 0 0 1 0 1 2 1 0 2 0 2 0 0 0 2 0 2 2 2 1 1 2 2 0 2 2 1 1 0 2 0 0 2 1 1 0 2 1 2 2 2 1 0 1 1 2 0 2 0 1 1 2 1 0 2 0 1 0 2 0 1 1 2 2 0 2 0 2 0 1 2 2 0 2 0 0 1 0 0 1 0 1 2 1 2 1 2 0 0 0 0 0 1 2 1 1 2 1 1 2 1 1 1 1 2 0 0 1 1 0 2 1 0 0 2 1 0 1 2 1 1 0 2 0 2 1 2 0 0 0 1 1 1 0 1 0 1 2 0 2 0 0 1 1 0 2 2 0 1 1 0 2 1 0 1 1 1 0 1 1 1 0 0 2 2 2 1 0 0 1 2 0 1 1 2 1 2 1 1 2 0 1 46 | 846 0 0 0 2 1 2 2 1 2 0 1 0 0 1 2 0 0 0 0 0 1 2 2 0 0 1 2 0 2 0 2 0 0 2 1 2 2 1 0 2 2 1 1 1 2 2 0 2 0 0 2 0 0 0 2 1 0 2 0 1 0 0 2 2 0 2 1 2 1 1 2 0 2 0 1 0 0 1 1 0 2 2 1 2 0 1 0 2 1 0 0 0 0 2 1 0 0 0 1 0 1 1 2 0 2 0 2 0 0 1 0 2 1 1 1 1 1 2 0 1 0 2 2 0 0 1 2 0 2 2 0 0 1 0 0 0 2 1 1 1 1 2 2 0 0 1 1 0 1 0 0 0 1 1 0 2 0 0 0 0 2 0 0 2 2 0 1 1 0 1 2 2 2 1 0 0 0 2 1 0 2 2 2 2 2 0 2 0 0 1 0 2 1 1 2 0 0 0 1 1 47 | 847 1 0 0 2 1 0 2 1 2 0 2 1 0 1 0 1 2 0 0 0 0 0 1 2 1 0 1 0 1 0 0 1 1 0 0 2 2 0 1 2 2 2 0 2 2 0 0 2 1 0 2 1 0 0 2 2 2 2 1 1 0 0 2 2 0 1 1 0 2 2 1 0 1 0 1 1 1 1 1 2 2 2 0 2 0 1 1 1 0 2 0 2 0 0 1 0 1 0 0 0 2 0 2 0 1 2 0 0 0 0 0 0 2 1 2 1 2 1 0 1 0 1 2 0 0 1 1 0 2 1 0 1 1 1 0 0 1 1 2 0 2 1 1 1 1 2 1 1 0 0 0 0 0 1 1 2 1 0 0 1 2 0 0 2 1 0 0 2 0 2 2 1 2 2 1 1 1 0 2 1 2 2 1 2 0 0 2 1 1 1 0 1 2 1 2 1 0 0 1 2 48 | 848 0 2 0 1 2 2 2 1 2 0 2 0 0 2 1 0 2 0 0 1 2 0 2 1 0 1 0 0 0 0 1 1 1 1 1 2 2 0 0 2 2 2 2 1 1 1 0 2 1 1 2 2 1 1 0 1 1 2 0 1 0 1 1 2 0 1 1 2 0 2 0 0 0 0 1 1 1 2 1 1 2 2 0 1 0 1 1 1 0 2 1 2 1 1 1 0 1 0 0 0 2 0 2 0 2 0 1 1 0 2 0 1 0 2 2 1 2 1 1 1 1 0 2 0 1 0 1 0 2 1 0 0 1 0 0 0 1 2 1 0 2 2 0 0 0 0 1 1 0 0 1 0 0 2 0 1 0 1 0 0 2 0 0 1 2 0 0 2 0 2 1 1 2 2 0 0 1 0 2 1 0 1 2 2 0 1 2 0 2 1 1 1 2 2 2 1 2 1 0 2 49 | 849 1 2 0 2 1 1 2 1 1 0 2 2 0 2 2 0 1 0 0 1 1 0 1 1 0 1 0 0 2 1 0 1 2 1 1 2 2 1 1 1 2 1 0 2 2 1 1 2 0 0 2 2 0 0 2 1 1 2 1 1 0 2 2 2 0 1 1 1 0 2 1 1 2 0 2 0 2 0 1 2 2 1 0 2 0 2 2 1 1 0 0 2 0 2 0 0 1 1 0 0 1 0 2 0 1 0 0 0 0 0 2 2 2 2 1 1 1 1 0 2 0 1 2 0 0 0 2 0 2 0 0 0 1 0 0 0 2 1 2 1 1 2 1 2 0 1 1 0 0 1 0 0 0 1 0 2 1 0 0 1 2 0 0 2 2 0 0 2 0 2 2 1 1 2 0 0 1 1 0 0 1 2 2 1 0 1 1 2 1 0 1 2 2 2 2 0 2 1 0 1 50 | 850 1 1 0 2 2 1 1 0 2 0 0 1 0 2 1 0 2 0 0 0 1 1 2 0 0 0 1 0 2 1 0 0 0 0 0 2 2 1 1 2 1 1 1 1 2 2 0 2 0 0 2 2 0 0 1 0 1 2 0 1 0 1 0 2 0 1 1 2 0 2 0 0 2 0 2 0 1 1 2 2 2 2 0 0 0 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 2 0 2 1 1 0 0 0 0 0 1 1 1 2 0 0 1 1 0 0 0 1 2 0 1 0 1 0 1 1 0 0 0 1 1 0 2 1 2 0 1 1 1 1 0 0 2 1 1 0 0 0 0 0 0 2 0 1 0 0 2 1 0 1 2 0 0 1 0 1 2 1 1 1 1 1 1 1 0 2 1 2 2 2 0 0 2 0 1 1 1 2 2 1 2 2 0 2 0 2 51 | 851 1 1 0 1 2 0 1 1 2 0 0 1 0 2 0 0 2 0 0 0 2 0 2 0 0 0 1 1 0 1 1 0 0 1 1 2 2 2 0 1 2 2 1 2 1 0 1 2 1 0 2 2 0 0 2 2 1 2 2 2 0 1 0 2 1 2 1 2 2 2 1 0 1 0 2 0 1 1 1 2 2 2 1 1 1 2 1 1 0 1 0 2 1 0 0 0 0 0 0 1 2 0 1 0 1 0 1 1 0 0 0 2 1 2 2 1 0 1 0 2 0 1 2 0 1 0 1 0 2 1 0 0 1 0 0 0 1 0 2 0 0 1 2 0 1 2 0 0 1 0 1 0 0 1 0 1 0 1 0 0 2 0 0 2 1 0 1 1 1 2 0 1 1 2 0 2 1 2 0 0 0 2 2 2 1 0 2 1 0 0 0 2 2 0 2 2 2 0 1 1 52 | 852 0 2 1 2 0 0 2 1 2 0 2 0 1 2 1 0 2 2 0 1 2 1 2 0 1 1 1 0 1 1 1 1 0 1 0 2 1 1 0 2 2 1 1 2 1 1 0 2 2 0 1 2 0 0 0 0 2 1 2 1 0 0 1 1 0 1 2 1 0 1 1 1 2 0 2 0 0 0 0 1 1 2 0 2 1 2 0 2 0 0 0 0 1 1 1 0 0 0 0 0 2 2 2 0 1 0 1 1 0 1 1 1 1 2 2 1 1 1 2 1 0 2 2 0 1 0 1 0 2 1 0 1 2 1 1 0 2 1 2 0 1 0 1 0 1 0 0 0 0 1 1 0 0 2 0 1 0 0 1 1 1 0 0 2 1 0 0 2 1 2 2 2 1 1 0 2 1 0 0 0 2 2 2 2 2 0 2 2 1 0 0 1 1 2 2 0 1 1 0 2 53 | 853 0 1 1 2 1 1 2 1 2 0 1 0 1 2 2 0 2 0 0 1 2 0 2 1 0 0 1 0 1 0 1 1 1 2 1 2 2 1 0 2 2 2 1 1 0 1 0 2 1 0 0 0 1 1 1 1 2 2 2 0 0 2 2 2 0 2 0 2 0 2 1 1 1 0 2 0 1 1 0 2 2 1 1 0 0 2 0 1 0 1 0 1 1 1 0 0 1 0 0 0 2 1 1 0 1 2 0 1 0 0 1 2 1 1 1 1 1 1 0 1 0 1 2 0 1 0 2 0 2 1 0 0 1 0 0 0 2 0 1 0 1 2 2 1 0 1 1 2 0 0 2 0 0 2 0 2 0 2 0 1 2 0 0 2 2 0 0 2 0 1 2 2 1 1 2 1 1 1 0 1 1 1 2 2 1 0 2 1 1 1 1 1 1 0 1 1 1 1 1 1 54 | 854 0 1 0 1 1 1 2 1 2 0 2 0 0 1 1 0 2 0 0 1 2 1 1 1 0 0 1 0 2 0 1 0 1 1 0 2 2 1 0 2 2 2 2 1 2 0 1 2 0 0 2 0 0 0 0 2 1 2 2 1 0 1 1 2 0 2 2 2 2 2 1 0 1 0 1 0 1 0 0 1 2 2 0 2 0 2 1 2 1 2 0 1 1 2 1 0 0 1 0 0 2 1 2 0 2 1 0 0 0 1 1 1 1 1 2 2 1 1 0 0 0 2 1 0 1 0 2 0 2 2 0 0 1 1 0 1 2 1 2 1 1 2 2 0 2 1 1 1 0 1 0 0 0 1 1 2 0 0 0 0 2 0 0 2 1 0 1 2 0 1 2 0 1 1 2 1 1 1 2 2 0 1 2 2 1 0 2 0 2 0 1 2 2 0 2 2 0 0 0 2 55 | 855 0 1 0 1 0 1 2 2 2 0 2 0 1 1 1 0 2 0 1 0 2 0 1 1 0 1 0 0 2 0 0 0 0 0 1 2 2 0 0 2 2 2 0 2 0 1 1 2 2 0 0 2 1 0 1 2 2 2 1 1 0 1 1 2 0 1 2 1 0 2 1 0 1 0 2 0 1 1 1 2 2 2 2 2 1 2 0 1 0 2 0 2 2 0 2 0 0 0 0 0 0 0 2 1 1 1 0 1 0 1 1 2 2 1 1 1 2 1 1 0 0 1 2 0 0 0 0 0 2 2 0 0 2 2 1 0 2 2 2 1 2 1 2 1 1 2 2 0 0 1 0 0 0 0 0 2 1 1 0 1 1 0 0 2 1 1 0 1 0 1 2 1 1 0 2 0 0 1 2 1 0 1 1 2 1 0 1 1 2 1 0 2 1 1 1 1 0 1 1 1 56 | 856 0 2 1 2 2 1 2 2 2 0 1 0 0 1 2 0 2 1 0 0 2 1 0 1 0 1 1 0 2 0 1 0 0 2 1 2 2 0 0 2 2 2 0 2 0 1 0 2 1 1 2 0 0 1 1 1 2 2 2 1 0 2 1 2 0 2 1 2 0 1 0 1 1 0 2 0 1 0 1 1 2 2 0 1 1 2 1 1 0 1 0 0 1 1 2 0 2 0 0 0 2 1 2 0 2 2 0 0 0 0 1 2 2 1 1 0 1 2 0 1 0 1 2 0 0 1 2 0 2 1 0 0 1 0 0 1 1 2 2 0 1 0 1 1 1 1 2 1 0 0 2 0 0 1 0 2 1 2 0 1 2 1 0 2 2 0 2 0 1 2 1 2 1 2 0 1 0 2 0 0 1 2 2 2 1 0 2 1 2 0 1 1 2 2 1 1 1 1 2 2 57 | 857 1 2 0 2 1 1 2 2 2 0 2 0 0 1 2 1 2 0 0 0 1 1 1 2 0 0 0 0 0 1 0 1 0 0 1 2 2 0 0 0 2 2 2 1 2 0 0 2 0 1 2 1 0 0 1 0 2 1 0 1 0 1 2 2 0 1 2 2 0 2 1 0 1 0 2 0 0 2 0 1 2 2 0 0 1 2 0 2 0 1 0 0 1 1 1 0 0 0 0 2 1 0 1 0 1 2 1 1 0 0 0 2 1 2 2 2 2 2 0 1 0 2 2 0 1 0 1 0 2 0 0 0 2 0 0 1 2 1 2 1 1 2 1 1 1 1 1 1 0 2 0 0 0 1 0 1 0 2 0 1 1 1 1 2 2 1 0 2 0 1 2 0 2 2 0 1 1 1 1 1 0 2 2 2 1 0 2 1 0 1 2 2 2 2 1 1 0 0 0 1 58 | 858 0 2 0 2 2 1 2 1 2 0 1 0 0 1 1 0 1 0 0 0 1 0 1 0 0 0 1 0 2 1 2 0 1 1 1 1 1 0 0 2 2 2 1 1 1 1 0 2 0 1 2 0 2 0 1 0 1 2 2 2 0 0 0 2 1 2 2 2 0 2 1 0 0 0 1 0 1 0 1 2 2 1 1 2 0 2 2 2 1 1 0 2 0 0 2 0 1 1 0 1 1 1 2 2 1 2 0 0 0 1 1 2 0 2 1 2 1 1 0 1 1 1 1 0 1 0 2 0 2 1 1 0 2 0 0 0 1 1 2 1 2 0 0 0 0 1 1 1 0 1 0 0 0 1 0 2 0 2 0 0 2 0 0 2 0 1 2 2 0 1 2 2 2 2 0 1 2 2 2 0 1 2 2 2 1 1 2 0 1 0 0 2 2 2 1 0 0 0 0 2 59 | 859 1 1 1 2 1 2 2 2 2 0 2 0 0 2 1 0 2 0 0 1 0 1 2 1 0 0 2 1 2 0 0 0 0 1 2 2 1 0 0 1 2 2 1 0 1 1 0 2 0 0 2 1 0 0 0 1 1 2 2 0 1 2 2 2 0 1 1 2 1 2 0 0 2 0 1 0 0 0 1 1 2 2 1 0 0 2 0 1 0 1 0 2 0 0 1 1 1 0 0 0 0 0 2 0 2 2 0 2 0 1 1 2 2 2 1 1 2 2 0 1 1 1 2 0 1 0 2 0 2 0 0 0 1 0 0 1 2 1 2 1 1 2 2 1 0 1 0 2 2 1 1 0 0 2 0 2 0 1 0 0 1 0 0 2 2 0 0 1 0 1 2 1 1 1 1 0 2 1 1 2 2 2 2 2 2 0 2 1 0 1 0 2 1 1 2 0 1 1 0 1 60 | 860 1 1 0 2 1 2 2 2 2 0 2 1 0 1 1 0 2 1 0 0 1 0 1 0 1 1 2 0 1 0 1 0 1 0 0 2 2 0 0 2 2 1 0 2 2 0 0 1 0 0 2 1 0 0 1 1 2 2 0 1 1 1 2 2 1 2 1 2 0 2 2 1 1 0 1 0 1 2 1 2 2 2 0 2 0 2 0 2 0 1 0 1 0 1 0 0 0 0 1 1 1 2 1 2 1 0 0 0 0 0 0 2 1 2 0 1 1 2 0 1 1 2 1 0 1 0 2 0 2 1 0 0 2 1 1 0 2 1 2 1 0 2 0 1 0 1 2 1 1 2 1 0 0 1 0 2 1 1 0 1 1 2 1 2 2 0 1 2 1 2 2 2 1 1 1 1 1 1 2 0 0 2 2 2 0 0 1 0 1 1 1 2 1 2 2 1 1 1 0 0 61 | 861 1 1 0 2 2 1 2 0 2 0 1 0 1 1 1 0 2 1 1 1 0 1 1 0 0 0 1 0 1 0 0 0 0 2 1 2 2 0 0 2 2 0 2 2 2 0 0 2 2 0 2 1 1 2 1 1 1 2 1 0 0 2 1 2 0 2 0 2 0 1 0 0 0 0 1 0 1 2 1 2 2 1 0 2 0 1 0 1 0 2 0 0 1 0 0 0 0 0 0 1 2 0 2 0 1 0 0 0 0 0 0 2 1 2 1 1 1 2 1 0 0 2 2 0 0 0 2 0 2 1 0 0 1 0 0 2 1 1 2 1 2 1 0 0 1 0 0 1 1 0 2 0 0 2 1 2 0 0 0 1 2 0 0 2 2 0 0 2 0 2 1 2 2 0 1 1 0 1 0 2 1 2 2 2 2 0 1 2 0 1 1 2 2 2 2 1 1 1 0 2 62 | 862 0 2 0 2 0 0 2 2 2 0 2 2 0 2 1 0 2 0 1 1 2 0 2 1 0 1 1 1 1 0 0 0 1 1 2 2 2 0 0 1 2 1 0 1 1 0 0 2 1 0 1 1 0 2 1 1 1 2 0 1 0 1 2 2 0 2 1 2 0 2 2 0 2 0 2 0 0 0 1 1 2 2 0 1 0 0 1 1 0 2 0 2 1 2 2 0 0 1 0 1 1 0 2 1 0 2 1 0 0 1 1 1 0 2 2 1 0 1 0 0 0 1 2 0 0 0 2 0 1 2 0 2 2 0 0 1 2 2 2 1 2 2 1 0 1 1 2 0 1 1 2 1 1 1 1 2 1 1 0 1 2 1 0 2 1 1 1 2 0 2 2 2 2 1 0 0 2 1 0 0 0 2 2 2 0 0 2 0 1 1 1 2 2 2 1 1 2 1 1 1 63 | 863 1 2 0 2 0 1 2 2 2 0 1 2 1 1 1 2 2 0 0 0 2 1 2 2 0 2 1 1 2 0 1 0 0 0 2 2 2 2 0 0 2 2 1 2 1 0 1 2 1 0 2 1 1 1 2 1 2 2 1 1 0 2 1 2 1 2 0 1 0 2 0 1 2 0 2 0 2 1 1 1 1 2 0 1 1 1 0 2 1 1 0 2 1 1 0 0 0 1 0 1 0 0 1 1 1 0 1 0 0 1 1 1 1 1 1 1 0 2 0 0 0 1 2 0 0 0 1 0 2 2 0 0 1 0 0 0 2 1 2 2 1 1 2 1 1 1 0 1 0 1 1 0 0 1 1 2 0 2 0 0 2 1 0 2 2 1 1 2 1 0 2 2 2 2 0 0 1 1 2 1 1 2 2 2 1 0 1 0 1 0 0 2 1 2 1 0 2 0 1 2 64 | 864 0 1 0 1 2 1 2 1 2 0 2 1 0 2 0 0 2 0 0 1 0 1 2 2 0 1 0 1 2 0 1 0 0 1 1 2 2 0 1 0 2 2 1 1 2 1 0 2 1 0 2 1 1 0 0 2 1 2 1 1 0 2 2 2 1 2 2 2 1 2 1 0 2 0 0 0 0 0 0 2 2 2 1 1 0 1 0 2 1 0 0 2 0 1 1 0 1 0 0 0 1 1 1 1 1 1 2 0 0 0 1 1 2 2 0 1 0 2 0 0 1 2 1 0 0 1 1 0 2 0 0 0 0 0 1 2 2 1 2 1 1 1 0 1 1 2 0 1 1 0 0 0 0 1 1 2 1 1 0 1 2 0 0 1 1 0 0 2 0 2 2 2 2 1 0 1 0 2 0 2 0 2 2 2 2 1 2 0 0 1 0 1 1 0 2 1 0 1 0 1 65 | 865 0 2 0 2 1 0 2 1 2 0 2 0 0 2 1 0 1 0 0 1 1 2 1 0 1 0 2 1 1 0 2 2 0 0 0 2 1 0 1 2 2 1 1 2 2 1 0 2 0 0 2 1 2 0 2 1 1 2 0 0 0 2 1 2 1 2 0 2 2 2 1 0 1 0 2 0 2 1 0 2 2 2 0 0 0 2 2 1 0 0 0 1 0 0 1 0 2 1 0 0 1 1 2 0 0 0 0 1 0 2 1 2 2 2 2 2 1 2 0 1 0 1 1 0 0 0 2 0 2 1 0 1 2 0 0 1 2 0 1 2 0 2 1 1 0 2 2 1 1 2 1 0 0 2 1 2 0 1 0 1 1 0 1 2 2 0 1 2 0 2 1 2 1 2 0 0 1 1 0 1 0 2 2 2 2 0 2 2 1 2 1 2 0 0 1 2 0 1 0 2 66 | 866 0 1 0 1 1 0 2 1 2 0 2 0 0 2 0 0 2 0 0 1 1 0 1 0 0 0 2 0 2 1 1 1 1 1 2 2 2 0 0 2 2 1 1 1 2 2 1 2 0 1 1 1 0 1 1 1 2 2 1 0 0 2 2 2 0 1 2 2 1 1 1 0 1 0 1 0 1 2 1 1 2 2 1 2 0 1 1 2 0 0 0 0 0 1 1 0 1 0 0 0 2 0 2 1 1 1 0 0 0 0 1 2 1 0 2 2 0 1 0 0 0 1 2 0 1 0 1 0 2 1 0 0 1 0 0 1 2 0 2 0 2 2 1 0 0 2 0 1 1 1 1 0 1 0 0 2 0 1 0 0 2 0 0 1 2 0 1 1 0 2 1 1 2 1 0 1 0 2 1 2 0 2 2 2 1 0 1 0 2 1 1 2 1 2 2 1 1 2 0 1 67 | 867 0 0 0 2 0 1 2 1 1 0 1 1 1 1 2 0 2 0 0 0 2 1 2 0 1 1 1 0 1 0 0 0 1 0 0 2 2 0 0 2 2 2 0 2 1 1 0 2 1 0 1 2 0 1 2 1 2 2 1 1 0 0 1 1 0 2 2 1 1 2 2 1 1 0 2 0 1 1 0 1 2 2 0 1 0 2 1 1 0 2 0 2 0 0 0 0 0 0 0 1 1 0 0 1 1 1 0 0 0 0 0 2 0 2 2 2 0 2 0 1 0 2 2 0 0 1 2 0 2 1 0 1 2 0 2 2 1 1 2 1 0 1 1 0 0 2 0 2 0 1 0 0 0 2 0 2 0 0 0 0 2 1 1 1 2 0 1 2 0 1 1 2 2 1 1 1 1 0 0 0 1 2 2 2 1 0 2 1 1 0 0 1 1 2 1 1 0 0 0 2 68 | 868 1 1 0 2 1 1 2 1 2 0 2 0 0 2 0 0 2 1 0 0 1 1 2 1 0 0 2 1 0 0 1 0 1 1 0 2 1 1 0 2 2 2 0 2 0 1 1 2 0 0 1 1 0 0 1 1 1 2 1 2 0 2 0 2 1 2 2 2 0 2 0 2 2 0 1 0 0 0 2 2 1 2 0 2 1 2 1 2 0 2 0 1 0 1 0 0 2 0 0 1 1 0 2 0 2 2 0 0 0 0 1 2 1 2 2 0 1 2 0 1 0 1 2 0 0 0 2 0 2 2 0 0 1 1 1 1 2 1 2 1 2 0 2 2 1 2 2 1 1 1 2 0 0 1 1 2 1 1 0 1 2 0 0 2 1 0 1 2 0 1 2 2 1 2 1 1 1 2 1 1 1 2 2 2 1 2 1 0 1 0 0 2 2 1 2 1 0 0 0 2 69 | 869 0 2 0 2 0 1 2 1 2 0 2 0 0 2 1 0 2 0 0 1 0 2 1 1 0 0 0 0 1 0 0 2 0 0 1 2 2 1 1 2 2 1 2 2 1 1 0 2 0 0 2 1 0 1 1 1 2 1 1 1 0 1 1 2 0 1 2 2 1 2 2 1 2 0 1 0 1 0 1 0 1 2 1 1 0 2 0 1 0 2 0 1 0 1 0 0 1 0 0 2 2 2 1 0 2 2 0 0 0 0 2 2 2 2 2 1 0 1 0 1 1 2 1 0 0 0 2 0 2 1 1 0 2 0 0 1 2 0 1 1 1 2 2 0 1 0 1 1 1 0 2 1 0 1 2 2 0 1 0 0 2 0 0 1 1 0 0 2 0 2 2 0 2 1 0 1 1 2 1 1 1 0 2 2 1 0 2 1 1 0 1 2 2 1 1 0 2 0 1 1 70 | 870 1 1 0 2 1 1 2 1 2 0 2 0 0 2 1 0 2 0 0 1 1 1 1 0 0 2 1 1 1 0 1 1 0 1 2 2 2 0 0 1 2 1 1 2 0 1 0 2 2 0 2 2 1 1 2 0 1 2 0 1 0 1 1 1 0 1 1 1 0 2 2 0 2 0 2 0 1 1 2 0 2 1 0 2 0 2 0 2 0 2 0 2 1 1 0 0 1 0 0 0 1 0 2 0 1 1 1 0 0 0 1 2 1 2 0 2 2 1 0 0 0 1 2 0 0 1 1 0 2 0 0 0 2 1 0 1 2 0 2 1 2 2 1 0 1 1 2 1 0 0 1 0 0 0 1 2 1 1 0 1 2 0 0 2 2 0 1 2 1 2 2 1 2 2 1 1 0 1 1 1 0 2 2 2 1 1 2 1 2 1 0 1 1 1 1 0 1 1 0 2 71 | 871 0 1 0 2 1 1 1 1 2 0 2 0 0 1 2 0 2 0 0 1 1 1 0 1 0 1 1 0 2 0 1 1 1 0 0 1 2 1 1 2 2 2 1 0 1 1 0 2 0 0 2 2 1 0 1 1 0 2 1 1 0 1 0 2 1 1 2 2 0 2 1 0 2 0 2 0 1 0 2 1 2 2 0 2 0 2 0 1 0 1 0 0 1 1 1 0 1 0 0 0 2 1 1 0 0 2 1 0 0 2 1 2 0 2 0 2 1 2 0 0 1 1 2 0 1 1 2 0 2 1 0 0 0 1 0 1 2 2 1 0 0 2 1 0 1 0 1 0 0 1 1 0 0 0 2 2 2 1 0 1 1 2 0 0 2 1 0 2 0 2 1 2 2 2 1 0 0 0 1 1 0 1 2 2 0 0 2 2 0 1 0 2 2 1 2 1 0 0 0 0 72 | 872 1 1 0 2 1 2 1 1 2 0 1 1 0 2 1 0 1 1 0 1 1 0 2 0 0 2 1 0 1 0 1 0 0 0 0 2 2 1 0 2 2 2 1 0 2 1 0 1 0 1 2 0 1 1 2 1 1 1 2 2 0 1 2 2 0 2 2 2 0 2 1 0 1 0 2 0 1 1 0 1 2 2 1 1 0 2 1 1 1 1 0 1 2 2 0 0 2 0 0 1 1 1 1 1 2 1 0 0 0 0 0 2 2 2 2 1 0 1 0 1 1 1 2 0 1 0 2 0 2 1 0 0 2 1 1 0 2 2 2 2 2 2 1 1 1 2 1 1 0 1 1 0 0 0 1 2 0 0 0 0 2 0 0 1 1 1 0 1 0 1 1 1 1 1 1 1 0 2 1 1 2 2 2 2 2 0 2 2 2 1 0 0 1 2 1 1 1 0 1 1 73 | 873 0 1 0 2 2 1 2 2 2 0 2 2 0 2 1 0 1 0 0 2 2 1 2 0 1 1 2 0 2 1 0 1 0 1 1 2 1 0 0 2 1 2 2 2 1 1 1 2 1 1 2 1 0 0 1 2 2 1 1 1 0 1 1 2 1 2 2 1 1 2 2 0 2 0 1 0 0 0 1 2 0 1 2 1 0 2 0 2 0 1 0 1 1 2 1 0 2 0 0 1 0 0 2 1 2 1 0 1 0 0 1 2 0 1 2 1 1 1 1 2 0 1 2 1 1 0 2 0 2 1 0 0 1 0 0 1 1 0 2 0 2 2 2 0 0 1 2 1 1 1 1 0 0 0 0 2 1 2 0 1 1 1 1 2 1 0 1 2 0 2 1 1 2 0 1 2 1 2 1 1 0 2 2 2 1 0 2 1 0 0 0 1 2 1 2 0 0 0 0 1 74 | 874 0 1 0 2 1 1 2 2 2 0 1 0 0 1 1 0 1 0 0 0 2 0 1 1 0 1 2 1 2 0 2 0 0 1 1 2 2 0 0 2 2 1 1 1 1 1 1 2 1 0 1 2 2 0 2 1 1 2 1 0 0 1 2 2 0 1 1 2 2 1 0 1 2 0 2 0 1 0 0 2 2 2 0 2 1 1 1 2 1 1 0 2 2 1 1 0 2 0 0 1 2 1 2 0 1 1 0 0 0 0 0 1 0 1 0 2 0 1 0 1 2 2 2 0 2 0 1 0 2 2 0 0 2 0 0 0 2 1 1 1 2 2 2 0 0 1 1 1 0 0 1 0 0 2 0 2 0 1 0 0 1 0 0 2 2 0 0 2 0 2 2 1 2 1 0 1 2 0 0 0 1 2 2 2 1 0 2 2 1 0 0 1 1 2 2 0 0 1 1 2 75 | 875 0 2 0 2 1 1 1 1 2 1 2 1 0 2 0 0 2 0 0 0 2 1 2 0 0 0 0 0 1 1 0 1 1 1 1 2 2 1 1 2 2 1 0 2 0 1 0 2 0 0 2 1 1 0 2 2 1 1 2 1 0 1 1 2 0 2 1 2 0 2 0 1 0 0 1 0 0 1 1 1 2 2 1 2 1 2 0 2 1 2 0 2 0 2 0 0 0 0 0 1 1 1 1 1 1 1 0 2 0 1 0 1 1 1 1 0 1 2 0 2 1 0 2 0 0 0 2 0 2 2 0 0 1 1 0 1 2 2 2 2 1 1 0 1 0 1 2 1 0 0 2 0 1 2 0 1 1 0 0 1 2 0 0 2 1 0 0 2 0 2 1 1 1 1 1 0 0 0 0 1 0 1 2 2 2 0 2 0 1 0 1 2 1 1 2 1 1 1 0 2 76 | 876 0 1 0 1 2 0 1 1 2 0 2 0 0 2 2 0 1 0 0 2 1 1 2 1 0 1 0 1 1 0 1 0 0 1 0 2 1 0 0 1 2 1 1 2 1 1 0 2 2 1 2 1 1 0 1 2 2 2 2 0 0 2 2 2 0 1 1 2 1 2 0 1 1 0 1 0 1 2 2 1 2 2 0 1 0 2 1 1 0 1 0 2 0 0 1 0 1 1 0 0 1 1 1 1 2 0 0 0 0 0 1 1 1 1 2 2 2 1 1 1 0 2 2 0 1 1 0 0 2 1 0 0 0 0 0 0 1 1 2 2 2 0 1 0 1 1 2 0 1 1 0 0 0 1 1 2 2 1 0 1 1 0 1 2 2 0 0 2 0 2 0 2 2 2 0 0 1 0 2 1 1 2 2 2 0 1 1 1 1 0 0 1 1 2 1 0 1 1 1 2 77 | 877 0 1 1 1 1 2 2 0 2 0 2 2 0 2 2 1 1 1 0 1 1 0 1 1 0 2 1 0 2 0 0 0 1 1 1 2 2 2 0 2 2 1 2 2 2 0 0 2 1 1 2 1 1 0 2 1 2 2 1 1 0 2 1 2 1 1 2 2 0 2 1 0 0 0 1 0 1 0 0 2 2 1 0 2 1 2 0 1 0 1 0 2 1 1 2 0 1 0 1 1 2 1 2 2 2 0 1 1 0 1 0 2 2 1 0 2 1 0 0 1 0 2 2 0 1 1 1 0 2 1 0 0 1 0 1 1 2 1 2 0 2 2 1 0 0 0 1 1 0 1 2 0 0 0 1 2 2 1 0 0 1 0 0 1 2 0 1 0 0 2 2 2 2 1 0 1 0 1 1 1 0 2 2 2 1 0 2 0 2 1 0 1 1 2 1 1 1 0 1 1 78 | 878 0 1 0 2 1 1 2 0 2 0 2 0 0 1 1 0 2 1 0 2 1 1 1 1 0 0 1 0 2 1 0 2 0 0 2 2 1 1 0 1 2 2 1 2 1 2 0 2 1 0 2 0 1 1 2 1 2 2 1 1 0 0 2 2 0 2 2 2 2 1 0 0 1 0 2 0 0 2 2 2 2 2 1 2 1 2 0 1 0 0 0 2 1 1 1 0 1 0 0 0 1 2 2 0 2 1 0 1 0 1 0 2 2 2 2 2 2 1 1 2 0 1 2 0 1 0 1 0 2 1 0 1 0 0 0 0 2 0 1 2 1 1 1 0 1 1 0 1 1 0 1 1 0 2 0 2 1 0 0 0 2 0 0 2 2 0 1 2 0 2 2 2 2 2 0 0 1 2 1 0 0 1 2 2 2 0 2 1 1 0 0 2 2 1 2 1 0 1 0 1 79 | 879 1 2 0 2 2 1 2 2 2 0 1 0 1 2 1 0 2 1 0 1 2 1 1 0 0 0 1 0 2 0 2 0 0 0 1 2 2 1 0 2 2 2 0 2 2 1 0 1 1 0 2 1 1 0 1 2 1 2 1 1 1 1 1 2 0 2 2 1 1 2 1 0 1 0 2 1 1 2 0 1 2 2 0 1 1 2 0 2 1 0 0 1 1 1 1 0 1 0 0 0 2 0 2 1 1 2 0 0 0 2 1 2 1 2 2 1 0 0 0 1 0 1 1 0 0 0 1 0 2 1 0 0 1 0 0 2 2 2 1 0 2 2 2 1 0 1 1 0 0 0 0 0 0 0 2 2 1 1 0 0 2 0 0 2 2 1 1 1 1 2 2 2 2 1 1 0 2 1 1 0 0 2 2 2 1 0 2 2 0 1 0 1 1 2 2 0 0 0 1 1 80 | 880 0 1 0 2 0 2 2 1 2 0 0 2 0 2 2 0 2 0 0 2 1 0 1 1 1 1 1 0 2 0 1 2 1 0 2 2 2 0 1 2 2 1 1 1 2 1 0 2 2 0 2 2 0 1 1 1 1 2 1 1 0 2 0 1 0 1 2 1 1 2 1 1 2 0 1 0 0 0 2 1 1 2 0 1 0 2 2 1 0 1 0 0 0 1 2 0 0 0 0 1 2 1 2 0 1 2 0 0 0 2 1 1 0 2 2 2 0 2 0 0 0 2 2 0 0 1 2 0 2 0 0 1 2 0 0 1 2 1 1 2 1 2 1 2 1 2 1 0 0 1 0 0 0 2 1 1 2 0 0 1 1 0 0 2 2 0 0 2 0 1 1 2 1 2 1 0 1 2 0 0 1 2 2 2 0 0 2 2 2 0 0 1 1 1 1 0 0 0 0 1 81 | 881 0 1 0 2 1 1 2 0 1 0 1 0 0 2 1 0 2 0 0 0 0 1 1 2 0 1 2 1 1 0 2 0 0 1 2 2 0 1 0 2 2 1 2 2 2 2 0 2 1 1 2 2 0 0 1 2 2 2 0 0 0 2 1 2 0 2 1 1 1 2 0 1 1 0 2 0 2 1 0 1 2 2 0 2 0 2 0 1 0 2 0 0 0 1 1 0 0 1 0 1 1 0 1 0 1 0 1 0 0 2 0 2 1 1 1 1 1 1 0 1 0 2 2 0 1 0 0 0 2 0 0 0 2 0 0 0 1 0 2 2 2 2 2 1 1 1 1 1 1 0 2 1 0 0 1 2 0 0 0 0 2 0 1 2 1 0 1 2 0 2 2 2 2 2 0 1 0 2 1 0 0 2 2 2 1 0 1 1 1 0 1 2 1 1 1 1 1 0 1 2 82 | 882 1 1 0 2 0 1 2 1 2 0 2 0 0 2 2 0 2 0 0 0 0 0 2 1 0 0 1 0 2 0 1 1 1 1 1 2 2 1 0 2 2 1 0 1 1 0 1 2 2 0 1 1 2 1 0 0 2 2 2 1 0 2 0 1 0 1 2 2 0 2 1 2 0 0 1 0 0 0 0 2 1 1 0 1 1 2 0 2 1 0 0 2 1 1 0 0 0 0 0 0 2 0 2 1 1 2 1 1 0 0 0 2 2 2 1 2 0 1 0 1 0 2 1 0 0 0 2 0 2 0 0 1 2 0 0 2 2 0 1 0 2 1 2 1 0 2 1 0 2 0 1 0 1 1 0 2 0 1 0 0 2 1 0 1 2 0 1 1 0 2 1 0 2 1 1 0 1 1 0 1 0 2 2 2 0 0 2 2 1 2 1 1 1 2 1 1 0 0 0 1 83 | 883 1 1 1 2 0 0 1 1 2 0 2 0 0 2 0 0 2 0 0 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 2 2 0 1 1 2 2 1 0 2 1 0 2 0 0 2 2 0 1 2 1 2 2 1 1 0 0 0 2 0 1 2 2 0 1 2 1 1 0 1 0 1 0 0 1 2 2 0 1 0 2 2 2 1 2 0 0 0 2 0 0 2 0 0 0 0 0 1 1 1 0 0 1 0 2 0 2 1 2 2 0 2 0 0 0 0 1 2 0 0 0 1 0 2 1 0 0 1 1 1 0 2 2 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 1 2 1 2 0 0 2 0 1 1 2 0 1 2 0 2 2 2 1 1 1 0 0 1 1 0 1 2 2 2 0 0 1 2 2 1 0 2 2 1 2 1 0 1 0 2 84 | 884 0 2 0 2 1 0 2 1 2 0 2 0 0 2 0 1 1 1 0 1 1 1 2 2 0 0 2 0 1 0 0 0 0 0 0 2 2 0 0 2 2 0 2 0 2 2 0 2 2 1 2 2 2 1 1 1 1 2 0 1 0 2 2 2 1 2 2 2 1 2 2 0 0 0 2 0 2 0 1 2 0 2 0 2 0 1 1 2 1 2 0 0 0 2 2 0 2 0 0 1 1 0 2 0 1 1 0 0 0 1 1 2 2 2 2 2 1 1 1 0 0 2 2 0 0 2 1 0 2 1 0 0 1 0 0 1 1 1 2 1 1 1 1 2 0 1 2 0 2 0 0 0 0 1 1 2 0 0 1 0 2 0 0 2 2 1 0 2 0 2 2 2 1 2 1 1 0 2 0 1 0 1 2 2 0 1 2 1 0 1 0 0 2 1 2 0 0 1 1 2 85 | 885 2 1 1 1 1 0 2 0 2 0 2 0 1 2 0 1 2 0 0 0 2 1 2 1 2 1 1 0 1 0 0 0 1 1 0 2 2 1 0 2 2 0 0 2 1 1 0 2 1 0 2 2 1 0 2 0 2 2 2 0 0 2 1 2 0 2 1 2 0 2 0 1 0 0 2 2 1 1 0 2 2 2 0 0 0 2 1 1 0 2 0 1 0 2 1 0 1 0 0 1 2 0 1 1 1 0 1 0 0 1 1 2 1 2 1 2 1 1 0 1 0 2 2 0 0 0 2 0 1 0 0 0 2 0 0 1 2 1 1 2 2 1 2 0 1 2 1 1 0 2 0 0 0 2 0 2 0 0 0 0 0 0 0 2 2 0 1 1 1 2 2 1 1 2 0 0 2 1 0 0 1 2 1 2 1 0 2 1 2 2 0 2 2 2 2 2 1 1 0 1 86 | 886 0 1 0 1 1 1 1 2 2 1 1 1 0 1 1 0 2 0 0 1 0 0 2 1 2 0 1 0 2 1 0 2 1 1 1 2 2 1 1 2 2 1 0 1 1 1 0 1 2 1 2 1 1 0 2 2 1 1 2 1 0 0 0 2 0 2 1 2 1 2 2 1 1 0 2 1 1 1 0 0 2 2 0 1 0 2 0 1 0 0 0 2 0 1 0 0 1 1 0 0 1 0 2 1 2 1 2 0 0 1 1 1 1 2 1 2 1 2 0 1 0 2 2 0 1 0 2 0 2 1 0 0 2 0 0 1 1 2 2 0 2 2 2 0 1 2 1 1 0 1 1 0 0 1 0 2 2 1 0 0 1 0 0 2 1 1 1 1 0 1 2 2 2 2 2 1 2 0 1 0 2 1 2 2 1 0 2 2 1 1 1 2 2 0 2 0 1 1 0 1 87 | 887 1 2 1 2 1 1 2 0 2 0 1 0 0 2 2 0 2 0 1 0 2 0 1 0 0 1 1 0 2 0 1 0 0 0 1 2 1 1 1 2 2 2 1 1 2 1 0 2 1 1 1 2 0 1 1 1 0 1 2 0 0 1 1 2 0 1 2 2 1 2 0 1 2 0 2 0 1 0 1 1 2 2 0 2 0 2 1 1 0 1 0 2 1 1 2 0 1 0 0 1 1 0 2 1 0 2 1 0 0 0 0 2 1 1 2 1 1 1 0 0 0 1 2 0 1 0 2 0 2 2 0 0 1 2 0 1 2 2 1 0 2 2 1 1 0 1 1 1 1 1 2 0 0 0 1 2 0 0 0 0 2 0 1 1 1 0 1 1 0 1 2 1 2 2 1 0 1 1 0 0 2 2 2 2 1 0 1 1 0 0 1 1 0 1 2 1 0 2 0 2 88 | 888 0 1 0 1 1 1 2 1 2 0 1 0 0 1 1 1 2 0 0 2 0 1 2 0 1 1 1 0 1 0 0 1 1 1 0 2 2 0 0 1 2 1 2 2 2 1 0 2 0 0 2 0 0 2 2 2 2 1 2 2 0 2 0 2 1 2 1 2 1 2 0 1 1 0 1 0 1 1 1 1 2 1 1 2 0 2 0 1 1 1 0 2 1 1 0 0 0 0 0 0 2 1 0 1 0 0 0 0 0 0 1 2 2 2 0 0 0 1 0 0 0 2 2 0 0 0 2 0 2 0 0 0 2 0 0 2 2 0 2 1 1 1 1 1 0 0 0 2 0 0 0 0 0 1 1 2 1 1 0 2 2 0 0 2 1 1 1 2 0 1 2 2 2 2 0 0 1 1 1 1 0 1 2 2 1 0 2 1 1 1 2 0 1 2 2 1 0 1 0 1 89 | 889 0 2 0 2 1 1 1 1 2 0 1 1 0 1 0 0 2 1 0 1 2 1 2 0 0 1 2 0 1 1 2 1 1 0 1 1 1 0 1 1 1 1 0 1 2 1 1 2 1 0 2 1 1 0 1 1 1 2 2 1 0 2 1 2 0 1 2 2 2 2 0 0 2 0 2 0 0 1 0 1 2 2 0 1 0 2 0 2 1 2 1 1 0 1 0 0 0 0 0 0 2 0 2 1 2 0 0 1 0 0 1 0 2 1 2 1 0 2 1 1 0 2 2 0 0 0 1 0 2 1 0 0 0 0 0 1 2 1 2 1 1 1 2 2 0 0 0 0 0 2 1 0 0 1 1 2 1 1 1 0 2 0 1 2 1 0 1 1 0 1 1 2 2 1 1 0 1 2 1 0 0 2 2 2 1 0 2 1 2 0 0 2 0 1 1 1 0 0 0 2 90 | 890 0 1 0 2 1 0 1 0 2 0 0 0 0 1 0 0 1 0 0 2 1 0 0 0 0 0 1 0 2 0 1 0 0 0 1 2 2 1 0 2 2 1 1 1 2 2 1 2 1 0 2 1 1 0 2 2 1 2 2 0 0 2 1 2 0 2 2 1 0 2 2 1 1 0 1 0 2 0 1 1 1 2 0 1 0 2 1 2 1 2 0 0 0 1 1 0 0 0 0 0 1 1 2 1 2 1 2 0 0 0 0 2 1 2 1 2 0 2 0 0 0 2 2 1 1 0 2 0 2 1 0 0 2 0 1 1 2 2 2 2 1 1 2 1 0 0 0 0 1 1 1 0 0 2 0 2 1 1 0 1 2 0 1 2 2 0 1 1 0 1 2 2 2 1 1 0 0 1 2 0 0 2 2 2 1 0 1 1 2 1 0 0 2 2 2 0 2 1 1 2 91 | 891 0 2 0 2 1 0 2 1 1 0 2 0 1 2 0 1 0 0 0 1 2 0 2 0 1 0 0 1 1 1 1 2 1 1 1 2 2 1 1 2 2 1 2 1 2 1 1 1 0 0 2 2 1 1 1 1 1 2 0 2 0 2 1 2 0 2 1 1 1 2 0 0 1 0 1 1 1 0 2 0 1 2 0 2 1 2 2 1 1 2 0 1 1 2 0 0 1 0 0 0 0 0 2 0 2 1 0 0 0 0 2 1 1 1 1 2 1 0 0 0 2 2 2 0 0 1 1 0 2 1 0 0 2 1 0 0 1 1 2 0 2 2 1 1 0 1 1 1 1 0 1 0 0 1 2 2 0 0 0 0 1 0 0 1 2 1 1 2 1 2 2 1 2 1 0 0 0 1 1 1 1 1 1 2 1 0 2 0 1 0 0 0 0 1 2 0 1 1 0 0 92 | 892 1 2 0 1 1 0 2 1 2 1 1 0 1 2 1 0 1 0 1 1 2 1 1 0 0 0 1 0 0 0 2 0 1 0 2 2 2 0 1 1 2 2 0 1 1 1 1 2 1 0 2 1 1 0 2 1 1 1 2 0 0 1 0 2 0 2 1 2 2 2 0 0 2 0 2 0 0 2 1 1 2 2 0 2 0 2 0 2 1 1 0 1 0 1 1 0 0 0 0 0 2 2 1 0 2 0 0 0 0 1 0 1 2 2 1 1 0 2 0 0 0 2 1 0 0 0 0 0 2 1 0 0 0 1 1 1 2 2 2 0 1 2 0 1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1 2 2 1 1 1 0 0 2 0 1 2 2 2 1 0 0 1 1 0 1 0 2 2 2 2 0 1 0 2 1 1 0 1 1 2 1 1 1 0 1 93 | 893 0 2 0 2 1 1 2 0 1 0 1 0 0 2 0 1 2 0 0 0 2 0 2 1 0 0 1 0 2 0 1 0 0 2 0 1 2 0 1 2 2 0 1 2 0 2 0 2 1 0 2 0 0 1 1 1 2 2 1 0 0 2 1 2 0 1 2 2 1 2 0 0 1 0 1 0 1 0 0 1 1 2 0 2 1 1 1 2 0 1 0 1 0 0 1 0 1 1 0 1 2 1 2 0 2 1 0 1 0 2 1 2 0 2 1 1 1 0 0 1 1 2 2 0 1 0 2 0 2 1 0 1 1 0 1 0 2 0 2 0 1 2 1 1 2 1 1 1 1 0 0 0 0 1 0 1 0 1 0 0 2 0 2 1 2 0 0 1 0 2 1 1 1 1 0 0 1 2 0 1 0 2 2 2 1 0 2 1 2 0 0 1 1 2 1 0 0 0 0 1 94 | 894 0 1 2 2 0 1 2 2 2 0 2 2 0 1 1 0 2 1 0 0 2 0 2 1 0 2 2 0 1 2 1 0 0 0 0 2 2 1 1 2 1 1 1 1 0 0 1 2 2 1 2 1 0 0 2 1 0 2 1 1 0 1 1 2 0 1 2 2 1 2 0 1 0 0 1 0 0 1 2 0 2 1 0 2 0 2 0 1 0 1 0 1 0 2 2 0 1 0 0 1 1 0 2 0 0 1 1 0 0 0 1 1 0 2 2 2 1 2 0 0 0 1 2 0 1 0 1 0 2 1 1 1 1 2 0 0 2 2 2 2 1 2 1 0 1 2 2 0 1 1 1 0 0 1 1 2 1 1 0 1 1 0 0 2 2 1 2 2 0 2 1 2 2 1 0 1 0 2 1 1 0 2 2 2 2 0 2 2 2 1 0 2 1 1 1 0 0 0 0 0 95 | 895 1 2 0 2 1 1 2 1 2 0 2 1 0 2 0 0 2 0 0 0 1 0 2 0 0 0 0 0 2 0 0 0 0 1 0 2 1 1 1 2 1 2 0 2 2 2 0 2 1 0 2 1 0 0 1 1 1 1 1 1 0 2 1 1 0 1 1 2 1 2 1 2 2 0 2 0 1 0 0 1 1 2 1 2 0 2 0 1 0 1 0 1 0 1 1 0 2 0 0 2 1 1 2 0 2 2 0 0 0 2 0 2 2 2 2 2 2 1 1 1 0 2 2 0 0 1 2 0 1 0 0 0 0 0 1 1 1 2 2 2 1 2 1 1 1 2 1 1 2 0 0 1 0 2 1 2 0 1 1 0 2 1 1 2 1 0 0 2 0 0 2 1 2 1 0 0 0 1 1 0 0 1 2 1 0 1 2 2 1 1 1 1 2 1 2 1 0 1 0 1 96 | 896 1 1 1 2 1 1 2 0 2 0 2 1 0 2 2 0 1 0 0 0 1 0 2 0 0 1 1 0 2 0 1 0 0 0 1 2 2 0 0 2 1 0 1 2 2 1 0 2 0 0 1 1 0 0 1 0 1 2 1 2 0 1 2 2 0 2 0 2 0 1 1 0 0 0 2 0 1 1 1 2 2 2 0 0 1 1 1 2 0 2 0 1 0 1 2 0 0 1 0 2 2 0 2 0 2 0 1 0 0 1 1 2 1 2 2 2 1 0 1 2 0 1 2 0 1 0 2 0 2 1 0 0 1 1 0 0 1 1 2 1 2 1 1 1 0 2 1 1 1 1 1 1 0 1 0 2 2 0 0 1 2 1 1 1 1 0 1 2 0 2 2 1 2 2 0 0 0 1 0 1 0 2 2 2 0 0 0 2 1 1 1 2 1 2 2 0 1 1 1 0 97 | 897 0 1 0 2 1 1 2 0 2 0 2 1 0 1 1 0 2 0 0 2 2 1 2 1 0 2 2 0 2 0 1 1 0 0 2 2 2 1 2 2 2 1 1 1 2 1 0 2 2 2 2 0 2 0 2 1 2 2 0 1 0 2 2 2 0 2 1 1 1 2 0 0 2 0 2 0 1 2 1 2 2 2 0 1 0 2 0 1 0 0 0 0 1 2 0 0 1 0 0 0 2 0 2 1 1 0 0 1 0 2 1 1 1 2 2 2 1 1 0 1 0 1 2 0 1 0 1 0 2 2 0 0 2 0 0 0 2 1 0 0 0 2 1 0 1 1 1 0 0 0 0 0 0 1 1 2 0 1 0 0 1 1 0 2 2 0 2 1 0 1 2 2 2 1 0 0 2 1 1 0 2 2 2 2 1 0 2 1 0 1 0 2 2 2 1 1 0 1 0 1 98 | 898 0 1 1 1 1 1 2 2 1 0 2 1 0 2 1 2 2 0 0 1 1 1 2 1 2 1 2 1 1 2 0 1 0 1 0 2 2 2 0 1 2 1 1 2 1 1 0 2 1 0 2 1 1 0 2 1 2 2 1 1 0 0 0 2 0 1 1 2 1 2 1 1 1 0 1 0 0 1 2 1 1 2 1 2 0 2 1 2 0 0 0 1 0 1 0 0 1 0 0 0 1 0 2 1 2 0 0 0 0 0 0 2 1 2 2 1 1 2 1 2 0 1 2 0 0 0 2 0 2 1 0 0 1 0 0 2 2 1 1 1 1 2 2 1 0 1 1 0 1 0 2 0 0 0 0 2 1 1 0 0 0 0 0 2 2 1 2 1 0 2 2 2 2 1 0 0 2 1 2 2 0 2 2 2 2 1 2 0 1 1 0 1 2 2 1 0 2 0 0 1 99 | 899 1 1 0 2 1 1 2 0 2 0 0 1 1 1 0 0 2 0 0 1 0 0 1 1 0 1 1 0 1 1 1 1 0 0 0 2 1 0 0 1 2 1 1 1 1 2 1 2 1 0 2 0 1 0 1 2 2 2 0 1 0 1 0 2 0 2 1 1 0 0 0 0 1 0 0 0 2 1 0 1 0 2 1 1 0 2 2 0 0 2 0 0 1 2 0 0 0 1 0 0 0 1 2 0 2 1 1 2 0 2 2 2 0 1 2 0 1 2 0 0 1 2 2 0 1 0 0 0 2 0 0 0 2 0 1 1 2 0 2 0 2 1 1 0 1 1 1 0 0 0 0 0 0 1 0 2 0 0 0 0 2 0 0 1 2 2 0 2 0 2 1 1 1 2 0 1 1 2 1 0 0 1 1 2 1 1 1 0 1 0 1 1 2 1 2 1 1 1 0 2 100 | 900 1 0 1 1 2 2 2 1 2 0 1 1 0 0 2 0 1 0 0 1 2 1 1 1 1 0 0 0 0 1 0 1 0 0 1 2 2 0 0 2 2 2 2 2 1 1 0 2 0 1 2 1 1 0 2 0 1 1 2 2 0 1 2 2 0 2 0 2 2 2 2 1 2 0 1 0 0 1 0 2 1 2 1 1 0 2 0 0 0 0 0 2 0 2 0 0 1 0 0 0 1 1 1 2 0 2 1 1 0 1 0 2 0 2 2 2 1 2 0 0 0 0 2 0 0 0 2 0 2 1 0 0 0 0 0 1 2 2 2 0 0 1 1 1 0 2 0 1 1 0 1 0 1 1 0 2 0 1 0 1 2 0 0 2 2 0 1 2 0 2 2 1 1 2 0 2 1 0 0 2 0 2 2 2 2 0 2 1 0 0 1 2 2 2 1 2 0 0 0 2 101 | 901 1 0 0 2 0 2 2 1 2 1 0 0 0 2 0 1 2 0 0 2 2 1 2 1 2 1 1 0 0 0 0 1 1 0 0 2 2 0 0 2 2 1 1 2 2 1 1 2 1 0 2 0 1 0 2 0 2 2 0 0 0 2 2 2 0 2 2 2 1 2 1 0 1 0 2 1 2 0 0 2 2 2 1 2 2 2 0 2 0 2 0 1 1 2 2 0 1 0 1 0 2 1 2 1 1 1 1 0 0 1 1 1 1 2 2 1 0 1 1 1 2 2 2 0 0 0 1 0 2 1 1 0 0 0 0 0 2 1 2 0 2 2 2 0 0 1 2 0 1 2 1 0 0 1 0 2 0 1 0 1 0 0 0 2 1 2 1 2 0 2 2 1 2 2 1 2 1 0 0 2 0 2 2 2 2 1 2 1 1 0 0 0 1 2 2 1 0 0 0 1 102 | 902 1 2 0 1 2 0 2 2 1 1 1 1 0 1 1 0 2 1 1 1 1 0 2 1 2 0 1 0 1 0 1 0 0 2 1 1 2 1 1 2 2 1 1 2 1 0 0 2 0 0 2 2 0 1 1 1 2 2 1 1 0 1 1 2 0 2 1 1 0 2 2 0 1 0 2 0 0 2 0 2 2 1 0 1 0 2 0 1 1 2 0 0 1 0 0 0 0 0 1 0 1 1 2 2 1 1 1 2 0 1 1 2 1 2 1 0 1 1 0 1 0 2 2 0 1 1 1 0 2 1 0 0 2 1 1 1 2 0 2 1 0 2 1 0 1 1 1 1 1 0 0 0 0 0 1 1 0 1 0 1 1 1 1 2 1 0 1 2 0 1 2 1 2 2 0 1 2 2 1 1 1 2 2 2 0 0 2 1 2 0 0 2 1 0 1 2 2 2 0 1 103 | 903 0 2 1 2 1 1 2 2 1 0 0 1 0 1 0 1 2 0 0 0 2 1 2 2 0 0 1 0 1 0 1 0 0 0 0 2 2 1 0 2 2 2 0 2 1 2 0 2 1 1 2 1 1 1 2 1 2 2 1 1 0 0 1 2 0 2 2 2 0 1 1 0 1 0 2 0 1 0 2 2 1 2 0 2 1 2 0 1 0 2 0 1 0 1 0 0 1 1 0 0 2 0 2 1 0 2 0 1 0 0 2 1 2 2 2 2 1 2 0 1 0 1 2 0 0 0 1 0 2 1 0 0 1 0 0 1 2 1 2 1 1 2 2 0 0 0 1 0 1 2 0 0 0 1 0 2 1 1 0 0 0 0 0 2 2 0 0 2 0 1 2 1 2 1 0 1 1 1 1 1 1 1 1 2 1 0 1 1 2 0 0 2 2 2 1 1 1 0 1 2 104 | 904 0 1 0 2 1 1 2 2 2 0 2 0 1 2 2 1 2 0 0 0 1 0 2 1 0 2 1 1 2 0 2 0 0 0 1 2 2 0 1 1 1 2 0 2 2 0 0 2 0 0 1 1 0 0 0 0 1 2 1 1 0 1 1 2 0 2 2 2 1 2 0 0 2 0 2 1 0 1 1 2 2 2 1 2 1 2 1 2 0 1 0 1 0 1 0 0 0 0 0 1 1 1 2 2 2 1 0 0 0 0 2 2 1 2 1 1 1 1 0 1 0 1 2 0 1 1 2 0 2 2 0 0 2 0 0 2 2 1 2 0 1 1 1 1 1 2 2 0 0 1 1 0 0 2 1 2 0 0 0 1 2 0 0 2 2 0 1 2 0 2 2 1 1 0 0 0 2 2 1 1 0 2 2 2 0 0 1 2 1 0 1 1 2 1 2 1 1 1 0 2 105 | 905 0 0 0 2 1 1 2 2 2 0 1 1 0 2 1 0 2 0 0 0 1 1 2 1 1 1 1 1 2 1 1 0 1 1 1 2 2 0 0 2 2 2 0 0 1 1 0 2 1 0 1 2 0 0 1 1 1 2 0 1 0 1 1 2 0 2 1 1 0 2 1 0 0 0 2 0 2 1 2 1 2 1 0 1 1 2 0 2 0 1 0 0 0 2 1 0 0 0 0 0 0 0 2 2 2 1 0 0 1 0 1 2 0 2 1 0 1 2 0 0 0 2 2 0 0 0 2 0 2 1 1 0 1 0 1 1 1 1 2 1 1 0 2 0 0 1 1 0 1 0 1 0 1 2 0 2 0 2 0 0 2 1 0 2 2 0 0 1 0 1 1 0 2 2 0 0 0 2 0 2 1 2 2 2 1 0 2 0 1 1 0 2 1 2 1 1 1 1 1 2 106 | 906 0 1 0 1 1 1 2 1 2 0 1 1 0 1 0 0 1 0 0 1 0 0 2 0 0 1 1 0 2 0 1 2 0 0 1 2 2 1 0 1 1 2 1 1 2 0 1 2 2 1 2 1 1 0 1 2 2 2 1 0 0 2 1 2 0 2 1 2 1 2 1 0 2 0 1 0 0 1 0 1 2 2 1 2 0 2 1 2 0 1 0 1 0 1 1 0 0 0 0 0 2 1 2 1 0 1 1 1 0 1 1 2 1 2 1 1 2 1 1 0 0 0 2 0 1 0 1 0 2 1 0 0 0 1 0 1 1 1 2 1 2 2 2 0 2 2 1 0 0 1 0 0 0 1 0 1 1 2 0 0 2 0 0 2 1 0 0 1 0 0 2 1 2 1 1 0 0 1 1 0 0 2 2 2 1 0 2 0 1 0 0 2 1 2 2 1 0 1 1 1 107 | 907 1 1 0 2 1 1 2 2 2 0 1 0 0 1 1 0 2 0 0 0 2 0 2 2 1 0 0 0 2 1 0 0 0 0 0 2 1 0 1 1 2 2 1 2 1 1 0 2 2 1 2 1 1 1 1 1 1 1 2 1 1 2 2 2 0 2 1 2 1 2 1 1 2 0 0 1 2 0 1 1 2 2 1 1 0 2 0 1 0 2 0 0 0 1 1 0 2 0 0 0 1 0 2 1 1 1 0 0 0 0 1 2 1 2 2 1 2 1 0 0 1 1 1 0 0 0 2 0 2 2 0 0 2 1 0 0 2 1 2 0 1 1 2 0 1 0 1 1 1 0 1 0 0 1 0 2 2 1 0 1 2 0 0 2 2 0 1 1 0 0 2 2 1 1 1 1 0 1 1 1 2 2 2 2 0 0 2 2 0 0 0 1 1 2 1 1 0 1 0 2 108 | 908 0 2 0 2 1 2 2 1 2 0 1 1 0 1 1 0 2 0 0 0 1 1 2 1 0 1 0 1 1 0 2 0 1 1 2 2 2 0 2 1 2 1 2 2 2 1 0 2 0 0 2 0 0 1 2 1 2 2 1 1 0 2 2 2 0 2 1 2 1 2 2 1 2 0 2 1 0 0 1 1 2 1 1 1 1 2 1 0 0 2 0 1 1 1 0 0 0 0 0 0 0 0 2 1 1 1 1 1 0 2 0 2 1 2 2 2 1 1 0 0 0 2 2 0 0 1 2 0 2 1 0 0 2 0 1 1 2 0 2 1 1 2 2 0 1 1 2 1 1 1 0 0 0 0 1 1 0 0 0 0 2 0 1 2 2 0 1 2 2 2 2 1 0 1 2 1 1 1 1 0 0 0 1 2 2 0 2 2 1 0 1 2 1 0 1 0 0 2 1 2 109 | 909 0 2 0 1 0 2 1 1 2 0 2 0 0 2 1 0 2 0 0 2 1 1 2 1 0 1 2 0 1 0 1 1 0 1 0 2 1 1 1 2 2 0 0 2 1 0 1 2 1 0 2 1 0 2 1 2 2 2 1 0 0 2 1 2 1 1 1 2 2 2 0 0 1 0 2 0 0 0 1 2 1 2 2 2 1 2 1 0 0 1 0 1 0 0 0 0 1 0 0 0 2 1 1 0 1 2 0 0 0 1 1 1 2 0 1 2 2 2 0 1 0 2 2 0 1 0 1 0 2 1 0 1 2 0 1 0 2 1 2 0 2 2 2 0 2 2 2 2 0 0 1 0 0 1 1 2 0 0 0 1 2 0 0 2 2 1 0 2 0 1 2 2 2 2 1 1 1 2 1 0 1 2 2 2 2 0 2 2 1 0 0 1 2 2 2 0 0 1 1 1 110 | 910 0 0 0 2 1 0 2 2 2 1 1 0 1 1 0 0 0 1 0 1 2 0 1 1 0 1 2 0 1 0 0 1 1 1 0 2 2 2 0 2 2 1 1 2 0 1 1 2 0 0 2 1 0 0 2 1 1 2 1 2 0 1 0 2 1 2 1 2 0 2 1 0 2 0 1 0 1 1 1 2 1 2 1 2 0 2 0 1 1 1 0 2 1 0 1 0 0 0 0 0 0 0 2 1 2 1 1 1 0 0 1 1 1 1 2 2 1 0 0 1 0 1 2 0 0 0 1 0 2 1 0 0 1 0 0 2 1 1 1 0 2 1 1 0 2 2 1 1 0 1 2 0 0 1 0 2 0 0 0 1 2 1 1 1 1 0 0 2 0 0 2 2 2 1 0 0 1 1 1 0 0 1 2 2 2 1 2 1 2 0 0 2 2 1 1 0 0 1 1 2 111 | 911 1 2 2 2 1 1 2 0 1 0 2 0 0 1 2 1 2 1 0 0 1 2 2 1 0 0 0 1 2 0 1 1 2 1 0 2 2 0 0 2 2 2 2 1 1 1 0 2 0 0 2 0 2 0 2 2 1 2 0 0 0 2 1 1 0 2 2 1 0 2 1 0 1 0 2 1 2 0 1 2 2 2 0 1 0 0 0 2 1 1 0 2 0 2 0 0 2 0 0 0 1 1 2 1 1 1 0 0 1 1 0 2 1 2 1 2 1 1 0 0 1 2 1 0 2 1 1 0 2 1 0 0 2 1 0 1 2 0 2 0 1 1 1 1 2 1 2 1 1 1 1 0 0 1 1 2 1 2 0 1 1 0 0 2 1 1 1 2 0 0 2 2 0 1 0 1 0 0 1 1 0 1 1 2 0 0 2 0 1 0 1 1 1 2 1 2 0 1 0 2 112 | 912 0 1 1 2 2 1 2 2 2 0 2 0 0 1 2 1 2 0 0 1 1 0 2 0 0 2 2 0 2 0 1 0 1 1 2 1 2 0 0 2 2 2 2 1 2 0 0 2 1 0 2 1 1 0 2 0 1 2 0 1 1 1 1 2 0 1 1 2 0 2 1 1 2 0 2 1 1 2 1 2 2 2 0 2 1 1 0 1 0 1 0 1 2 0 1 0 2 0 0 1 0 1 2 1 1 2 1 1 0 1 0 2 1 1 2 2 0 2 0 0 2 1 2 0 0 1 1 0 2 0 0 0 2 1 0 1 2 2 2 2 2 1 2 0 0 1 1 2 1 1 0 0 0 0 1 2 2 1 0 0 2 0 0 2 1 0 0 1 1 2 2 0 2 2 1 1 0 2 0 1 0 2 2 2 2 0 2 1 1 0 0 1 1 2 2 1 0 1 0 2 113 | 913 0 1 0 2 0 0 2 2 2 0 2 0 0 1 1 0 1 0 0 1 1 0 1 2 0 2 1 0 2 0 1 2 0 0 1 2 1 1 0 0 1 2 1 1 1 0 0 2 1 1 1 1 1 0 1 2 0 2 2 1 0 2 1 2 0 2 1 2 2 2 0 0 2 0 2 2 1 0 1 0 2 2 0 0 1 1 0 1 1 2 0 2 1 1 1 0 0 0 0 0 1 1 2 0 1 2 0 0 0 1 1 1 1 2 1 1 1 2 0 0 0 2 2 0 1 1 2 0 2 2 0 0 2 0 0 0 2 2 2 1 1 2 1 0 1 2 0 0 0 1 0 0 0 1 0 2 1 0 0 0 2 0 0 2 1 1 2 2 0 2 1 2 2 0 1 1 1 2 2 1 1 2 2 2 0 0 1 0 1 0 0 2 2 1 1 1 0 1 2 2 114 | 914 1 0 0 1 1 2 2 2 2 0 2 1 0 1 1 0 2 0 0 1 2 1 2 1 1 0 1 0 1 0 0 0 0 1 0 2 2 0 0 2 2 1 2 2 1 0 0 2 2 0 1 2 1 1 0 2 1 2 1 1 0 0 2 2 0 1 2 0 2 1 1 0 0 0 2 0 0 1 1 1 2 2 1 1 1 2 1 1 0 0 0 2 0 0 1 0 0 0 0 1 1 1 2 0 2 2 0 0 0 0 1 2 1 0 2 2 2 2 0 1 0 0 2 0 0 1 1 0 2 1 0 0 2 0 0 2 2 0 2 2 2 1 1 1 0 0 0 0 1 1 2 0 0 2 0 2 1 0 0 2 2 0 1 2 2 0 1 2 0 2 2 0 2 2 2 0 0 1 1 1 0 2 2 2 1 0 2 1 1 0 0 2 1 1 2 0 0 1 1 2 115 | 915 0 1 0 1 0 0 2 1 2 0 2 0 0 2 2 0 1 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1 0 2 2 1 0 1 2 2 2 2 0 2 2 1 0 0 2 0 1 1 2 1 1 2 0 0 1 2 2 2 0 2 2 2 0 2 1 0 2 1 2 0 1 0 0 1 2 2 1 2 0 2 2 1 0 0 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 0 0 1 1 2 2 0 1 1 2 0 0 0 1 2 2 0 2 1 2 0 2 1 1 0 1 2 1 2 2 0 1 0 2 0 2 0 1 1 1 2 1 1 0 0 1 1 0 2 2 1 0 1 1 0 0 2 2 0 1 2 0 2 0 2 1 2 0 1 1 0 0 0 1 1 2 2 2 0 2 1 1 2 0 2 2 1 1 1 2 0 0 1 116 | 916 1 2 0 2 0 0 2 1 2 0 2 2 0 1 2 0 1 0 0 0 2 1 2 1 1 0 1 0 2 1 0 1 0 0 0 2 1 1 0 2 2 1 1 2 1 1 0 2 1 0 2 0 1 0 1 1 1 2 2 0 0 1 1 2 0 2 1 2 1 1 2 1 1 0 2 1 1 0 1 2 2 2 0 1 1 2 0 1 0 1 0 2 1 1 0 0 1 1 0 0 1 0 2 1 2 0 2 0 0 0 0 2 0 1 2 2 1 1 0 0 1 2 2 0 1 0 2 0 2 1 0 1 1 1 1 1 2 1 1 0 2 2 1 2 0 2 0 1 0 1 1 0 0 1 0 2 1 1 0 2 1 0 0 2 2 0 0 2 0 1 2 2 2 1 1 0 1 1 1 1 0 2 2 2 0 0 2 2 0 2 0 0 1 1 1 0 1 0 0 2 117 | 917 0 1 0 2 0 1 2 2 2 0 2 0 0 2 1 0 2 0 0 1 0 1 1 1 0 1 2 0 1 1 0 0 0 0 2 2 2 0 1 2 2 1 1 1 2 1 1 2 1 0 2 1 0 0 2 0 2 1 0 0 0 0 2 1 0 2 1 1 1 2 1 1 0 0 2 0 0 0 1 2 2 2 0 0 0 0 1 1 0 2 0 0 0 1 0 0 0 1 0 0 2 1 1 1 0 0 2 1 0 2 1 2 2 2 2 1 0 0 0 2 0 2 2 0 0 0 2 0 2 2 0 0 1 0 0 0 2 1 2 2 1 1 0 1 0 2 2 1 1 1 2 0 1 2 2 2 0 0 0 0 2 0 0 2 1 0 1 2 0 1 2 1 1 2 2 0 0 1 1 0 0 1 2 2 1 0 1 1 2 0 0 1 1 2 1 0 0 1 0 2 118 | 918 1 2 0 2 0 1 2 1 2 1 1 1 1 1 1 0 1 0 0 0 0 0 2 0 0 0 2 1 2 1 0 1 1 2 0 2 2 1 0 2 2 2 0 2 2 0 0 2 0 0 1 2 1 1 1 1 2 1 1 0 0 2 1 2 0 2 2 1 1 2 0 1 1 0 1 0 1 0 1 1 2 2 1 1 2 1 1 1 0 2 0 2 2 2 0 1 2 0 1 1 1 2 1 1 2 2 1 1 0 1 0 2 2 2 1 2 2 1 0 0 1 1 2 0 0 0 2 0 2 1 0 0 1 1 1 0 2 1 1 1 2 1 2 1 0 2 1 0 2 0 2 0 0 1 1 1 1 2 0 0 2 0 0 1 2 0 0 2 1 2 2 2 1 2 1 1 0 1 1 2 0 2 2 2 0 1 1 1 0 2 0 1 1 2 1 1 1 0 0 1 119 | 919 0 2 0 2 1 0 1 0 2 0 1 0 1 2 0 0 2 0 0 1 1 1 2 1 0 0 2 0 1 0 2 1 0 1 0 2 2 1 0 2 2 2 0 2 2 1 0 2 1 0 2 1 1 0 1 0 2 2 0 1 0 2 1 2 0 1 1 2 0 2 0 0 1 0 2 0 2 0 1 0 2 2 1 1 0 1 1 2 0 2 0 1 1 1 1 0 2 2 0 1 1 0 2 0 2 1 1 0 0 0 1 1 2 1 1 1 1 2 0 1 0 2 1 1 1 0 2 0 2 2 0 1 2 0 0 1 2 2 2 1 2 1 1 2 2 1 1 1 1 0 0 0 0 1 0 2 1 1 0 0 2 0 1 2 2 0 0 2 1 2 2 2 2 2 1 1 1 1 2 0 0 2 2 2 2 0 2 0 1 0 0 2 2 2 1 1 1 1 0 1 120 | 920 0 0 0 2 0 1 2 0 2 0 2 0 0 2 2 0 2 0 0 1 2 2 2 1 0 1 1 1 1 0 1 2 1 2 1 2 1 1 0 2 2 2 2 1 1 0 0 2 1 0 1 0 0 1 1 1 1 1 1 0 0 1 2 2 0 1 2 1 0 2 1 1 1 0 0 0 1 0 1 1 2 2 1 2 0 2 0 2 0 2 0 0 0 1 0 0 0 1 0 2 0 1 2 1 2 1 1 0 0 1 1 2 0 1 2 1 0 2 0 1 0 0 2 0 1 0 1 0 2 0 0 0 2 0 1 1 2 2 2 1 2 0 1 2 0 2 2 0 1 0 1 0 0 1 1 2 0 1 0 0 1 0 0 0 2 0 0 1 0 1 2 2 2 1 0 0 0 1 1 0 0 0 2 2 0 1 2 2 1 0 0 2 1 1 2 0 2 0 0 2 121 | 921 0 1 0 2 0 1 1 1 2 0 1 1 0 2 1 0 2 0 0 0 1 1 2 0 0 0 1 1 2 1 1 0 1 0 2 2 2 0 0 0 2 2 2 2 2 1 0 2 2 0 2 2 2 0 1 2 2 2 0 0 0 2 2 2 0 2 2 1 1 2 2 1 1 0 0 0 0 0 2 2 2 2 2 1 0 2 0 1 0 0 0 0 0 2 0 0 0 1 0 0 2 2 2 1 1 1 1 2 0 1 0 0 2 1 1 1 1 0 0 1 1 0 1 0 0 0 2 1 2 2 0 0 1 0 0 1 1 1 1 0 2 2 2 0 0 2 1 0 0 0 0 0 1 2 1 2 0 1 0 1 2 1 0 2 1 0 0 2 1 1 2 2 2 1 0 1 1 2 1 0 0 1 2 2 2 0 1 1 0 0 0 1 2 2 2 1 1 2 1 1 122 | 922 1 0 0 2 2 1 2 2 2 0 1 2 0 2 1 1 2 0 0 0 1 1 1 0 0 0 1 0 2 1 0 0 0 2 1 1 2 0 1 1 2 1 2 1 2 0 0 2 0 0 2 1 0 0 2 2 1 2 0 0 0 2 2 2 1 2 1 2 1 2 2 0 2 0 2 0 1 1 1 0 1 2 0 1 0 2 0 1 1 2 0 1 0 1 0 0 2 1 0 1 1 1 2 0 1 0 0 1 0 0 1 1 1 2 1 2 2 2 0 1 1 1 2 0 0 1 2 0 2 2 0 0 2 0 1 0 2 0 1 0 1 2 2 0 0 2 1 0 0 0 1 0 0 1 1 2 0 0 0 0 2 0 1 2 2 0 0 1 0 2 1 2 2 0 0 2 1 1 0 0 0 2 2 2 1 0 2 1 0 0 0 1 2 2 1 0 2 1 0 1 123 | 923 0 2 0 2 0 1 2 2 2 0 2 0 0 2 1 0 2 1 0 1 0 1 1 1 0 2 1 0 2 0 1 1 1 1 0 2 1 0 0 2 2 2 1 2 1 0 1 2 0 0 1 1 1 0 1 1 0 2 2 0 0 2 1 2 1 2 1 2 0 2 2 0 1 0 2 0 0 1 0 2 2 1 1 1 0 2 0 2 0 1 0 0 1 2 1 0 1 0 0 0 1 0 1 0 2 2 0 0 0 1 1 2 1 2 2 1 0 2 0 2 1 2 2 0 0 0 2 0 2 2 0 0 1 0 0 1 1 1 2 1 2 2 2 0 0 1 1 1 0 0 2 0 0 1 0 2 2 0 0 1 1 0 0 0 1 0 1 2 0 1 2 2 2 2 0 0 2 2 1 1 1 2 2 2 0 0 2 2 2 0 1 1 2 2 2 2 2 0 0 1 124 | 924 0 1 0 2 0 2 2 1 2 0 1 1 0 0 0 0 2 1 0 1 1 1 2 0 1 0 1 0 2 0 1 0 1 0 2 2 2 0 0 2 1 1 1 1 2 1 0 2 0 1 2 2 0 1 1 1 2 2 1 0 0 2 1 2 0 1 1 2 1 2 2 0 2 0 2 0 0 0 0 0 2 1 1 0 0 2 1 2 0 1 0 1 1 2 1 0 1 0 0 0 2 0 2 0 2 2 0 1 2 0 0 2 2 1 1 1 1 0 0 0 0 2 2 0 0 1 2 0 2 1 0 0 2 0 0 0 2 1 2 2 2 2 1 0 0 1 1 1 1 0 0 0 0 1 0 2 0 0 0 0 1 0 1 2 1 1 2 2 0 2 2 1 1 2 0 0 0 1 1 1 0 1 2 2 2 0 1 0 0 1 0 1 1 0 2 1 1 1 0 1 125 | 925 0 2 1 2 2 2 1 1 2 0 2 1 0 2 2 0 1 0 0 0 2 1 1 0 0 2 2 0 2 0 1 2 0 0 1 2 2 2 0 2 2 2 1 2 2 0 0 2 1 1 2 2 1 0 2 1 1 2 1 1 0 1 0 2 1 2 2 2 0 1 0 1 1 0 2 0 2 0 0 2 2 2 0 2 0 2 1 2 0 2 0 0 1 0 1 1 1 1 0 2 0 2 2 1 2 2 0 1 0 1 1 2 1 1 1 1 2 2 0 2 0 1 2 0 1 1 2 0 2 1 0 0 2 0 1 1 1 2 1 1 2 2 2 0 0 0 1 1 0 2 1 0 0 0 1 2 0 0 0 2 1 1 0 2 2 1 0 2 0 1 2 1 2 1 1 0 0 1 1 1 1 1 2 2 0 0 1 2 2 1 0 2 1 2 1 0 0 1 0 2 126 | 926 1 1 0 1 2 1 2 0 1 0 2 0 0 1 1 0 1 1 1 2 1 1 2 1 0 0 1 0 2 0 2 1 2 0 1 2 2 0 0 2 2 2 1 1 2 1 0 2 0 1 1 1 1 0 1 1 1 2 1 0 0 1 0 2 0 2 1 2 0 2 1 0 1 0 0 0 1 0 1 2 2 1 0 1 0 2 1 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 2 1 2 2 1 2 0 1 1 2 0 1 2 2 2 2 0 1 1 2 1 0 1 0 0 0 2 1 0 1 1 0 1 0 2 2 1 0 1 2 2 1 0 0 1 2 1 0 0 0 0 1 0 2 1 0 0 1 2 0 0 2 2 0 0 2 1 1 2 2 2 2 1 0 1 1 2 0 0 2 2 2 0 2 1 1 1 1 0 2 1 2 2 0 1 0 0 1 127 | 927 0 1 2 2 1 1 2 1 2 0 1 0 0 2 2 1 2 0 0 0 1 1 2 1 1 0 2 1 1 0 0 0 0 0 1 2 2 1 1 1 2 2 1 2 2 2 1 2 1 0 2 1 1 0 1 1 2 2 1 1 0 1 1 2 1 2 1 2 0 2 1 1 1 0 1 0 2 1 1 0 2 2 1 1 0 2 0 2 0 2 0 0 0 2 1 0 0 0 0 2 0 1 1 1 2 0 1 0 0 1 1 2 1 1 1 1 1 1 0 1 0 1 2 0 1 0 1 0 2 0 0 0 0 0 0 0 2 1 2 0 1 1 2 1 0 2 0 1 1 1 2 0 0 1 0 2 0 1 0 1 2 0 0 2 1 0 1 2 0 2 2 0 2 2 0 1 1 1 2 1 0 2 2 2 1 0 2 1 1 0 0 2 1 1 2 1 0 1 0 2 128 | 928 1 2 0 2 1 0 2 0 2 0 2 0 0 1 1 1 2 1 0 1 1 0 2 0 1 0 1 0 1 0 1 1 1 0 1 2 2 0 0 1 1 1 1 1 2 2 2 2 1 1 1 1 1 0 1 1 2 2 0 1 0 2 1 2 0 1 0 2 0 1 0 1 1 0 2 0 2 0 1 2 2 2 0 0 0 2 1 1 1 2 0 1 0 1 2 0 0 1 0 1 1 0 1 0 0 2 2 1 0 0 1 2 1 1 2 1 2 1 0 1 0 1 1 1 0 0 1 0 2 0 0 0 2 1 0 1 2 1 2 0 2 1 2 0 0 2 2 0 1 0 0 0 0 2 1 2 1 1 0 2 1 0 0 1 1 0 1 2 0 1 2 2 2 0 0 0 1 0 0 0 1 1 2 2 0 0 2 1 2 1 1 2 0 1 1 1 0 0 0 1 129 | 929 0 1 0 2 0 1 1 1 2 0 1 1 0 2 0 2 2 1 0 1 1 1 2 1 0 1 2 1 2 0 1 1 0 0 0 2 2 2 0 2 1 1 1 2 0 0 1 2 0 1 1 0 1 0 0 0 2 1 0 0 0 1 1 2 0 2 1 1 1 2 2 0 1 0 1 0 0 1 1 2 2 1 1 1 1 0 0 1 0 1 0 2 2 1 0 0 1 0 0 0 1 2 2 1 1 1 0 0 0 1 1 2 0 2 1 1 1 2 0 1 0 1 2 0 0 1 1 0 2 1 0 0 1 0 0 1 2 1 1 1 1 1 1 0 0 2 1 0 0 0 1 0 1 2 1 2 0 1 0 1 0 0 0 2 1 0 1 2 0 2 1 2 1 2 1 1 1 2 1 1 1 2 2 2 2 0 1 2 1 1 0 2 2 1 2 2 1 0 0 2 130 | 930 0 1 0 1 2 1 2 1 2 0 1 0 0 0 0 0 1 0 0 1 2 0 2 0 1 2 2 1 2 0 1 1 0 1 1 2 2 1 2 2 1 2 1 0 2 1 0 1 1 0 2 0 0 1 1 2 1 2 1 0 0 2 0 2 0 1 2 2 1 1 2 1 2 0 2 0 1 1 1 1 2 2 0 1 1 1 1 2 1 2 0 1 1 1 0 1 1 0 0 1 2 0 2 1 1 0 0 0 1 2 1 2 1 0 1 2 0 1 0 1 1 0 2 0 0 0 2 0 2 2 0 0 2 1 1 1 1 1 1 1 1 1 2 0 0 1 2 0 0 0 1 0 0 2 1 2 0 1 0 2 1 0 1 2 2 0 1 1 0 1 2 2 2 1 2 0 0 1 0 1 0 2 2 2 1 0 0 2 1 0 0 2 1 2 1 2 1 2 0 2 131 | 931 0 2 1 2 1 0 2 1 2 0 2 0 0 2 0 0 2 0 0 1 2 1 2 0 0 0 2 1 0 0 1 0 1 1 0 2 2 0 0 2 1 2 2 1 2 1 0 2 1 0 2 1 1 1 1 1 2 2 0 0 0 1 1 2 0 2 2 2 2 1 1 1 2 0 1 0 2 0 1 2 2 1 0 1 1 2 1 2 0 0 0 1 0 1 1 0 0 0 0 1 1 1 2 0 2 1 0 1 0 1 1 2 2 1 2 1 1 2 0 0 0 1 0 0 1 1 2 0 2 0 0 0 1 0 1 1 1 1 2 1 0 0 1 0 0 1 1 1 0 1 1 0 0 1 0 2 0 0 0 1 2 0 0 2 2 1 1 2 0 2 2 0 2 2 1 0 0 0 2 1 2 1 2 2 1 1 2 2 1 0 2 2 2 2 2 0 1 0 0 2 132 | 932 0 2 1 2 0 0 2 1 2 0 1 0 2 2 2 0 2 1 2 0 2 0 1 2 0 1 1 0 2 2 0 0 1 2 2 2 2 1 0 1 2 2 1 1 2 2 0 2 2 1 1 1 1 1 2 2 1 1 1 0 0 2 2 2 0 1 1 2 0 2 0 0 2 0 2 0 1 1 2 2 2 2 0 1 0 2 0 1 0 1 0 2 0 1 1 0 2 0 0 0 2 0 1 2 2 2 0 0 0 0 0 2 1 2 1 2 1 1 0 1 1 1 1 0 2 0 2 0 2 0 0 0 1 1 0 0 2 1 2 0 1 1 2 1 1 2 0 1 0 0 2 0 0 0 0 2 0 1 0 0 2 1 0 2 2 1 1 2 0 2 1 0 2 1 0 2 1 2 0 1 0 2 2 2 0 0 2 0 1 2 1 1 2 0 1 1 1 1 0 2 133 | 933 0 2 0 2 2 1 2 2 2 0 0 0 0 2 0 1 2 1 0 0 1 1 1 0 0 2 1 0 1 1 0 0 1 1 0 2 2 0 1 2 2 1 1 1 0 1 0 2 0 1 1 1 2 1 2 1 2 2 0 1 0 1 1 2 0 1 2 1 1 2 0 2 2 0 1 0 0 0 1 2 2 2 0 2 0 2 1 2 0 2 0 1 1 2 0 0 1 0 0 1 2 1 1 1 1 2 0 1 0 1 1 2 2 1 2 1 2 1 0 1 0 1 2 0 0 1 1 0 1 0 0 0 0 0 1 0 2 0 1 1 2 1 1 2 0 1 0 0 1 1 1 0 0 1 1 2 1 1 0 2 1 0 0 1 2 0 0 2 0 2 2 2 2 2 1 0 1 1 0 0 0 2 2 2 0 0 1 1 2 1 1 2 2 1 2 0 0 0 1 2 134 | 934 0 2 0 1 1 1 2 1 1 0 2 0 0 2 0 1 2 0 0 1 1 1 0 0 0 1 2 1 2 1 1 0 2 0 2 2 2 1 1 1 2 1 2 2 2 0 0 2 0 0 1 0 1 0 2 1 2 1 2 0 0 2 1 2 0 1 2 2 1 2 2 0 2 0 1 0 2 0 0 1 1 2 0 2 0 2 1 1 0 2 0 0 1 2 0 0 1 0 0 0 1 0 2 0 1 0 1 0 0 1 1 1 0 1 1 0 1 1 1 0 2 0 2 0 0 0 1 0 2 2 0 0 1 1 0 1 2 1 1 2 2 2 1 0 1 0 1 1 0 2 2 1 0 1 0 2 0 0 0 1 1 0 0 1 2 0 2 2 0 1 2 0 2 2 1 0 1 2 1 1 1 1 2 2 1 0 2 1 2 1 0 2 2 1 2 2 0 2 0 1 135 | 935 0 2 0 2 2 1 2 2 2 0 2 0 0 2 1 0 2 0 1 1 2 0 2 2 0 1 2 0 2 0 2 0 1 1 0 2 2 0 0 2 2 2 1 2 2 2 0 2 0 0 2 0 0 1 1 1 1 2 0 1 0 0 2 2 0 1 1 2 1 2 1 0 2 0 1 0 0 0 2 1 2 1 0 2 1 2 1 2 0 2 0 1 1 2 1 0 0 1 0 1 0 1 1 0 1 0 0 1 0 0 0 2 2 2 2 2 1 2 0 1 1 1 2 1 1 0 2 0 2 0 0 0 2 0 0 1 2 1 1 0 1 1 2 2 1 1 2 0 0 1 1 0 0 2 0 2 0 0 0 0 2 0 0 2 1 0 0 2 0 1 2 2 0 0 2 0 0 2 2 1 0 2 2 2 2 1 2 1 2 0 1 2 0 1 0 2 0 1 0 1 136 | 936 0 1 0 2 2 0 2 1 2 0 1 1 0 1 1 0 2 0 0 0 1 0 2 1 0 0 0 1 2 0 1 0 0 1 0 2 2 1 2 2 2 1 1 1 1 1 0 2 1 0 1 1 1 1 1 0 1 1 1 1 0 2 1 2 0 1 1 1 0 2 2 0 1 0 1 0 1 1 1 1 2 2 1 1 0 2 1 2 0 0 0 1 1 1 1 0 1 0 0 2 1 0 2 0 2 1 0 0 0 1 0 2 2 2 2 1 0 2 0 1 0 2 2 0 1 1 1 0 1 1 0 0 0 0 0 1 2 1 2 1 1 2 1 1 1 0 1 0 1 0 1 0 0 1 1 0 0 0 0 1 2 0 0 1 2 1 0 2 0 1 0 2 2 1 1 0 0 2 0 0 0 2 2 2 1 0 2 1 1 1 0 2 2 1 1 1 1 0 1 2 137 | 937 0 1 0 2 0 0 1 0 0 0 2 0 0 2 1 0 2 0 0 0 2 1 1 1 0 0 1 1 1 1 1 2 1 1 1 2 2 0 1 1 2 2 0 1 2 0 0 2 1 0 2 2 0 1 0 1 2 2 0 1 0 1 1 2 0 1 1 2 1 2 2 1 1 0 2 0 1 0 0 2 2 1 1 2 0 2 1 1 0 2 0 1 1 0 0 0 1 1 0 0 2 1 2 0 2 2 0 0 0 1 0 2 2 1 2 1 0 1 1 1 1 2 2 0 0 1 2 0 2 0 0 0 2 0 0 0 2 1 2 1 1 1 1 0 1 2 0 0 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 2 1 0 0 1 0 1 1 1 2 1 0 1 2 1 1 0 1 2 2 2 0 0 2 2 1 1 0 1 1 2 2 1 0 1 0 1 138 | 938 0 1 0 2 1 1 2 1 2 1 1 0 0 2 1 1 2 0 0 1 0 0 0 2 1 0 2 0 2 0 1 1 0 1 2 2 2 1 0 1 2 1 1 2 1 1 0 2 0 0 2 0 0 1 2 1 0 0 0 1 0 2 1 2 0 2 2 2 0 2 1 1 2 0 2 0 1 1 0 2 2 2 0 2 1 2 1 0 0 1 0 0 0 1 0 0 0 0 0 1 2 1 2 1 1 2 1 0 0 0 0 2 1 2 1 1 2 1 0 0 1 2 2 0 0 0 2 0 2 1 0 0 2 0 0 2 2 1 2 1 1 2 1 1 1 2 1 1 1 0 0 0 0 0 0 2 0 0 0 0 2 1 1 1 2 0 0 1 0 1 2 0 1 1 1 0 0 1 1 1 0 2 2 2 1 0 2 2 1 1 0 1 2 2 2 2 0 1 0 1 139 | 939 0 0 0 2 0 2 2 2 2 0 1 1 0 2 1 0 2 0 0 1 1 0 2 0 0 1 1 1 0 0 1 1 2 1 1 2 2 1 0 2 2 1 2 2 2 1 2 2 1 0 1 1 1 2 2 2 1 2 1 0 0 1 1 2 0 2 1 2 1 2 1 1 2 0 1 0 2 1 1 2 1 2 0 0 0 2 0 2 0 2 0 2 1 1 1 0 1 0 0 0 1 0 2 2 1 1 1 0 0 0 0 2 1 2 1 2 0 0 0 1 0 2 2 0 0 0 0 0 2 1 0 0 2 0 1 1 2 1 2 1 2 1 2 1 0 1 1 2 0 0 1 0 0 2 0 1 1 1 0 1 2 0 0 1 1 0 0 1 1 1 1 2 2 1 1 0 0 0 2 1 0 2 2 2 1 0 1 1 1 0 0 2 1 2 2 1 1 0 0 1 140 | 940 1 2 1 2 0 1 2 1 2 0 2 2 0 1 1 0 2 0 0 0 1 0 1 2 0 1 1 1 2 1 1 0 0 1 1 2 1 0 1 1 2 2 1 0 1 1 1 2 0 0 2 1 1 2 1 2 2 2 1 0 1 2 2 2 0 2 1 0 1 2 1 1 2 0 1 0 1 0 0 1 0 2 0 1 0 2 0 1 0 1 0 1 0 2 1 0 0 0 1 0 1 0 0 0 2 2 0 0 0 1 1 1 1 1 2 0 2 1 0 0 0 2 1 0 1 0 0 0 2 1 0 0 1 0 0 1 2 2 2 1 1 2 1 0 1 1 2 1 0 1 1 0 0 0 0 2 0 0 0 1 2 0 0 1 2 0 1 1 0 1 1 2 2 1 0 1 1 1 1 1 1 2 2 2 1 0 2 2 0 0 0 2 1 1 1 2 2 1 0 2 141 | 941 0 1 1 2 1 0 2 1 2 0 2 2 0 1 2 0 2 0 0 2 1 1 2 0 1 0 1 0 1 1 2 0 0 1 1 2 2 0 0 2 2 1 2 1 1 2 0 2 0 0 1 2 0 0 1 1 1 2 2 1 0 1 2 2 0 2 0 2 0 2 1 0 1 1 1 0 1 1 2 1 2 2 0 0 0 2 0 1 0 2 0 1 1 0 1 0 2 0 0 0 2 1 1 0 2 0 2 0 1 0 0 1 0 1 2 2 2 2 0 0 0 2 2 0 0 0 2 0 2 0 0 0 2 0 0 1 1 0 2 1 2 2 2 0 0 2 1 0 0 1 1 0 0 1 0 2 0 1 0 0 2 0 0 2 2 1 2 2 0 2 1 2 2 1 1 0 1 0 1 0 1 1 2 2 0 0 2 1 1 0 0 1 1 1 0 1 0 1 0 1 142 | 942 0 1 0 1 1 0 1 2 2 0 2 1 0 2 0 0 2 0 1 2 2 0 2 1 0 0 1 1 2 0 1 2 2 0 0 2 2 0 1 2 2 1 0 2 1 0 0 1 0 0 2 1 0 1 0 1 1 2 1 0 0 1 0 2 0 2 2 2 0 1 0 0 1 0 2 0 1 1 1 2 1 2 0 0 0 1 1 2 1 0 0 1 0 1 1 0 0 1 0 0 2 2 2 1 0 2 1 0 0 0 0 2 1 1 0 1 2 2 0 1 1 2 2 0 0 1 1 0 2 1 0 0 1 0 0 2 2 0 2 0 2 2 1 0 2 1 2 1 2 0 0 0 0 2 0 2 1 0 0 0 2 0 0 1 2 1 1 1 0 2 2 1 2 2 1 0 0 0 2 1 0 2 2 2 1 0 1 0 0 2 1 2 1 2 1 0 1 1 0 1 143 | 943 1 0 0 2 2 1 2 1 1 0 2 1 0 2 2 0 2 0 0 0 1 2 1 2 0 1 2 0 1 0 1 0 1 0 0 1 2 0 0 2 2 2 0 2 1 1 0 2 1 0 1 0 1 0 2 1 1 2 2 1 0 2 2 2 0 0 2 2 0 1 1 1 0 0 1 0 2 2 0 1 2 1 0 0 0 2 0 2 0 1 0 1 1 1 0 0 0 0 0 1 2 0 2 2 2 1 0 1 0 1 1 2 1 1 2 2 1 1 0 0 2 1 2 0 1 0 2 0 1 1 1 0 1 0 0 1 2 1 2 0 1 0 1 2 0 2 0 0 0 0 1 0 0 1 0 2 1 1 0 0 1 0 0 2 2 1 0 2 0 2 1 1 1 2 0 0 0 1 1 0 1 2 2 2 1 1 2 1 0 0 0 1 1 0 2 0 0 0 0 2 144 | 944 0 1 0 2 0 2 2 2 2 0 1 0 0 1 1 1 2 0 1 1 1 2 2 1 1 1 1 1 2 1 1 1 1 1 1 2 2 1 0 2 2 1 2 2 2 2 0 2 0 1 2 0 0 0 2 1 1 2 1 0 0 1 1 2 0 1 1 2 0 2 2 1 1 0 2 0 1 0 2 2 2 2 0 2 1 2 0 0 0 2 0 2 0 2 0 0 0 0 0 1 1 1 2 1 1 1 0 2 0 2 0 1 2 2 2 2 1 2 0 0 0 2 2 0 0 0 2 0 2 0 0 1 1 0 0 1 1 0 2 1 1 1 1 0 0 0 0 2 0 2 2 0 0 1 0 2 0 0 0 0 1 1 0 1 2 0 0 2 0 2 1 2 2 2 0 1 0 2 1 1 1 2 2 2 0 0 2 0 2 1 0 2 1 2 2 1 0 1 1 2 145 | 945 0 1 0 2 1 0 2 1 2 0 2 0 0 2 2 0 2 0 0 0 2 2 0 0 0 1 1 0 2 0 0 1 0 1 0 2 2 0 0 1 2 1 1 1 1 2 0 2 1 0 2 2 2 0 1 1 1 2 1 1 0 2 1 2 0 1 2 1 0 2 1 1 1 0 2 0 2 0 0 1 2 2 0 1 0 1 0 2 0 1 0 1 0 2 1 0 1 0 0 0 2 2 2 0 1 1 0 0 0 2 0 2 1 2 1 0 1 0 0 0 0 1 2 0 0 0 2 0 2 1 0 0 2 2 0 0 2 1 1 1 1 0 1 0 1 0 1 0 1 1 1 0 0 0 0 2 1 1 0 0 1 0 0 2 2 1 1 1 0 2 2 1 1 2 1 1 1 0 2 1 0 2 2 2 1 0 2 1 1 0 1 2 1 1 2 1 0 2 0 2 146 | 946 1 2 1 2 0 0 1 1 2 0 2 2 0 1 2 0 2 0 1 1 2 1 0 1 2 1 2 0 2 1 1 1 2 1 2 2 2 1 0 2 2 2 1 2 1 0 0 2 0 0 2 0 1 0 1 2 1 1 2 1 0 0 1 2 0 1 1 2 0 2 1 1 1 0 1 0 1 1 0 2 2 0 0 2 0 2 0 2 0 1 0 1 2 2 1 1 0 0 1 1 2 2 2 0 2 1 0 1 0 1 0 1 2 2 1 1 1 0 0 0 0 1 2 1 1 0 1 0 1 1 0 0 1 0 0 0 2 1 1 1 2 1 2 0 1 1 0 1 0 1 2 0 0 1 0 2 1 0 0 0 1 0 0 1 2 1 0 1 0 2 2 1 2 1 0 1 1 1 1 0 1 2 2 2 2 1 0 2 1 0 0 2 0 1 2 1 1 2 0 1 147 | 947 0 2 1 2 0 1 2 0 2 0 2 1 1 2 0 0 1 1 0 1 0 1 2 1 0 1 1 0 2 0 1 1 0 1 0 2 2 0 1 2 2 1 1 1 2 1 1 2 0 0 1 1 2 1 0 2 0 1 1 1 0 2 2 2 0 1 2 2 1 1 1 0 2 1 2 0 0 0 1 2 2 2 1 1 0 2 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 1 2 1 2 1 1 0 0 2 1 2 1 2 0 2 0 2 1 2 1 0 2 1 2 1 1 0 2 2 0 0 2 1 1 1 2 1 2 1 1 1 1 0 1 1 1 0 2 0 0 0 0 1 1 2 0 1 0 0 2 1 0 1 0 0 0 2 0 2 2 2 2 1 0 1 1 1 2 1 0 1 2 2 0 0 1 1 1 1 0 2 1 2 1 0 1 1 0 1 148 | 948 0 1 0 2 1 1 1 1 0 0 1 1 0 2 2 2 2 1 0 1 1 0 2 2 0 1 1 0 2 1 1 0 0 1 1 2 2 0 0 2 2 2 1 2 1 1 0 2 0 0 0 1 0 0 2 1 1 2 0 1 0 2 2 1 1 2 2 2 1 2 1 0 2 0 1 1 1 2 0 2 2 2 2 1 0 2 1 2 1 1 0 2 1 2 1 0 2 1 0 2 2 1 2 0 1 1 1 0 0 1 1 2 1 1 2 1 2 1 0 1 1 2 2 0 1 0 1 0 2 0 1 0 2 0 0 0 2 1 1 1 1 0 2 2 0 2 1 0 1 2 1 0 0 2 0 2 1 1 0 0 1 0 0 2 1 0 2 2 0 2 2 1 1 1 0 1 1 1 1 2 1 2 2 2 0 0 2 0 2 1 0 0 2 1 2 0 1 1 0 0 149 | 949 0 2 0 1 0 1 2 2 2 0 2 1 0 2 1 0 2 0 0 1 2 0 1 0 0 0 0 0 1 0 0 2 1 1 1 2 2 0 1 2 2 0 1 2 2 2 0 2 1 0 2 0 1 1 2 1 2 0 2 0 0 1 0 2 0 2 2 2 0 2 0 0 0 0 2 0 2 0 2 1 2 2 0 1 0 2 1 1 0 1 0 1 0 2 2 0 0 1 1 1 2 1 2 0 1 1 0 1 0 1 1 1 0 2 2 2 0 2 0 0 0 1 2 0 0 0 2 0 2 1 0 0 1 0 1 1 2 0 2 1 0 1 2 0 1 2 1 1 2 0 0 0 0 2 0 2 1 1 1 0 2 1 0 2 2 0 0 1 0 2 2 2 2 0 1 1 0 1 2 1 2 2 2 2 0 0 2 0 1 0 0 2 1 1 1 0 0 0 0 2 150 | 950 0 2 0 2 1 1 1 2 2 0 1 0 0 2 1 0 0 0 0 1 2 0 2 1 1 0 0 0 2 0 0 0 0 1 1 2 2 2 0 1 2 1 1 0 1 0 0 2 1 1 2 2 0 0 0 2 2 2 1 0 0 2 0 2 0 1 2 2 1 2 1 1 2 0 1 0 1 1 2 2 1 2 1 1 0 1 1 0 0 0 0 1 1 1 0 0 1 0 0 0 1 1 2 0 2 0 0 0 0 0 1 2 1 2 1 1 0 0 0 1 0 2 0 0 0 0 1 0 2 1 0 0 0 0 1 1 1 1 2 0 1 0 1 1 0 1 0 1 2 0 1 0 0 1 0 1 0 0 1 0 2 0 0 2 1 0 1 2 0 2 2 1 2 2 0 1 2 1 2 0 2 2 2 2 0 0 1 2 1 2 0 2 2 1 1 2 2 1 0 2 151 | 951 0 2 0 2 1 1 2 1 2 0 2 1 0 2 2 0 1 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 0 0 2 1 2 0 1 2 2 1 1 1 1 1 0 2 2 0 1 0 1 1 1 0 1 1 0 0 0 1 0 1 0 1 2 2 0 2 1 1 1 0 2 0 0 2 0 2 2 2 0 2 0 2 1 1 0 2 0 2 0 2 0 0 1 1 0 1 2 0 1 1 1 1 0 0 0 0 0 2 1 1 1 1 1 2 0 0 0 2 2 0 1 1 2 0 2 1 0 0 1 2 0 1 2 0 1 0 2 2 2 0 1 1 0 0 1 2 1 0 0 1 0 2 1 1 1 0 1 0 0 2 2 1 0 1 0 1 1 1 2 1 1 0 0 2 1 0 0 2 2 2 0 0 0 1 1 0 0 2 1 1 0 2 0 2 0 2 152 | 952 0 2 1 2 1 1 2 2 2 0 2 0 0 2 1 0 2 0 0 0 2 2 2 1 1 0 0 1 1 1 1 0 1 1 1 2 2 0 1 2 2 1 0 2 1 1 0 2 0 0 1 1 1 0 1 2 1 0 0 0 0 1 2 2 1 1 0 2 0 1 1 0 0 0 0 0 1 1 1 1 2 1 0 2 1 1 0 1 1 0 0 1 1 0 2 0 2 1 0 1 1 2 1 1 2 1 0 1 0 0 0 2 2 2 2 2 1 1 0 0 1 1 2 0 0 0 2 0 2 0 0 0 2 1 1 0 2 2 2 2 2 1 1 0 1 1 0 0 0 0 0 0 0 2 0 2 1 1 0 0 2 1 0 0 1 0 0 2 0 1 2 1 1 1 1 1 0 1 1 0 1 2 2 2 2 0 2 0 1 2 1 2 1 1 1 0 1 1 1 1 153 | 953 0 2 0 2 1 0 2 1 2 0 1 0 0 2 1 1 2 1 0 2 0 0 2 1 0 1 2 0 2 2 0 1 0 0 1 2 2 1 0 1 2 1 0 2 0 2 0 2 0 1 1 1 2 1 1 1 1 2 2 0 0 2 1 2 0 2 2 2 0 2 2 0 2 0 1 0 1 0 2 1 1 2 0 1 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 2 0 1 0 2 2 1 0 0 1 2 2 2 2 1 1 1 1 1 1 2 2 0 1 0 2 1 2 2 0 0 1 0 0 1 2 1 2 2 1 2 1 0 0 2 2 1 0 1 0 0 0 1 0 2 2 1 0 0 2 0 1 1 1 0 0 0 0 1 2 2 2 1 0 2 0 1 0 1 1 2 2 2 2 0 2 1 2 1 1 2 0 2 0 1 1 1 0 2 154 | 954 0 0 0 2 2 1 2 2 2 1 0 0 0 1 0 0 1 0 0 1 2 2 0 0 0 1 2 1 2 0 2 0 0 0 1 2 2 0 0 2 2 1 1 2 1 1 0 2 1 0 1 2 0 0 2 2 1 2 0 1 0 1 1 2 0 1 2 2 0 2 1 0 2 1 0 0 2 0 0 1 2 2 2 2 0 2 0 2 1 1 0 1 0 0 1 1 1 1 1 0 0 0 2 2 2 0 0 2 0 0 1 2 0 2 1 2 1 1 0 1 1 1 2 0 0 1 2 0 2 0 0 0 2 0 1 1 2 2 2 0 1 2 2 0 1 2 0 0 0 1 2 0 0 2 1 2 2 0 0 0 2 0 0 2 1 0 0 2 0 1 2 2 0 2 0 1 2 2 0 0 1 1 2 2 0 0 0 1 1 1 1 1 1 1 2 0 0 2 0 2 155 | 955 0 0 0 2 0 1 2 1 2 0 0 1 0 1 1 0 1 0 0 0 0 0 2 1 0 1 2 0 2 1 0 2 1 0 1 2 1 0 0 2 2 1 1 2 1 1 1 2 1 2 2 1 1 2 0 2 2 2 0 1 0 2 2 1 1 2 0 2 1 2 2 0 0 0 1 0 0 1 0 1 1 2 1 2 0 2 1 1 0 2 0 0 0 2 0 1 2 0 0 1 2 1 1 0 2 1 1 1 0 1 1 2 1 2 0 1 0 0 0 0 2 2 2 0 0 0 2 0 2 0 0 0 1 0 0 2 2 0 2 1 1 0 2 1 1 1 0 2 0 2 2 0 0 1 2 2 0 0 0 0 1 0 1 2 2 0 1 1 1 1 2 1 2 1 0 0 1 1 1 0 1 2 2 2 2 1 1 2 1 0 0 1 2 2 2 2 1 0 0 2 156 | 956 0 2 0 2 1 1 2 0 2 0 1 0 1 2 1 0 2 0 1 2 1 0 2 0 0 1 1 0 2 0 0 0 0 0 0 2 2 1 0 2 2 1 0 2 2 0 0 2 1 1 1 1 0 0 1 1 2 2 2 0 0 0 1 2 0 1 2 2 0 2 1 0 1 0 0 0 1 2 1 1 2 2 0 1 0 2 1 1 1 0 0 2 0 1 0 0 1 0 0 0 2 0 2 1 0 1 0 0 0 0 0 2 1 1 0 0 2 1 0 2 2 1 2 0 2 0 0 0 2 0 0 0 0 2 1 0 2 1 1 1 1 2 2 0 1 1 0 0 0 0 1 0 0 2 1 2 0 1 1 1 2 0 0 1 2 1 1 2 0 1 2 2 1 1 1 0 0 1 2 1 1 1 2 2 1 0 1 0 1 0 1 0 1 0 0 0 1 1 0 1 157 | 957 1 1 0 2 0 2 2 2 2 0 1 0 0 1 1 0 2 0 0 0 1 0 2 1 0 1 0 0 2 0 1 0 1 2 1 2 2 1 0 2 2 1 1 1 1 1 1 2 1 1 2 0 0 0 0 0 1 2 2 1 0 1 1 2 0 2 2 2 2 2 2 0 2 0 2 0 1 0 0 2 2 1 0 0 0 2 0 2 0 1 0 0 0 1 1 0 2 0 0 0 0 0 2 1 2 0 0 1 1 0 0 2 2 1 1 2 1 2 0 0 0 2 2 0 0 1 0 0 1 2 1 0 0 0 1 1 2 0 2 0 2 2 2 0 0 2 1 1 1 1 1 0 0 1 1 1 1 1 0 0 2 0 0 2 1 1 0 1 0 1 1 2 2 0 0 0 0 2 1 1 0 2 2 2 2 0 1 0 2 1 0 1 1 1 2 1 0 0 0 2 158 | 958 1 2 0 2 1 2 2 2 1 0 1 0 1 1 0 0 1 0 0 2 1 1 1 1 0 0 1 0 2 1 1 1 0 0 2 2 2 0 0 2 1 2 0 2 1 1 0 2 0 0 1 1 0 1 1 0 1 2 1 0 0 2 2 2 0 2 1 2 1 2 1 1 1 0 1 0 0 0 1 2 2 2 0 1 1 2 1 1 0 2 0 2 0 1 1 0 1 1 0 1 2 0 2 1 2 0 1 0 0 0 1 2 1 2 2 2 2 0 1 2 0 2 2 0 0 0 0 0 2 1 1 0 1 0 0 2 2 1 1 0 1 0 2 1 1 2 0 0 0 2 0 0 0 2 1 2 1 1 0 1 2 0 0 1 2 0 1 2 0 2 1 2 2 1 0 1 0 1 1 1 0 2 2 2 2 1 2 1 0 0 0 1 1 1 1 1 0 0 1 1 159 | 959 0 2 0 2 0 1 2 2 2 0 2 1 0 2 0 0 2 0 1 0 1 1 2 0 1 0 2 0 1 1 0 0 2 2 1 0 2 0 0 2 2 1 1 1 2 0 0 2 1 0 1 1 1 1 0 2 1 2 1 1 0 2 2 2 0 2 1 2 2 2 2 2 2 0 2 0 1 0 1 1 2 1 1 1 0 2 2 1 2 2 1 1 0 1 0 0 1 0 1 1 2 0 2 1 1 1 0 1 0 0 2 1 0 2 2 2 1 2 0 2 1 1 1 0 1 0 1 0 2 1 0 0 1 0 0 2 2 0 2 2 2 0 2 0 0 1 1 0 1 0 2 0 0 2 1 2 1 1 0 1 2 0 0 2 2 0 1 1 0 2 2 2 2 1 1 1 1 0 2 1 1 2 2 2 2 0 2 0 2 0 1 2 2 1 1 1 0 1 1 2 160 | 960 0 2 0 2 1 0 2 1 2 0 2 0 0 2 0 0 1 0 0 1 1 1 2 0 0 1 1 0 2 1 0 1 1 2 1 2 2 1 0 2 2 0 2 2 2 1 0 2 1 1 2 0 1 1 1 2 2 2 1 1 0 0 2 1 0 1 1 2 1 1 1 0 0 0 2 0 1 0 1 0 1 2 0 1 0 2 0 1 0 0 0 1 0 1 1 0 2 0 0 0 2 0 2 2 1 1 1 1 0 2 0 2 0 2 1 1 1 0 0 0 1 1 2 0 0 0 2 1 1 1 0 0 1 0 0 0 2 2 2 0 1 2 2 1 1 0 1 1 1 0 1 0 0 1 1 2 1 0 0 0 0 0 0 0 2 0 1 2 0 1 1 2 1 1 0 0 1 1 2 0 1 2 2 2 1 0 2 1 2 0 0 2 2 2 2 1 0 0 0 1 161 | 961 1 2 1 2 0 0 1 0 2 0 1 1 1 1 0 0 2 2 0 0 1 2 1 0 0 0 2 0 2 0 0 0 2 0 1 2 2 1 0 2 2 1 0 2 1 1 0 2 1 1 2 1 0 0 0 0 2 2 0 1 0 2 2 2 0 2 2 2 0 2 2 1 2 1 1 0 0 1 0 2 2 2 0 1 0 2 0 2 1 0 1 1 1 1 0 0 1 1 0 0 2 1 2 1 0 1 1 1 0 1 1 1 2 2 2 1 1 1 0 2 2 1 2 0 0 0 2 0 1 2 0 0 1 0 0 1 2 1 2 1 2 2 2 1 2 1 2 0 1 1 0 0 0 2 1 2 1 1 0 0 2 0 0 1 2 0 2 2 0 2 2 2 2 2 0 1 1 2 1 0 1 1 2 2 2 0 0 1 2 0 0 2 1 2 2 2 1 1 1 2 162 | 962 0 0 0 1 2 1 2 0 2 0 2 0 0 2 0 0 2 0 1 0 1 0 2 1 1 1 2 0 1 1 2 1 0 1 0 2 2 0 1 2 2 1 0 2 1 1 0 2 2 1 2 0 0 0 1 2 1 1 1 1 0 2 1 2 0 1 0 2 1 2 1 0 1 0 2 0 0 0 1 0 2 2 0 0 0 2 0 1 0 2 0 1 0 2 0 0 0 0 0 0 2 1 2 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 0 1 0 2 1 0 1 0 2 0 2 0 0 1 2 0 1 1 2 1 1 0 0 2 2 1 1 0 2 1 1 1 1 0 0 1 0 2 0 2 0 1 1 0 1 1 2 0 0 2 0 1 2 1 2 1 1 1 1 1 2 0 0 1 2 2 1 0 2 1 1 1 0 1 2 2 0 0 0 2 0 1 163 | 963 1 2 0 2 2 1 2 1 2 0 2 1 0 1 0 0 2 0 0 0 1 0 1 2 0 0 1 0 1 0 0 2 0 1 0 2 2 0 0 2 2 2 1 2 1 2 0 2 0 1 2 1 1 0 2 1 1 2 2 1 0 0 1 2 1 2 1 2 1 2 0 1 2 0 2 1 2 1 2 2 2 2 0 2 0 2 1 2 0 0 0 1 1 0 2 0 1 0 1 1 1 0 1 1 1 0 0 0 0 2 1 2 1 1 0 1 0 1 0 2 0 2 2 0 1 0 1 0 2 1 0 0 1 1 0 1 2 1 2 1 1 2 2 0 0 1 0 1 0 2 1 0 0 1 0 2 2 0 0 1 1 0 0 1 2 0 1 1 0 2 2 1 2 2 0 0 1 1 0 1 0 1 2 2 1 0 1 1 0 0 1 1 1 1 1 0 1 2 0 2 164 | 964 0 2 0 1 1 2 2 0 1 0 0 0 0 2 1 0 2 1 0 1 0 1 2 0 0 0 2 1 1 0 0 0 2 0 2 2 2 1 1 2 2 2 1 2 1 1 1 2 0 0 2 1 0 1 2 2 1 0 2 1 0 1 2 2 0 2 1 1 0 2 1 0 2 0 2 0 0 0 1 2 2 2 0 2 0 2 1 1 1 2 0 2 0 1 1 0 1 0 0 1 1 1 2 0 1 2 0 0 0 0 1 1 2 2 2 2 1 2 0 0 0 2 2 0 1 0 1 0 2 1 0 0 1 0 0 1 1 1 2 1 1 2 2 0 1 1 1 0 2 1 1 0 0 0 1 1 0 1 0 0 2 0 1 2 2 0 0 2 0 1 2 2 2 1 0 0 1 0 1 1 0 2 2 2 1 0 1 1 0 1 1 1 2 1 2 0 0 1 1 2 165 | 965 0 2 0 2 1 2 2 2 2 0 1 1 0 0 1 0 2 0 0 2 2 1 1 2 2 1 1 0 2 2 0 1 1 1 0 2 2 1 0 1 2 0 0 2 1 0 0 2 1 1 2 1 0 1 2 1 1 2 0 0 0 2 2 2 0 2 0 2 1 2 2 0 1 0 2 0 1 0 2 1 2 2 0 1 0 2 1 1 1 2 0 1 1 2 0 0 1 0 0 1 2 1 1 0 1 1 0 0 0 0 1 1 1 1 1 1 1 2 0 2 1 1 2 0 0 0 2 0 2 0 0 0 1 0 0 1 2 1 2 0 2 1 2 0 1 2 2 1 0 0 1 0 0 2 0 2 0 0 0 0 2 1 0 1 1 0 0 2 1 2 2 1 2 2 1 1 2 1 1 1 0 2 2 2 1 0 2 1 1 0 0 2 1 1 1 1 2 1 0 2 166 | 966 2 1 0 1 0 1 2 2 2 0 2 1 0 1 0 1 2 1 1 2 2 0 1 1 1 1 2 0 2 0 1 0 1 0 0 2 2 0 1 2 2 2 1 2 1 1 0 2 1 0 1 0 2 0 2 1 1 2 0 1 0 2 2 2 0 1 1 2 0 2 1 0 2 0 2 1 2 0 1 1 2 2 0 0 0 2 0 1 0 2 0 2 1 1 0 0 0 0 0 0 2 0 2 0 0 1 0 1 0 0 1 2 0 1 1 2 1 2 0 0 0 0 2 1 0 0 2 0 2 2 0 0 2 0 0 0 2 0 2 1 2 2 0 0 0 1 1 1 0 0 2 0 0 0 0 2 0 1 0 0 1 2 0 2 1 1 0 2 0 1 1 1 2 2 2 2 0 2 1 1 0 2 2 2 1 0 2 1 1 0 0 2 2 1 1 1 1 0 1 2 167 | 967 1 1 2 2 0 1 2 2 2 0 1 1 0 2 2 1 1 0 0 2 1 1 0 0 1 1 1 0 1 0 2 1 0 0 2 2 2 0 0 1 2 2 2 2 2 1 0 2 1 0 1 1 0 0 1 1 1 2 0 1 1 2 1 2 0 2 1 1 1 2 1 0 2 0 2 0 1 0 1 1 1 2 0 1 0 2 2 1 1 2 0 0 1 0 2 0 1 0 0 1 2 1 2 0 1 2 0 0 0 1 0 2 1 2 1 1 2 2 0 0 0 2 2 1 0 0 2 0 2 1 0 0 0 0 0 0 2 1 2 0 1 2 2 1 1 2 1 0 0 1 1 0 0 1 1 2 1 1 0 2 2 0 0 2 2 1 0 2 0 1 2 1 1 2 0 2 1 1 1 2 1 1 2 2 1 0 1 0 1 0 2 2 2 2 2 1 1 1 0 1 168 | 968 0 1 1 1 0 1 2 1 1 0 0 0 1 2 2 0 2 0 0 2 2 0 2 1 0 0 2 0 2 0 1 0 2 2 1 2 2 0 0 2 2 2 1 2 2 2 0 2 0 0 1 1 1 2 2 2 2 2 2 2 0 1 2 2 0 2 2 2 0 2 1 1 1 0 2 0 1 0 0 0 2 2 0 1 1 2 0 1 1 2 0 1 1 1 1 0 1 0 0 1 2 2 1 1 1 0 0 0 0 1 1 2 0 1 1 2 1 1 0 1 0 2 2 0 1 1 2 0 2 1 0 1 1 0 1 0 2 1 2 0 2 2 0 0 0 1 1 1 0 0 0 0 0 1 0 1 0 2 0 0 1 0 1 2 1 0 1 2 0 2 2 1 2 1 0 1 0 1 1 1 0 2 2 2 0 0 1 1 1 0 0 1 1 1 1 1 0 1 1 2 169 | 969 0 2 0 1 1 2 2 0 2 0 2 0 0 1 1 0 2 0 0 1 0 0 2 0 0 1 1 1 2 0 0 0 0 0 1 2 2 0 0 2 2 2 2 1 1 2 0 2 0 0 2 1 0 1 0 1 1 1 2 1 0 1 1 2 0 1 2 2 1 2 1 0 2 0 0 0 0 1 2 1 1 1 0 2 0 2 1 2 0 0 0 0 2 1 0 0 1 0 0 0 2 0 2 1 2 1 0 1 0 1 1 2 1 2 2 1 0 1 0 1 1 2 1 0 2 0 2 0 2 2 0 0 2 1 1 1 2 1 0 1 1 2 2 0 0 1 1 2 0 1 2 0 0 2 0 1 1 1 0 1 1 0 0 1 1 1 1 2 0 1 2 1 2 2 0 0 0 1 0 2 1 1 2 2 1 0 2 1 2 1 0 1 0 2 1 1 0 0 1 1 170 | 970 0 0 1 2 2 2 2 2 2 0 1 0 1 2 1 0 2 0 0 2 0 0 2 0 0 0 1 0 2 2 0 1 1 0 2 2 1 0 0 1 2 1 1 1 1 0 0 2 1 0 2 2 0 0 2 2 1 2 1 1 0 2 0 2 0 0 1 2 1 2 0 0 1 0 1 0 0 1 1 1 2 2 1 1 2 2 1 2 0 1 0 1 1 1 1 0 1 0 0 1 1 0 2 0 2 2 0 0 0 0 0 2 2 2 2 2 0 0 0 1 0 2 1 0 0 0 2 0 2 1 0 0 2 0 0 2 2 1 1 1 1 1 1 1 1 2 2 0 0 2 0 0 0 0 0 2 1 0 0 0 2 0 1 2 2 0 2 2 0 0 1 1 2 2 0 1 1 1 0 2 1 2 2 2 0 0 2 0 0 0 1 2 1 1 1 1 1 1 1 1 171 | 971 0 1 0 2 0 1 2 2 2 0 2 0 0 2 0 0 2 0 0 0 2 1 0 1 0 1 1 0 2 2 0 1 1 1 1 1 2 1 0 0 2 1 2 2 2 0 0 2 0 1 2 2 1 1 2 1 1 2 1 0 0 1 2 2 1 2 2 1 0 2 1 0 2 0 2 0 0 2 1 1 1 2 1 2 0 2 2 1 0 1 0 1 0 1 0 0 2 0 0 1 2 0 1 0 1 2 0 1 0 1 0 1 0 2 1 1 0 1 0 0 0 2 2 0 2 1 2 0 2 2 0 1 2 1 0 1 1 1 1 0 1 1 2 0 1 2 1 0 0 0 0 0 0 0 0 2 1 1 0 2 0 0 0 1 2 1 1 2 0 2 2 1 2 0 1 0 0 2 1 0 0 2 2 2 1 0 2 1 1 1 1 2 1 1 1 1 1 0 0 1 172 | 972 0 1 1 2 1 1 2 1 2 0 0 1 0 2 2 2 2 0 0 1 2 1 1 0 0 1 2 0 2 1 0 1 2 1 0 2 2 0 0 2 2 1 2 2 1 1 0 2 1 0 2 2 1 1 1 1 1 2 0 0 1 1 0 2 1 2 2 2 0 2 0 0 1 0 2 0 0 0 1 1 2 1 0 2 0 1 1 2 0 1 1 1 1 2 0 0 0 1 0 1 1 1 2 1 1 0 0 1 0 0 1 1 0 2 2 1 0 2 0 0 0 0 2 1 2 1 1 0 2 1 0 1 0 1 0 2 2 2 1 0 2 0 2 0 1 1 1 1 1 0 0 1 0 1 1 1 0 0 0 2 2 0 1 2 2 0 0 1 0 2 2 2 2 1 0 2 1 2 2 0 1 2 2 2 2 0 1 0 0 1 1 2 1 2 2 2 1 0 0 2 173 | 973 0 1 0 2 1 1 2 2 2 0 2 1 0 2 1 0 1 0 0 0 1 1 2 0 0 1 1 1 2 1 1 1 0 2 0 2 2 0 1 1 2 1 1 1 0 0 1 2 1 0 1 2 0 0 0 1 1 2 0 1 0 2 2 2 1 2 1 2 0 2 2 0 2 0 2 0 1 0 1 2 2 1 0 2 1 2 0 1 1 2 0 1 0 0 0 0 0 0 0 0 1 1 1 1 2 1 1 0 1 2 1 1 0 1 2 2 1 2 0 1 0 2 1 0 1 1 2 0 2 2 0 0 2 0 0 1 2 2 2 2 1 1 1 1 0 1 2 0 1 1 0 0 0 0 1 1 0 2 0 0 2 0 0 2 2 0 1 2 0 1 2 2 0 1 1 0 2 1 1 0 0 2 2 2 1 0 2 1 1 0 0 1 1 1 1 0 2 0 0 1 174 | 974 1 1 1 1 1 2 2 0 2 0 2 0 1 2 1 1 2 0 0 1 2 1 1 0 0 0 2 1 2 0 2 0 1 0 0 2 1 0 0 2 1 0 0 2 2 1 0 2 2 0 2 1 1 0 1 1 1 2 0 0 0 2 0 2 0 2 2 2 1 2 1 0 1 0 2 0 0 0 0 1 2 2 0 2 1 0 0 2 0 1 0 1 0 1 2 0 0 1 1 2 1 0 2 1 2 0 0 0 0 1 2 1 1 2 2 2 2 1 0 0 0 2 2 1 1 0 1 0 2 1 0 1 0 0 0 0 2 2 1 0 1 1 1 0 0 0 0 0 1 0 0 0 0 1 1 2 0 2 0 0 2 0 2 2 2 0 0 2 0 2 2 2 2 1 0 1 0 1 1 0 0 1 2 2 1 0 2 1 1 2 0 2 2 1 0 1 0 1 1 0 175 | 975 1 2 2 1 1 2 2 2 1 0 2 0 0 1 2 0 1 0 0 1 2 1 2 1 1 0 1 0 2 0 1 1 0 0 1 2 2 1 0 2 1 1 2 2 2 1 0 2 1 0 1 1 1 0 1 2 1 1 1 1 0 1 2 2 0 1 1 2 1 1 1 1 1 0 1 0 2 0 1 1 2 2 0 1 0 1 1 1 0 1 0 0 0 2 1 0 1 1 0 0 2 1 2 0 1 1 0 0 1 2 1 2 0 2 2 0 1 2 0 2 0 1 2 0 0 0 1 0 2 1 0 0 1 0 0 0 2 2 2 0 1 1 2 0 0 1 1 0 1 0 0 0 0 0 1 2 0 1 0 0 1 0 1 2 1 0 0 2 0 2 1 1 2 2 0 1 1 1 2 1 0 2 2 2 1 0 1 1 1 1 0 2 2 0 2 1 2 0 0 2 176 | 976 0 1 0 2 2 0 1 2 1 0 2 0 0 1 0 0 2 1 0 1 2 2 2 1 0 0 2 0 2 1 1 1 0 1 2 2 1 1 1 2 2 2 2 1 1 0 0 2 0 0 2 1 1 0 2 1 1 2 0 0 0 1 1 2 0 2 2 2 1 2 1 0 1 0 1 0 1 1 1 0 1 2 0 2 1 2 1 1 0 2 0 1 1 1 0 0 1 0 0 0 1 1 2 1 2 1 0 1 1 2 1 2 1 2 2 2 1 1 0 1 0 2 2 0 1 0 2 0 2 1 0 0 1 1 0 0 2 1 1 1 2 2 2 0 1 1 1 1 0 0 0 0 0 2 0 2 1 0 0 0 1 0 0 2 2 0 1 2 0 2 2 1 2 2 0 1 1 2 1 0 0 2 2 2 1 0 2 1 1 2 0 2 2 1 0 1 2 1 0 1 177 | 977 1 1 0 2 1 0 2 2 1 0 2 0 0 2 0 0 2 0 0 1 2 2 2 0 0 1 1 1 2 0 0 0 1 0 2 2 2 0 0 1 2 1 2 1 2 1 0 2 1 0 2 1 0 1 1 2 2 1 1 1 0 1 0 2 0 2 1 1 0 2 0 0 1 0 1 0 0 1 0 2 2 2 1 2 0 2 2 1 0 1 0 0 0 1 2 0 0 0 0 0 2 0 2 1 0 0 0 0 0 1 0 2 1 2 2 2 1 2 1 0 0 2 2 0 0 0 2 0 2 1 0 0 1 0 0 2 1 1 1 0 1 2 0 0 0 2 1 1 0 1 0 0 0 2 0 1 1 1 0 1 1 0 0 2 1 1 0 2 0 2 1 1 0 2 0 1 0 1 1 0 0 2 2 2 2 0 2 1 0 0 0 2 1 2 2 0 1 1 0 1 178 | 978 1 1 0 1 1 2 1 2 2 0 1 0 0 2 0 0 2 0 0 1 2 1 2 1 0 0 0 1 1 0 1 0 1 0 0 2 2 0 0 2 2 0 0 1 1 1 1 2 0 1 1 1 0 0 1 0 2 2 0 0 1 0 1 2 0 1 1 2 1 2 1 1 1 0 1 0 1 1 2 1 2 2 0 1 1 2 1 2 2 1 0 1 0 1 1 0 1 0 0 1 0 2 2 0 1 1 0 0 1 1 1 1 1 1 2 1 0 2 1 1 0 2 2 0 0 1 2 0 2 1 0 1 2 0 0 0 1 0 2 0 2 1 2 1 0 0 1 1 0 2 0 0 0 1 1 1 0 2 0 2 2 0 0 2 2 0 1 2 0 2 2 1 2 1 1 0 0 0 1 1 1 2 2 2 0 0 2 0 1 1 0 2 2 1 2 1 0 0 0 1 179 | 979 0 2 0 1 0 2 2 2 2 0 1 1 1 2 2 0 2 0 0 0 0 2 1 0 1 0 1 0 2 0 2 0 1 0 0 2 2 0 0 1 2 2 1 1 1 1 1 2 0 1 1 0 0 0 2 2 0 2 1 0 0 1 1 2 0 2 1 2 0 2 0 0 2 0 1 0 0 1 1 2 2 1 2 1 0 2 0 1 0 2 0 2 1 2 0 0 2 0 0 1 2 1 1 0 2 1 0 1 0 2 0 2 1 2 1 2 0 1 1 0 1 1 2 0 2 0 2 0 2 1 0 0 2 1 1 1 1 2 2 1 1 2 2 1 1 1 1 0 0 1 1 0 1 2 0 2 1 2 0 2 2 0 0 2 1 0 1 2 0 2 2 2 2 1 1 1 0 1 1 1 0 2 2 1 1 0 2 0 2 2 0 1 1 2 1 1 2 0 0 1 180 | 980 0 2 1 2 1 0 2 1 2 0 1 1 0 2 1 0 2 0 1 0 1 1 2 1 0 0 1 0 2 0 2 0 0 0 2 2 2 0 0 2 2 1 1 2 1 1 0 2 0 0 2 1 1 0 1 2 2 1 1 0 0 2 0 2 0 2 2 2 2 2 1 0 2 0 2 0 2 2 0 2 1 2 1 2 0 2 0 2 0 1 0 1 1 0 1 0 1 0 0 0 1 0 2 2 2 1 0 1 0 1 1 2 1 2 2 1 0 1 0 0 0 2 2 0 0 0 2 0 1 1 0 0 0 1 0 1 2 0 2 0 1 2 1 0 2 2 2 1 0 0 2 0 0 1 1 2 0 0 0 0 2 0 1 2 2 0 0 1 0 1 2 2 1 1 0 1 1 1 1 1 0 2 2 2 0 0 2 0 1 0 0 2 2 1 2 1 1 1 0 1 181 | 981 0 1 0 1 2 2 2 1 1 0 0 0 1 1 0 1 2 0 0 1 0 1 2 1 1 2 0 0 1 2 2 0 0 1 0 2 2 1 0 1 2 1 1 2 1 1 0 2 1 1 2 2 1 0 2 1 1 2 2 2 0 1 2 2 0 1 1 2 0 1 1 0 1 0 2 0 1 0 1 2 2 2 0 1 0 2 1 2 1 1 0 1 0 2 2 0 1 1 1 0 1 0 2 2 2 2 1 1 0 1 0 2 0 2 2 2 1 1 0 1 1 1 2 0 0 0 1 0 2 1 0 0 2 0 0 0 2 0 2 0 2 1 1 1 0 1 0 1 1 0 0 0 0 1 1 2 0 0 0 1 2 0 0 2 2 0 0 2 0 2 2 1 2 0 0 0 1 0 1 1 1 1 2 2 1 1 2 0 2 1 0 2 1 1 2 0 1 1 1 1 182 | 982 0 0 1 2 1 1 2 1 2 0 1 2 0 2 0 0 2 2 0 0 1 1 0 1 0 0 1 0 1 2 2 0 0 1 0 2 2 0 1 2 2 2 1 1 1 1 1 1 1 0 1 1 0 1 2 1 2 2 1 0 0 1 1 2 0 2 1 0 0 2 1 1 1 0 2 0 2 1 1 1 2 1 1 1 0 2 0 2 0 1 0 2 1 1 0 0 1 0 0 1 2 1 1 1 1 1 0 0 0 2 0 2 1 2 2 1 1 2 0 2 0 2 2 0 0 0 2 0 2 2 0 0 2 1 0 1 2 1 2 1 2 2 2 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 0 1 1 1 0 2 2 0 0 2 0 1 2 0 2 1 0 2 1 1 0 1 0 1 2 2 0 1 2 1 0 0 0 1 1 1 2 2 0 1 2 2 183 | 983 1 2 0 2 0 1 2 1 1 0 1 0 0 2 2 1 2 0 0 0 1 0 2 0 0 0 1 1 1 0 2 1 1 1 1 2 2 1 0 2 2 2 2 2 1 0 0 2 1 1 1 2 1 0 1 1 1 2 1 2 0 2 1 2 0 1 2 2 2 2 2 1 2 0 2 1 2 0 1 1 1 2 0 1 0 2 1 2 0 2 0 1 0 1 0 0 2 0 0 2 2 1 2 0 2 0 1 0 0 0 0 2 2 2 1 1 1 0 1 1 1 1 2 0 1 0 1 0 1 2 0 0 1 0 0 0 2 1 2 0 1 1 2 0 0 2 0 0 2 0 1 0 0 2 0 2 0 2 0 0 1 2 0 1 2 0 1 2 0 2 2 2 1 1 0 0 1 1 1 1 0 2 2 2 1 0 1 2 1 1 0 0 0 1 1 1 1 1 1 2 184 | 984 0 1 0 2 1 1 2 2 2 0 1 0 0 1 0 0 2 0 0 0 1 1 2 1 0 1 1 0 2 0 2 1 1 1 1 2 2 0 0 2 2 2 1 2 0 2 0 2 1 0 1 1 0 0 1 1 1 1 0 1 0 0 0 2 0 1 1 2 0 0 1 2 0 0 2 0 1 0 0 1 1 2 1 1 0 2 1 2 0 2 0 1 1 1 1 0 1 1 0 0 1 0 2 0 1 1 0 1 0 1 1 2 0 1 2 2 1 2 0 2 1 2 1 0 0 1 2 0 2 1 1 0 2 0 0 1 2 2 1 0 0 2 2 1 1 1 1 1 2 0 2 0 0 2 0 2 1 0 0 0 2 0 1 2 1 0 1 2 0 1 2 1 2 2 0 1 0 1 2 0 1 2 2 2 0 0 1 0 2 1 0 1 2 0 1 0 1 0 0 2 185 | 985 0 1 1 1 1 0 2 1 2 0 1 0 0 2 1 0 2 0 0 0 1 0 1 0 0 0 1 0 2 0 1 1 1 0 1 2 1 1 1 2 2 0 1 1 2 1 0 2 0 0 1 1 1 0 2 0 1 2 1 1 1 1 1 2 1 2 1 2 0 2 2 1 2 1 2 0 1 0 2 2 1 2 1 1 0 2 1 2 0 1 0 0 0 2 2 0 0 1 0 1 0 0 2 1 0 1 0 0 0 0 2 1 2 2 1 0 1 1 0 0 0 1 2 0 1 0 2 0 2 1 0 1 1 0 0 2 2 0 1 0 0 0 2 1 0 1 1 0 1 1 2 0 1 1 0 2 0 0 0 1 2 0 1 1 2 0 1 2 0 2 2 2 2 2 1 1 0 0 2 1 0 2 2 2 2 0 2 2 1 2 1 2 2 1 1 1 0 0 1 2 186 | 986 0 0 0 2 0 1 2 0 2 0 2 1 1 0 1 0 1 0 0 0 1 0 2 2 0 0 2 0 2 1 2 1 0 0 1 2 2 0 0 2 2 1 1 1 2 0 0 2 2 0 2 0 0 0 1 2 1 2 1 1 0 2 1 2 0 2 2 2 0 2 1 2 2 0 2 0 0 1 1 1 1 2 1 2 2 0 1 2 0 2 0 2 1 2 1 0 1 1 1 0 2 1 2 2 2 2 0 0 0 1 1 2 1 2 2 1 0 2 0 0 2 1 2 0 1 1 1 0 2 1 1 0 1 1 1 1 1 2 2 0 1 1 2 1 2 2 2 1 0 0 1 0 1 0 2 1 0 0 0 0 1 1 1 2 2 1 0 1 0 2 2 1 2 1 2 0 1 0 1 0 1 2 2 2 1 0 1 0 1 1 0 2 2 1 2 1 0 0 1 2 187 | 987 0 0 0 2 1 1 2 1 2 0 1 0 0 2 2 0 2 0 0 1 1 1 2 0 2 0 1 0 1 0 0 1 1 1 2 2 2 0 0 2 1 2 0 2 1 1 1 2 1 1 1 2 1 2 0 1 1 2 2 1 0 1 1 2 1 1 1 1 0 1 2 1 2 0 2 0 0 0 1 0 2 2 0 2 1 2 0 1 0 0 0 1 0 2 1 0 1 0 0 1 1 0 2 1 2 1 0 0 0 1 1 1 0 1 1 1 2 1 0 0 0 1 1 0 0 1 0 0 1 1 0 0 0 0 1 0 1 1 2 1 2 2 1 0 0 1 0 1 1 1 0 1 0 0 0 2 0 0 0 1 1 0 1 2 2 0 0 2 0 1 2 2 1 0 0 1 2 0 1 1 0 1 2 2 1 0 2 0 1 1 0 1 1 2 2 0 0 1 0 1 188 | 988 1 1 0 2 1 0 2 2 2 0 2 0 1 2 0 0 1 0 0 0 1 0 1 1 1 1 2 0 2 0 0 1 1 0 1 2 2 0 1 1 2 2 1 1 2 0 0 2 1 0 2 1 1 0 2 1 2 2 0 0 0 2 1 2 0 2 2 2 0 1 0 0 2 0 0 0 0 0 1 1 1 1 1 1 0 1 1 2 1 1 0 2 1 2 1 0 0 0 0 0 2 0 2 2 2 0 1 1 0 1 0 2 0 2 2 1 1 1 0 2 1 2 2 0 0 1 1 0 2 2 0 0 1 1 1 1 2 1 1 1 1 0 2 0 0 1 2 1 1 0 1 0 1 2 1 2 0 1 0 0 2 0 0 1 2 0 0 2 0 1 1 1 2 1 0 1 2 2 1 1 0 2 2 2 1 0 1 1 0 1 0 1 1 1 1 2 1 1 1 1 189 | 989 0 1 1 2 0 1 2 2 2 0 2 1 0 2 2 0 1 0 0 0 0 1 2 1 1 0 1 0 2 1 0 0 1 1 1 2 2 1 1 2 2 2 1 2 2 1 0 2 1 0 2 0 1 1 0 0 2 1 1 1 0 2 0 2 0 2 1 2 0 1 1 0 1 0 2 0 1 1 1 2 2 2 0 1 0 2 0 1 1 1 0 1 1 1 1 0 2 0 0 0 2 0 2 0 2 0 0 1 0 0 0 2 1 1 1 2 1 2 0 0 1 2 1 0 1 0 2 0 2 1 0 0 2 0 0 2 2 0 1 1 1 2 2 2 0 0 1 1 1 1 0 0 0 1 0 2 1 2 0 0 2 0 0 2 2 0 1 2 1 2 2 2 1 1 0 0 0 0 1 1 0 2 2 2 0 0 2 1 1 2 0 1 2 1 2 0 0 0 1 2 190 | 990 0 1 0 1 1 1 1 1 2 0 2 0 0 1 2 2 2 0 0 0 1 1 1 0 0 1 2 0 2 0 0 0 0 0 2 2 2 1 2 2 2 1 2 1 1 0 1 2 1 1 1 0 0 2 1 1 1 2 1 0 0 0 1 2 0 1 1 2 0 2 2 0 2 0 2 1 0 0 1 2 1 2 0 1 1 2 0 2 0 0 0 1 1 0 1 0 2 1 0 1 1 2 1 2 2 0 1 0 0 1 1 2 1 2 2 2 0 2 0 2 1 2 2 0 1 0 2 0 2 1 1 0 2 0 0 1 1 0 1 1 1 2 2 0 2 2 0 1 1 0 1 0 0 1 0 2 0 1 0 1 2 0 1 2 1 0 1 2 0 1 1 1 2 1 2 1 2 1 1 1 0 1 2 2 1 0 2 1 1 1 0 1 1 0 1 0 2 1 1 2 191 | 991 0 1 0 1 1 1 2 0 2 0 2 1 0 1 0 0 1 0 0 1 0 2 2 2 0 0 0 0 1 0 0 1 0 0 1 2 2 0 1 2 2 0 2 2 1 2 0 1 0 1 2 0 0 1 1 1 1 2 2 2 0 2 1 2 0 1 1 1 0 2 0 0 2 0 2 0 1 1 1 2 1 1 0 1 0 2 1 1 1 2 0 1 0 2 2 0 1 0 0 1 1 0 2 0 1 1 0 0 0 0 1 2 0 2 1 2 0 1 0 1 0 2 2 0 1 0 1 0 2 2 0 0 1 0 0 0 2 2 0 0 0 1 1 0 0 2 1 2 1 0 0 0 0 2 1 2 0 0 0 1 2 0 0 2 2 0 0 2 0 2 2 2 2 2 0 1 0 2 0 1 1 2 2 2 1 0 2 1 2 0 0 1 2 1 2 1 1 1 0 1 192 | 992 0 1 0 2 0 1 2 1 2 0 1 1 0 2 1 0 1 0 0 1 2 0 1 1 1 1 1 0 1 1 1 1 1 1 0 2 2 0 0 2 2 1 1 2 2 1 0 2 1 0 1 1 0 0 2 2 0 2 0 0 0 2 1 2 1 1 2 2 0 2 2 1 2 1 2 0 0 0 1 2 2 2 1 1 0 2 0 1 0 1 0 1 0 0 1 0 0 1 0 1 1 1 2 0 2 2 0 1 0 1 0 2 0 2 2 2 0 1 0 0 1 1 1 0 0 1 2 0 2 1 1 0 0 0 0 0 2 2 2 0 1 1 1 0 1 2 0 0 1 0 1 0 0 1 1 2 1 2 0 1 2 0 0 2 1 0 0 2 0 2 2 1 2 2 1 0 2 1 1 2 0 2 1 2 1 0 1 2 1 0 0 0 0 1 1 0 2 1 0 1 193 | 993 1 2 1 2 1 1 1 2 1 0 2 2 1 1 1 1 2 0 0 0 0 0 1 1 1 0 0 0 2 0 0 1 1 0 0 2 2 1 1 1 2 1 0 2 2 0 1 2 1 1 1 2 0 0 2 1 2 2 2 0 0 1 0 2 0 2 2 1 0 2 1 1 2 0 2 0 0 0 0 2 2 2 0 2 1 1 1 1 0 1 0 1 0 2 2 0 0 0 0 1 1 1 1 1 2 0 0 0 0 1 1 1 1 2 1 2 0 0 0 1 0 1 2 0 0 0 2 0 2 0 0 0 0 1 0 1 2 1 2 0 1 2 2 1 0 1 1 0 1 0 1 0 0 1 0 2 2 1 0 0 1 1 0 1 2 1 0 2 1 1 1 2 2 2 0 0 1 1 0 2 1 2 2 2 0 0 1 0 0 0 0 1 1 1 2 1 0 2 0 1 194 | 994 0 2 1 2 1 1 2 1 2 0 1 0 1 1 0 1 2 0 0 2 1 1 2 0 0 2 2 0 2 1 0 0 0 1 0 2 2 1 2 1 2 1 1 2 0 2 0 2 2 1 2 2 0 0 2 0 1 2 2 0 0 2 2 2 0 2 1 2 1 2 2 2 2 0 1 0 1 0 1 0 2 2 0 2 1 2 1 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1 1 1 2 0 0 0 0 0 0 0 1 2 2 1 1 2 0 0 0 2 2 0 1 0 2 0 2 1 0 0 2 0 1 1 2 1 2 1 0 2 1 1 1 1 1 1 2 0 0 0 0 2 1 1 0 1 0 1 2 0 0 1 2 0 1 2 0 2 1 1 1 1 0 0 1 1 1 0 0 1 2 2 2 0 2 1 1 0 1 1 1 1 2 0 1 0 1 2 195 | 995 0 1 1 2 0 2 2 1 2 0 2 0 0 1 2 1 1 0 0 2 0 0 2 2 0 1 1 0 1 0 1 1 1 0 1 2 2 1 0 2 2 2 2 2 0 0 0 2 0 0 2 1 1 1 1 1 2 2 0 1 0 2 1 2 0 2 2 2 1 2 2 0 1 0 2 0 0 0 1 1 2 2 1 2 1 2 1 2 0 1 0 1 0 2 0 0 1 0 0 1 2 1 2 1 1 1 0 0 0 0 1 2 1 1 1 1 0 2 0 1 0 2 2 0 0 0 2 0 1 0 0 0 2 0 0 1 2 0 2 1 2 2 2 0 1 2 1 1 0 1 1 0 0 2 0 2 0 1 0 1 1 1 0 1 2 0 1 2 0 2 1 1 2 1 1 0 1 1 1 1 2 2 2 2 1 0 2 2 1 2 1 1 1 2 1 1 2 1 0 2 196 | 996 0 2 0 2 2 2 2 1 2 0 2 1 0 2 2 0 1 0 0 1 1 0 2 1 1 0 1 0 2 0 1 1 0 2 1 2 2 1 0 2 2 1 1 2 1 1 0 2 2 0 1 0 1 1 2 1 1 1 1 0 0 1 2 2 0 2 2 2 0 2 2 0 1 1 1 0 0 1 0 1 2 1 1 0 0 2 1 1 0 1 0 1 0 2 1 0 0 0 0 2 1 0 2 1 2 2 0 1 0 0 0 2 2 1 2 0 0 1 0 0 0 0 1 0 1 0 1 0 2 0 0 0 2 0 0 2 2 0 2 0 1 0 2 1 0 1 0 1 1 2 0 0 0 1 0 2 0 0 0 0 2 1 1 1 1 0 0 0 0 2 2 1 2 1 1 2 1 1 2 1 0 0 2 2 0 0 2 1 1 0 0 2 2 1 2 0 0 0 0 2 197 | 997 0 2 0 1 1 1 2 2 2 0 2 0 2 1 1 0 1 1 0 0 2 0 2 1 0 0 0 1 2 0 2 0 0 2 1 2 1 0 1 2 2 2 1 2 2 0 0 1 1 0 2 1 1 1 2 1 1 2 2 1 0 1 0 2 0 2 1 1 0 1 1 0 2 0 1 0 1 0 1 0 1 1 1 1 2 2 1 2 0 1 0 2 1 1 0 0 0 1 0 1 2 0 1 1 2 0 2 0 0 0 0 2 1 2 2 2 1 2 0 1 2 2 1 0 0 0 2 0 2 2 0 0 1 0 0 2 2 2 2 0 1 2 1 0 0 2 1 2 1 1 1 0 0 2 0 2 1 1 0 1 2 0 0 2 2 0 1 2 0 1 1 2 2 2 0 1 1 1 1 1 0 2 2 2 1 0 2 1 1 0 0 2 1 2 1 0 1 1 1 1 198 | 998 0 0 0 1 0 0 2 0 2 0 2 2 0 1 1 0 2 0 0 1 1 1 2 2 0 0 1 0 2 0 0 1 1 0 1 1 2 1 0 2 2 2 1 1 1 2 0 2 0 0 2 0 2 1 2 0 2 2 0 1 0 1 1 2 1 2 1 1 0 2 1 0 1 0 1 0 0 1 1 2 2 2 0 2 1 2 1 0 0 2 0 2 1 2 1 0 0 1 0 0 2 1 2 0 2 1 0 0 0 1 2 2 1 2 2 1 1 2 0 2 1 2 2 0 0 1 2 0 2 1 0 0 1 0 0 0 2 1 2 1 2 2 2 0 0 1 2 1 2 0 2 0 0 1 1 2 1 1 0 0 0 1 1 2 2 0 1 2 0 2 2 1 2 2 0 0 0 1 1 2 1 2 2 2 1 0 2 1 0 0 0 2 1 1 0 0 1 0 0 1 199 | 999 0 1 0 2 2 0 1 1 2 0 2 1 0 2 2 0 2 0 0 1 1 1 2 1 1 1 1 0 2 0 1 0 0 1 0 2 2 0 1 2 2 2 2 2 2 2 0 2 1 1 1 1 0 0 1 2 1 1 1 0 0 1 1 2 0 1 1 2 2 2 2 0 1 0 2 0 0 1 1 1 2 1 1 0 0 1 0 2 1 1 0 1 0 1 1 0 2 0 1 0 1 2 2 1 1 1 0 0 0 2 0 2 1 2 0 1 1 1 0 1 0 2 1 0 1 0 1 0 2 0 0 0 2 0 0 0 2 2 2 0 0 1 2 0 0 1 0 0 2 1 1 0 0 1 1 2 2 1 0 1 2 1 0 1 1 0 1 2 0 2 1 1 2 0 1 0 1 1 1 0 0 2 2 2 0 1 2 0 1 0 0 2 2 2 2 1 0 1 0 2 200 | 1000 0 2 0 0 1 1 2 1 1 0 1 0 1 2 1 0 2 0 0 1 1 1 2 1 0 0 2 0 0 1 1 0 2 1 2 2 2 1 1 2 1 1 2 2 2 1 1 1 1 0 1 0 1 2 1 0 0 0 1 1 0 1 2 2 0 1 1 2 1 2 1 0 1 0 0 0 2 0 1 1 2 2 0 0 0 2 1 2 0 1 0 0 1 0 1 0 2 0 0 0 2 0 1 0 1 1 1 0 0 0 1 1 0 2 1 1 0 1 0 1 1 1 1 0 0 0 2 0 2 2 0 0 1 0 1 1 2 1 1 1 2 1 0 0 0 1 1 0 0 2 2 0 0 0 1 2 0 1 0 1 1 0 0 2 2 0 0 2 0 2 2 1 2 0 1 0 1 1 1 0 1 2 2 2 1 1 1 0 0 0 1 2 0 2 2 2 1 0 0 2 201 | -------------------------------------------------------------------------------- /spec/forest_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Nimbus::Forest do 5 | describe "Regression" do 6 | before(:each) do 7 | @config = Nimbus::Configuration.new 8 | @config.load fixture_file('regression/config.yml') 9 | @config.load_training_data if @config.do_training 10 | @forest = ::Nimbus::Forest.new @config 11 | end 12 | 13 | it 'grows a regression forest of N trees' do 14 | expect(@forest.trees).to eq [] 15 | expect(@config.forest_size).to eq 3 16 | expect(@forest).to_not be_classification 17 | expect(@forest).to be_regression 18 | @forest.grow 19 | expect(@forest.trees.size).to eq @config.forest_size 20 | @forest.trees.each{|t| expect(t).to be_kind_of Hash} 21 | end 22 | 23 | it 'creates averaged predictions for individuals in the training set' do 24 | expect(@forest.predictions).to eq({}) 25 | @forest.grow 26 | expect((@forest.predictions.keys - (1..800).to_a )).to eq [] # 800 individuals in the training file 27 | @forest.predictions.values.each{|v| expect(v).to be_kind_of Numeric} 28 | end 29 | 30 | it 'computes averaged SNP importances for every SNP' do 31 | expect(@forest.snp_importances).to eq({}) 32 | @forest.grow 33 | expect(@forest.snp_importances.keys.sort).to eq (1..200).to_a # 200 snps in the training file 34 | @forest.snp_importances.values.each{|v| expect(v).to be_kind_of Numeric} 35 | end 36 | 37 | it 'does not compute SNP importances if config set to false' do 38 | expect(@forest.snp_importances).to eq({}) 39 | @forest.options.do_importances = false 40 | @forest.grow 41 | expect(@forest.snp_importances).to eq({}) 42 | end 43 | 44 | it 'traverses a set of testing individuals through every tree in the forest and returns predictions' do 45 | @forest = @config.load_forest 46 | expect(@forest.predictions).to eq({}) 47 | 48 | tree_structure = Psych.load(File.open fixture_file('regression/random_forest.yml')) 49 | expected_predictions = {} 50 | @config.read_testing_data{|individual| 51 | individual_prediction = 0.0 52 | tree_structure.each do |t| 53 | individual_prediction = (individual_prediction + Nimbus::Tree.traverse(t, individual.snp_list)).round(5) 54 | end 55 | expected_predictions[individual.id] = (individual_prediction / 3).round(5) 56 | } 57 | 58 | @forest.traverse 59 | expect(@forest.predictions).to eq expected_predictions 60 | end 61 | 62 | it 'can output forest structure in YAML format' do 63 | @forest = @config.load_forest 64 | Psych.load(File.open fixture_file('regression/random_forest.yml')) == Psych.load(@forest.to_yaml) 65 | end 66 | end 67 | 68 | describe "Classification" do 69 | before(:each) do 70 | @config = Nimbus::Configuration.new 71 | @config.load fixture_file('classification/config.yml') 72 | @config.load_training_data 73 | @forest = ::Nimbus::Forest.new @config 74 | end 75 | 76 | it 'grows a classification forest of N trees' do 77 | expect(@forest.trees).to eq [] 78 | expect(@config.forest_size).to eq 3 79 | expect(@forest).to be_classification 80 | expect(@forest).to_not be_regression 81 | @forest.grow 82 | expect(@forest.trees.size).to eq @config.forest_size 83 | @forest.trees.each{|t| expect(t).to be_kind_of Hash} 84 | end 85 | 86 | it 'creates predictions for individuals in the training set' do 87 | expect(@forest.predictions).to eq({}) 88 | @forest.grow 89 | expect((@forest.predictions.keys - (1..1000).to_a )).to eq [] # 1000 individuals in the training file 90 | @forest.predictions.values.each{|v| expect(v).to be_kind_of String} 91 | end 92 | 93 | it 'computes averaged SNP importances for every SNP' do 94 | expect(@forest.snp_importances).to eq({}) 95 | @forest.options.do_importances = true 96 | @forest.grow 97 | expect(@forest.snp_importances.keys.sort).to eq (1..100).to_a # 100 snps in the training file 98 | @forest.snp_importances.values.each{|v| expect(v).to be_kind_of Numeric} 99 | end 100 | 101 | it 'does not compute SNP importances if config set to false' do 102 | expect(@forest.snp_importances).to eq({}) 103 | @forest.options.do_importances = false 104 | @forest.grow 105 | expect(@forest.snp_importances).to eq({}) 106 | end 107 | 108 | it 'traverses a set of testing individuals through every tree in the forest and returns predictions' do 109 | @forest = @config.load_forest 110 | expect(@forest.predictions).to eq({}) 111 | 112 | tree_structure = Psych.load(File.open fixture_file('classification/random_forest.yml')) 113 | expected_predictions = {} 114 | @config.read_testing_data{|individual| 115 | individual_prediction = [] 116 | tree_structure.each do |t| 117 | individual_prediction << Nimbus::Tree.traverse(t, individual.snp_list) 118 | end 119 | class_sizes = Nimbus::LossFunctions.class_sizes_in_list(individual_prediction, @config.tree[:classes]).map{|p| (p/individual_prediction.size.to_f).round(3)} 120 | expected_predictions[individual.id] = Hash[@config.tree[:classes].zip class_sizes].map{|k,v| "'#{k}': #{v}"}.join(' , ') 121 | } 122 | 123 | @forest.traverse 124 | expect(@forest.predictions).to eq expected_predictions 125 | end 126 | 127 | it 'can output forest structure in YAML format' do 128 | @forest = @config.load_forest 129 | Psych.load(File.open fixture_file('classification/random_forest.yml')) == Psych.load(@forest.to_yaml) 130 | end 131 | end 132 | end -------------------------------------------------------------------------------- /spec/individual_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Nimbus::Individual do 5 | 6 | it "stores id, fenotype and SNPs information for an individual" do 7 | @individual = Nimbus::Individual.new(11, 33.275, [1,0,2,1]) 8 | expect(@individual.id).to eq 11 9 | expect(@individual.fenotype).to eq 33.275 10 | expect(@individual.snp_list).to eq [1,0,2,1] 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /spec/loss_functions_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Nimbus::LossFunctions do 5 | 6 | it "method for average" do 7 | ids = [1,3,5,7] 8 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 9 | 10 | expect(Nimbus::LossFunctions.average(ids, values)).to eq 18.25 # (10 + 21 + 31 + 11 = 73)/4 11 | end 12 | 13 | it "method for mean squared error" do 14 | ids = [3,7,85] 15 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 16 | 17 | expect(Nimbus::LossFunctions.mean_squared_error(ids, values)).to eq 74.0 # (avg(21 + 11 + 22) = 18: sum (x-18)^2 18 | end 19 | 20 | it "method for quadratic_loss" do 21 | ids = [1,4] 22 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 23 | 24 | expect(Nimbus::LossFunctions.quadratic_loss(ids, values).round(5)).to eq 1 25 | end 26 | 27 | it "quadratic loss is mean squared error averaged" do 28 | ids = [1,2,3,4,5,7,85] 29 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 30 | expect(Nimbus::LossFunctions.quadratic_loss(ids, values).round(5)).to eq (Nimbus::LossFunctions.mean_squared_error(ids, values)/7 ).round(5) 31 | end 32 | 33 | it "method for pseudo Huber error" do 34 | ids = [3,7,85] 35 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 36 | expect(Nimbus::LossFunctions.pseudo_huber_error(ids, values).round(5)).to eq 11.92337 # (avg(21 + 11 + 22) = 18: log(cosh(x-18)) 37 | end 38 | 39 | it "method for pseudo Huber loss function" do 40 | ids = [1,4] 41 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 42 | expect(Nimbus::LossFunctions.pseudo_huber_loss(ids, values).round(5)).to eq 0.43378 43 | end 44 | 45 | it "pseudo Huber loss is pseudo Huber error averaged" do 46 | ids = [1,2,3,4,5,7,85] 47 | values = {1 => 10, 2 => 5, 3 => 21, 4 => 8, 5 => 31, 7 => 11, 85 => 22} 48 | expect(Nimbus::LossFunctions.pseudo_huber_loss(ids, values).round(5)).to eq (Nimbus::LossFunctions.pseudo_huber_error(ids, values)/7 ).round(5) 49 | end 50 | 51 | it "method for squared difference" do 52 | expect(Nimbus::LossFunctions.squared_difference(50, 40)).to eq 100.0 53 | expect(Nimbus::LossFunctions.squared_difference(22, 10)).to eq 144.0 54 | end 55 | 56 | it "method for majority class" do 57 | ids = [1,2,3,4,5,7,85] 58 | values = {1 => 'B', 2 => 'C', 3 => 'A', 4 => 'A', 5 => 'C', 7 => 'B', 85 => 'C'} #3C, 2A, 2B 59 | classes = ['A', 'B', 'C'] 60 | expect(Nimbus::LossFunctions.majority_class(ids, values, classes)).to eq 'C' 61 | end 62 | 63 | it "majority class method selects randomly if more than one majority class" do 64 | ids = [1,2,3,4,5,7,85,99] 65 | values = {1 => 'B', 2 => 'C', 3 => 'A', 4 => 'A', 5 => 'C', 7 => 'B', 85 => 'C', 99 => 'A'} #3C, 3A, 2B 66 | classes = ['A', 'B', 'C'] 67 | results = [] 68 | 20.times do 69 | results << Nimbus::LossFunctions.majority_class(ids, values, classes) 70 | end 71 | expect(results).to include('A') 72 | expect(results).to include('C') 73 | end 74 | 75 | it "method for majority class in list" do 76 | list = %w(A A A B B B C A B C A B A) 77 | classes = ['A', 'B', 'C'] 78 | expect(Nimbus::LossFunctions.majority_class_in_list(list, classes)).to eq 'A' 79 | end 80 | 81 | it "method for class sizes" do 82 | ids = [1,2,3,4,5,7,85] 83 | values = {1 => 'B', 2 => 'C', 3 => 'A', 4 => 'A', 5 => 'C', 7 => 'B', 85 => 'C'} #2A, 2B, 3C 84 | classes = ['A', 'B', 'C'] 85 | expect(Nimbus::LossFunctions.class_sizes(ids, values, classes)).to eq [2, 2, 3] 86 | end 87 | 88 | it "method for class sizes in list" do 89 | list = %w(A A A B B B C A B C A B A) # 6A, 5B, 2C 90 | classes = ['A', 'B', 'C'] 91 | expect(Nimbus::LossFunctions.class_sizes_in_list(list, classes)).to eq [6, 5, 2] 92 | end 93 | 94 | it "Gini index" do 95 | ids = [1,2,3,4,5,7] 96 | values = {1 => 'B', 2 => 'C', 3 => 'A', 4 => 'A', 5 => 'C', 7 => 'C'} #3C, 2A, 1B 97 | classes = ['A', 'B', 'C'] 98 | # Gini = 1 - ( (3/6)^2 + (2/6)^2 + (1/6)^2 ) = 0.61111 99 | expect(Nimbus::LossFunctions.gini_index(ids, values, classes)).to eq 0.61111 100 | end 101 | 102 | end -------------------------------------------------------------------------------- /spec/nimbus_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | 5 | describe 'Nimbus module' do 6 | 7 | it "manages a Nimbus::Application object" do 8 | app = Nimbus.application 9 | expect(app).to be_kind_of Nimbus::Application 10 | end 11 | 12 | it "accepts setting an external Nimbus::Application" do 13 | app = Nimbus::Application.new 14 | Nimbus.application = app 15 | expect(Nimbus.application).to eq app 16 | end 17 | 18 | end -------------------------------------------------------------------------------- /spec/regression_tree_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/spec_helper' 2 | 3 | describe Nimbus::RegressionTree do 4 | 5 | before(:each) do 6 | @config = Nimbus::Configuration.new 7 | @config.load fixture_file('regression/config.yml') 8 | 9 | @tree = Nimbus::RegressionTree.new @config.tree 10 | end 11 | 12 | it "is initialized with tree config info" do 13 | expect(@tree.snp_total_count).to eq 200 14 | expect(@tree.snp_sample_size).to eq 60 15 | expect(@tree.node_min_size).to eq 5 16 | end 17 | 18 | it "creates a tree structure when seeded with training data" do 19 | @config.load_training_data 20 | expect(@tree.structure).to be_nil 21 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 22 | expect(@tree.structure).to_not be_nil 23 | expect(@tree.structure).to be_kind_of Hash 24 | 25 | expect(@tree.structure.keys.first).to eq @tree.used_snps.last 26 | expect(@tree.used_snps).to_not be_empty 27 | end 28 | 29 | it "split node when building a node and finds a suitable split" do 30 | @config.load_training_data 31 | allow_any_instance_of(Nimbus::RegressionTree).to receive(:snps_random_sample).and_return((141..200).to_a) #189 is best split 32 | 33 | @tree.individuals = @config.training_set.individuals 34 | @tree.id_to_fenotype = @config.training_set.ids_fenotypes 35 | @tree.used_snps = [] 36 | @tree.predictions = {} 37 | 38 | branch = @tree.build_node @config.training_set.all_ids, Nimbus::LossFunctions.average(@config.training_set.all_ids, @config.training_set.ids_fenotypes) 39 | expect(branch.keys.size).to eq 1 40 | expect(branch.keys.first).to eq 189 41 | expect(branch[189].size).to eq 3 42 | expect(branch[189][0]).to be_kind_of Hash 43 | expect([Nimbus::Tree::NODE_SPLIT_01_2, Nimbus::Tree::NODE_SPLIT_0_12]).to include(branch[189][1]) 44 | expect(branch[189][2]).to be_kind_of Hash 45 | end 46 | 47 | it "keeps track of all SNPs used for the tree" do 48 | @config.load_training_data 49 | snps = (131..190).to_a 50 | allow_any_instance_of(Nimbus::RegressionTree).to receive(:snps_random_sample).and_return(snps) 51 | expect(@tree.used_snps).to be_nil 52 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 53 | expect(@tree.used_snps.size).to be > 4 54 | @tree.used_snps.each{|snp| 55 | expect(snps.include?(snp)).to be true 56 | } 57 | end 58 | 59 | it "labels node when building a node and there is not a suitable split" do 60 | @config.load_training_data 61 | allow_any_instance_of(Nimbus::RegressionTree).to receive(:snps_random_sample).and_return([91]) 62 | 63 | @tree.individuals = @config.training_set.individuals 64 | @tree.id_to_fenotype = @config.training_set.ids_fenotypes 65 | @tree.used_snps = [] 66 | @tree.predictions = {} 67 | 68 | branch = @tree.build_node @config.training_set.all_ids, Nimbus::LossFunctions.average(@config.training_set.all_ids, @config.training_set.ids_fenotypes) 69 | expect(branch[91][0]).to be_kind_of Numeric 70 | expect([Nimbus::Tree::NODE_SPLIT_01_2, Nimbus::Tree::NODE_SPLIT_0_12]).to include(branch[91][1]) 71 | expect(branch[91][2]).to be_kind_of Numeric 72 | end 73 | 74 | it "labels node when building a node with less individuals than the minimum node size" do 75 | @config.load_training_data 76 | 77 | @tree.individuals = @config.training_set.individuals 78 | @tree.id_to_fenotype = @config.training_set.ids_fenotypes 79 | @tree.used_snps = [] 80 | @tree.predictions = {} 81 | 82 | label = @tree.build_node [1, 10, 33], Nimbus::LossFunctions.average(@config.training_set.all_ids, @config.training_set.ids_fenotypes) 83 | expect(label).to be_kind_of Numeric 84 | 85 | label = @tree.build_node [2, 10], Nimbus::LossFunctions.average(@config.training_set.all_ids, @config.training_set.ids_fenotypes) 86 | expect(label).to be_kind_of Numeric 87 | 88 | label = @tree.build_node [1, 10, 33], Nimbus::LossFunctions.average(@config.training_set.all_ids, @config.training_set.ids_fenotypes) 89 | expect(label).to be_kind_of Numeric 90 | 91 | label = @tree.build_node [108, 22, 10, 33], Nimbus::LossFunctions.average(@config.training_set.all_ids, @config.training_set.ids_fenotypes) 92 | expect(label).to be_kind_of Numeric 93 | end 94 | 95 | it 'computes generalization error for the tree' do 96 | @config.load_training_data 97 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 98 | expect(@tree.generalization_error).to be_nil 99 | @tree.generalization_error_from_oob((2..200).to_a) 100 | expect(@tree.generalization_error).to be_kind_of Numeric 101 | expect(@tree.generalization_error).to be > 0.0 102 | expect(@tree.generalization_error).to be < 1.0 103 | end 104 | 105 | it 'estimates importance for all SNPs' do 106 | @config.load_training_data 107 | @tree.seed(@config.training_set.individuals, @config.training_set.all_ids, @config.training_set.ids_fenotypes) 108 | expect(@tree.importances).to be_nil 109 | @tree.estimate_importances((300..533).to_a) 110 | expect(@tree.importances).to be_kind_of Hash 111 | expect(@tree.importances.keys).to_not be_empty 112 | expect((@tree.importances.keys - (1..200).to_a)).to be_empty #all keys are snp indexes (200 snps in training file) 113 | end 114 | 115 | it 'get prediction for an individual pushing it down a tree structure' do 116 | tree_structure = Psych.load(File.open fixture_file('regression/random_forest.yml')).first 117 | individual_data = [0]*200 118 | prediction = Nimbus::Tree.traverse tree_structure, individual_data 119 | expect(prediction).to eq -0.90813 120 | 121 | individual_data[44-1] = 2 122 | individual_data[98-1] = 1 123 | individual_data[22-1] = 1 124 | individual_data[31-1] = 2 125 | prediction = Nimbus::Tree.traverse tree_structure, individual_data 126 | expect(prediction).to eq -0.95805 127 | end 128 | 129 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/../lib/nimbus' 3 | $fixtures_path = File.dirname(__FILE__) + '/fixtures' 4 | ENV['nimbus_test'] = 'running_nimbus_tests' 5 | 6 | def fixture_file(filename) #:nodoc: 7 | return "#{$fixtures_path}/#{filename}" 8 | end -------------------------------------------------------------------------------- /spec/training_set_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Nimbus::TrainingSet do 5 | 6 | it "stores individuals list and fenotype data for them" do 7 | i1 = Nimbus::Individual.new 1, 11.0, [1,0,2,1] 8 | i2 = Nimbus::Individual.new 2, 22.0, [2,1,2,2] 9 | i3 = Nimbus::Individual.new 3, 33.0, [0,2,1,0] 10 | @training_set = Nimbus::TrainingSet.new [i1, i3], {i1.id => 11.0, i3.id => 33.0} 11 | 12 | expect(@training_set.individuals).to eq [i1, i3] 13 | expect(@training_set.ids_fenotypes).to eq ({i1.id => 11.0, i3.id => 33.0}) 14 | end 15 | 16 | it "keeps track of ids of all individuals in the training set" do 17 | i1 = Nimbus::Individual.new 1, 11.0, [1,0,2,1] 18 | i2 = Nimbus::Individual.new 2, 22.0, [2,1,2,2] 19 | i3 = Nimbus::Individual.new 3, 33.0, [0,2,1,0] 20 | @training_set = Nimbus::TrainingSet.new [i1, i3], {i1.id => 11.0, i3.id => 33.0} 21 | 22 | expect(@training_set.all_ids).to eq [1,3] 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /spec/tree_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | describe Nimbus::Tree do 5 | 6 | before(:each) do 7 | @config = Nimbus::Configuration.new 8 | @config.load fixture_file('regression/config.yml') 9 | 10 | @tree = Nimbus::Tree.new @config.tree 11 | end 12 | 13 | it "is initialized with tree config info" do 14 | expect(@tree.snp_total_count).to eq 200 15 | expect(@tree.snp_sample_size).to eq 60 16 | expect(@tree.node_min_size).to eq 5 17 | end 18 | 19 | end --------------------------------------------------------------------------------