text:
# ---
# title: 118. Pascal's Triangle
# id: problem118
# author: Tian Jun
# date: 2020-10-31
# difficulty: Easy
# categories: Array
# link: <https://leetcode.com/problems/pascals-triangle/description/>
# hidden: true
# ---
#
# Given a non-negative integer  _numRows_ , generate the first _numRows_ of
# Pascal's triangle.
#
# ![](https://upload.wikimedia.org/wikipedia/commons/0/0d/PascalTriangleAnimated2.gif)
# In Pascal's triangle, each number is the sum of the two numbers directly above
# it.
#
# **Example:**
#
#
#
#     Input: 5
#     Output:
#     [
#          [1],
#         [1,1],
#        [1,2,1],
#       [1,3,3,1],
#      [1,4,6,4,1]
#     ]
#
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
using NumericFuns
using Base.Test

@test sqr(2) === 4
@test sqr(2.0) === 4.0

@test rcp(2) === 0.5
@test rsqrt(4) === 0.5
@test rcbrt(8) === 0.5

@test xlogx(0) === 0.0
@test_approx_eq xlogx(2) 2.0 * log(2.0)

@test xlogy(0, 1) === 0.0
@test_approx_eq xlogy(2, 3) 2.0 * log(3.0)

@test_approx_eq logistic(2) 1.0 / (1.0 + exp(-2.0))
@test_approx_eq logit(0.5) 0.0
@test_approx_eq logit(sigmoid(2)) 2.0

@test_approx_eq softplus(2.0) log(1.0 + exp(2.0))
@test_approx_eq softplus(-2.0) log(1.0 + exp(-2.0))
@test_approx_eq softplus(10000) 10000.0
@test_approx_eq softplus(-10000) 0.0

@test_approx_eq invsoftplus(softplus(2.0)) 2.0
@test_approx_eq invsoftplus(softplus(-2.0)) -2.0

@test_approx_eq logsumexp(2.0, 3.0) log(exp(2.0) + exp(3.0))
@test_approx_eq logsumexp(10002, 10003) 10000 + logsumexp(2.0, 3.0)

ext: jl


text:
# functions related to Poisson distribution

import .RFunctions:
    poispdf,
    poislogpdf,
    poiscdf,
    poisccdf,
    poislogcdf,
    poislogccdf,
    poisinvcdf,
    poisinvccdf,
    poisinvlogcdf,
    poisinvlogccdf


text:
bid_price=[ 8203 3927 4958 9533 8779 9117 9232 5891 2568 7923 4253 7314 8871 6818 6240 9972 2761 2144 8747 3599 1159 5090 1169 7046 2000 4670 4484 9104 5170 6980 1699 1246 4507 3879 1784 3254 4875 9979 9203 7868 2851 8215 8642 5568 8095 7675 6086 7432 6729 2511 8037 3417 9278 6284 7213 1659 5049 1873 1786 1678 1134 7336 8881 8496 4514 1873 1802 4710 2130 1806 7473 8619 6117 4503 8172 5311 5870 9169 4344 2199 9376 2803 9537 4826 2580 2640 7984 4024 8213 7121 3722 9144 7353 6035 6567 1566 5887 1545 6477 8311 2913 4787 2210 5275 1640 4938 6626 5186 2843 2300 4115 8836 7617 1086 3756 5655 5737 9566 9106 6742 8529 3877 6903 4864 9506 8542 3777 2666 1269 7443 3547 9215 1380 9630 5131 6182 3358 4965 5588 6753 9685 9078 3160 3228 6884 7043 5702 4300 5434 6528 6421 9738 4350 2790 6414 9696 5775 8571 3857 5924 4973 4717 8958 6803 3613 4848 9932 6709 1111 9117 3027 2355 4607 7284 3908 4197 9650 2755 1616 1582 9980 7383 9117 8290 8570 1956 5402 7369 2578 5446 5310 1906 3294 5331 6398 6803 6728 6093 5406 2015 2566 1800 1144 6653 9901 9754 5729 6614 9223 1486 3113 9704 1599  ]

bid_sets=[
0 1 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 1 0 0 0 1 1 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 1 1 0 0 0 1 1 0 0 1 0 0 0 1 0  ;
0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 0 1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 1 0 0 0  ;
0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 0 0 1 0 1 0 0 0 1 0 0 1 0 0 1 0 1 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0  ;
0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0  ;
0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 1 1 0 1 1 1 0 0 0 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 1 0 1 1 0 0 0 0 1 1 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 1 1 1 0 0 0  ;
1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0  ;
1 0 1 0 0 0 1 1 1 0 0 1 0 0 0 1 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0  ;
0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 1 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1  ;
0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 0 1 1 0 1 0 1 0 0 0 0 0 1 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0  ;
0 0 1 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0  ;
1 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 1 1 0 0 1 0 1 1 0 1 0 0 1 1 0 0 1 0 1 0 0 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 0 0 1  ;
0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 0  ;
0 0 1 0 1 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0  ;
0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 1 1 0 1 0 1 0 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 1 0 1 0 0 1 1 1 1 0 0 0 0 0 1 0 1  ;
0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 1 1 1 0  ;
0 0 0 0 1 1 0 0 0 1 1 0 0 1 0 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 1  ;
1 1 1 0 1 0 1 0 0 1 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 0 0 0 0 0 1 1 1 0 1 0 1 1 0 1 0 0 0 1 1 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0  ;
1 1 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 1 0 1 1 0 1 0 0 1 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 0 1 0 0 1 1 0 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0 1 1 0 1 0 0 0 1 1 0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 1 0 0 0  ;
0 1 0 1 1 0 1 1 1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 1 0 1 0 1 0 0 1 1 1 1 1 0 1 0 0 1 0 0 0 1 1 0 0 1 1 0 0 0 1 0 0 0 0  ;
1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 1 1 0 0 1 0 1 1 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0  ;
0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 1 1 1 1 0  ;
1 1 1 0 1 0 1 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0  ;
0 1 0 1 1 0 0 1 0 0 1 1 0 0 0 1 1 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 1 1 0 1 0 1 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 1 0 0 1 0 0 1 0 1  ;
0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 1 1 0 1 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 0 0 0  ;
0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 1 1 0 1 0 0  ;
0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 1 1 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0  ;
1 1 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1  ;
0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 1 0 1 1 0 0 1 0 1 0 0 0 1 0 0 1 0 1 0 0 0 1 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 0 0 1 1 0 1 0  ;
1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 1 0 0 0 0  ;
1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 1  ;
1 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 0 0 0 1 1 0 1 0 0 1 1 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 1 1  ;
0 1 1 1 0 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 0 1 1 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 1 0 0 1 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 0 0 1  ;
0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 1 1 0 0 1 0 1 0 0 0 1 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0  ;
0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 1 1 0 1 1 0 0 1 1 0 1 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 1 0 0 0 0 0 1 1 0 0 0  ;
1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 1 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 1 0 0 0  ;
1 0 0 1 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 1 1 0 1 0 1 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0  ;
0 1 1 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 1 1 1  ;
1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 1 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 1 1 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0  ;
1 0 1 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 1 0 1  ;
0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 1 0 1 1 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 1  ;
0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 1 0 0 0 1 0 1 0 1 1 0 0 0 1 1 0 1 0 1 1 1 1 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 0  ;
0 0 1 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0  ;
0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 1 0 1 1 0 1 0 0 0 0 1 0 1 0 0 0 0  ;
1 0 1 1 1 0 0 1 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 1 1 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0 0  ;
1 0 0 1 0 0 1 0 0 0 0 0 1 0 1 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0  ;
1 1 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0  ;
1 1 1 1 1 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 1 0 1 0 1 0 1 1 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 1 1 0 0 0  ;
0 0 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 0 1 1 1 0 1  ;
0 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 1 1 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 1 0 1 1 0 1 0 0 0 1 0 0 1 1 0 0 1 1 1 0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0  ;
1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 1 0 0  ;
0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 0  ;
0 0 0 1 0 0 1 1 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1  ;
0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0  ;
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0  ;
0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 1 0 0 0 1 0 0 1 0 1 1 0 0 0 0  ;
0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1  ;
0 0 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 0  ;
0 0 0 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0  ;
1 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 1 0 0 1 1 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0  ;
0 1 0 0 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 0 1 1 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 1 0 1 0 1  ;
1 0 0 0 1 0 0 0 0 0 1 1 0 1 0 1 0 1 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 1 0 0  ;
1 0 0 1 0 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1  ;
1 1 0 0 0 1 1 0 0 0 1 0 0 1 1 0 1 1 0 0 0 0 0 1 0 1 0 1 1 0 1 1 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0  ;
0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 1 0  ;
1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1  ;
0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 1 0 0 0 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 1 0 0 0 1 0 1 1  ;
1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 1 0 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 0  ;
0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 0 1 0 0 1 0 0 1 0 1 1 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1  ;
0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0  ;
0 1 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 1 1 0 1 0 0 0 0 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 1 0  ;
1 0 0 0 0 1 1 0 1 1 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 1 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 0 0 1 0  ;
0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 1 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0  ;
0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 1 0 1 1 0 0  ;
0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 1 0 0 1 0 0 1 1 1  ;
0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 0 1 0 0 1 1 0 1 1 0 0 0 0  ;
0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 1 0  ;
1 1 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0  ;
0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 1 0 0 0  ;
0 0 1 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 1 1 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 1 0 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0  ;
0 1 1 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 1 0 0 1 0 0 1 0 1 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0  ;
0 0 0 1 0 0 1 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 1  ;
0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 1 0 1 0 0 0 0 1 1  ;
1 1 0 0 0 0 0 1 0 1 0 0 1 0 1 1 0 0 1 0 1 0 1 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 1 0  ;
0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 1 0 1 1 1 0 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 1 1 1 0 1 1 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0  ;
0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0  ;
0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0  ;
0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 1 1 1 0 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0  ;
0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0  ;
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 1 1 0 0 1 0 1 0 0 0 1 0 0 0 0 1 1 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 1 0 0 1 0  ;
0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0 0 0 1 1 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 1 1 0 0  ;
1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 0 1 1 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1  ;
0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 1 0 0 1 0 1 1 1 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 1 1 0 1 0  ;
0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 0 0 1 1 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 1 0 0 1 1 0 1 0  ;
0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 1 1 0 1 1 1 1 1 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0  ;
1 0 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 1 1 1 0 1 0 1 1 0 1 1 1 0 0 0 1 1 1 0 0 1 1 0 0 1 0 0 0 1 0 0 1 1 0 1 1 1 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0  ;
0 0 1 1 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 1 0 1 1 0 1 0 0 1 0 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1  ;
0 0 1 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 0 0 0 1 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0  ;
0 1 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 1 0 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0  ;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 1 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0  ;
1 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1 0 0 1 0 0 1 0 1 1  ;
0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 0  ;
0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 1 1 0 1 0 0 1 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0  ;
0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0  ;
0 1 1 1 0 0 1 0 0 1 0 0 1 1 0 1 1 0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 1 0 0 0 0 1 0 0 1 0 1  ;
0 0 1 0 0 0 1 1 0 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 0 1 1 1 1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 1 1 1 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0  ;
1 0 0 1 1 1 0 0 1 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 1 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 1 1 0  ;
0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0  ;
0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 0 1 1 0 0 0 1 0 0 1 0 1 0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0 0 1  ;
0 1 0 0 0 1 0 1 0 0 1 0 0 1 1 0 1 0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 1 1 1 0 1 1 1 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 1  ;
0 1 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 1 1 1 1 1 0 1 0 0 1 0 1 1 0 0 0 0 0 0 1 1  ;
1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0  ;
0 0 0 1 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 1 0 0  ;
0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 1 1 1 1 0 1 0 1 0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1  ;
1 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 1 1 1 0 1 1 0 0 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 1 0 0 1 0 0 0 0 0  ;
1 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 0  ;
0 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0  ;
0 1 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0  ;
0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 0 0 1 1 1 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0 1 0 1 1 0 1 0 0 1 0 0 1 1 1 0 0 0 0 1 1 0 1 0 0  ;
1 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0  ;
0 1 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 1 0 1 1 0 0 0 1 1 1 1 1 0  ;
0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 0 0 1 0 1 1 0 0 0 1 0 1 0 0 0 0 1 0 0 1 0 1 1 0 0 0 1 1 1 0 0  ;
0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0  ;
0 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 0 1 1 0 1 0 1 0 0 1 0 0 1 1 1 1 0 1 0 0 0  ;
1 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 1 1 0  ;
1 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 1 0 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 1 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 1 1 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 0 0 1 0  ;
0 0 0 1 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 0 1 0 0 0 1 0 0 0 0 1 0 1 1 1 1 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0  ;
0 1 0 1 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 1 0 1 1 1 1 0 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 1 1 1 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0  ;
1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 1 0 0 1 0 1 0 1 1 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 0 1 0 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0  ;
0 0 0 0 0 0 1 1 0 0 1 1 1 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1  ;
0 0 1 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1 0 1 0 1 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 0 0 1  ;
0 0 1 0 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0  ;
0 0 0 1 1 0 0 0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  ;
0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 1 1 0 1 1 1 1 1  ;
1 1 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 1 0 1 0 1 0 0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0  ;
0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0  ;
1 0 0 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 1 0 1 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 0 1 0  ;
0 0 1 1 0 1 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 0 0 0 1 1 1 1 0 1  ;
0 0 0 0 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 1 0 1 1 0 0 0 1 1 0 1 0 0 1 1 0 1 0 1 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 1 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1  ;
0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 1  ;
1 0 1 0 1 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 1 1 1 1 0 0 0 0 0 1 1 0 1 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 0 0 1 0 0 1 1 0 1 1 1 0 0 0 0 1 1 0 1 0 0 0  ;
1 0 0 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 1 1 1 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0  ;
0 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 1 1 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 1 0 1 1 0 1  ;
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 0 1 0 0 0 1 1 1 0 0 1 1 1 1 0 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 0 1 0 0 1 1 1  ;
0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 0 0 1 1 0 0 0 1 1 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1  ;
0 0 1 1 0 1 1 0 0 1 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 1 0 0  ;
0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 1 0  ;
0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 1 1 1 0 0 1 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0  ;
0 1 0 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 1 1 0 0 0 1 0  ;
1 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0  ;
0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 1 0  ;
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 0 1 0 0 1 1 1 1 0 1 1 1 0 1 1 0 0 1 1 0 1 1 0 0 0 0 0 0  ;
0 1 0 1 1 1 0 0 1 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0  ;
1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 1 1 0 1 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 1 0 0 0  ;
1 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 1 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 0 1 0 0 1 0 1 1 0 0 0 0 1  ;
1 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 1 0 0 0  ;
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0  ;
0 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 1 0 1 1 0 0 0 1  ;
0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 1 0 1 1 0 1 0 0 0 0 1 0 1 0 0 1 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1  ;
0 1 1 0 0 1 1 1 0 1 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 0 1 0 1 0 0 0 1 1 0 1 0 0 0 1 0  ;
0 0 1 0 1 1 0 0 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 1 1 0 0 1 0 0 1 1 1 0 0 1 0 1 1 0 1 0 1 0 1 0 1 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0  ;
1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0  ;
0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 1 1 0 0 0 0 0 1 1 1 0 1 1 1 0 0 0 0 0  ;
0 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 1 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 1 0 0 0 1 1 1 0 1 1 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 1 0  ;
0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 1  ;
0 1 0 1 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 1 0 0 1 0 0 0 1 1 0 0 1 1 1  ;
1 0 0 0 0 1 0 0 1 1 1 0 0 1 1 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 1 1  ;
0 1 1 0 0 0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 0 1 1 0 0 1 0 1 0 0 0 1  ;
0 0 0 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 1 0 1 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 1 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 1 0 1 1 1 0 1 0 1 1 1 0 1  ;
0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 1 1  ;
0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 1 1 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0  ;
0 1 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0  ;
0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0  ;
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0  ;
0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 1  ;
0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 1 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0  ;
1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 0 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 1 1 1 0 1 1 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 0 0 0  ;
0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 1 1 0 1 1 1 0  ;
1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1  ;
0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 1 0 0 0 0 0 1 1 0 0 0 1 0 1 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0  ;
1 0 1 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0  ;
1 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0  ;
0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 0 1 0 1 0 0 0 1 0 1 0 1 1  ;
0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1  ;
0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 1 1 1 1 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 1 0 0 0 1 1 1 0 0 0 0 1 0 1 0 0  ;
0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 1 1 0 1 0 1 0 1 1 1 1 1 0 0 0 1 0 0 0 1 1 0 0 1 0 0 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 1 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0  ;
0 0 0 1 1 0 0 1 0 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 1 0 0 0 1 1 0 1 0 1 0 0  ;
0 0 1 1 0 0 1 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 1 1 0 0 0  ;
1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 1  ;
1 0 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 0 0 1 0 0 0 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 1 0 0 0 0 1 1 0 1 1 0 0 1 0 0 0 0 1 1 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 1 1 1 0 1 1 1 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0  ;
1 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1  ;
0 0 0 0 1 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0  ;
0 0 1 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1  ;
1 0 0 1 0 0 1 1 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1  ;
1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 1 0 1 1 1 0 1 1 1 1 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0  ;
1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0  ;
0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0  ;
0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 1 1 0 0 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 1 0 0  ;
0 0 0 1 1 1 0 1 0 0 0 1 1 1 0 0 0 1 1 0 1 1 0 0 1 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 1 0  ;
0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 1 1 1 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0  ;
1 0 0 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 1 0 0 1 0 0 1 0 1 1 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 1 1 0 0 1 1 0 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0  ;
1 0 0 0 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0  ;
1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 1 1 0 1 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0  ;
0 0 1 0 1 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 1 1 0 1 0 0 1 0 0 0 0  ;
0 1 1 1 1 0 0 0 1 1 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 0 1 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 0 0 1 0  ;
0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 1 0 1 0 0  ;
1 0 0 1 0 0 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 0 1 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 1 0 0 0 0  ;
0 1 1 1 1 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1  ;
1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 0 1 1 0 1 0 1 0 1 1  ;
1 1 1 1 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 1  ;
0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 1 0 0 0 1 0 1 1 0 1 0 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 1 1 0 0 1 0 0 0 1 1 0 1 0 0 1 0 1 0 0 0 0 1 1 0 0 0 0 0  ;
1 0 0 0 0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 1 1 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0  ;
1 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 0 0 1 1 0 1 1 0 1 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0  ;
0 0 1 0 1 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 1 0  ]


text:
import CUDAdrv: CUtexObject, cuTexObjectCreate, cuTexObjectDestroy,
CUDA_RESOURCE_DESC, CUDA_TEXTURE_DESC, CUDA_RESOURCE_VIEW_DESC,
CU_RESOURCE_TYPE_ARRAY, CU_RESOURCE_TYPE_LINEAR, CU_RESOURCE_TYPE_PITCH2D,
CUaddress_mode_enum, CU_TR_ADDRESS_MODE_WRAP, CU_TR_ADDRESS_MODE_CLAMP,
CU_TR_ADDRESS_MODE_MIRROR, CU_TR_ADDRESS_MODE_BORDER,
CUfilter_mode_enum, CU_TR_FILTER_MODE_POINT, CU_TR_FILTER_MODE_LINEAR,
CU_TRSF_NORMALIZED_COORDINATES, CU_TRSF_READ_AS_INTEGER

import CuArrays: CuArray

export mode_wrap, mode_clamp, mode_mirror, mode_border, mode_point, mode_linear

const AddressMode = CUDAdrv.CUaddress_mode_enum
const mode_wrap = CU_TR_ADDRESS_MODE_WRAP
const mode_clamp = CU_TR_ADDRESS_MODE_CLAMP
const mode_mirror = CU_TR_ADDRESS_MODE_MIRROR
const mode_border = CU_TR_ADDRESS_MODE_BORDER

const FilterMode = CUDAdrv.CUfilter_mode_enum
const mode_point = CU_TR_FILTER_MODE_POINT
const mode_linear = CU_TR_FILTER_MODE_LINEAR

"""
Type to handle CUDA texture objects. These objects do not hold data by themselves,
but instead are bound either to `CuTextureArray`s (CUDA arrays) or to `CuArray`s.
(Note: For correct wrapping `CuArray`s it is necessary the their memory is well aligned and strided (good pitch).
Currently, that is not being enforced.)

Theses objects are meant to be used to do texture fetchts inside CUDAnative.jl kernels.
When passed to CUDAnative.jl kernels, `CuTexture` objects are transformed into `CuDeviceTexture`s objects.
"""
mutable struct CuTexture{T,N,Mem}
    mem::Mem
    handle::CUtexObject

    function CuTexture{T,N,Mem}(texmemory::Mem,
                                address_modes::NTuple{N,AddressMode},
                                filter_mode::FilterMode) where {T,N,Mem}

        Ta = cuda_texture_alias_type(T)
        _assert_alias_size(T, Ta)
        nchan, format, Te = _alias_type_to_nchan_and_format(Ta)

        resDesc_ref = _construct_CUDA_RESOURCE_DESC(texmemory)
        resDesc_ref = pointer_from_objref(resDesc_ref)

        address_modes = tuple(address_modes..., ntuple(_->mode_clamp, 3 - N)...)
        filter_mode = filter_mode
        flags = zero(CU_TRSF_NORMALIZED_COORDINATES)  # use absolute (non-normalized) coordinates
        flags = flags | (Te <: Integer ? CU_TRSF_READ_AS_INTEGER : zero(CU_TRSF_READ_AS_INTEGER))

        texDesc_ref = Ref(CUDA_TEXTURE_DESC(address_modes, # addressMode::NTuple{3, CUaddress_mode}
                                            filter_mode, # filterMode::CUfilter_mode
                                            flags, # flags::UInt32
                                            1, # maxAnisotropy::UInt32
                                            filter_mode, # mipmapFilterMode::CUfilter_mode
                                            0, # mipmapLevelBias::Cfloat
                                            0, # minMipmapLevelClamp::Cfloat
                                            0, # maxMipmapLevelClamp::Cfloat
                                            ntuple(_->Cfloat(zero(Te)), 4), # borderColor::NTuple{4, Cfloat}
                                            ntuple(_->Cint(0), 12)))

        texObject_ref = Ref{CUtexObject}(0)
        cuTexObjectCreate(texObject_ref, resDesc_ref, texDesc_ref, C_NULL)

        t = new{T,N,Mem}(texmemory, texObject_ref[])
        finalizer(unsafe_free!, t)
        return t
    end
end

function _construct_CUDA_RESOURCE_DESC(texarr::CuTextureArray{T,N}) where {T,N}
    # #### TODO: use CUDAdrv wrapped struct when its padding becomes fixed
    # res = Ref{CUDAdrv.ANONYMOUS1_res}()
    # unsafe_store!(Ptr{CUarray}(pointer_from_objref(res)), texarr.handle)
    # resDesc_ref = Ref(CUDA_RESOURCE_DESC(
    #     CU_RESOURCE_TYPE_ARRAY, # resType::CUresourcetype
    #     res[], # res::ANONYMOUS1_res
    #     0 # flags::UInt32
    # ))
    resDesc_ref = Ref((CU_RESOURCE_TYPE_ARRAY, # resType::CUresourcetype
                        texarr.handle, # 1 x UInt64
                        ntuple(_->Int64(0), 15), # 15 x UInt64
                        UInt32(0)))
    return resDesc_ref
end

function _construct_CUDA_RESOURCE_DESC(arr::CuArray{T,N}) where {T,N}
    # TODO: take care of allowed pitches
    @assert 1 <= N <= 2 "Only 1D or 2D (dimension) CuArray objects can be wrapped in a texture"

    Ta = cuda_texture_alias_type(T)
    _assert_alias_size(T, Ta)
    nchan, format, Te = _alias_type_to_nchan_and_format(Ta)

    # #### TODO: use CUDAdrv wrapped struct when its padding becomes fixed
    resDesc_ref = Ref(((N == 1 ? CU_RESOURCE_TYPE_LINEAR : CU_RESOURCE_TYPE_PITCH2D), # resType::CUresourcetype
                        pointer(arr), # 1 x UInt64 (CUdeviceptr)
                        format, # 1/2 x UInt64 (CUarray_format)
                        UInt32(nchan), # 1/2 x UInt64
                        (N == 2 ? size(arr, 1) : size(arr, 1) * sizeof(T)), # 1 x UInt64 nx
                        (N == 2 ? size(arr, 2) : 0), # 1 x UInt64 ny
                        (N == 2 ? size(arr, 1) * sizeof(T) : 0), # 1 x UInt64 pitch
                        ntuple(_->Int64(0), 11), # 11 x UInt64
                        UInt32(0)))
    return resDesc_ref
end

function unsafe_free!(t::CuTexture)
    if t.handle != C_NULL
        cuTexObjectDestroy(t.handle)
        t.handle = C_NULL
    end
    return nothing
end

@inline function CuTexture{T,N,Mem}(texmemory::Mem;
                            address_mode::AddressMode = mode_clamp,
                            address_modes::NTuple{N,AddressMode} = ntuple(_->address_mode, N),
                            filter_mode::FilterMode = mode_linear
                            ) where {T,N,Mem}
    CuTexture{T,N,Mem}(texmemory, address_modes, filter_mode)
end
CuTexture(texarr::CuTextureArray{T,N}; kwargs...) where {T,N} = CuTexture{T,N,CuTextureArray{T,N}}(texarr; kwargs...)
CuTexture{T}(n::Int; kwargs...) where {T} = CuTexture(CuTextureArray{T,1}((n,)); kwargs...)
CuTexture{T}(nx::Int, ny::Int; kwargs...) where {T} = CuTexture(CuTextureArray{T,2}((nx, ny)); kwargs...)
CuTexture{T}(nx::Int, ny::Int, nz::Int; kwargs...) where {T} = CuTexture(CuTextureArray{T,3}((nx, ny, nz)); kwargs...)
CuTexture(cuarr::CuArray{T,N}; kwargs...) where {T,N} = CuTexture{T,N,CuArray{T,N}}(cuarr; kwargs...)

Base.eltype(tm::CuTexture{T,N}) where {T,N} = T
Base.size(tm::CuTexture) = size(tm.mem)


text:
using Meganet, JLD, BenchmarkTools

history = Pkg.dir("Meganet")*"//benchmarks//micro//bm_multConv2Dblock.jld"
file = Pkg.dir("Meganet")*"/benchmarks/micro/vars_multConv2Dblock.jld"

x = load(file, "x")
K = load(file, "K")
y = load(file, "y")
t = load(file, "t")
shiftX = load(file, "shiftX")
shiftT = load(file, "shiftT")
imIdx = load(file, "imIdx")
doDerivative = load(file, "doDerivative")

trial = @benchmark Meganet.multConv2Dblock(x, K, y, t, shiftX, shiftT, imIdx, doDerivative = doDerivative)
@enter Meganet.multConv2Dblock(x, K, y, t, shiftX, shiftT, imIdx, doDerivative = doDerivative)
display(trial)
hist = load(history, "multConv2Dblock")

if false
    Meganet.updatehistory!(history, trial, "multConv2Dblock")
end

y = load(file, "y")
Meganet.multConv2Dblock(x, K, y, t, shiftX, shiftT, imIdx, doDerivative = doDerivative)
y = load(file, "y")
Profile.clear_malloc_data()
Meganet.multConv2Dblock(x, K, y, t, shiftX, shiftT, imIdx, doDerivative = doDerivative);


text:
@doc raw"""
Euler extrapolation method with arbitrary order p.

Solves the ordinary differential equation
```math
\begin{aligned}
\dot{x} &= v(t, x) , &
x(t_0) &= x_0 ,
\end{aligned}
```
for $x_1 = x(t_1)$.

Call with
```julia
_euler_extrapolation_ode!(v, t₀, t₁, x₀, x₁, s)
```

where

* `v`:  function to compute vector field with signature `v(t,x,ẋ)`
* `t₀`: initial time
* `t₁`: final   time
* `x₀`: initial value $x_0 = x(t_0)$
* `x₁`: final   value $x_1 = x(t_1)$
* `s`:  number of interpolations (order $p=s+1$)

"""
function _euler_extrapolation_ode!(v::Function, t₀::TT, t₁::TT, x₀::AbstractVector{DT}, x₁::AbstractVector{DT}, s::Int) where {DT,TT}
    @assert size(x₀) == size(x₁)

    F   = collect(1:(s+1))
    Δt  = t₁ - t₀
    σ   = Δt ./ F
    pts = repeat(x₀, outer = [1, s+1])

    local xᵢ = zero(x₀)
    local vᵢ = zero(x₀)

    for i in F
        for _ in 1:(F[i]-1)
            tᵢ = t₀ + σ[i]
            for k in axes(pts,1)
                xᵢ[k] = pts[k,i]
            end
            v(tᵢ, xᵢ, vᵢ)
            for k in axes(pts,1)
                pts[k,i] += σ[i] * vᵢ[k]
            end
        end
    end

    aitken_neville!(σ, pts, zero(TT), x₁)
    return x₁
end

# struct EulerExtrapolation <: Extrapolation
#     s::Int
# end

# function GeometricBase.evaluate!(extrap::EulerExtrapolation, v::Function, t₀::TT, t₁::TT, x₀::AbstractVector{DT}, x₁::AbstractVector{DT}) where {DT,TT}
#     _euler_extrapolation_ode!(v, t₀, t₁, x₀, x₁, extrap.s)
# end

# function GeometricBase.evaluate!(extrap::EulerExtrapolation, v::Function, f::Function, t₀::TT, t₁::TT, q₀::AbstractVector{DT}, q₁::AbstractVector{DT}, p₀::AbstractVector{DT}, p₁::AbstractVector{DT}) where {DT,TT}
#     _euler_extrapolation_pode!(v, t₀, t₁, q₀, q₁, p₀, p₁, extrap.s)
# end

struct EulerExtrapolationODE{VT} <: Extrapolation
    s::Int
    v::VT

    function EulerExtrapolationODE{VT}(v, s) where {VT}
        new(s,v)
    end
end

function EulerExtrapolationODE(v::VT, s::Int) where {VT}
    EulerExtrapolationODE{VT}(v, s)
end

function EulerExtrapolation(equ::ODE, s::Int)
    EulerExtrapolationODE(_get_v(equ), s)
end

function GeometricBase.evaluate!(extrap::EulerExtrapolationODE, t₀::TT, t₁::TT, x₀::AbstractVector{DT}, x₁::AbstractVector{DT}) where {DT,TT}
    _euler_extrapolation_ode!(extrap.v, t₀, t₁, x₀, x₁, extrap.s)
end

# struct EulerExtrapolationIODE{VT,FT} <: Extrapolation
#     s::Int
#     v::VT
#     f::FT

#     function EulerExtrapolationIODE{VT,FT}(v, f, s) where {VT,FT}
#         new(s,v,f)
#     end
# end

# function EulerExtrapolationIODE(v::VT, f::FT, s::Int) where {VT,FT}
#     EulerExtrapolationIODE{VT,FT}(v, f, s)
# end

# function EulerExtrapolation(equ::IODE, s::Int)
#     EulerExtrapolationIODE(_get_v(equ), _get_f(equ), s)
# end

# function GeometricBase.evaluate!(extrap::EulerExtrapolationIODE, t₀::TT, t₁::TT, q₀::AbstractVector{DT}, q₁::AbstractVector{DT}, p₀::AbstractVector{DT}, p₁::AbstractVector{DT}) where {DT,TT}
#     _euler_extrapolation_iode!(extrap.v, t₀, t₁, q₀, q₁, p₀, p₁, extrap.s)
# end

# struct EulerExtrapolationPODE{VT,FT} <: Extrapolation
#     s::Int
#     v::VT
#     f::FT

#     function EulerExtrapolationPODE{VT,FT}(v, f, s) where {VT,FT}
#         new(s,v,f)
#     end
# end

# function EulerExtrapolationPODE(v::VT, f::FT, s::Int) where {VT,FT}
#     EulerExtrapolationPODE{VT,FT}(v, f, s)
# end

# function EulerExtrapolation(equ::PODE, s::Int)
#     EulerExtrapolationPODE(_get_v(equ), _get_f(equ), s)
# end

# function GeometricBase.evaluate!(extrap::EulerExtrapolationPODE, t₀::TT, t₁::TT, q₀::AbstractVector{DT}, q₁::AbstractVector{DT}, p₀::AbstractVector{DT}, p₁::AbstractVector{DT}) where {DT,TT}
#     _euler_extrapolation_pode!(extrap.v, t₀, t₁, q₀, q₁, p₀, p₁, extrap.s)
# end


text:
module NewtonsMethod

greet() = print("Hello World!")

using LinearAlgebra, ForwardDiff, Statistics

function newtonroot(f, f_prime; x0, tol = 1E-7, maxiter = 1000)
    x_old = x0
    iter = 1
    while normdiff > tol && iter <= maxiter
        x_new = x_old - (f(x_old)/f_prime(x_old))
        normdiff = norm(x_new - x_old)
        x_old = x_new
        iter = iter + 1
    end
    if normdiff > tol
        return nothing
    else
        return x_old
    end

end

function newtonroot(f; x0, tol = 1E-7, maxiter = 1000)
    f_prime(x) = ForwardDiff.derivative(f,x)
    x_old = x0
    normdiff = Inf
    iter = 1
    while normdiff > tol && iter <= maxiter
        x_new = x_old - (f(x_old)/f_prime(x_old))
        normdiff = norm(x_new - x_old)
        x_old = x_new
        iter = iter + 1
    end
    if normdiff > tol
        return nothing
    else
        return x_old
    end
end

export newtonroot
end # module


text:
module DocThemeIndigo

using Sass

function install(m::Module)
    doc = joinpath(dirname(dirname(pathof(m))), "docs")
    src = joinpath(@__DIR__, "..", "assets", "indigo.scss")
    assets = joinpath(doc, "src", "assets")
    dst = joinpath(assets, "indigo.css")
    ispath(assets) || mkpath(assets)
    Sass.compile_file(src, dst)
    return joinpath("assets", "indigo.css")
end

end


text:
function fib(n)
    p = (1+sqrt(5))/2
    return (round(((p)^n)/sqrt(5)))
end

for i=0:92
    println("fibonacci of ",i," = ",trunc(Int,fib(i)))
end

max_stars_repo_name: landrumb/julia

max_issues_repo_name: landrumb/julia

max_forks_repo_name: landrumb/julia


text:
# Implicit compressed sensing via the GenSPGL package, which allows an implicit
# DCT-II transform that significantly speeds things up.

export csimage, simulate_csimage

const dσ = 1e-1
const doptions = (verbosity=0,)

function csimage(cs; σ = dσ, options = doptions)
    BLAS.set_num_threads(16)
    s = spgl1(joMatrix(cs.patterns) * joDCT(size(cs.patterns)[2]),
              cs.measurements,
              tau     = 0.0,
              sigma   = σ,
              options = spgOptions(;stepMin=10.0*eps(), iterations=5000,
                                   options...))[1]
    renormalize(dct(s))
end

"Simulate compressed sensing of `img` with `m` measurements."
function simulate_csimage(img, m, σ = dσ, options = doptions)
    spgl_sense(measure(imvec(img), m), σ, options)
end


text:
function simple_effect_probabilities(chain, data)
    γpost_x = collect(hcat(data.Xunique...)')
    γpost_mins = [minimum(γpost_x[:, i]) for i in 1:size(γpost_x, 2)]
    γpost_x = γpost_x - ones(size(γpost_x, 1)) * (γpost_mins .- 1)'

    ch_gamma_mat = collect(hcat(chain.gamma...)')
    nvar = size(γpost_x, 2)
    p_effects = zeros(nvar)
    for i in 1:nvar
        subset = deepcopy(γpost_x)
        subset[:, i] = 2 .- (subset[:, i] .!= 1)
        subset = maximum(subset, dims = 2)[:, 1] .== 1
        if sum(subset) > 0
            p_effects[i] = mean(maximum(ch_gamma_mat[:, subset], dims = 2))
        end
    end
    return DataFrame(var1 = ["x$i" for i in 1:nvar], prob = p_effects)
end


text:
using CyclicArrays
using Plots
using Statistics
using LaTeXStrings

# 1d array example

faces=zeros(1,1,2,4);
faces[1,1,1,:]=[1,1,2,0];
faces[1,1,2,:]=[1,1,1,0];

grid=CyclicArray(faces);
x=[-3:0.1:3;];
rho0=exp.(-(x).^2)
rho0=10*rho0/sum(rho0);
DT=0.5;
DX=1;
NT=400;
rho1=zeros(length(rho0));

crho0=CyclicArray(rho0,grid);
crho1=CyclicArray(rho0*0,grid);
plot_array = Any[]
anim = @animate for i ∈ 1:NT
    crho1=crho0+(shiftc(crho0,dims=1,shift=1) + shiftc(crho0,dims=1,shift=-1) - (2 * crho0))*DT/DX^2
    println(i," = ",(maximum(crho1)))
    global crho0=crho1;
    plt=plot(x,crho1.data,ylims=(0,0.6),
                          title=string(i/2," seconds"),
                          ylabel=L"\rho~~[kg~m^{-3}]",
                          xlabel="x [m]",
                          leg=false)
    push!(plot_array,plt)
    println(mean(crho1))
end
gif(anim, "Diffusion1D.gif", fps = 30)


text:
module StomataModels

using ConstrainedRootSolvers: NewtonBisectionMethod, SolutionTolerance,
      find_zero
using DocStringExtensions: TYPEDFIELDS
using ..Photosynthesis: AbstractPhotoModelParaSet, AirLayer, GCO₂Mode, Leaf,
      leaf_ETR!, leaf_fluorescence!, leaf_photosynthesis!,
      leaf_temperature_dependence!
using PkgUtility: CP_D_MOL, K_STEFAN, M_H₂O, T_25
using ..PlantHydraulics: AbstractSoilVC, LeafHydraulics, TreeSimple,
      critical_flow, end_pressure, soil_k_ratio_p25, temperature_effects!,
      xylem_k_ratio, xylem_risk
using UnPack: @unpack
using WaterPhysics: latent_heat_vapor, relative_diffusive_coefficient,
      saturation_vapor_pressure

# export public types and structs
export BetaGLinearKleaf, BetaGLinearKsoil, BetaGLinearPleaf, BetaGLinearPsoil,
       BetaGLinearSWC, BetaVLinearKleaf, BetaVLinearKsoil, BetaVLinearPleaf,
       BetaVLinearPsoil, BetaVLinearSWC, CanopyLayer, EmpiricalStomatalModel,
       ESMBallBerry, ESMGentine, ESMLeuning, ESMMedlyn, GlcDrive, GswDrive,
       OptimizationStomatalModel, OSMEller, OSMSperry, OSMWang, OSMWAP,
       OSMWAPMod

# export public functions
export gsw_control!, gas_exchange!, prognostic_gsw!, solution_diff!,
       stomatal_conductance, update_leaf_AK!, update_leaf_TP!

include("types/beta.jl"         )
include("types/canopylayer.jl"  )
include("types/drive.jl"        )
include("types/stomatalmodel.jl")

include("model/beta.jl"       )
include("model/control.jl"    )
include("model/empirical.jl"  )
include("model/gasexchange.jl")
include("model/nocturnal.jl"  )
include("model/prognostic.jl" )
include("model/refresh.jl"    )
include("model/solution.jl"   )

end # module

max_stars_repo_name: climate-machine/Land

max_issues_repo_name: climate-machine/Land

max_forks_repo_name: climate-machine/Land


text:
"""
This function returns a dataframe of given csv data.
"""
function read_data(file_name::String)
    projectdata = DataFrames.DataFrame(CSV.File(file_name;
                        truestrings=["T", "TRUE", "true"],
                        falsestrings=["F", "FALSE", "false"]));
    return projectdata
end

function write_data(dir:: String, file_name::String, data::DataFrames.DataFrame)

    dir_exists(dir::String)

    CSV.write(joinpath(dir, file_name), data)

    return
end

"""
This function makes the directory specified in the argument, if it doesn't exist.
Returns nothing.
"""
function dir_exists(dir::String)
    try
      readdir(dir)
    catch err
      mkpath(dir)
    end
    return
end


text:
using DataTypesBasic
using Test

const logging = []
cm(i=4, prefix="") = @ContextManager function(cont)
  push!(logging, "$(prefix)before $i")
  result = cont(i)
  push!(logging, "$(prefix)after $i")
  result
end
@test eltype(cm()) == Int
@test eltype(typeof(cm())) == Int
@test eltype(ContextManager) == Any

empty!(logging)
@test run(cm(1)) == 1
@test logging == ["before 1", "after 1"]

empty!(logging)
@test run(x -> 3x, cm(2)) == 6
@test logging == ["before 2", "after 2"]

empty!(logging)
@test foreach(x -> push!(logging, "x = $x"), cm(2)) == nothing
@test logging == ["before 2", "x = 2", "after 2"]

# FunctorApplicativeMonad
# =======================

cm_cm = map(x -> cm(x*x), cm())

# TODO check why the typeinference is worse now
# @test Out(cm_cm, typeof(x -> x)) <: ContextManager
empty!(logging)
@test Iterators.flatten(cm_cm)(x->x) == 16
@test logging == ["before 4", "before 16", "after 16", "after 4"]


text:
"""
direct product of two or more discrete functions

if we have:
    ``f_1 : D_1 → R_1``
    `` …``
    ``f_k : D_k → R_k``
then FunProduct((f1, …, fk)) is F
    F : D1 × … × Dk → R1 × … × Rk
"""
struct FunProduct{N, T, D, R} <: CompositeFunction
    factors :: NTuple{N, T}
    dom :: D
    rng :: R
end

const FProd = FunProduct

domain(fp :: FProd) = fp.dom
codomain(fp :: FProd) = fp.rng
factors(fp :: FProd) = fp.factors

# creating functional product from dom and codom
function FProd(dom :: AbstractProduct, codom :: AbstractProduct)
    fs = Tuple(ResFun(dom[i], codom[i]) for i in 1 : ndims(dom))
    return FProd(fs, dom, codom)
end

# creating functional product from two tuples
# of equal length!

function FProd(from :: NTuple{N, Int}, to :: NTuple{N, Int}) where N
    fromto = zip(from, to)
    funs = tuple(map(ResidueFunction, fromto)...)
    return FProd(funs)
end

function FProd(factors)
    dom = DirectProduct(map(domain, factors))
    rng = DirectProduct(map(codomain, factors))
    return FProd(factors, dom, rng)
end

FProd(factors...) = FProd(tuple(factors...))

function (fp :: FProd{N, T, D, R})(v) where {N,T,D,R}
    funs = factors(fp)
    n = length(v)
    return NTuple{N}(funs[i](v[i]) for i in 1 : n)
end

function Base.setindex!(fp :: FProd, v :: NTuple, k :: NTuple)
    fs = factors(fp)
    for i in 1 : size(fp)
        fs[i][k[i]] = v[i]
    end
end
(fs :: FProd)(v...) = fs(tuple(v...))

max_stars_repo_name: kirtsar/DiscreteFunctions

max_issues_repo_name: kirtsar/DiscreteFunctions


text:
#############################################################################
#############################################################################
#
# This file implement several basic algorithms for integers.
#
#############################################################################
#############################################################################

"""
The quotient between two numbers
"""
function quo(a::T,b::T) where T <: Integer
    a < 0 && return -quo(-a,b)
    a < b && return 0
    return 1 + quo(a-b, b)
end

"""
Euclid's algorithm.
"""
function euclid_alg(a, b; rem_function = %)
    b == 0 && return a
    return euclid_alg(b, rem_function(a,b); rem_function = rem_function)
end

"""
Euclid's algorithm on multiple arguments.
"""
euclid_alg(a...) = foldl((a,b)->euclid_alg(a,b), a; init = 0)

"""
Euclid's algorithm on a vector.
"""
euclid_alg(a::Vector{T}) where T <: Integer = euclid_alg(a...)

"""
The extended Euclidean algorithm.
"""
function ext_euclid_alg(a, b, rem_function = %, div_function = ÷)
    a == 0 && return b, 0, 1
    g, t, s = ext_euclid_alg(rem_function(b,a), a, rem_function, div_function)
    s = s - div_function(b,a)*t
    @assert g == a*s + b*t
    return g, s, t
end

"""
Display the result of the extended Euclidean algorithm.
"""
pretty_print_egcd((a,b),(g,s,t)) = println("$a × $s + $b × $t = $g") #\times + [TAB]

"""
Integer inverse symmetric mod
"""
function int_inverse_mod(a::Int, m::Int)::Int
    if mod(a, m) == 0
        error("Can't find inverse of $a mod $m because $m divides $a")
    end
    return mod(ext_euclid_alg(a,m)[2],m)
end


text:
#------------------------------
# Linear ODE system with four unkown parameters
#------------------------------

#------------------------------
# setup
#------------------------------

using Pkg;

# all paths are relative to the repository main folder

Pkg.activate(".")
Pkg.instantiate()
Pkg.status()

using Distributions
using Random
using Flux
using DiffEqFlux
using OrdinaryDiffEq
using Plots
using LaTeXStrings
gr()

include("../src/simulation.jl")
include("../src/model.jl")
include("../src/batches.jl")
include("../src/plotting.jl")

#------------------------------
# define and obtain ground-truth developments
#------------------------------

# define initial condition
true_u0 = Float32[4, 2]
# define time span on which to solve the ODE
tspan = (0.0f0, 10.0f0)
# define parameters for the two distinct groups
true_odeparams_group1 = Float32[-0.2, 0.1, 0.1, -0.2]
true_odeparams_group2 = Float32[-0.2, 0.1, -0.1, 0.25]

# define corresponding ODE problems for the two groups
prob1 = ODEProblem(linear_2d_system,true_u0,tspan,true_odeparams_group1)
prob2 = ODEProblem(linear_2d_system,true_u0,tspan,true_odeparams_group2)

# solve ODE systems to obtain "true" underlying trajectory in each group
dt=0.1
sol_group1 = solve(prob1, Tsit5(), saveat = dt);
sol_group2 = solve(prob2, Tsit5(), saveat = dt);

#------------------------------
# simulate data based on true trajectories
#------------------------------

# generate data
n = 100
p = 10
q, q_info = 50, 20
baseline_simulation="trueparams"

Random.seed!(12)#Random.seed!(1234)
xs, tvals, group1, group2 = generate_xs(n, p, true_u0, sol_group1, sol_group2; maxntps=1, σ_ind=0.1); # default vals: t_start=1.5, t_end=10, maxntps=10, dt=0.1, σ_var=0.1, σ_ind=0.5
if baseline_simulation == "trueparams"
    x_baseline = generate_baseline_4params(n, q, q_info, group1, true_odeparams_group1, true_odeparams_group2); # defaul vals: σ_info=0.1, σ_noise=0.1
elseif baseline_simulation == "groupsonly"
    x_baseline = generate_baseline(n, q, q_info, group1); # default vals: σ_info=1, σ_noise=1
else
    error("Please select either 'trueparams' or 'groupsonly' as mode for simulation of baseline variables")
end

#------------------------------
# define model
#------------------------------

zdim = 2
nODEparams = 4
m = init_vae(p, q, zdim, nODEparams, prob1, seed=550) # 543
opt = ADAM(0.0005) # 0.001
nepochs = 10

batchsize = 10
kernel=tricube

L = batchloss_wrapper(m)
ps = getparams(m)

evalcb_zs() = eval_z_trajectories(xs, x_baseline, tvals, group1, sol_group1, sol_group2, m, dt)

#------------------------------
# train model
#------------------------------

numbers_of_correctly_classified = []

for epoch in 1:10
    distmat = getdistmat_odesols_mean(m, xs, x_baseline; centralise=true);
    minibatches, batch_weights = findminibatches_distmat(distmat, batchsize, kernel);
    #minibatches, batch_weights = randomminibatches(x_baseline,batchsize)
    corr_class_g1, corr_class_g2, corr_class_total  = evaluate_minibatches_pergroup(minibatches,group1,group2);
    @info "epoch " epoch
    @info "group1 : $(round(corr_class_g1, digits=3)) correctly classified"
    @info "group2 : $(round(corr_class_g2, digits=3)) correctly classified"
    @info "in total $(round(corr_class_total, digits=3)) correctly classified"
    batch_xs = collect(xs[minibatches[i]] for i in 1:length(xs));
    batch_x_baseline = collect(x_baseline[minibatches[i]] for i in 1:length(x_baseline));
    batch_tvals = collect(tvals[minibatches[i]] for i in 1:length(tvals));
    batchdata = zip(batch_xs, batch_x_baseline, batch_tvals, batch_weights);
    push!(numbers_of_correctly_classified,[corr_class_g1, corr_class_g2, corr_class_total])
    Flux.train!(L, ps, batchdata, opt)
    @show(mean(L.(batch_xs, batch_x_baseline, batch_tvals, batch_weights)))
    evalcb_zs()
end

#------------------------------
# visualize results
#------------------------------

data = simdata(xs, x_baseline, tvals, group1, group2);

plot(plot_truesolution(2, data, sol_group1, sol_group2, showdata=false),
    plot_truesolution(1,  data, sol_group1, sol_group2, showdata=false),
    plot_truesolution(2, data, sol_group1, sol_group2, showdata=true),
    plot_truesolution(1,  data, sol_group1, sol_group2, showdata=true),
    layout = (2,2),
    legend = false
)

p1 = plot_batch_solution(66, xs, x_baseline, tvals, group1, sol_group1, sol_group2, m, dt)
p2 = plot_batch_solution(87, xs, x_baseline, tvals, group1, sol_group1, sol_group2, m, dt, showlegend=false)

p3 = allindsplot(2, data, m, sol_group1, sol_group2)
p4 = allindsplot(1, data, m, sol_group1, sol_group2, showlegend=false)

plot(p3, p4, layout = (1,2))


text:
# a few utility functions copied/adapted from Images.jl

"blur images `A` and `B` with `sigma` and then compute a difference"
function blurdiff(A::AbstractArray, B::AbstractArray, sigma)
    # make sure arrays have the same size
    if size(A) != size(B)
        newsize = map(max, size(A), size(B))
        if size(A) != newsize
            A = copyto!(zeros(eltype(A), newsize...), A)
        end
        if size(B) != newsize
            B = copyto!(zeros(eltype(B), newsize...), B)
        end
    end

    kern = KernelFactors.IIRGaussian(sigma)
    Af = imfilter(A, kern, NA())
    Bf = imfilter(B, kern, NA())
    d = sad(Af, Bf)
    diffscale = max(maxabsfinite(A), maxabsfinite(B))
    diffpct = d / (length(Af) * diffscale)

    diffpct
end

sad(A::AbstractArray, B::AbstractArray) = sumdiff(abs, A, B)

function sumdiff(f, A::AbstractArray, B::AbstractArray)
    axes(A) == axes(B) || throw(DimensionMismatch("A and B must have the same indices"))
    T = promote_type(difftype(eltype(A)), difftype(eltype(B)))
    s = zero(accum(eltype(T)))
    for (a, b) in zip(A, B)
        x = convert(T, a) - convert(T, b)
        s += f(x)
    end
    s
end

difftype(::Type{T}) where {T<:Integer} = Int
difftype(::Type{T}) where {T<:Real} = Float32
difftype(::Type{Float64}) = Float64
difftype(::Type{CV}) where {CV<:Colorant} = difftype(CV, eltype(CV))
difftype(::Type{CV}, ::Type{T}) where {CV<:RGBA,T<:Real} = RGBA{Float32}
difftype(::Type{CV}, ::Type{Float64}) where {CV<:RGBA} = RGBA{Float64}
difftype(::Type{CV}, ::Type{T}) where {CV<:BGRA,T<:Real} = BGRA{Float32}
difftype(::Type{CV}, ::Type{Float64}) where {CV<:BGRA} = BGRA{Float64}
difftype(::Type{CV}, ::Type{T}) where {CV<:AbstractGray,T<:Real} = Gray{Float32}
difftype(::Type{CV}, ::Type{Float64}) where {CV<:AbstractGray} = Gray{Float64}
difftype(::Type{CV}, ::Type{T}) where {CV<:AbstractRGB,T<:Real} = RGB{Float32}
difftype(::Type{CV}, ::Type{Float64}) where {CV<:AbstractRGB} = RGB{Float64}

accum(::Type{T}) where {T<:Integer} = Int
accum(::Type{Float32})    = Float32
accum(::Type{T}) where {T<:Real} = Float64
accum(::Type{C}) where {C<:Colorant} = base_colorant_type(C){accum(eltype(C))}

"maximum absolute value in `A` ignoring `Inf` or `NaN`"
function maxabsfinite(A)
    m = -Inf
    for a in A
        v = abs(a)
        if isfinite(v) && v > m
            m = v
        end
    end
    m
end


text:
# Defaults for PSU ICS-ACI HPF
base_path_default = "/gpfs/group/cfb12/default/HPFData"
manifest_subdir_default = joinpath("Processed","Manifests")
data_subdir_default = joinpath("Processed","FlatSkyCorrected")


text:
# returns MxN matrix of squared distance values, where M is the number of test points and N the number of model points
function squareddistances(X::Matrix{Float64}, Y::Matrix{Float64})
    @assert size(Y,2)==size(X,2) # sequence space dimensions should be the same
    sum(Y.^2;dims=2)' .+ sum(X.^2;dims=2) .- 2*X*Y'
end

# returns MxN matrix of edit distance values
function editdistance(X,Y)
    @assert size(Y,2)==size(X,2)
    M,N = size(X,1),size(Y,1)
    D = zeros(Int,M,N)
    for j=1:N
        for i=1:M
            D[i,j] = sum(X[i,:].!=Y[j,:])
        end
    end
    D
end

function groupequal(X,Y)
    @assert size(Y,2)==size(X,2)
    M,N = size(X,1),size(Y,1)
    D = falses(M,N)
    for j=1:N
        for i=1:M
            D[i,j] = X[i,:] == Y[j,:]
        end
    end
    D
end

mutable struct LandscapeModel
    X::Matrix{Float64}
    fitness::Vector{Float64}
    σ::Float64

    function LandscapeModel(X::Matrix{Float64}, fitness::Vector{Float64}, σ::Float64)
        @assert size(X,1)==length(fitness)
        new(X,fitness,σ)
    end
end
LandscapeModel(X::AbstractMatrix,fitness::AbstractVector,σ::Real) = LandscapeModel(convert(Matrix{Float64},X), convert(Vector{Float64},fitness), convert(Float64,σ))

LandscapeModel(X::Matrix{Float64},fitness::Vector{Float64},σValues::AbstractVector{Float64}; kwargs...) =
    LandscapeModel(X,fitness,landscapekernelwidth(X,fitness,σValues;kwargs...)[1])
LandscapeModel(X,fitness,σValues::AbstractVector; kwargs...) =
    LandscapeModel(convert(Matrix{Float64},X), convert(Vector{Float64},fitness), convert(Vector{Float64},σValues); kwargs...)

# D2 - MxN matrix of squared distance values, where M is the number of test points and N the number of training points
function _predictfitness(D2::Matrix{Float64}, fitness::Vector{Float64}, σ::Float64)
    # Wᵢⱼ = e^( -Dᵢⱼ²/(2σ²) )
    # But since we only care about each row of W up to a constant value, we can multiply each row with e^(Aᵢ/2σ²) where Aᵢ=minⱼ Dᵢⱼ²
    # this guarantees that we get precision in the computations, by making Wᵢⱼ=1 for the closest training point.
    W = exp.( (minimum(D2;dims=2).-D2)/(2σ^2) )
    W = W./sum(W;dims=2) # normalize sum of weights to 1 for each row
    W*fitness # the test point fitness is a weighted average of the fitness values for the training points
end

predictfitness(model::LandscapeModel, X::Matrix{Float64}) = _predictfitness(squareddistances(X,model.X), model.fitness, model.σ)

_fitnessresidualssquared(D2::Matrix{Float64}, fitnessTrain::Vector{Float64}, fitnessTest::Vector{Float64}, σ::Float64) =
    (fitnessTest - _predictfitness(D2, fitnessTrain, σ)).^2

_fitnesserrortransform(x) = x       # mean squared error
#_fitnesserrortransform(x) = sqrt(x) # RMS error

_fitnesserror(residualsSquared::Array{Float64}, args...; kwargs...) = _fitnesserrortransform(mean(residualsSquared, args...; kwargs...))

_fitnesserror(D2::Matrix{Float64}, fitnessTrain::Vector{Float64}, fitnessTest::Vector{Float64}, σ::Float64) =
    _fitnesserror(_fitnessresidualssquared(D2,fitnessTrain,fitnessTest,σ))

# find optimal kernel width by repeated random subsampling
function landscapekernelwidth(X::Matrix{Float64}, fitness::Vector{Float64}, σValues::AbstractVector{Float64}; nbrIter=100, trainingProp=0.9)
    N = size(X,1)
    X2 = sum(X.^2; dims=2)
    D2 = X2 .+ X2' .- 2*X*X'

    nbrTest = round(Int,N*(1-trainingProp))
    @assert 0 < nbrTest < N

    allErrors = zeros(length(σValues), nbrIter)

    testMask = falses(N)
    for i=1:nbrIter
        # select a random training subset each time
        testMask[:] .= false
        testMask[sample(1:N,nbrTest,replace=false)] .= true

        D2TestTrain = D2[testMask,.~testMask]
        fitnessTrain = fitness[.~testMask]
        fitnessTest = fitness[testMask]

        allErrors[:,i] = map(x->_fitnesserror(D2TestTrain,fitnessTrain,fitnessTest,x), σValues)
    end

    meanErrors = mean(allErrors,dims=2)[:]
    ind = argmin(meanErrors)
    σValues[ind], meanErrors[ind], meanErrors
end

# find optimal kernel width by repeated random subsampling
# gives one σ per sample, where σ is estimated from all samples except the one left out
function leaveoneoutlandscapekernelwidth(X::Matrix{Float64}, fitness::Vector{Float64}, σValues::AbstractVector{Float64}; nbrIter=100, trainingProp=0.9)
    N = size(X,1)
    @assert N==length(fitness)
    X2 = sum(X.^2;dims=2)
    D2 = X2 .+ X2' .- 2*X*X'

    nbrTest = round(Int,N*(1-trainingProp))
    @assert 1 < nbrTest < N

    #allErrors = zeros(length(σValues), nbrIter)
    allErrors = zeros(N, length(σValues), nbrIter)

    testMask = falses(N)
    for i=1:nbrIter
        # select a random training subset each time
        testMask[:] .= false
        testMask[sample(1:N,nbrTest,replace=false)] .= true

        D2TestTrain = D2[testMask,.~testMask]
        fitnessTrain = fitness[.~testMask]
        fitnessTest = fitness[testMask]

        # TODO: merge common computations of case A and B

        # Case A, samples part of the Training set (different set of weights depending on which sample we leave out)
        for (j,σ) in enumerate(σValues)
            W = exp.( (minimum(D2TestTrain;dims=2).-D2TestTrain)/(2σ^2) )

            A = W*fitnessTrain .- W.*fitnessTrain' # nbrTest x nbrTrain - unnormalized fitness for test sample i, with training sample j removed
            B = sum(W;dims=2) .- W                 # nbrTest x nbrTrain - sum of weights for test sample i, with training sample j removed

            # for elements with Bᵤᵥ<<1, we are not guaranteed good precision in the computations (because the closest sample has a much larger weight than the others, and it has been removed)
            # update Aᵤᵥ and Bᵤᵥ for those elements
            # for (u,v) in zip(ind2sub(size(B), findall(B.<1e-3))...) # 1e-3 is still a conservative bound
            for (u,v) in Tuple.(CartesianIndices(B)[findall(B.<1e-3)]) # 1e-3 is still a conservative bound
                d2 = D2TestTrain[u,:]
                d2[v] = Inf # remove training sample by setting it to infinitely far away
                w = exp.( (minimum(d2).-d2)/(2σ^2) ) # note that minimum(d2) is different from above, which gives us the precision needed
                A[u,v] = dot(w,fitnessTrain)
                B[u,v] = sum(w)
            end

            #minimum(B)<0.99 && println(minimum(B)) # printout when the sample with the largest weight has been removed, but precision was good enough
            #@assert all(B.>4eps()) # just for testing the code

            F = A./B # nbrTest x nbrTrain - Fᵤᵥ = predicted value of test sample i, with training sample j removed
            residuals = (F .- fitnessTest).^2 # nbrTest x nbrTrain - squared residual for test sample i, with training sample j removed

            #allErrors[.~testMask, j, i] = dropdims(mean(residuals;dims=1); dims=1) # nbrTrain - mean squared error with training sample j removed
            allErrors[.~testMask, j, i] = dropdims(_fitnesserror(residuals;dims=1); dims=1) # nbrTrain - mean squared error with training sample j removed
        end

        # Case B, samples part of the Test set (different set of residuals depending on which sample we leave out)
        for (j,σ) in enumerate(σValues)
            residuals = _fitnessresidualssquared(D2TestTrain, fitnessTrain, fitnessTest, σ)
            totError = sum(residuals)

            # take the mean with one sample removed
            # allErrors[testMask, j, i] = (totError.-residuals)/(nbrTest-1)
            allErrors[testMask, j, i] = _fitnesserrortransform( (totError.-residuals)/(nbrTest-1) )
        end
    end

    meanErrors = dropdims(mean(allErrors; dims=3); dims=3)
    ind = mapslices(argmin, meanErrors; dims=2)[:]
    σValues[ind], getindex.((meanErrors,), 1:length(ind), ind), meanErrors
end

mutable struct NearestNeighborModel{T}
    X::Matrix{T}
    fitness::Vector{Float64}
    function NearestNeighborModel{T}(X::Matrix{T}, fitness::Vector{Float64}) where T
        @assert size(X,1)==length(fitness)
        new(X,fitness)
    end
end
# euclidean distance
NearestNeighborModel(X::AbstractMatrix{T},fitness) where {T<:Number} = NearestNeighborModel{Float64}(convert(Matrix{Float64},X), convert(Vector{Float64},fitness))
# edit distance
NearestNeighborModel(X::AbstractMatrix{Symbol},fitness) = NearestNeighborModel{Symbol}(convert(Matrix{Symbol},X), convert(Vector{Float64},fitness))

# euclidean distance
_predictfitnessnn(D2::Matrix{Float64}, fitness::Vector{Float64}) =
    Float64[ fitness[argmin(D2[i,:])] for i=1:size(D2,1) ] # find nearest neighbour (expect no ties for Float64)
predictfitness(model::NearestNeighborModel{Float64}, X::Matrix{Float64}) = _predictfitnessnn(squareddistances(X,model.X), model.fitness)

# edit distance
function _predictfitnesseditdist(D::Matrix{Int}, fitness::Vector{Float64})
    M,N = size(D)
    minValues = minimum(D;dims=2)
    hits = D.==minValues # mask of all closest samples
    Float64[ mean(fitness[hits[i,:][:]]) for i=1:M ]
end
predictfitness(model::NearestNeighborModel{Symbol}, X::Matrix{Symbol}) = _predictfitnesseditdist(editdistance(X,model.X), model.fitness)

# every row of X should consist of categorical values will be compared to other rows
# X could be for instance be a vector of group IDs, a Matrix with categorical values or a DataFrame.
mutable struct GroupModel{T}
    X::T
    fitness::Vector{Float64}
    function GroupModel{T}(X::T, fitness::Vector{Float64}) where T
        @assert size(X,1)==length(fitness)
        new(X,fitness)
    end
end
GroupModel(X::T,fitness) where {T} = GroupModel{T}(X, convert(Vector{Float64},fitness))

function _predictfitnessgroup(D::AbstractArray{Bool}, fitness::Vector{Float64})
    M,N = size(D)
    f = fill(NaN,M)
    for i=1:M
        any(D[i,:]) || continue # return NaN if there is no matching group
        f[i] = mean( fitness[D[i,:][:]] )
    end
    f
end
predictfitness(model::GroupModel{T}, X::T) where {T} = _predictfitnessgroup(groupequal(X,model.X), model.fitness)

function leaveoneoutpredict(modelType, modelData, fitness, modelArgs...; perSampleModelArgs=())
    typeof(perSampleModelArgs) <: Tuple || (perSampleModelArgs=(perSampleModelArgs,)) # wrap in tuple if a single argument was given

    N = size(modelData,1)
    nd = ndims(modelData)
    colons = repeat([Colon()],nd-1) # for slicing in first dimension only

    predicted = zeros(N)
    for i=1:N
        mask = trues(N)
        mask[i] = false # all except the current one
        model = modelType(modelData[mask,colons...], fitness[mask], modelArgs..., map(x->x[i], perSampleModelArgs)...)
        predicted[i] = predictfitness(model, modelData[i:i,colons...])[1]
    end
    predicted
end

varianceunexplained(predicted,truth) = sum((predicted.-truth).^2) / sum((truth.-mean(truth)).^2)


text:
# Canonical form of multivariate normal

## generic types

immutable GenericMvNormalCanon{Prec<:AbstractPDMat} <: AbstractMvNormal
	dim::Int             # dimension of sample space
	zeromean::Bool       # whether the mean vector is zero
	μ::Vector{Float64}   # the mean vector
    h::Vector{Float64}   # potential vector, i.e. inv(Σ) * μ
    J::Prec              # precision matrix, i.e. inv(Σ)

    function GenericMvNormalCanon(μ::Vector{Float64}, h::Vector{Float64}, J::Prec, zmean::Bool)
    	d = dim(J)
    	length(μ) == length(h) == d || throw(ArgumentError("Inconsistent argument dimensions."))
    	new(d, zmean, μ, h, J)
    end

	function GenericMvNormalCanon(J::Prec)
		d = dim(J)
    	new(d, true, zeros(d), zeros(d), J)
    end
end

function GenericMvNormalCanon{P<:AbstractPDMat}(μ::Vector{Float64}, h::Vector{Float64}, J::P)
	GenericMvNormalCanon{P}(μ, h, J, allzeros(μ))
end

function GenericMvNormalCanon{P<:AbstractPDMat}(h::Vector{Float64}, J::P, zmean::Bool)
	μ = zmean ? zeros(length(h)) : (J \ h)
	GenericMvNormalCanon{P}(μ, h, J, zmean)
end

function GenericMvNormalCanon{P<:AbstractPDMat}(h::Vector{Float64}, J::P)
	GenericMvNormalCanon(h, J, allzeros(h))
end

function GenericMvNormalCanon{P<:AbstractPDMat}(J::P)
    d = dim(J)
    GenericMvNormalCanon{P}(zeros(d), zeros(d), J, true)
end

## type aliases and convenient constructors

typealias MvNormalCanon   GenericMvNormalCanon{PDMat}
typealias DiagNormalCanon GenericMvNormalCanon{PDiagMat}
typealias IsoNormalCanon  GenericMvNormalCanon{ScalMat}

MvNormalCanon(J::PDMat) = GenericMvNormalCanon(J)
MvNormalCanon(J::Matrix{Float64}) = GenericMvNormalCanon(PDMat(J))
MvNormalCanon(h::Vector{Float64}, J::PDMat) = GenericMvNormalCanon(h, J)
MvNormalCanon(h::Vector{Float64}, J::Matrix{Float64}) = GenericMvNormalCanon(h, PDMat(J))

DiagNormalCanon(J::PDiagMat) = GenericMvNormalCanon(J)
DiagNormalCanon(J::Vector{Float64}) = GenericMvNormalCanon(PDiagMat(J))
DiagNormalCanon(h::Vector{Float64}, J::PDiagMat) = GenericMvNormalCanon(h, J)
DiagNormalCanon(h::Vector{Float64}, J::Vector{Float64}) = GenericMvNormalCanon(h, PDiagMat(J))

IsoNormalCanon(J::ScalMat) = GenericMvNormalCanon(J)
IsoNormalCanon(d::Integer, prec::Real) = GenericMvNormalCanon(ScalMat(int(d), float64(prec)))
IsoNormalCanon(h::Vector{Float64}, J::ScalMat) = GenericMvNormalCanon(h, J)
IsoNormalCanon(h::Vector{Float64}, prec::Real) = GenericMvNormalCanon(h, ScalMat(length(h), float64(prec)))

# conversion between conventional form and canonical form

function Base.convert{C<:AbstractPDMat}(D::Type{GenericMvNormal{C}}, cf::GenericMvNormalCanon{C})
	GenericMvNormal{C}(cf.dim, cf.zeromean, cf.μ, inv(cf.J))
end

function Base.convert{C<:AbstractPDMat}(D::Type{GenericMvNormalCanon{C}}, d::GenericMvNormal{C})
	J::C = inv(d.Σ)
	h::Vector{Float64} = J * d.μ
	GenericMvNormalCanon{C}(d.μ, h, J, d.zeromean)
end

canonform{C<:AbstractPDMat}(d::GenericMvNormal{C}) = convert(GenericMvNormalCanon{C}, d)

# Basic statistics

dim(d::GenericMvNormalCanon) = d.dim

mean(d::GenericMvNormalCanon) = d.μ
mode(d::GenericMvNormalCanon) = d.μ
modes(d::GenericMvNormalCanon) = [mode(d)]

var(d::GenericMvNormalCanon) = diag(inv(d.J))
cov(d::GenericMvNormalCanon) = full(inv(d.J))
invcov(d::GenericMvNormalCanon) = full(d.J)
logdet_cov(d::GenericMvNormalCanon) = -logdet(d.J)

entropy(d::GenericMvNormalCanon) = 0.5 * (dim(d) * (float64(log2π) + 1.0) - logdet(d.J))

# PDF evaluation

function sqmahal(d::GenericMvNormalCanon, x::Vector{Float64})
    z::Vector{Float64} = d.zeromean ? x : x - d.μ
    quad(d.J, z)
end

function sqmahal!(r::Array{Float64}, d::GenericMvNormalCanon, x::Matrix{Float64})
    if !(size(x, 1) == dim(d) && size(x, 2) == length(r))
        throw(ArgumentError("Inconsistent argument dimensions."))
    end
    z::Matrix{Float64} = d.zeromean ? x : bsubtract(x, d.μ, 1)
    quad!(r, d.J, z)
end

# Sampling (for GenericMvNormal)

function rand!(d::GenericMvNormalCanon, x::Vector{Float64})
    unwhiten_winv!(d.J, randn!(x))
    if !d.zeromean
        add!(x, d.μ)
    end
    x
end

function rand!(d::GenericMvNormalCanon, x::Matrix{Float64})
    unwhiten_winv!(d.J, randn!(x))
    if !d.zeromean
        badd!(x, d.μ, 1)
    end
    x
end


text:
function num_var(model::Hopper)
    nq = model.nq
    nq + 4 + 4 + 2 + 2 + 2 + 2
end

function indices_z(model::Hopper)
    nq = model.nq
    q = collect(1:nq)
    γ = collect(nq .+ (1:4))
    sγ = collect(nq + 4 .+ (1:4))
    ψ = collect(nq + 4 + 4 .+ (1:2))
    b = collect(nq + 4 + 4 + 2 .+ (1:2))
    sψ = collect(nq + 4 + 4 + 2 + 2 .+ (1:2))
    sb = collect(nq + 4 + 4 + 2 + 2 + 2 .+ (1:2))
    IndicesZ(q, γ, sγ, ψ, b, sψ, sb)
end

function indices_optimization(model::Hopper)
    nq = model.nq
    nz = num_var(model)

    IndicesOptimization(
        nz,
        nz,
        [collect(nq .+ (1:4)), collect(nq + 4 .+ (1:4))],
        [collect(nq .+ (1:4)), collect(nq + 4 .+ (1:4))],
        [[collect([nq + 4 + 4 + 1, nq + 4 + 4 + 2 + 1]), collect([nq + 4 + 4 + 2 + 2 + 1, nq + 4 + 4 + 2 + 2 + 2 + 1])],
         [collect([nq + 4 + 4 + 2, nq + 4 + 4 + 2 + 2]), collect([nq + 4 + 4 + 2 + 2 + 2, nq + 4 + 4 + 2 + 2 + 2 + 2])]],
        [[collect([nq + 4 + 4 + 1, nq + 4 + 4 + 2 + 1]), collect([nq + 4 + 4 + 2 + 2 + 1, nq + 4 + 4 + 2 + 2 + 2 + 1])],
         [collect([nq + 4 + 4 + 2, nq + 4 + 4 + 2 + 2]), collect([nq + 4 + 4 + 2 + 2 + 2, nq + 4 + 4 + 2 + 2 + 2 + 2])]],
        collect(1:(nq + 4 + 1 + 1 + 1 + 1)),
        collect(nq + 4 + 1 + 1 + 1 + 1 .+ (1:4)),
        collect(nq + 4 + 1 + 1 + 1 + 1 + 4 .+ (1:4)),
        [collect(nq + 4 + 1 + 1 + 1 + 1 + 4 + (i - 1) * 2 .+ (1:2)) for i = 1:2],
        collect(nq + 4 + 1 + 1 + 1 + 1 .+ (1:8)))
end

Trajectory(model::Hopper, T) = Trajectory(model, T, nc=4, nb=2)
GradientTrajectory(model::Hopper, T) = GradientTrajectory(model, T, nc=4, nb=2)

function residual(model::Hopper, mass_matrix, dynamics_bias, kinematics, kinematics_jacobians, z, θ, μ)
    # dimensions
    nq = model.nq
    nu = model.nu
    nw = model.nw

    # unpack data
    q0 = θ[1:nq]
    q1 = θ[nq .+ (1:nq)]
    u1 = θ[2nq .+ (1:nu)]
    w1 = θ[2nq + nu .+ (1:nw)]
    friction_body_world = θ[2nq + nu + nw .+ (1:1)]
    friction_foot_world = θ[2nq + nu + nw + 1 .+ (1:1)]
    h = θ[2nq + nu + nw + 2 .+ (1:1)]

    # unpack variables
    q2 = z[1:nq]
    γ1 = z[nq .+ (1:4)]
    sγ1 = z[nq + 4 .+ (1:4)]
    ψ1 = z[nq + 4 + 4 .+ (1:2)]
    b1 = z[nq + 4 + 4 + 2 .+ (1:2)]
    sψ1 = z[nq + 4 + 4 + 2 + 2 .+ (1:2)]
    sb1 = z[nq + 4 + 4 + 2 + 2 + 2 .+ (1:2)]

    rotation_body = vector_rotation_matrix([0.0; 1.0])
    rotation_foot = vector_rotation_matrix([0.0; 1.0])

    # tangential velocity
    v1 = (q2 - q1) / h[1]
    vT_body = (rotation_body * v1[1:2])[1] + model.body_radius * v1[3]
    vT_foot = (rotation_foot * kinematics_foot_jacobian(model, q2) * v1)[1]

    # contact forces
    J = contact_jacobian(model, q1)
    λ1 = transpose(J) * [transpose(rotation_body) * [b1[1]; γ1[1]];
                         transpose(rotation_foot) * [b1[2]; γ1[2]];
                        γ1[3:4]]
    λ1[3] += model.body_radius * b1[1] # friction on body creates a moment

    [
    dynamics(model, mass_matrix, dynamics_bias, h, q0, q1, u1, w1, λ1, q2);
    signed_distance(model, q2) - sγ1;
    vT_body - sb1[1];
    vT_foot - sb1[2];
    friction_body_world[1] * γ1[1] - ψ1[1];
    friction_foot_world[1] * γ1[2] - ψ1[2];
    γ1 .* sγ1 .- μ[1];
    cone_product([ψ1[1]; b1[1]], [sψ1[1]; sb1[1]]) - [μ[1]; 0.0];
    cone_product([ψ1[2]; b1[2]], [sψ1[2]; sb1[2]]) - [μ[1]; 0.0];
    ]
end


text:
include("spherical_helper.jl")

"""
    power_spectrum_1d(::AtmosGCMConfigType, var_grid, z, lat, lon, weight)

Calculates the 1D (zonal) power spectra using the fourier transform at each latitude and level from a 3D velocity field.
The snapshots of these spectra should be averaged to obtain a time-average.
The input velocities must be interpolated to a Gaussian grid.

# References
- [Wiin1967](@cite)
- [Koshyk2001](@cite)

# Arguments
- var_grid: variable (typically u or v) on a Gausian (lon, lat, z) grid to be transformed
- z: vertical coordinate (height or pressure)
- lat: latitude
- lon: longitude
- mass_weight: for mass-weighted calculations
"""
function power_spectrum_1d(::AtmosGCMConfigType, var_grid, z, lat, lon, weight)
    num_lev = length(z)
    num_lat = length(lat)
    num_lon = length(lon)
    num_fourier = Int64(num_lon)

    # get number of positive Fourier coefficients incl. 0
    if mod(num_lon, 2) == 0 # even
        num_pfourier = div(num_lon, 2)
    else # odd
        num_pfourier = div(num_lon, 2) + 1
    end

    zon_spectrum = zeros(Float64, num_pfourier, num_lat, num_lev)
    freqs = zeros(Float64, num_pfourier, num_lat, num_lev)

    for k in 1:num_lev
        for j in 1:num_lat
            # compute fft frequencies for each latitude
            x = lon ./ 180.0 .* π
            dx = (lon[2] - lon[1]) ./ 180.0 .* π

            freqs_ = fftfreq(num_fourier, 1.0 / dx) # 0,+ve freq,-ve freqs (lowest to highest)
            freqs[:, j, k] = freqs_[1:num_pfourier] .* 2.0 .* π

            # compute the fourier coefficients for all latitudes
            fourier = fft(var_grid[:, j, k]) # e.g. vcos_grid, ucos_grid
            fourier = (fourier / num_fourier)

            # convert to energy spectra
            zon_spectrum[1, j, k] =
                zon_spectrum[1, j, k] +
                weight[k] * fourier[1] .* conj(fourier[1])

            for m in 2:num_pfourier
                zon_spectrum[m, j, k] =
                    zon_spectrum[m, j, k] +
                    2.0 * weight[k] * fourier[m] * conj(fourier[m]) # factor 2 for neg freq contribution
            end
        end
    end
    return zon_spectrum, freqs
end

"""
    power_spectrum_2d(::AtmosGCMConfigType, var_grid, mass_weight)

- transform variable on grid to the 2d spectral space using fft on latitude circles
(as for the 1D spectrum) and Legendre polynomials for meridians, and calculate spectra

# Arguments
- var_grid: variable (typically u or v) on a Gausian (lon, lat, z) grid to be transformed
- mass_weight: weight for mass-weighted calculations

# References
- [Baer1972](@cite)
"""
function power_spectrum_2d(::AtmosGCMConfigType, var_grid, mass_weight)
    #  initialize spherical mesh variables
    nθ, nd = (size(var_grid, 2), size(var_grid, 3))
    mesh = SpectralSphericalMesh(nθ, nd)
    var_spectrum = mesh.var_spectrum
    var_spherical = mesh.var_spherical

    sinθ, wts = compute_gaussian!(mesh.nθ) # latitude weights using Gaussian quadrature, to orthogonalize Legendre polynomials upon summation
    mesh.qnm =
        compute_legendre!(mesh.num_fourier, mesh.num_spherical, sinθ, mesh.nθ) #  normalized associated Legendre polynomials

    for k in 1:(mesh.nd)
        # apply Gaussian quadrature weights
        for i in 1:(mesh.nθ)
            mesh.qwg[:, :, i] .= mesh.qnm[:, :, i] * wts[i] * mass_weight[k]
        end

        # Transform variable using spherical harmonics
        var_spherical[:, :, k, :] =
            trans_grid_to_spherical!(mesh, var_grid[:, :, k]) # var_spherical[m,n,k,sinθ]

        # Calculate energy spectra
        var_spectrum[:, :, k] =
            2.0 .* sum(var_spherical[:, :, k, :], dims = 3) .*
            conj(sum(var_spherical[:, :, k, :], dims = 3))  # var_spectrum[m,n,k] # factor 2 to account for negative Fourier frequencies
        var_spectrum[1, :, k] = var_spectrum[1, :, k] ./ 2.0 # m=0
    end
    return var_spectrum, mesh.wave_numbers, var_spherical, mesh
end

# TODO: enable mass weighted and vertically integrated calculations

max_forks_repo_name: jm-c/CLIMA


text:
module PointProcessWavelet

using StaticArrays: SVector
using Statistics: mean, std
using FFTW: fft, ifft, fftshift, ifftshift, fftfreq
using LinearAlgebra: I, norm
using Distributions: MvNormal, pdf

include("wavelets.jl")
include("wavelet_params.jl")
include("wavelet_functions.jl")

export WaveletParams
export v_λ_k_all, K_all, W_all

end